root/src/utils/noit_log.c

Revision afd0d1449a9bfb02d702fa5496264707a8bfaa5f, 39.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 months ago)

fix leak of unremoved subscriber names

  • 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/uio.h>
40 #include <sys/time.h>
41 #include <assert.h>
42 #if HAVE_ERRNO_H
43 #include <errno.h>
44 #endif
45 #if HAVE_DIRENT_H
46 #include <dirent.h>
47 #endif
48
49 #define noit_log_impl
50 #include "utils/noit_log.h"
51 #include "utils/noit_hash.h"
52 #include "utils/noit_atomic.h"
53 #include "jlog/jlog.h"
54 #include "jlog/jlog_private.h"
55 #ifdef DTRACE_ENABLED
56 #include "dtrace_probes.h"
57 #else
58 #define NOIT_LOG_LOG(a,b,c,d)
59 #define NOIT_LOG_LOG_ENABLED() 0
60 #endif
61
62 static int DEBUG_LOG_ENABLED() {
63   static int enabled = -1;
64   if(enabled == -1) {
65     char *env = getenv("NOIT_LOG_DEBUG");
66     enabled = env ? atoi(env) : 0;
67   }
68   return enabled;
69 }
70 #define debug_printf(a...) do { \
71   if(DEBUG_LOG_ENABLED()) fprintf(stderr, a); \
72 } while(0)
73
74 struct _noit_log_stream {
75   unsigned flags;
76   /* Above is exposed... 'do not change it... dragons' */
77   char *type;
78   char *name;
79   int mode;
80   char *path;
81   logops_t *ops;
82   void *op_ctx;
83   noit_hash_table *config;
84   struct _noit_log_stream_outlet_list *outlets;
85   pthread_rwlock_t *lock;
86   noit_atomic32_t written;
87   unsigned deps_materialized:1;
88   unsigned flags_below;
89 };
90
91 typedef struct {
92   u_int64_t head;
93   u_int64_t tail;
94   int noffsets;
95   int *offsets;
96   int segmentsize;
97   int segmentcut;
98   char *segment;
99 } membuf_ctx_t;
100
101 static membuf_ctx_t *
102 log_stream_membuf_init(int nlogs, int nbytes) {
103   membuf_ctx_t *membuf;
104   membuf = calloc(1, sizeof(*membuf));
105   membuf->head = membuf->tail = 0;
106   membuf->segment = malloc(nbytes);
107   membuf->segmentsize = nbytes;
108   membuf->segmentcut = membuf->segmentsize;
109   membuf->offsets = calloc(nlogs, sizeof(*membuf->offsets));
110   membuf->noffsets = nlogs;
111   return membuf;
112 }
113 static void
114 log_stream_membuf_free(membuf_ctx_t *membuf) {
115   if(membuf->offsets) free(membuf->offsets);
116   if(membuf->segment) free(membuf->segment);
117   free(membuf);
118 }
119
120 static int
121 membuf_logio_open(noit_log_stream_t ls) {
122   int cnt = 0, size = 0;
123   char *cp;
124   cp = strchr(ls->path, ',');
125   cnt = atoi(ls->path);
126   if(cp) size = atoi(cp+1);
127   if(!cnt) cnt = 10000;
128   if(!size) size = 100000;
129   ls->op_ctx = log_stream_membuf_init(cnt, size);
130   return 0;
131 }
132
133 static int
134 intersect_seg(int a1, int a2, int b1, int b2) {
135   int rv = 0;
136   if(a1 >= b1 && a1 <= b2) rv=1;
137   if(a2 >= b1 && a2 <= b2) rv=1;
138   assert(a1 < a2 && b1 < b2);
139   return rv;
140 }
141 static int
142 membuf_logio_writev(noit_log_stream_t ls, const struct timeval *whence,
143                     const struct iovec *iov, int iovcnt) {
144   struct timeval __now;
145   int i, offset, headoffset, headend, tailoffset, tailend,
146       attemptoffset = -3, attemptend = -1, nexttailoff, nexttail, faketail;
147   pthread_rwlock_t *lock = ls->lock;
148   membuf_ctx_t *membuf = ls->op_ctx;
149   size_t len = sizeof(*whence);
150
151   for(i=0; i<iovcnt; i++) len += iov[i].iov_len;
152   if(len > membuf->segmentsize) return 0;
153
154   if(whence == NULL) {
155     gettimeofday(&__now, NULL);
156     whence = &__now;
157   }
158  
159   if(lock) pthread_rwlock_wrlock(lock);
160   /* use tail */
161   offset = membuf->offsets[membuf->tail % membuf->noffsets];
162   if(offset + len > membuf->segmentcut)
163     membuf->segmentcut = membuf->segmentsize;
164   if(offset + len > membuf->segmentsize) {
165     attemptoffset = offset;
166     attemptend = offset + len;
167     membuf->segmentcut = offset;
168     offset = 0;
169     membuf->offsets[membuf->tail % membuf->noffsets] = offset;
170   }
171   nexttailoff = offset + len;
172   nexttail = membuf->tail + 1;
173
174   faketail = ((membuf->tail % membuf->noffsets) < (membuf->head % membuf->noffsets)) ?
175                ((membuf->tail % membuf->noffsets) + membuf->noffsets) :
176                (membuf->tail % membuf->noffsets);
177   /* clean up head until it is ahead of the next tail */
178   headoffset = membuf->offsets[membuf->head % membuf->noffsets];
179   headend = membuf->offsets[(membuf->head+1) % membuf->noffsets];
180   if(headend < headoffset) headend = membuf->segmentsize;
181   tailoffset = membuf->offsets[membuf->tail % membuf->noffsets];
182   tailend = nexttailoff;
183   /* while we're about to write over the head (attempt or actual), advance */
184   while(membuf->head != membuf->tail &&
185         (intersect_seg(headoffset, headend-1, attemptoffset, attemptend-1) ||
186          intersect_seg(headoffset, headend-1, tailoffset, tailend-1))) {
187     membuf->head++;
188     headoffset = membuf->offsets[membuf->head % membuf->noffsets];
189     headend = membuf->offsets[(membuf->head+1) % membuf->noffsets];
190     if(headend < headoffset) headend = membuf->segmentsize;
191     //if((membuf->head % membuf->noffsets) == 0) {
192     if(headoffset == 0) {
193       faketail = (membuf->tail % membuf->noffsets); /* reset */
194     }
195   }
196
197   /* move tail forward updating head if needed */
198   if((nexttail % membuf->noffsets) == (membuf->head % membuf->noffsets))
199     membuf->head++;
200   /* note where the new tail is */
201   membuf->offsets[nexttail % membuf->noffsets] = nexttailoff;
202
203   len = 0;
204   memcpy(membuf->segment + offset, whence, sizeof(*whence));
205   len += sizeof(*whence);
206   for(i=0;i<iovcnt;i++) {
207     memcpy(membuf->segment + offset + len, iov[i].iov_base, iov[i].iov_len);
208     len += iov[i].iov_len;
209   }
210   membuf->tail = nexttail;
211
212   if(lock) pthread_rwlock_unlock(lock);
213   return len;
214 }
215
216 static int
217 membuf_logio_write(noit_log_stream_t ls, const struct timeval *whence,
218                    const void *buf, size_t len) {
219   struct iovec iov;
220   iov.iov_base = (char *)buf;
221   iov.iov_len = len;
222   return membuf_logio_writev(ls, whence, &iov, 1);
223 }
224 static int
225 membuf_logio_reopen(noit_log_stream_t ls) {
226   return 0;
227 }
228 static int
229 membuf_logio_close(noit_log_stream_t ls) {
230   membuf_ctx_t *membuf = ls->op_ctx;
231   log_stream_membuf_free(membuf);
232   ls->op_ctx = NULL;
233   return 0;
234 }
235 static size_t
236 membuf_logio_size(noit_log_stream_t ls) {
237   membuf_ctx_t *membuf = ls->op_ctx;
238   return membuf->segmentsize;
239 }
240 static int
241 membuf_logio_rename(noit_log_stream_t ls, const char *newname) {
242   /* Not supported (and makes no sense) */
243   return -1;
244 }
245
246 static logops_t membuf_logio_ops = {
247   membuf_logio_open,
248   membuf_logio_reopen,
249   membuf_logio_write,
250   membuf_logio_writev,
251   membuf_logio_close,
252   membuf_logio_size,
253   membuf_logio_rename
254 };
255
256 int
257 noit_log_memory_lines(noit_log_stream_t ls, int log_lines,
258                       int (*f)(u_int64_t, const struct timeval *,
259                                const char *, size_t, void *),
260                       void *closure) {
261   int nmsg;
262   pthread_rwlock_t *lock = ls->lock;
263   u_int64_t idx;
264   if(strcmp(ls->type, "memory")) return -1;
265   membuf_ctx_t *membuf = ls->op_ctx;
266   if(membuf == NULL) return 0;
267
268   if(lock) pthread_rwlock_wrlock(lock);
269   nmsg = ((membuf->tail % membuf->noffsets) >= (membuf->head % membuf->noffsets)) ?
270            ((membuf->tail % membuf->noffsets) - (membuf->head % membuf->noffsets)) :
271            ((membuf->tail % membuf->noffsets) + membuf->noffsets - (membuf->head % membuf->noffsets));
272   assert(nmsg < membuf->noffsets);
273   if(log_lines == 0) log_lines = nmsg;
274   log_lines = MIN(log_lines,nmsg);
275   idx = (membuf->tail >= log_lines) ?
276           (membuf->tail - log_lines) : 0;
277   if(lock) pthread_rwlock_unlock(lock);
278   return noit_log_memory_lines_since(ls, idx, f, closure);
279 }
280
281 int
282 noit_log_memory_lines_since(noit_log_stream_t ls, u_int64_t afterwhich,
283                             int (*f)(u_int64_t, const struct timeval *,
284                                     const char *, size_t, void *),
285                             void *closure) {
286   int nmsg, count = 0;
287   pthread_rwlock_t *lock = ls->lock;
288   u_int64_t idx = afterwhich;
289   if(strcmp(ls->type, "memory")) return -1;
290   membuf_ctx_t *membuf = ls->op_ctx;
291   if(membuf == NULL) return 0;
292
293   if(lock) pthread_rwlock_wrlock(lock);
294   nmsg = ((membuf->tail % membuf->noffsets) >= (membuf->head % membuf->noffsets)) ?
295            ((membuf->tail % membuf->noffsets) - (membuf->head % membuf->noffsets)) :
296            ((membuf->tail % membuf->noffsets) + membuf->noffsets - (membuf->head % membuf->noffsets));
297   assert(nmsg < membuf->noffsets);
298   /* We want stuff *after* this, so add one */
299   idx++;
300   if(idx == membuf->tail) return 0;
301
302   /* If we're asked for a starting index outside our range, then we should set it to head. */
303   if((membuf->head > membuf->tail && idx < membuf->head && idx >= membuf->tail) ||
304      (membuf->head < membuf->tail && (idx >= membuf->tail || idx < membuf->head)))
305     idx = membuf->head;
306
307   while(idx != membuf->tail) {
308     u_int64_t nidx;
309     size_t len;
310     nidx = idx + 1;
311     len = (membuf->offsets[idx % membuf->noffsets] < membuf->offsets[nidx % membuf->noffsets]) ?
312             membuf->offsets[nidx % membuf->noffsets] - membuf->offsets[idx % membuf->noffsets] :
313             membuf->segmentcut - membuf->offsets[idx % membuf->noffsets];
314     struct timeval copy;
315     const char *logline;
316     memcpy(&copy, membuf->segment + membuf->offsets[idx % membuf->noffsets], sizeof(copy));
317     logline = membuf->segment + membuf->offsets[idx % membuf->noffsets] + sizeof(copy);
318     len -= sizeof(copy);
319     if(f(idx, &copy, logline, len, closure))
320       break;
321     idx = nidx;
322     count++;
323   }
324   if(lock) pthread_rwlock_unlock(lock);
325   return count;
326 }
327 #define IS_ENABLED_ON(ls) ((ls)->flags & NOIT_LOG_STREAM_ENABLED)
328 #define IS_TIMESTAMPS_ON(ls) ((ls)->flags & NOIT_LOG_STREAM_TIMESTAMPS)
329 #define IS_DEBUG_ON(ls) ((ls)->flags & NOIT_LOG_STREAM_DEBUG)
330 #define IS_FACILITY_ON(ls) ((ls)->flags & NOIT_LOG_STREAM_FACILITY)
331 #define IS_ENABLED_BELOW(ls) ((ls)->flags_below & NOIT_LOG_STREAM_ENABLED)
332 #define IS_TIMESTAMPS_BELOW(ls) ((ls)->flags_below & NOIT_LOG_STREAM_TIMESTAMPS)
333 #define IS_DEBUG_BELOW(ls) ((ls)->flags_below & NOIT_LOG_STREAM_DEBUG)
334 #define IS_FACILITY_BELOW(ls) ((ls)->flags_below & NOIT_LOG_STREAM_FACILITY)
335
336 static noit_hash_table noit_loggers = NOIT_HASH_EMPTY;
337 static noit_hash_table noit_logops = NOIT_HASH_EMPTY;
338 noit_log_stream_t noit_stderr = NULL;
339 noit_log_stream_t noit_error = NULL;
340 noit_log_stream_t noit_debug = NULL;
341 noit_log_stream_t noit_notice = NULL;
342
343 int noit_log_global_enabled() {
344   return NOIT_LOG_LOG_ENABLED();
345 }
346
347 static void
348 noit_log_dematerialize() {
349   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
350   const char *k;
351   int klen;
352   void *data;
353
354   while(noit_hash_next(&noit_loggers, &iter, &k, &klen, &data)) {
355     noit_log_stream_t ls = data;
356     ls->deps_materialized = 0;
357     ls->flags &= ~NOIT_LOG_STREAM_RECALCULATE;
358     debug_printf("dematerializing(%s)\n", ls->name);
359   }
360 }
361
362 #define MATERIALIZE_DEPS(lstream) do { \
363   if(lstream->flags & NOIT_LOG_STREAM_RECALCULATE) noit_log_dematerialize(); \
364   if(!(lstream)->deps_materialized) materialize_deps(lstream); \
365 } while(0)
366
367 static void materialize_deps(noit_log_stream_t ls) {
368   struct _noit_log_stream_outlet_list *node;
369   if(ls->deps_materialized) {
370     debug_printf("materialize(%s) [already done]\n", ls->name);
371     return;
372   }
373   /* pass forward all but enabled */
374   ls->flags_below |= (ls->flags & NOIT_LOG_STREAM_FEATURES);
375
376   /* we might have children than need these */
377   for(node = ls->outlets; node; node = node->next) {
378     MATERIALIZE_DEPS(node->outlet);
379     /* our flags_below should be augments with our outlets flags_below
380        unless we have them in our flags already */
381     ls->flags_below |= (~(ls->flags) & NOIT_LOG_STREAM_FEATURES) &
382                        node->outlet->flags_below;
383     debug_printf("materialize(%s) |= (%s) %x\n", ls->name,
384                  node->outlet->name,
385                  node->outlet->flags_below & NOIT_LOG_STREAM_FEATURES);
386   }
387   debug_printf("materialize(%s) -> %x\n", ls->name, ls->flags_below);
388   ls->deps_materialized = 1;
389 }
390 static int
391 posix_logio_open(noit_log_stream_t ls) {
392   int fd;
393   struct stat sb;
394   ls->mode = 0664;
395   fd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND, ls->mode);
396   debug_printf("opened '%s' => %d\n", ls->path, fd);
397   if(fd < 0) {
398     ls->op_ctx = NULL;
399     return -1;
400   }
401   if(fstat(fd, &sb) == 0) ls->written = (int32_t)sb.st_size;
402   ls->op_ctx = (void *)(vpsized_int)fd;
403   return 0;
404 }
405 static int
406 posix_logio_reopen(noit_log_stream_t ls) {
407   if(ls->path) {
408     pthread_rwlock_t *lock = ls->lock;
409     int newfd, oldfd, rv = -1;
410     if(lock) pthread_rwlock_wrlock(lock);
411     oldfd = (int)(vpsized_int)ls->op_ctx;
412     newfd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND, ls->mode);
413     ls->written = 0;
414     if(newfd >= 0) {
415       struct stat sb;
416       ls->op_ctx = (void *)(vpsized_int)newfd;
417       if(oldfd >= 0) close(oldfd);
418       rv = 0;
419       if(fstat(newfd, &sb) == 0) ls->written = (int32_t)sb.st_size;
420     }
421     if(lock) pthread_rwlock_unlock(lock);
422     return rv;
423   }
424   return -1;
425 }
426 static int
427 posix_logio_write(noit_log_stream_t ls, const struct timeval *whence,
428                   const void *buf, size_t len) {
429   int fd, rv = -1;
430   pthread_rwlock_t *lock = ls->lock;
431   (void)whence;
432   if(lock) pthread_rwlock_rdlock(lock);
433   fd = (int)(vpsized_int)ls->op_ctx;
434   debug_printf("writing to %d\n", fd);
435   if(fd >= 0) rv = write(fd, buf, len);
436   if(lock) pthread_rwlock_unlock(lock);
437   if(rv > 0) noit_atomic_add32(&ls->written, rv);
438   return rv;
439 }
440 static int
441 posix_logio_writev(noit_log_stream_t ls, const struct timeval *whence,
442                    const struct iovec *iov, int iovcnt) {
443   int fd, rv = -1;
444   pthread_rwlock_t *lock = ls->lock;
445   (void)whence;
446   if(lock) pthread_rwlock_rdlock(lock);
447   fd = (int)(vpsized_int)ls->op_ctx;
448   debug_printf("writ(v)ing to %d\n", fd);
449   if(fd >= 0) rv = writev(fd, iov, iovcnt);
450   if(lock) pthread_rwlock_unlock(lock);
451   if(rv > 0) noit_atomic_add32(&ls->written, rv);
452   return rv;
453 }
454 static int
455 posix_logio_close(noit_log_stream_t ls) {
456   int fd, rv;
457   pthread_rwlock_t *lock = ls->lock;
458   if(lock) pthread_rwlock_wrlock(lock);
459   fd = (int)(vpsized_int)ls->op_ctx;
460   rv = close(fd);
461   if(lock) pthread_rwlock_unlock(lock);
462   return rv;
463 }
464 static size_t
465 posix_logio_size(noit_log_stream_t ls) {
466   int fd;
467   size_t s = (size_t)-1;
468   struct stat sb;
469   pthread_rwlock_t *lock = ls->lock;
470   if(lock) pthread_rwlock_rdlock(lock);
471   fd = (int)(vpsized_int)ls->op_ctx;
472   if(fstat(fd, &sb) == 0) {
473     s = (size_t)sb.st_size;
474   }
475   if(lock) pthread_rwlock_unlock(lock);
476   return s;
477 }
478 static int
479 posix_logio_rename(noit_log_stream_t ls, const char *name) {
480   int rv = 0;
481   char autoname[PATH_MAX];
482   pthread_rwlock_t *lock = ls->lock;
483   if(name == NOIT_LOG_RENAME_AUTOTIME) {
484     time_t now = time(NULL);
485     snprintf(autoname, sizeof(autoname), "%s.%llu",
486              ls->path, (unsigned long long)now);
487     name = autoname;
488   }
489   if(!strcmp(name, ls->path)) return 0; /* noop */
490   if(lock) pthread_rwlock_rdlock(lock);
491   rv = rename(ls->path, name);
492   if(lock) pthread_rwlock_unlock(lock);
493   return rv;
494 }
495 static logops_t posix_logio_ops = {
496   posix_logio_open,
497   posix_logio_reopen,
498   posix_logio_write,
499   posix_logio_writev,
500   posix_logio_close,
501   posix_logio_size,
502   posix_logio_rename
503 };
504
505 typedef struct jlog_line {
506   char *buf;
507   char buf_static[512];
508   char *buf_dynamic;
509   int len;
510   void *next;
511 } jlog_line;
512
513 typedef struct {
514   jlog_ctx *log;
515   pthread_t writer;
516   void *head;
517   noit_atomic32_t gen;  /* generation */
518 } jlog_asynch_ctx;
519
520 static int
521 jlog_lspath_to_fspath(noit_log_stream_t ls, char *buff, int len,
522                       char **subout) {
523   char *sub;
524   if(subout) *subout = NULL;
525   if(!ls->path) return -1;
526   strlcpy(buff, ls->path, len);
527   sub = strchr(buff, '(');
528   if(sub) {
529     char *esub = strchr(sub, ')');
530     if(esub) {
531       *esub = '\0';
532       *sub = '\0';
533       sub += 1;
534       if(subout) *subout = sub;
535     }
536   }
537   return strlen(buff);
538 }
539
540 /* These next functions arr basically cribbed from jlogctl.c */
541 static int
542 is_datafile(const char *f, u_int32_t *logid) {
543   int i;
544   u_int32_t l = 0;
545   for(i=0; i<8; i++) {
546     if((f[i] >= '0' && f[i] <= '9') ||
547        (f[i] >= 'a' && f[i] <= 'f')) {
548       l <<= 4;
549       l |= (f[i] < 'a') ? (f[i] - '0') : (f[i] - 'a' + 10);
550     }
551     else
552       return 0;
553   }
554   if(f[i] != '\0') return 0;
555   if(logid) *logid = l;
556   return 1;
557 }
558
559 static int
560 jlog_logio_cleanse(noit_log_stream_t ls) {
561   jlog_asynch_ctx *actx;
562   jlog_ctx *log;
563   DIR *d;
564   struct dirent *de, *entry;
565   int cnt = 0;
566   char path[PATH_MAX], current_log[9];
567   int size = 0;
568
569   actx = (jlog_asynch_ctx *)ls->op_ctx;
570   if(!actx) return -1;
571   log = actx->log;
572   if(!log) return -1;
573   if(jlog_lspath_to_fspath(ls, path, sizeof(path), NULL) <= 0) return -1;
574   d = opendir(path);
575   snprintf(current_log, sizeof(current_log), "%08x", log->current_log);
576
577 #ifdef _PC_NAME_MAX
578   size = pathconf(path, _PC_NAME_MAX);
579   if(size < 0) size = PATH_MAX + 128;
580 #endif
581   size = MIN(size, PATH_MAX + 128);
582   de = alloca(size);
583
584   if(!d) return -1;
585   while(portable_readdir_r(d, de, &entry) == 0 && entry != NULL) {
586     u_int32_t logid;
587     /* the current log file isn't a deletion target. period. */
588     if(is_datafile(entry->d_name, &logid) &&
589        strcmp(current_log, entry->d_name)) {
590       int rv;
591       struct stat st;
592       char fullfile[PATH_MAX];
593       char fullidx[PATH_MAX];
594
595       snprintf(fullfile, sizeof(fullfile), "%s/%s", path, entry->d_name);
596       snprintf(fullidx, sizeof(fullidx), "%s/%s" INDEX_EXT,
597                path, entry->d_name);
598       /* coverity[fs_check_call] */
599       while((rv = stat(fullfile, &st)) != 0 && errno == EINTR);
600       if(rv == 0) {
601         int readers;
602         readers = __jlog_pending_readers(log, logid);
603         if(readers == 0) {
604           /* coverity[toctou] */
605           unlink(fullfile);
606           unlink(fullidx);
607         }
608       }
609     }
610   }
611   closedir(d);
612   return cnt;
613 }
614
615 static jlog_line *
616 jlog_asynch_pop(jlog_asynch_ctx *actx, jlog_line **iter) {
617   jlog_line *h = NULL, *rev = NULL;
618
619   if(*iter) { /* we have more on the previous list */
620     h = *iter;
621     *iter = h->next;
622     return h;
623   }
624
625   while(1) {
626     h = (void *)(volatile void *)actx->head;
627     if(noit_atomic_casptr((volatile void **)&actx->head, NULL, h) == h) break;
628     /* TODO: load-load */
629   }
630   while(h) {
631     /* which unshifted things into the queue -- it's backwards, reverse it */
632     jlog_line *tmp = h;
633     h = h->next;
634     tmp->next = rev;
635     rev = tmp;
636   }
637   if(rev) *iter = rev->next;
638   else *iter = NULL;
639   return rev;
640 }
641 void
642 jlog_asynch_push(jlog_asynch_ctx *actx, jlog_line *n) {
643   while(1) {
644     n->next = (void *)(volatile void *)actx->head;
645     if(noit_atomic_casptr((volatile void **)&actx->head, n, n->next) == n->next) return;
646     /* TODO: load-load */
647   }
648 }
649 static void *
650 jlog_logio_asynch_writer(void *vls) {
651   noit_log_stream_t ls = vls;
652   jlog_asynch_ctx *actx = ls->op_ctx;
653   jlog_line *iter = NULL;
654   int gen;
655   gen = noit_atomic_inc32(&actx->gen);
656   noitL(noit_debug, "starting asynchronous jlog writer[%d/%p]\n",
657         (int)getpid(), (void *)(vpsized_int)pthread_self());
658   while(gen == actx->gen) {
659     pthread_rwlock_t *lock;
660     int fast = 0, max = 1000;
661     jlog_line *line;
662     lock = ls->lock;
663     if(lock) pthread_rwlock_rdlock(lock);
664     while(max > 0 && NULL != (line = jlog_asynch_pop(actx, &iter))) {
665       if(jlog_ctx_write(actx->log, line->buf_dynamic ?
666                                      line->buf_dynamic :
667                                      line->buf_static,
668                         line->len) == -1) {
669         noitL(noit_error, "jlog_ctx_write failed(%d): %s\n",
670               jlog_ctx_errno(actx->log), jlog_ctx_err_string(actx->log));
671         abort();
672       }
673       if(line->buf_dynamic != NULL) free(line->buf_dynamic);
674       free(line);
675       fast = 1;
676       max--;
677     }
678     if(lock) pthread_rwlock_unlock(lock);
679     if(max > 0) {
680       /* we didn't hit our limit... so we ran the queue dry */
681       /* 200ms if there was nothing, 10ms otherwise */
682       usleep(fast ? 10000 : 200000);
683     }
684   }
685   noitL(noit_debug, "stopping asynchronous jlog writer[%d/%p]\n",
686         (int)getpid(), (void *)(vpsized_int)pthread_self());
687   pthread_exit((void *)0);
688 }
689 static int
690 jlog_logio_reopen(noit_log_stream_t ls) {
691   char **subs;
692   jlog_asynch_ctx *actx = ls->op_ctx;
693   pthread_rwlock_t *lock = ls->lock;
694   pthread_attr_t tattr;
695   int i;
696   /* reopening only has the effect of removing temporary subscriptions */
697   /* (they start with ~ in our hair-brained model */
698
699   if(lock) pthread_rwlock_wrlock(lock);
700   if(jlog_ctx_list_subscribers(actx->log, &subs) == -1)
701     goto bail;
702
703   for(i=0;subs[i];i++)
704     if(subs[i][0] == '~')
705       jlog_ctx_remove_subscriber(actx->log, subs[i]);
706
707   jlog_ctx_list_subscribers_dispose(actx->log, subs);
708   jlog_logio_cleanse(ls);
709  bail:
710   if(lock) pthread_rwlock_unlock(lock);
711
712   pthread_attr_init(&tattr);
713   pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
714   if(pthread_create(&actx->writer, &tattr, jlog_logio_asynch_writer, ls) != 0)
715     return -1;
716  
717   return 0;
718 }
719 static void
720 noit_log_jlog_err(void *ctx, const char *format, ...) {
721   struct timeval now;
722   va_list arg;
723   va_start(arg, format);
724   gettimeofday(&now, NULL);
725   (void)noit_vlog(noit_error, &now, "jlog.c", 0, format, arg);
726   va_end(arg);
727 }
728 static int
729 jlog_logio_open(noit_log_stream_t ls) {
730   char path[PATH_MAX], *sub, **subs, *p;
731   jlog_asynch_ctx *actx;
732   jlog_ctx *log = NULL;
733   int i, listed, found, allow_unmatched = 0;
734
735   if(jlog_lspath_to_fspath(ls, path, sizeof(path), &sub) <= 0) return -1;
736   log = jlog_new(path);
737   if(!log) return -1;
738   jlog_set_error_func(log, noit_log_jlog_err, ls);
739   /* Open the writer. */
740   if(jlog_ctx_open_writer(log)) {
741     /* If that fails, we'll give one attempt at initiailizing it. */
742     /* But, since we attempted to open it as a writer, it is tainted. */
743     /* path: close, new, init, close, new, writer, add subscriber */
744     jlog_ctx_close(log);
745     log = jlog_new(path);
746     jlog_set_error_func(log, noit_log_jlog_err, ls);
747     if(jlog_ctx_init(log)) {
748       noitL(noit_error, "Cannot init jlog writer: %s\n",
749             jlog_ctx_err_string(log));
750       jlog_ctx_close(log);
751       return -1;
752     }
753     /* After it is initialized, we can try to reopen it as a writer. */
754     jlog_ctx_close(log);
755     log = jlog_new(path);
756     jlog_set_error_func(log, noit_log_jlog_err, ls);
757     if(jlog_ctx_open_writer(log)) {
758       noitL(noit_error, "Cannot open jlog writer: %s\n",
759             jlog_ctx_err_string(log));
760       jlog_ctx_close(log);
761       return -1;
762     }
763   }
764
765   /* Add or remove subscribers according to the current configuration. */
766   listed = jlog_ctx_list_subscribers(log, &subs);
767   if(listed == -1) {
768     noitL(noit_error, "Cannot list jlog subscribers: %s\n",
769           jlog_ctx_err_string(log));
770     jlog_ctx_close(log);
771     return -1;
772   }
773
774   if(sub) {
775     /* Match all configured subscribers against jlog's list. */
776     for(p=strtok(sub, ",");p;p=strtok(NULL, ",")) {
777       if(!strcmp(p,"*")) allow_unmatched = 1;
778       for(i=0;i<listed;i++) {
779         if((subs[i]) && (strcmp(p, subs[i]) == 0)) {
780           free(subs[i]);
781           subs[i] = NULL;
782           break;
783         }
784       }
785       if(i == listed && strcmp(p,"*"))
786         jlog_ctx_add_subscriber(log, p, JLOG_BEGIN);
787     }
788
789     /* Remove all unmatched subscribers. */
790     for(i=0;i<listed;i++) {
791       if(subs[i] &&
792          (!allow_unmatched || subs[i][0] == '~')) {
793         jlog_ctx_remove_subscriber(log, subs[i]);
794       }
795       free(subs[i]);
796       subs[i] = NULL;
797     }
798
799     free(subs);
800     subs = NULL;
801   } else {
802     /* Remove all subscribers other than DEFAULT_JLOG_SUBSCRIBER. */
803     found = 0;
804     for(i=0;i<listed;i++) {
805       if((subs[i]) && (strcmp(DEFAULT_JLOG_SUBSCRIBER, subs[i]) == 0)) {
806         found = 1;
807         continue;
808       }
809       jlog_ctx_remove_subscriber(log, subs[i]);
810     }
811
812     /* Add DEFAULT_JLOG_SUBSCRIBER if it wasn't already on the jlog's list. */
813     if(!found)
814       jlog_ctx_add_subscriber(log, DEFAULT_JLOG_SUBSCRIBER, JLOG_BEGIN);
815
816     jlog_ctx_list_subscribers_dispose(log, subs);
817   }
818
819   actx = calloc(1, sizeof(*actx));
820   actx->log = log;
821   ls->op_ctx = actx;
822
823   /* We do this to clean things up and start our thread */
824   return jlog_logio_reopen(ls);
825 }
826 static int
827 jlog_logio_write(noit_log_stream_t ls, const struct timeval *whence,
828                  const void *buf, size_t len) {
829   int rv = -1;
830   jlog_asynch_ctx *actx;
831   jlog_line *line;
832   (void)whence;
833   if(!ls->op_ctx) return -1;
834   actx = ls->op_ctx;
835   line = calloc(1, sizeof(*line));
836   if(len > sizeof(line->buf_static)) {
837     line->buf_dynamic = malloc(len);
838     memcpy(line->buf_dynamic, buf, len);
839   }
840   else {
841     memcpy(line->buf_static, buf, len);
842   }
843   line->len = len;
844   jlog_asynch_push(actx, line);
845   return rv;
846 }
847 static int
848 jlog_logio_close(noit_log_stream_t ls) {
849   if(ls->op_ctx) {
850     jlog_asynch_ctx *actx = ls->op_ctx;
851     jlog_ctx_close(actx->log);
852     ls->op_ctx = NULL;
853   }
854   return 0;
855 }
856 static size_t
857 jlog_logio_size(noit_log_stream_t ls) {
858   size_t size;
859   jlog_asynch_ctx *actx;
860   pthread_rwlock_t *lock = ls->lock;
861   if(!ls->op_ctx) return -1;
862   actx = ls->op_ctx;
863   if(lock) pthread_rwlock_rdlock(lock);
864   size = jlog_raw_size(actx->log);
865   if(lock) pthread_rwlock_unlock(lock);
866   return size;
867 }
868 static int
869 jlog_logio_rename(noit_log_stream_t ls, const char *newname) {
870   /* Not supported (and makes no sense) */
871   return -1;
872 }
873 static logops_t jlog_logio_ops = {
874   jlog_logio_open,
875   jlog_logio_reopen,
876   jlog_logio_write,
877   NULL,
878   jlog_logio_close,
879   jlog_logio_size,
880   jlog_logio_rename
881 };
882
883 void
884 noit_log_init(int debug_on) {
885   noit_hash_init(&noit_loggers);
886   noit_hash_init(&noit_logops);
887   noit_register_logops("file", &posix_logio_ops);
888   noit_register_logops("jlog", &jlog_logio_ops);
889   noit_register_logops("memory", &membuf_logio_ops);
890   noit_stderr = noit_log_stream_new_on_fd("stderr", 2, NULL);
891   noit_stderr->flags |= NOIT_LOG_STREAM_TIMESTAMPS;
892   noit_stderr->flags |= NOIT_LOG_STREAM_FACILITY;
893   noit_error = noit_log_stream_new("error", NULL, NULL, NULL, NULL);
894   noit_debug = noit_log_stream_new("debug", NULL, NULL, NULL, NULL);
895   noit_notice = noit_log_stream_new("notice", NULL, NULL, NULL, NULL);
896   noit_debug->flags = (noit_debug->flags & ~NOIT_LOG_STREAM_DEBUG) |
897                       (debug_on ? NOIT_LOG_STREAM_DEBUG : 0);
898   if(debug_on) noit_debug->flags |= NOIT_LOG_STREAM_ENABLED;
899   else noit_debug->flags &= ~NOIT_LOG_STREAM_ENABLED;
900 }
901
902 void
903 noit_register_logops(const char *name, logops_t *ops) {
904   noit_hash_store(&noit_logops, strdup(name), strlen(name), ops);
905 }
906
907 void *
908 noit_log_stream_get_ctx(noit_log_stream_t ls) {
909   return ls->op_ctx;
910 }
911
912 void
913 noit_log_stream_set_ctx(noit_log_stream_t ls, void *nctx) {
914   ls->op_ctx = nctx;
915 }
916
917 int
918 noit_log_stream_get_flags(noit_log_stream_t ls) {
919   return ls->flags;
920 }
921
922 int
923 noit_log_stream_set_flags(noit_log_stream_t ls, int new_flags) {
924   int previous_flags = ls->flags;
925   ls->flags = new_flags | NOIT_LOG_STREAM_RECALCULATE;
926   return previous_flags;
927 }
928
929 const char *
930 noit_log_stream_get_type(noit_log_stream_t ls) {
931   return ls->type;
932 }
933
934 const char *
935 noit_log_stream_get_name(noit_log_stream_t ls) {
936   return ls->name;
937 }
938
939 const char *
940 noit_log_stream_get_path(noit_log_stream_t ls) {
941   return ls->path;
942 }
943
944 const char *
945 noit_log_stream_get_property(noit_log_stream_t ls,
946                              const char *prop) {
947   const char *v;
948   if(ls && ls->config &&
949      noit_hash_retr_str(ls->config, prop, strlen(prop), &v))
950     return v;
951   return NULL;
952 }
953
954 void
955 noit_log_stream_set_property(noit_log_stream_t ls,
956                              const char *prop, const char *v) {
957   if(!ls) return;
958   if(!ls->config) {
959     ls->config = calloc(1, sizeof(*ls->config));
960     noit_hash_init(ls->config);
961   }
962   noit_hash_replace(ls->config, prop, strlen(prop), (void *)v, free, free);
963 }
964
965 static void
966 noit_log_init_rwlock(noit_log_stream_t ls) {
967   pthread_rwlockattr_t attr;
968   pthread_rwlockattr_init(&attr);
969   pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
970   pthread_rwlock_init(ls->lock, &attr);
971 }
972
973 noit_log_stream_t
974 noit_log_stream_new_on_fd(const char *name, int fd, noit_hash_table *config) {
975   char *lsname;
976   noit_log_stream_t ls;
977   ls = calloc(1, sizeof(*ls));
978   ls->name = strdup(name);
979   ls->ops = &posix_logio_ops;
980   ls->op_ctx = (void *)(vpsized_int)fd;
981   ls->flags |= NOIT_LOG_STREAM_ENABLED;
982   ls->config = config;
983   ls->lock = calloc(1, sizeof(*ls->lock));
984   noit_log_init_rwlock(ls);
985   /* This double strdup of ls->name is needed, look for the next one
986    * for an explanation.
987    */
988   lsname = strdup(ls->name);
989   if(noit_hash_store(&noit_loggers,
990                      lsname, strlen(ls->name), ls) == 0) {
991     free(lsname);
992     free(ls->name);
993     free(ls);
994     return NULL;
995   }
996   return ls;
997 }
998
999 noit_log_stream_t
1000 noit_log_stream_new_on_file(const char *path, noit_hash_table *config) {
1001   return noit_log_stream_new(path, "file", path, NULL, config);
1002 }
1003
1004 noit_log_stream_t
1005 noit_log_stream_new(const char *name, const char *type, const char *path,
1006                     void *ctx, noit_hash_table *config) {
1007   noit_log_stream_t ls, saved;
1008   struct _noit_log_stream tmpbuf;
1009   void *vops = NULL;
1010
1011   ls = calloc(1, sizeof(*ls));
1012   ls->name = strdup(name);
1013   ls->path = path ? strdup(path) : NULL;
1014   ls->type = type ? strdup(type) : NULL;
1015   ls->flags |= NOIT_LOG_STREAM_ENABLED;
1016   ls->config = config;
1017   if(!type)
1018     ls->ops = NULL;
1019   else if(noit_hash_retrieve(&noit_logops, type, strlen(type),
1020                              &vops))
1021     ls->ops = vops;
1022   else
1023     goto freebail;
1024  
1025   if(ls->ops && ls->ops->openop(ls)) goto freebail;
1026
1027   saved = noit_log_stream_find(name);
1028   if(saved) {
1029     pthread_rwlock_t *lock = saved->lock;
1030     memcpy(&tmpbuf, saved, sizeof(*saved));
1031     memcpy(saved, ls, sizeof(*saved));
1032     memcpy(ls, &tmpbuf, sizeof(*saved));
1033     saved->lock = lock;
1034
1035     ls->lock = NULL;
1036     noit_log_stream_free(ls);
1037     ls = saved;
1038   }
1039   else {
1040     /* We strdup the name *again*.  We'going to kansas city shuffle the
1041      * ls later (see memcpy above).  However, if don't strdup, then the
1042      * noit_log_stream_free up there will sweep our key right our from
1043      * under us.
1044      */
1045     char *lsname;
1046     lsname = strdup(ls->name);
1047     if(noit_hash_store(&noit_loggers,
1048                        lsname, strlen(ls->name), ls) == 0) {
1049       free(lsname);
1050       goto freebail;
1051     }
1052     ls->lock = calloc(1, sizeof(*ls->lock));
1053     noit_log_init_rwlock(ls);
1054   }
1055   /* This is for things that don't open on paths */
1056   if(ctx) ls->op_ctx = ctx;
1057   return ls;
1058
1059  freebail:
1060   fprintf(stderr, "Failed to instantiate logger(%s,%s,%s)\n",
1061           name, type ? type : "[null]", path ? path : "[null]");
1062   free(ls->name);
1063   if(ls->path) free(ls->path);
1064   if(ls->type) free(ls->type);
1065   free(ls);
1066   return NULL;
1067 }
1068
1069 noit_log_stream_t
1070 noit_log_stream_find(const char *name) {
1071   void *vls;
1072   if(noit_hash_retrieve(&noit_loggers, name, strlen(name), &vls)) {
1073     return (noit_log_stream_t)vls;
1074   }
1075   return NULL;
1076 }
1077
1078 void
1079 noit_log_stream_remove(const char *name) {
1080   noit_hash_delete(&noit_loggers, name, strlen(name), free, NULL);
1081 }
1082
1083 void
1084 noit_log_stream_add_stream(noit_log_stream_t ls, noit_log_stream_t outlet) {
1085   struct _noit_log_stream_outlet_list *newnode;
1086   newnode = calloc(1, sizeof(*newnode));
1087   newnode->outlet = outlet;
1088   newnode->next = ls->outlets;
1089   ls->outlets = newnode;
1090   ls->flags |= NOIT_LOG_STREAM_RECALCULATE;
1091 }
1092
1093 noit_log_stream_t
1094 noit_log_stream_remove_stream(noit_log_stream_t ls, const char *name) {
1095   noit_log_stream_t outlet;
1096   struct _noit_log_stream_outlet_list *node, *tmp;
1097   if(!ls->outlets) return NULL;
1098   if(!strcmp(ls->outlets->outlet->name, name)) {
1099     node = ls->outlets;
1100     ls->outlets = node->next;
1101     outlet = node->outlet;
1102     free(node);
1103     ls->flags |= NOIT_LOG_STREAM_RECALCULATE;
1104     return outlet;
1105   }
1106   for(node = ls->outlets; node->next; node = node->next) {
1107     if(!strcmp(node->next->outlet->name, name)) {
1108       /* splice */
1109       tmp = node->next;
1110       node->next = tmp->next;
1111       /* pluck */
1112       outlet = tmp->outlet;
1113       /* shed */
1114       free(tmp);
1115       /* return */
1116       ls->flags |= NOIT_LOG_STREAM_RECALCULATE;
1117       return outlet;
1118     }
1119   }
1120   return NULL;
1121 }
1122
1123 void noit_log_stream_reopen(noit_log_stream_t ls) {
1124   struct _noit_log_stream_outlet_list *node;
1125   if(ls->ops) ls->ops->reopenop(ls);
1126   for(node = ls->outlets; node; node = node->next) {
1127     noit_log_stream_reopen(node->outlet);
1128   }
1129 }
1130
1131 int noit_log_stream_rename(noit_log_stream_t ls, const char *newname) {
1132   return (ls->ops && ls->ops->renameop) ? ls->ops->renameop(ls, newname) : -1;
1133 }
1134
1135 void
1136 noit_log_stream_close(noit_log_stream_t ls) {
1137   if(ls->ops) ls->ops->closeop(ls);
1138 }
1139
1140 size_t
1141 noit_log_stream_size(noit_log_stream_t ls) {
1142   if(ls->ops && ls->ops->sizeop) return ls->ops->sizeop(ls);
1143   return -1;
1144 }
1145
1146 size_t
1147 noit_log_stream_written(noit_log_stream_t ls) {
1148   return ls->written;
1149 }
1150
1151 void
1152 noit_log_stream_free(noit_log_stream_t ls) {
1153   if(ls) {
1154     struct _noit_log_stream_outlet_list *node;
1155     if(ls->name) free(ls->name);
1156     if(ls->path) free(ls->path);
1157     if(ls->type) free(ls->type);
1158     while(ls->outlets) {
1159       node = ls->outlets->next;
1160       free(ls->outlets);
1161       ls->outlets = node;
1162     }
1163     if(ls->config) {
1164       noit_hash_destroy(ls->config, free, free);
1165       free(ls->config);
1166     }
1167     if(ls->lock) {
1168       pthread_rwlock_destroy(ls->lock);
1169       free(ls->lock);
1170     }
1171     free(ls);
1172   }
1173 }
1174
1175 static int
1176 noit_log_writev(noit_log_stream_t ls, struct timeval *whence,
1177                 const struct iovec *iov, int iovcnt) {
1178   /* This emulates writev into a buffer for ops that don't support it */
1179   char stackbuff[4096], *tofree = NULL, *buff = NULL;
1180   int i, s = 0, ins = 0;
1181
1182   if(!ls->ops) return -1;
1183   if(ls->ops->writevop) return ls->ops->writevop(ls, whence, iov, iovcnt);
1184   if(!ls->ops->writeop) return -1;
1185   if(iovcnt == 1) return ls->ops->writeop(ls, whence, iov[0].iov_base, iov[0].iov_len);
1186
1187   for(i=0;i<iovcnt;i++) s+=iov[i].iov_len;
1188   if(s > sizeof(stackbuff)) {
1189     tofree = buff = malloc(s);
1190     if(tofree == NULL) return -1;
1191   }
1192   else buff = stackbuff;
1193   for(i=0;i<iovcnt;i++) {
1194     memcpy(buff + ins, iov[i].iov_base, iov[i].iov_len);
1195     ins += iov[i].iov_len;
1196   }
1197   i = ls->ops->writeop(ls, whence, buff, s);
1198   if(tofree) free(tofree);
1199   return i;
1200 }
1201
1202 static int
1203 noit_log_line(noit_log_stream_t ls, noit_log_stream_t bitor,
1204               struct timeval *whence,
1205               const char *timebuf, int timebuflen,
1206               const char *debugbuf, int debugbuflen,
1207               const char *buffer, size_t len) {
1208   int rv = 0;
1209   struct _noit_log_stream_outlet_list *node;
1210   struct _noit_log_stream bitor_onstack;
1211   memcpy(&bitor_onstack, ls, sizeof(bitor_onstack));
1212   if(bitor) {
1213     bitor_onstack.name = bitor->name;
1214     bitor_onstack.flags |= bitor->flags & NOIT_LOG_STREAM_FACILITY;
1215   }
1216   bitor = &bitor_onstack;
1217   if(ls->ops) {
1218     int iovcnt = 0;
1219     struct iovec iov[6];
1220     if(IS_TIMESTAMPS_ON(bitor)) {
1221       iov[iovcnt].iov_base = (void *)timebuf;
1222       iov[iovcnt].iov_len = timebuflen;
1223       iovcnt++;
1224     }
1225     if(IS_FACILITY_ON(bitor)) {
1226       iov[iovcnt].iov_base = (void *)"[";
1227       iov[iovcnt].iov_len = 1;
1228       iovcnt++;
1229       iov[iovcnt].iov_base = (void *)bitor->name;
1230       iov[iovcnt].iov_len = strlen(bitor->name);
1231       iovcnt++;
1232       iov[iovcnt].iov_base = (void *)"] ";
1233       iov[iovcnt].iov_len = 2;
1234       iovcnt++;
1235     }
1236     if(IS_DEBUG_ON(bitor)) {
1237       iov[iovcnt].iov_base = (void *)debugbuf;
1238       iov[iovcnt].iov_len = debugbuflen;
1239       iovcnt++;
1240     }
1241     iov[iovcnt].iov_base = (void *)buffer;
1242     iov[iovcnt].iov_len = len;
1243     iovcnt++;
1244     rv = noit_log_writev(ls, whence, iov, iovcnt);
1245   }
1246   for(node = ls->outlets; node; node = node->next) {
1247     int srv = 0;
1248     debug_printf(" %s -> %s\n", ls->name, node->outlet->name);
1249     srv = noit_log_line(node->outlet, bitor, whence, timebuf,
1250                         timebuflen, debugbuf, debugbuflen, buffer, len);
1251     if(srv) rv = srv;
1252   }
1253   return rv;
1254 }
1255 int
1256 noit_vlog(noit_log_stream_t ls, struct timeval *now,
1257           const char *file, int line,
1258           const char *format, va_list arg) {
1259   int rv = 0, allocd = 0;
1260   char buffer[4096], *dynbuff = NULL;
1261 #ifdef va_copy
1262   va_list copy;
1263 #endif
1264
1265   if(IS_ENABLED_ON(ls) || NOIT_LOG_LOG_ENABLED()) {
1266     int len;
1267     char tbuf[48], dbuf[80];
1268     int tbuflen = 0, dbuflen = 0;
1269     MATERIALIZE_DEPS(ls);
1270     if(IS_TIMESTAMPS_BELOW(ls)) {
1271       struct tm _tm, *tm;
1272       char tempbuf[32];
1273       time_t s = (time_t)now->tv_sec;
1274       tm = localtime_r(&s, &_tm);
1275       strftime(tempbuf, sizeof(tempbuf), "%Y-%m-%d %H:%M:%S", tm);
1276       snprintf(tbuf, sizeof(tbuf), "[%s.%06d] ", tempbuf, (int)now->tv_usec);
1277       tbuflen = strlen(tbuf);
1278     }
1279     else tbuf[0] = '\0';
1280     if(IS_DEBUG_BELOW(ls)) {
1281       snprintf(dbuf, sizeof(dbuf), "[%s:%d] ", file, line);
1282       dbuflen = strlen(dbuf);
1283     }
1284     else dbuf[0] = '\0';
1285 #ifdef va_copy
1286     va_copy(copy, arg);
1287     len = vsnprintf(buffer, sizeof(buffer), format, copy);
1288     va_end(copy);
1289 #else
1290     len = vsnprintf(buffer, sizeof(buffer), format, arg);
1291 #endif
1292     if(len > sizeof(buffer)) {
1293       allocd = sizeof(buffer);
1294       while(len > allocd) { /* guaranteed true the first time */
1295         while(len > allocd) allocd <<= 2;
1296         if(dynbuff) free(dynbuff);
1297         dynbuff = malloc(allocd);
1298         assert(dynbuff);
1299 #ifdef va_copy
1300         va_copy(copy, arg);
1301         len = vsnprintf(dynbuff, allocd, format, copy);
1302         va_end(copy);
1303 #else
1304         len = vsnprintf(dynbuff, allocd, format, arg);
1305 #endif
1306       }
1307       NOIT_LOG_LOG(ls->name, (char *)file, line, dynbuff);
1308       if(IS_ENABLED_ON(ls))
1309         rv = noit_log_line(ls, NULL, now, tbuf, tbuflen, dbuf, dbuflen, dynbuff, len);
1310       free(dynbuff);
1311     }
1312     else {
1313       NOIT_LOG_LOG(ls->name, (char *)file, line, buffer);
1314       if(IS_ENABLED_ON(ls))
1315         rv = noit_log_line(ls, NULL, now, tbuf, tbuflen, dbuf, dbuflen, buffer, len);
1316     }
1317     if(rv == len) return 0;
1318     return -1;
1319   }
1320   return 0;
1321 }
1322
1323 int
1324 noit_log(noit_log_stream_t ls, struct timeval *now,
1325          const char *file, int line, const char *format, ...) {
1326   int rv;
1327   va_list arg;
1328   va_start(arg, format);
1329   rv = noit_vlog(ls, now, file, line, format, arg);
1330   va_end(arg);
1331   return rv;
1332 }
1333
1334 int
1335 noit_log_reopen_all() {
1336   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1337   const char *k;
1338   int klen, rv = 0;
1339   void *data;
1340   noit_log_stream_t ls;
1341
1342   while(noit_hash_next(&noit_loggers, &iter, &k, &klen, &data)) {
1343     ls = data;
1344     if(ls->ops) if(ls->ops->reopenop(ls) < 0) rv = -1;
1345   }
1346   return rv;
1347 }
1348
1349 int
1350 noit_log_list(noit_log_stream_t *loggers, int nsize) {
1351   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1352   const char *k;
1353   int klen, count = 0, total = 0, out_of_space_flag = 1;
1354   void *data;
1355
1356   while(noit_hash_next(&noit_loggers, &iter, &k, &klen, &data)) {
1357     if(count < nsize) loggers[count++] = (noit_log_stream_t)data;
1358     else out_of_space_flag = -1;
1359     total++;
1360   }
1361   return total * out_of_space_flag;
1362 }
1363
Note: See TracBrowser for help on using the browser.