| 1 |
/* |
|---|
| 2 |
* Copyright (c) 2007, OmniTI Computer Consulting, Inc. |
|---|
| 3 |
* All rights reserved. |
|---|
| 4 |
*/ |
|---|
| 5 |
|
|---|
| 6 |
#define DEFAULT_JLOG_SUBSCRIBER "stratcon" |
|---|
| 7 |
|
|---|
| 8 |
#include "noit_defines.h" |
|---|
| 9 |
#include <stdio.h> |
|---|
| 10 |
#include <fcntl.h> |
|---|
| 11 |
#include <unistd.h> |
|---|
| 12 |
#include <sys/time.h> |
|---|
| 13 |
|
|---|
| 14 |
#include "utils/noit_log.h" |
|---|
| 15 |
#include "utils/noit_hash.h" |
|---|
| 16 |
#include "jlog/jlog.h" |
|---|
| 17 |
|
|---|
| 18 |
static noit_hash_table noit_loggers = NOIT_HASH_EMPTY; |
|---|
| 19 |
static noit_hash_table noit_logops = NOIT_HASH_EMPTY; |
|---|
| 20 |
noit_log_stream_t noit_stderr = NULL; |
|---|
| 21 |
noit_log_stream_t noit_error = NULL; |
|---|
| 22 |
noit_log_stream_t noit_debug = NULL; |
|---|
| 23 |
|
|---|
| 24 |
static int |
|---|
| 25 |
posix_logio_open(noit_log_stream_t ls) { |
|---|
| 26 |
int fd; |
|---|
| 27 |
fd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND); |
|---|
| 28 |
if(fd < 0) { |
|---|
| 29 |
ls->op_ctx = NULL; |
|---|
| 30 |
return -1; |
|---|
| 31 |
} |
|---|
| 32 |
ls->op_ctx = (void *)fd; |
|---|
| 33 |
return 0; |
|---|
| 34 |
} |
|---|
| 35 |
static int |
|---|
| 36 |
posix_logio_reopen(noit_log_stream_t ls) { |
|---|
| 37 |
if(ls->path) { |
|---|
| 38 |
int newfd, oldfd; |
|---|
| 39 |
oldfd = (int)ls->op_ctx; |
|---|
| 40 |
newfd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND); |
|---|
| 41 |
if(newfd >= 0) { |
|---|
| 42 |
ls->op_ctx = (void *)newfd; |
|---|
| 43 |
if(oldfd >= 0) close(oldfd); |
|---|
| 44 |
return 0; |
|---|
| 45 |
} |
|---|
| 46 |
} |
|---|
| 47 |
return -1; |
|---|
| 48 |
} |
|---|
| 49 |
static int |
|---|
| 50 |
posix_logio_write(noit_log_stream_t ls, const void *buf, size_t len) { |
|---|
| 51 |
int fd; |
|---|
| 52 |
fd = (int)ls->op_ctx; |
|---|
| 53 |
if(fd < 0) return -1; |
|---|
| 54 |
return write(fd, buf, len); |
|---|
| 55 |
} |
|---|
| 56 |
static int |
|---|
| 57 |
posix_logio_close(noit_log_stream_t ls) { |
|---|
| 58 |
int fd; |
|---|
| 59 |
fd = (int)ls->op_ctx; |
|---|
| 60 |
return close(fd); |
|---|
| 61 |
} |
|---|
| 62 |
static logops_t posix_logio_ops = { |
|---|
| 63 |
posix_logio_open, |
|---|
| 64 |
posix_logio_reopen, |
|---|
| 65 |
posix_logio_write, |
|---|
| 66 |
posix_logio_close, |
|---|
| 67 |
}; |
|---|
| 68 |
|
|---|
| 69 |
static int |
|---|
| 70 |
jlog_logio_open(noit_log_stream_t ls) { |
|---|
| 71 |
char path[PATH_MAX], *sub; |
|---|
| 72 |
jlog_ctx *log = NULL; |
|---|
| 73 |
if(!ls->path) return -1; |
|---|
| 74 |
strlcpy(path, ls->path, sizeof(path)); |
|---|
| 75 |
sub = strstr(path, "=>"); |
|---|
| 76 |
if(sub) { |
|---|
| 77 |
*sub = '\0'; |
|---|
| 78 |
sub += 2; |
|---|
| 79 |
} |
|---|
| 80 |
log = jlog_new(ls->path); |
|---|
| 81 |
if(!log) return -1; |
|---|
| 82 |
/* Open the writer. */ |
|---|
| 83 |
if(jlog_ctx_open_writer(log)) { |
|---|
| 84 |
/* If that fails, we'll give one attempt at initiailizing it. */ |
|---|
| 85 |
/* But, since we attempted to open it as a writer, it is tainted. */ |
|---|
| 86 |
/* path: close, new, init, close, new, writer, add subscriber */ |
|---|
| 87 |
jlog_ctx_close(log); |
|---|
| 88 |
log = jlog_new(ls->path); |
|---|
| 89 |
if(jlog_ctx_init(log)) { |
|---|
| 90 |
noitL(noit_error, "Cannot init jlog writer: %s\n", |
|---|
| 91 |
jlog_ctx_err_string(log)); |
|---|
| 92 |
jlog_ctx_close(log); |
|---|
| 93 |
return -1; |
|---|
| 94 |
} |
|---|
| 95 |
/* After it is initialized, we can try to reopen it as a writer. */ |
|---|
| 96 |
jlog_ctx_close(log); |
|---|
| 97 |
log = jlog_new(ls->path); |
|---|
| 98 |
if(jlog_ctx_open_writer(log)) { |
|---|
| 99 |
noitL(noit_error, "Cannot open jlog writer: %s\n", |
|---|
| 100 |
jlog_ctx_err_string(log)); |
|---|
| 101 |
jlog_ctx_close(log); |
|---|
| 102 |
return -1; |
|---|
| 103 |
} |
|---|
| 104 |
/* The first time we open after an init, we should add the subscriber. */ |
|---|
| 105 |
if(sub) |
|---|
| 106 |
jlog_ctx_add_subscriber(log, sub, JLOG_BEGIN); |
|---|
| 107 |
else |
|---|
| 108 |
jlog_ctx_add_subscriber(log, DEFAULT_JLOG_SUBSCRIBER, JLOG_BEGIN); |
|---|
| 109 |
} |
|---|
| 110 |
ls->op_ctx = log; |
|---|
| 111 |
return 0; |
|---|
| 112 |
} |
|---|
| 113 |
static int |
|---|
| 114 |
jlog_logio_reopen(noit_log_stream_t ls) { |
|---|
| 115 |
return 0; |
|---|
| 116 |
} |
|---|
| 117 |
static int |
|---|
| 118 |
jlog_logio_write(noit_log_stream_t ls, const void *buf, size_t len) { |
|---|
| 119 |
if(!ls->op_ctx) return -1; |
|---|
| 120 |
if(jlog_ctx_write((jlog_ctx *)ls->op_ctx, buf, len) != 0) |
|---|
| 121 |
return -1; |
|---|
| 122 |
return len; |
|---|
| 123 |
} |
|---|
| 124 |
static int |
|---|
| 125 |
jlog_logio_close(noit_log_stream_t ls) { |
|---|
| 126 |
if(ls->op_ctx) { |
|---|
| 127 |
jlog_ctx_close((jlog_ctx *)ls->op_ctx); |
|---|
| 128 |
ls->op_ctx = NULL; |
|---|
| 129 |
} |
|---|
| 130 |
return 0; |
|---|
| 131 |
} |
|---|
| 132 |
static logops_t jlog_logio_ops = { |
|---|
| 133 |
jlog_logio_open, |
|---|
| 134 |
jlog_logio_reopen, |
|---|
| 135 |
jlog_logio_write, |
|---|
| 136 |
jlog_logio_close, |
|---|
| 137 |
}; |
|---|
| 138 |
|
|---|
| 139 |
void |
|---|
| 140 |
noit_log_init() { |
|---|
| 141 |
noit_hash_init(&noit_loggers); |
|---|
| 142 |
noit_hash_init(&noit_logops); |
|---|
| 143 |
noit_register_logops("file", &posix_logio_ops); |
|---|
| 144 |
noit_register_logops("jlog", &jlog_logio_ops); |
|---|
| 145 |
noit_stderr = noit_log_stream_new_on_fd("stderr", 2, NULL); |
|---|
| 146 |
noit_error = noit_log_stream_new("error", NULL, NULL, NULL); |
|---|
| 147 |
noit_debug = noit_log_stream_new("debug", NULL, NULL, NULL); |
|---|
| 148 |
} |
|---|
| 149 |
|
|---|
| 150 |
void |
|---|
| 151 |
noit_register_logops(const char *name, logops_t *ops) { |
|---|
| 152 |
noit_hash_store(&noit_logops, strdup(name), strlen(name), ops); |
|---|
| 153 |
} |
|---|
| 154 |
|
|---|
| 155 |
noit_log_stream_t |
|---|
| 156 |
noit_log_stream_new_on_fd(const char *name, int fd, noit_hash_table *config) { |
|---|
| 157 |
noit_log_stream_t ls; |
|---|
| 158 |
ls = calloc(1, sizeof(*ls)); |
|---|
| 159 |
ls->name = strdup(name); |
|---|
| 160 |
ls->ops = &posix_logio_ops; |
|---|
| 161 |
ls->op_ctx = (void *)fd; |
|---|
| 162 |
ls->enabled = 1; |
|---|
| 163 |
ls->config = config; |
|---|
| 164 |
/* This double strdup of ls->name is needed, look for the next one |
|---|
| 165 |
* for an explanation. |
|---|
| 166 |
*/ |
|---|
| 167 |
if(noit_hash_store(&noit_loggers, |
|---|
| 168 |
strdup(ls->name), strlen(ls->name), ls) == 0) { |
|---|
| 169 |
free(ls->name); |
|---|
| 170 |
free(ls); |
|---|
| 171 |
return NULL; |
|---|
| 172 |
} |
|---|
| 173 |
return ls; |
|---|
| 174 |
} |
|---|
| 175 |
|
|---|
| 176 |
noit_log_stream_t |
|---|
| 177 |
noit_log_stream_new_on_file(const char *path, noit_hash_table *config) { |
|---|
| 178 |
return noit_log_stream_new(path, "file", path, config); |
|---|
| 179 |
} |
|---|
| 180 |
|
|---|
| 181 |
noit_log_stream_t |
|---|
| 182 |
noit_log_stream_new(const char *name, const char *type, const char *path, |
|---|
| 183 |
noit_hash_table *config) { |
|---|
| 184 |
noit_log_stream_t ls, saved; |
|---|
| 185 |
struct _noit_log_stream tmpbuf; |
|---|
| 186 |
ls = calloc(1, sizeof(*ls)); |
|---|
| 187 |
ls->name = strdup(name); |
|---|
| 188 |
ls->path = path ? strdup(path) : NULL; |
|---|
| 189 |
ls->type = type ? strdup(type) : NULL; |
|---|
| 190 |
ls->enabled = 1; |
|---|
| 191 |
ls->config = config; |
|---|
| 192 |
if(!type) |
|---|
| 193 |
ls->ops = NULL; |
|---|
| 194 |
else if(!noit_hash_retrieve(&noit_logops, type, strlen(type), |
|---|
| 195 |
(void **)&ls->ops)) |
|---|
| 196 |
goto freebail; |
|---|
| 197 |
|
|---|
| 198 |
if(ls->ops && ls->ops->openop(ls)) goto freebail; |
|---|
| 199 |
|
|---|
| 200 |
saved = noit_log_stream_find(name); |
|---|
| 201 |
if(saved) { |
|---|
| 202 |
memcpy(&tmpbuf, saved, sizeof(*saved)); |
|---|
| 203 |
memcpy(saved, ls, sizeof(*saved)); |
|---|
| 204 |
memcpy(ls, &tmpbuf, sizeof(*saved)); |
|---|
| 205 |
noit_log_stream_free(ls); |
|---|
| 206 |
ls = saved; |
|---|
| 207 |
} |
|---|
| 208 |
else |
|---|
| 209 |
/* We strdup the name *again*. We'going to kansas city shuffle the |
|---|
| 210 |
* ls later (see memcpy above). However, if don't strdup, then the |
|---|
| 211 |
* noit_log_stream_free up there will sweep our key right our from |
|---|
| 212 |
* under us. |
|---|
| 213 |
*/ |
|---|
| 214 |
if(noit_hash_store(&noit_loggers, |
|---|
| 215 |
strdup(ls->name), strlen(ls->name), ls) == 0) |
|---|
| 216 |
goto freebail; |
|---|
| 217 |
|
|---|
| 218 |
return ls; |
|---|
| 219 |
|
|---|
| 220 |
freebail: |
|---|
| 221 |
fprintf(stderr, "Failed to instantiate logger(%s,%s,%s)\n", |
|---|
| 222 |
name, type ? type : "[null]", path ? path : "[null]"); |
|---|
| 223 |
free(ls->name); |
|---|
| 224 |
if(ls->path) free(ls->path); |
|---|
| 225 |
if(ls->type) free(ls->type); |
|---|
| 226 |
free(ls); |
|---|
| 227 |
return NULL; |
|---|
| 228 |
} |
|---|
| 229 |
|
|---|
| 230 |
noit_log_stream_t |
|---|
| 231 |
noit_log_stream_find(const char *name) { |
|---|
| 232 |
noit_log_stream_t ls; |
|---|
| 233 |
if(noit_hash_retrieve(&noit_loggers, name, strlen(name), (void **)&ls)) { |
|---|
| 234 |
return ls; |
|---|
| 235 |
} |
|---|
| 236 |
return NULL; |
|---|
| 237 |
} |
|---|
| 238 |
|
|---|
| 239 |
void |
|---|
| 240 |
noit_log_stream_remove(const char *name) { |
|---|
| 241 |
noit_hash_delete(&noit_loggers, name, strlen(name), NULL, NULL); |
|---|
| 242 |
} |
|---|
| 243 |
|
|---|
| 244 |
void |
|---|
| 245 |
noit_log_stream_add_stream(noit_log_stream_t ls, noit_log_stream_t outlet) { |
|---|
| 246 |
struct _noit_log_stream_outlet_list *newnode; |
|---|
| 247 |
newnode = calloc(1, sizeof(*newnode)); |
|---|
| 248 |
newnode->outlet = outlet; |
|---|
| 249 |
newnode->next = ls->outlets; |
|---|
| 250 |
ls->outlets = newnode; |
|---|
| 251 |
} |
|---|
| 252 |
|
|---|
| 253 |
noit_log_stream_t |
|---|
| 254 |
noit_log_stream_remove_stream(noit_log_stream_t ls, const char *name) { |
|---|
| 255 |
noit_log_stream_t outlet; |
|---|
| 256 |
struct _noit_log_stream_outlet_list *node, *tmp; |
|---|
| 257 |
if(!ls->outlets) return NULL; |
|---|
| 258 |
if(!strcmp(ls->outlets->outlet->name, name)) { |
|---|
| 259 |
node = ls->outlets; |
|---|
| 260 |
ls->outlets = node->next; |
|---|
| 261 |
outlet = node->outlet; |
|---|
| 262 |
free(node); |
|---|
| 263 |
return outlet; |
|---|
| 264 |
} |
|---|
| 265 |
for(node = ls->outlets; node->next; node = node->next) { |
|---|
| 266 |
if(!strcmp(node->next->outlet->name, name)) { |
|---|
| 267 |
/* splice */ |
|---|
| 268 |
tmp = node->next; |
|---|
| 269 |
node->next = tmp->next; |
|---|
| 270 |
/* pluck */ |
|---|
| 271 |
outlet = tmp->outlet; |
|---|
| 272 |
/* shed */ |
|---|
| 273 |
free(tmp); |
|---|
| 274 |
/* return */ |
|---|
| 275 |
return outlet; |
|---|
| 276 |
} |
|---|
| 277 |
} |
|---|
| 278 |
return NULL; |
|---|
| 279 |
} |
|---|
| 280 |
|
|---|
| 281 |
void noit_log_stream_reopen(noit_log_stream_t ls) { |
|---|
| 282 |
struct _noit_log_stream_outlet_list *node; |
|---|
| 283 |
if(ls->ops) ls->ops->reopenop(ls); |
|---|
| 284 |
for(node = ls->outlets; node; node = node->next) { |
|---|
| 285 |
noit_log_stream_reopen(node->outlet); |
|---|
| 286 |
} |
|---|
| 287 |
} |
|---|
| 288 |
|
|---|
| 289 |
void |
|---|
| 290 |
noit_log_stream_close(noit_log_stream_t ls) { |
|---|
| 291 |
if(ls->ops) ls->ops->closeop(ls); |
|---|
| 292 |
} |
|---|
| 293 |
|
|---|
| 294 |
void |
|---|
| 295 |
noit_log_stream_free(noit_log_stream_t ls) { |
|---|
| 296 |
if(ls) { |
|---|
| 297 |
struct _noit_log_stream_outlet_list *node; |
|---|
| 298 |
if(ls->name) free(ls->name); |
|---|
| 299 |
if(ls->path) free(ls->path); |
|---|
| 300 |
while(ls->outlets) { |
|---|
| 301 |
node = ls->outlets->next; |
|---|
| 302 |
free(ls->outlets); |
|---|
| 303 |
ls->outlets = node; |
|---|
| 304 |
} |
|---|
| 305 |
if(ls->config) { |
|---|
| 306 |
noit_hash_destroy(ls->config, free, free); |
|---|
| 307 |
free(ls->config); |
|---|
| 308 |
} |
|---|
| 309 |
free(ls); |
|---|
| 310 |
} |
|---|
| 311 |
} |
|---|
| 312 |
|
|---|
| 313 |
void |
|---|
| 314 |
noit_vlog(noit_log_stream_t ls, struct timeval *now, |
|---|
| 315 |
const char *file, int line, |
|---|
| 316 |
const char *format, va_list arg) { |
|---|
| 317 |
char buffer[4096]; |
|---|
| 318 |
struct _noit_log_stream_outlet_list *node; |
|---|
| 319 |
#ifdef va_copy |
|---|
| 320 |
va_list copy; |
|---|
| 321 |
#endif |
|---|
| 322 |
|
|---|
| 323 |
if(ls->enabled) { |
|---|
| 324 |
int len; |
|---|
| 325 |
if(ls->ops) { |
|---|
| 326 |
#ifdef va_copy |
|---|
| 327 |
va_copy(copy, arg); |
|---|
| 328 |
len = vsnprintf(buffer, sizeof(buffer), format, copy); |
|---|
| 329 |
va_end(copy); |
|---|
| 330 |
#else |
|---|
| 331 |
len = vsnprintf(buffer, sizeof(buffer), format, arg); |
|---|
| 332 |
#endif |
|---|
| 333 |
ls->ops->writeop(ls, buffer, len); /* Not much one can do about errors */ |
|---|
| 334 |
} |
|---|
| 335 |
|
|---|
| 336 |
for(node = ls->outlets; node; node = node->next) { |
|---|
| 337 |
#ifdef va_copy |
|---|
| 338 |
va_copy(copy, arg); |
|---|
| 339 |
noit_vlog(node->outlet, now, file, line, format, copy); |
|---|
| 340 |
va_end(copy); |
|---|
| 341 |
#else |
|---|
| 342 |
noit_vlog(node->outlet, now, file, line, format, arg); |
|---|
| 343 |
#endif |
|---|
| 344 |
} |
|---|
| 345 |
} |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
void |
|---|
| 349 |
noit_log(noit_log_stream_t ls, struct timeval *now, |
|---|
| 350 |
const char *file, int line, const char *format, ...) { |
|---|
| 351 |
va_list arg; |
|---|
| 352 |
va_start(arg, format); |
|---|
| 353 |
noit_vlog(ls, now, file, line, format, arg); |
|---|
| 354 |
va_end(arg); |
|---|
| 355 |
} |
|---|
| 356 |
|
|---|