root/src/utils/noit_log.c

Revision eefe963e40f81b16fa16eb492f1ccba8abfb572d, 16.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

make this work on Solaris (and possibly other places)

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2005-2009, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *    * Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    * Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *    * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *      of its contributors may be used to endorse or promote products
17  *      derived from this software without specific prior written
18  *      permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #define DEFAULT_JLOG_SUBSCRIBER "stratcon"
34
35 #include "noit_defines.h"
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <sys/time.h>
40 #include <assert.h>
41 #if HAVE_ERRNO_H
42 #include <errno.h>
43 #endif
44 #if HAVE_DIRENT_H
45 #include <dirent.h>
46 #endif
47
48 #define noit_log_impl
49 #include "utils/noit_log.h"
50 #include "utils/noit_hash.h"
51 #include "jlog/jlog.h"
52 #include "jlog/jlog_private.h"
53
54 struct _noit_log_stream {
55   unsigned enabled:1;
56   unsigned debug:1;
57   /* Above is exposed... */
58   char *type;
59   char *name;
60   int mode;
61   char *path;
62   logops_t *ops;
63   void *op_ctx;
64   noit_hash_table *config;
65   struct _noit_log_stream_outlet_list *outlets;
66   pthread_rwlock_t *lock;
67 };
68
69 static noit_hash_table noit_loggers = NOIT_HASH_EMPTY;
70 static noit_hash_table noit_logops = NOIT_HASH_EMPTY;
71 noit_log_stream_t noit_stderr = NULL;
72 noit_log_stream_t noit_error = NULL;
73 noit_log_stream_t noit_debug = NULL;
74
75 static int
76 posix_logio_open(noit_log_stream_t ls) {
77   int fd;
78   ls->mode = 0664;
79   fd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND, ls->mode);
80   if(fd < 0) {
81     ls->op_ctx = NULL;
82     return -1;
83   }
84   ls->op_ctx = (void *)fd;
85   return 0;
86 }
87 static int
88 posix_logio_reopen(noit_log_stream_t ls) {
89   if(ls->path) {
90     int newfd, oldfd;
91     oldfd = (int)ls->op_ctx;
92     newfd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND, ls->mode);
93     if(newfd >= 0) {
94       ls->op_ctx = (void *)newfd;
95       if(oldfd >= 0) close(oldfd);
96       return 0;
97     }
98   }
99   return -1;
100 }
101 static int
102 posix_logio_write(noit_log_stream_t ls, const void *buf, size_t len) {
103   int fd;
104   fd = (int)ls->op_ctx;
105   if(fd < 0) return -1;
106   return write(fd, buf, len);
107 }
108 static int
109 posix_logio_close(noit_log_stream_t ls) {
110   int fd;
111   fd = (int)ls->op_ctx;
112   return close(fd);
113 }
114 static size_t
115 posix_logio_size(noit_log_stream_t ls) {
116   int fd;
117   struct stat sb;
118   fd = (int)ls->op_ctx;
119   if(fstat(fd, &sb) == 0) {
120     return (size_t)sb.st_size;
121   }
122   return -1;
123 }
124 static logops_t posix_logio_ops = {
125   posix_logio_open,
126   posix_logio_reopen,
127   posix_logio_write,
128   posix_logio_close,
129   posix_logio_size
130 };
131
132 static int
133 jlog_lspath_to_fspath(noit_log_stream_t ls, char *buff, int len,
134                       char **subout) {
135   char *sub;
136   if(subout) *subout = NULL;
137   if(!ls->path) return -1;
138   strlcpy(buff, ls->path, len);
139   sub = strchr(buff, '(');
140   if(sub) {
141     char *esub = strchr(sub, ')');
142     if(esub) {
143       *esub = '\0';
144       *sub = '\0';
145       sub += 1;
146       if(subout) *subout = sub;
147     }
148   }
149   return strlen(buff);
150 }
151
152 /* These next functions arr basically cribbed from jlogctl.c */
153 static int
154 is_datafile(const char *f, u_int32_t *logid) {
155   int i;
156   u_int32_t l = 0;
157   for(i=0; i<8; i++) {
158     if((f[i] >= '0' && f[i] <= '9') ||
159        (f[i] >= 'a' && f[i] <= 'f')) {
160       l <<= 4;
161       l |= (f[i] < 'a') ? (f[i] - '0') : (f[i] - 'a' + 10);
162     }
163     else
164       return 0;
165   }
166   if(f[i] != '\0') return 0;
167   if(logid) *logid = l;
168   return 1;
169 }
170
171 static int
172 jlog_logio_cleanse(noit_log_stream_t ls) {
173   jlog_ctx *log;
174   DIR *d;
175   struct dirent *de, *entry;
176   int cnt = 0;
177   char path[PATH_MAX];
178   int size = 0;
179
180   log = (jlog_ctx *)ls->op_ctx;
181   if(!log) return -1;
182   if(jlog_lspath_to_fspath(ls, path, sizeof(path), NULL) <= 0) return -1;
183   d = opendir(path);
184
185 #ifdef _PC_NAME_MAX
186   size = pathconf(path, _PC_NAME_MAX);
187 #endif
188   size = MIN(size, PATH_MAX + 128);
189   de = alloca(size);
190
191   if(!d) return -1;
192   while(portable_readdir_r(d, de, &entry) == 0 && entry != NULL) {
193     u_int32_t logid;
194     if(is_datafile(entry->d_name, &logid)) {
195       int rv;
196       struct stat st;
197       char fullfile[PATH_MAX];
198       char fullidx[PATH_MAX];
199
200       snprintf(fullfile, sizeof(fullfile), "%s/%s", path, entry->d_name);
201       snprintf(fullidx, sizeof(fullidx), "%s/%s" INDEX_EXT,
202                path, entry->d_name);
203       while((rv = stat(fullfile, &st)) != 0 && errno == EINTR);
204       if(rv == 0) {
205         int readers;
206         readers = __jlog_pending_readers(log, logid);
207         if(readers == 0) {
208           unlink(fullfile);
209           unlink(fullidx);
210         }
211       }
212     }
213   }
214   closedir(d);
215   return cnt;
216 }
217
218 static int
219 jlog_logio_reopen(noit_log_stream_t ls) {
220   char **subs;
221   int i;
222   /* reopening only has the effect of removing temporary subscriptions */
223   /* (they start with ~ in our hair-brained model */
224
225   if(ls->lock) pthread_rwlock_wrlock(ls->lock);
226   if(jlog_ctx_list_subscribers(ls->op_ctx, &subs) == -1)
227     goto bail;
228
229   for(i=0;subs[i];i++)
230     if(subs[i][0] == '~')
231       jlog_ctx_remove_subscriber(ls->op_ctx, subs[i]);
232
233   jlog_ctx_list_subscribers_dispose(ls->op_ctx, subs);
234   jlog_logio_cleanse(ls);
235  bail:
236   if(ls->lock) pthread_rwlock_unlock(ls->lock);
237   return 0;
238 }
239 static int
240 jlog_logio_open(noit_log_stream_t ls) {
241   char path[PATH_MAX], *sub;
242   jlog_ctx *log = NULL;
243
244   if(jlog_lspath_to_fspath(ls, path, sizeof(path), &sub) <= 0) return -1;
245   log = jlog_new(path);
246   if(!log) return -1;
247   /* Open the writer. */
248   if(jlog_ctx_open_writer(log)) {
249     /* If that fails, we'll give one attempt at initiailizing it. */
250     /* But, since we attempted to open it as a writer, it is tainted. */
251     /* path: close, new, init, close, new, writer, add subscriber */
252     jlog_ctx_close(log);
253     log = jlog_new(path);
254     if(jlog_ctx_init(log)) {
255       noitL(noit_error, "Cannot init jlog writer: %s\n",
256             jlog_ctx_err_string(log));
257       jlog_ctx_close(log);
258       return -1;
259     }
260     /* After it is initialized, we can try to reopen it as a writer. */
261     jlog_ctx_close(log);
262     log = jlog_new(path);
263     if(jlog_ctx_open_writer(log)) {
264       noitL(noit_error, "Cannot open jlog writer: %s\n",
265             jlog_ctx_err_string(log));
266       jlog_ctx_close(log);
267       return -1;
268     }
269     /* The first time we open after an init, we should add the subscriber. */
270     if(sub)
271       jlog_ctx_add_subscriber(log, sub, JLOG_BEGIN);
272     else
273       jlog_ctx_add_subscriber(log, DEFAULT_JLOG_SUBSCRIBER, JLOG_BEGIN);
274   }
275   ls->op_ctx = log;
276   /* We do this to clean things up */
277   jlog_logio_reopen(ls);
278   return 0;
279 }
280 static int
281 jlog_logio_write(noit_log_stream_t ls, const void *buf, size_t len) {
282   int rv = -1;
283   if(!ls->op_ctx) return -1;
284   pthread_rwlock_rdlock(ls->lock);
285   if(jlog_ctx_write((jlog_ctx *)ls->op_ctx, buf, len) == 0)
286     rv = len;
287   pthread_rwlock_unlock(ls->lock);
288   return rv;
289 }
290 static int
291 jlog_logio_close(noit_log_stream_t ls) {
292   if(ls->op_ctx) {
293     jlog_ctx_close((jlog_ctx *)ls->op_ctx);
294     ls->op_ctx = NULL;
295   }
296   return 0;
297 }
298 static size_t
299 jlog_logio_size(noit_log_stream_t ls) {
300   size_t size;
301   if(!ls->op_ctx) return -1;
302   pthread_rwlock_rdlock(ls->lock);
303   size = jlog_raw_size((jlog_ctx *)ls->op_ctx);
304   pthread_rwlock_unlock(ls->lock);
305   return size;
306 }
307 static logops_t jlog_logio_ops = {
308   jlog_logio_open,
309   jlog_logio_reopen,
310   jlog_logio_write,
311   jlog_logio_close,
312   jlog_logio_size
313 };
314
315 void
316 noit_log_init() {
317   noit_hash_init(&noit_loggers);
318   noit_hash_init(&noit_logops);
319   noit_register_logops("file", &posix_logio_ops);
320   noit_register_logops("jlog", &jlog_logio_ops);
321   noit_stderr = noit_log_stream_new_on_fd("stderr", 2, NULL);
322   noit_error = noit_log_stream_new("error", NULL, NULL, NULL, NULL);
323   noit_debug = noit_log_stream_new("debug", NULL, NULL, NULL, NULL);
324 }
325
326 void
327 noit_register_logops(const char *name, logops_t *ops) {
328   noit_hash_store(&noit_logops, strdup(name), strlen(name), ops);
329 }
330
331 void *
332 noit_log_stream_get_ctx(noit_log_stream_t ls) {
333   return ls->op_ctx;
334 }
335
336 void
337 noit_log_stream_set_ctx(noit_log_stream_t ls, void *nctx) {
338   ls->op_ctx = nctx;
339 }
340
341 const char *
342 noit_log_stream_get_type(noit_log_stream_t ls) {
343   return ls->type;
344 }
345
346 const char *
347 noit_log_stream_get_name(noit_log_stream_t ls) {
348   return ls->name;
349 }
350
351 const char *
352 noit_log_stream_get_path(noit_log_stream_t ls) {
353   return ls->path;
354 }
355
356 noit_log_stream_t
357 noit_log_stream_new_on_fd(const char *name, int fd, noit_hash_table *config) {
358   noit_log_stream_t ls;
359   ls = calloc(1, sizeof(*ls));
360   ls->name = strdup(name);
361   ls->ops = &posix_logio_ops;
362   ls->op_ctx = (void *)fd;
363   ls->enabled = 1;
364   ls->config = config;
365   ls->lock = calloc(1, sizeof(*ls->lock));
366   pthread_rwlock_init(ls->lock, NULL);
367   /* This double strdup of ls->name is needed, look for the next one
368    * for an explanation.
369    */
370   if(noit_hash_store(&noit_loggers,
371                      strdup(ls->name), strlen(ls->name), ls) == 0) {
372     free(ls->name);
373     free(ls);
374     return NULL;
375   }
376   return ls;
377 }
378
379 noit_log_stream_t
380 noit_log_stream_new_on_file(const char *path, noit_hash_table *config) {
381   return noit_log_stream_new(path, "file", path, NULL, config);
382 }
383
384 noit_log_stream_t
385 noit_log_stream_new(const char *name, const char *type, const char *path,
386                     void *ctx, noit_hash_table *config) {
387   noit_log_stream_t ls, saved;
388   struct _noit_log_stream tmpbuf;
389   ls = calloc(1, sizeof(*ls));
390   ls->name = strdup(name);
391   ls->path = path ? strdup(path) : NULL;
392   ls->type = type ? strdup(type) : NULL;
393   ls->enabled = 1;
394   ls->config = config;
395   if(!type)
396     ls->ops = NULL;
397   else if(!noit_hash_retrieve(&noit_logops, type, strlen(type),
398                               (void **)&ls->ops))
399     goto freebail;
400  
401   if(ls->ops && ls->ops->openop(ls)) goto freebail;
402
403   saved = noit_log_stream_find(name);
404   if(saved) {
405     pthread_rwlock_t *lock = saved->lock;
406     memcpy(&tmpbuf, saved, sizeof(*saved));
407     memcpy(saved, ls, sizeof(*saved));
408     memcpy(ls, &tmpbuf, sizeof(*saved));
409     saved->lock = lock;
410
411     ls->lock = NULL;
412     noit_log_stream_free(ls);
413     ls = saved;
414   }
415   else {
416     /* We strdup the name *again*.  We'going to kansas city shuffle the
417      * ls later (see memcpy above).  However, if don't strdup, then the
418      * noit_log_stream_free up there will sweep our key right our from
419      * under us.
420      */
421     if(noit_hash_store(&noit_loggers,
422                        strdup(ls->name), strlen(ls->name), ls) == 0)
423       goto freebail;
424     ls->lock = calloc(1, sizeof(*ls->lock));
425     pthread_rwlock_init(ls->lock, NULL);
426   }
427   /* This is for things that don't open on paths */
428   if(ctx) ls->op_ctx = ctx;
429   return ls;
430
431  freebail:
432   fprintf(stderr, "Failed to instantiate logger(%s,%s,%s)\n",
433           name, type ? type : "[null]", path ? path : "[null]");
434   free(ls->name);
435   if(ls->path) free(ls->path);
436   if(ls->type) free(ls->type);
437   free(ls);
438   return NULL;
439 }
440
441 noit_log_stream_t
442 noit_log_stream_find(const char *name) {
443   void *vls;
444   if(noit_hash_retrieve(&noit_loggers, name, strlen(name), &vls)) {
445     return (noit_log_stream_t)vls;
446   }
447   return NULL;
448 }
449
450 void
451 noit_log_stream_remove(const char *name) {
452   noit_hash_delete(&noit_loggers, name, strlen(name), NULL, NULL);
453 }
454
455 void
456 noit_log_stream_add_stream(noit_log_stream_t ls, noit_log_stream_t outlet) {
457   struct _noit_log_stream_outlet_list *newnode;
458   newnode = calloc(1, sizeof(*newnode));
459   newnode->outlet = outlet;
460   newnode->next = ls->outlets;
461   ls->outlets = newnode;
462 }
463
464 noit_log_stream_t
465 noit_log_stream_remove_stream(noit_log_stream_t ls, const char *name) {
466   noit_log_stream_t outlet;
467   struct _noit_log_stream_outlet_list *node, *tmp;
468   if(!ls->outlets) return NULL;
469   if(!strcmp(ls->outlets->outlet->name, name)) {
470     node = ls->outlets;
471     ls->outlets = node->next;
472     outlet = node->outlet;
473     free(node);
474     return outlet;
475   }
476   for(node = ls->outlets; node->next; node = node->next) {
477     if(!strcmp(node->next->outlet->name, name)) {
478       /* splice */
479       tmp = node->next;
480       node->next = tmp->next;
481       /* pluck */
482       outlet = tmp->outlet;
483       /* shed */
484       free(tmp);
485       /* return */
486       return outlet;
487     }
488   }
489   return NULL;
490 }
491
492 void noit_log_stream_reopen(noit_log_stream_t ls) {
493   struct _noit_log_stream_outlet_list *node;
494   if(ls->ops) ls->ops->reopenop(ls);
495   for(node = ls->outlets; node; node = node->next) {
496     noit_log_stream_reopen(node->outlet);
497   }
498 }
499
500 void
501 noit_log_stream_close(noit_log_stream_t ls) {
502   if(ls->ops) ls->ops->closeop(ls);
503 }
504
505 size_t
506 noit_log_stream_size(noit_log_stream_t ls) {
507   if(ls->ops && ls->ops->sizeop) return ls->ops->sizeop(ls);
508   return -1;
509 }
510
511 void
512 noit_log_stream_free(noit_log_stream_t ls) {
513   if(ls) {
514     struct _noit_log_stream_outlet_list *node;
515     if(ls->name) free(ls->name);
516     if(ls->path) free(ls->path);
517     while(ls->outlets) {
518       node = ls->outlets->next;
519       free(ls->outlets);
520       ls->outlets = node;
521     }
522     if(ls->config) {
523       noit_hash_destroy(ls->config, free, free);
524       free(ls->config);
525     }
526     if(ls->lock) {
527       pthread_rwlock_destroy(ls->lock);
528       free(ls->lock);
529     }
530     free(ls);
531   }
532 }
533
534 static int
535 noit_log_line(noit_log_stream_t ls, char *buffer, size_t len) {
536   int rv = 0;
537   struct _noit_log_stream_outlet_list *node;
538   if(ls->ops)
539     rv = ls->ops->writeop(ls, buffer, len); /* Not much one can do about errors */
540   for(node = ls->outlets; node; node = node->next) {
541     int srv = 0;
542     srv = noit_log_line(node->outlet, buffer, len);
543     if(srv) rv = srv;
544   }
545   return rv;
546 }
547 int
548 noit_vlog(noit_log_stream_t ls, struct timeval *now,
549           const char *file, int line,
550           const char *format, va_list arg) {
551   int rv = 0, allocd = 0;
552   char buffer[4096], *dynbuff = NULL;
553   char fbuf[1024];
554 #ifdef va_copy
555   va_list copy;
556 #endif
557
558   if(ls->enabled) {
559     int len;
560     if(ls->debug) {
561       struct tm _tm, *tm = &_tm;
562       char tbuf[32];
563       time_t s = (time_t)now->tv_sec;
564       tm = localtime_r(&s, &_tm);
565       strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", tm);
566       snprintf(fbuf, sizeof(fbuf), "[%s.%06d %s:%d] %s",
567                tbuf, (int)now->tv_usec, file, line, format);
568       format = fbuf;
569     }
570 #ifdef va_copy
571     va_copy(copy, arg);
572     len = vsnprintf(buffer, sizeof(buffer), format, copy);
573     va_end(copy);
574 #else
575     len = vsnprintf(buffer, sizeof(buffer), format, arg);
576 #endif
577     if(len > sizeof(buffer)) {
578       allocd = sizeof(buffer);
579       while(len > allocd) { /* guaranteed true the first time */
580         while(len > allocd) allocd <<= 2;
581         if(dynbuff) free(dynbuff);
582         dynbuff = malloc(allocd);
583         assert(dynbuff);
584 #ifdef va_copy
585         va_copy(copy, arg);
586         len = vsnprintf(dynbuff, allocd, format, copy);
587         va_end(copy);
588 #else
589         len = vsnprintf(dynbuff, allocd, format, arg);
590 #endif
591       }
592       rv = noit_log_line(ls, dynbuff, len);
593       free(dynbuff);
594     }
595     else {
596       rv = noit_log_line(ls, buffer, len);
597     }
598     if(rv == len) return 0;
599     return -1;
600   }
601   return 0;
602 }
603
604 int
605 noit_log(noit_log_stream_t ls, struct timeval *now,
606          const char *file, int line, const char *format, ...) {
607   int rv;
608   va_list arg;
609   va_start(arg, format);
610   rv = noit_vlog(ls, now, file, line, format, arg);
611   va_end(arg);
612   return rv;
613 }
614
615 int
616 noit_log_reopen_all() {
617   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
618   const char *k;
619   int klen, rv = 0;
620   void *data;
621   noit_log_stream_t ls;
622
623   while(noit_hash_next(&noit_loggers, &iter, &k, &klen, &data)) {
624     ls = data;
625     if(ls->ops) if(ls->ops->reopenop(ls) < 0) rv = -1;
626   }
627   return rv;
628 }
629
Note: See TracBrowser for help on using the browser.