tor  0.4.1.0-alpha-dev
log.c
Go to the documentation of this file.
1 /* Copyright (c) 2001, Matej Pfajfar.
2  * Copyright (c) 2001-2004, Roger Dingledine.
3  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4  * Copyright (c) 2007-2019, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
6 
12 #include "orconfig.h"
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #ifdef HAVE_SYS_TIME_H
18 #include <sys/time.h>
19 #endif
20 #ifdef HAVE_TIME_H
21 #include <time.h>
22 #endif
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26 #ifdef HAVE_SYS_TYPES_H
27 #include <sys/types.h>
28 #endif
29 #ifdef HAVE_FCNTL_H
30 #include <fcntl.h>
31 #endif
32 
33 #define LOG_PRIVATE
34 #include "lib/log/log.h"
35 #include "lib/log/log_sys.h"
36 #include "lib/version/git_revision.h"
37 #include "lib/log/ratelim.h"
38 #include "lib/lock/compat_mutex.h"
42 #include "lib/err/torerr.h"
43 #include "lib/intmath/bits.h"
45 #include "lib/string/printf.h"
46 #include "lib/malloc/malloc.h"
47 #include "lib/string/util_string.h"
51 #include "lib/fdio/fdio.h"
52 #include "lib/cc/ctassert.h"
53 
54 #ifdef HAVE_ANDROID_LOG_H
55 #include <android/log.h>
56 #endif // HAVE_ANDROID_LOG_H.
57 
60 #define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR)
61 
65 #define TRUNCATED_STR "[...truncated]"
66 #define TRUNCATED_STR_LEN 14
67 
71 const int LOG_WARN_ = LOG_WARN;
72 const int LOG_NOTICE_ = LOG_NOTICE;
73 const log_domain_mask_t LD_GENERAL_ = LD_GENERAL;
74 const log_domain_mask_t LD_NET_ = LD_NET;
75 
77 typedef struct logfile_t {
78  struct logfile_t *next;
79  char *filename;
80  int fd;
81  int seems_dead;
84  int is_syslog;
85  int is_android;
86  char *android_tag;
90 } logfile_t;
91 
92 static void log_free_(logfile_t *victim);
93 #define log_free(lg) \
94  FREE_AND_NULL(logfile_t, log_free_, (lg))
95 
97 static inline const char *
98 sev_to_string(int severity)
99 {
100  switch (severity) {
101  case LOG_DEBUG: return "debug";
102  case LOG_INFO: return "info";
103  case LOG_NOTICE: return "notice";
104  case LOG_WARN: return "warn";
105  case LOG_ERR: return "err";
106  default: /* Call raw_assert, not tor_assert, since tor_assert
107  * calls log on failure. */
108  raw_assert_unreached(); return "UNKNOWN"; // LCOV_EXCL_LINE
109  }
110 }
111 
113 static inline int
115 {
116  switch (severity) {
117  case LOG_DEBUG:
118  case LOG_INFO:
119  /* All debugging messages occur in interesting places. */
120  return (domain & LD_NOFUNCNAME) == 0;
121  case LOG_NOTICE:
122  case LOG_WARN:
123  case LOG_ERR:
124  /* We care about places where bugs occur. */
125  return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG;
126  default:
127  /* Call raw_assert, not tor_assert, since tor_assert calls
128  * log on failure. */
129  raw_assert(0); return 0; // LCOV_EXCL_LINE
130  }
131 }
132 
133 #ifdef HAVE_ANDROID_LOG_H
134 
137 static int
138 severity_to_android_log_priority(int severity)
139 {
140  switch (severity) {
141  case LOG_DEBUG:
142  return ANDROID_LOG_VERBOSE;
143  case LOG_INFO:
144  return ANDROID_LOG_DEBUG;
145  case LOG_NOTICE:
146  return ANDROID_LOG_INFO;
147  case LOG_WARN:
148  return ANDROID_LOG_WARN;
149  case LOG_ERR:
150  return ANDROID_LOG_ERROR;
151  default:
152  // LCOV_EXCL_START
153  raw_assert(0);
154  return 0;
155  // LCOV_EXCL_STOP
156  }
157 }
158 #endif // HAVE_ANDROID_LOG_H.
159 
163 static int log_mutex_initialized = 0;
164 
166 static logfile_t *logfiles = NULL;
168 static int log_domains_are_logged = 0;
169 
170 #ifdef HAVE_SYSLOG_H
171 
173 static int syslog_count = 0;
174 #endif
175 
178 typedef struct pending_log_message_t {
179  int severity;
181  char *fullmsg;
182  char *msg;
184 
187 
189 static pending_callback_callback pending_cb_cb = NULL;
190 
194 
199 
202 static int queue_startup_messages = 1;
203 
205 static int pretty_fn_has_parens = 0;
206 
209 #define MAX_STARTUP_MSG_LEN (1<<16)
210 
212 #define LOCK_LOGS() STMT_BEGIN \
213  raw_assert(log_mutex_initialized); \
214  tor_mutex_acquire(&log_mutex); \
215  STMT_END
216 
217 #define UNLOCK_LOGS() STMT_BEGIN \
218  raw_assert(log_mutex_initialized); \
219  tor_mutex_release(&log_mutex); \
220  STMT_END
221 
225 
226 static void delete_log(logfile_t *victim);
227 static void close_log(logfile_t *victim);
228 
229 static char *domain_to_string(log_domain_mask_t domain,
230  char *buf, size_t buflen);
231 static inline char *format_msg(char *buf, size_t buf_len,
232  log_domain_mask_t domain, int severity, const char *funcname,
233  const char *suffix,
234  const char *format, va_list ap, size_t *msg_len_out)
235  CHECK_PRINTF(7,0);
236 
239 static char *appname = NULL;
240 
246 void
247 log_set_application_name(const char *name)
248 {
249  tor_free(appname);
250  appname = name ? tor_strdup(name) : NULL;
251 }
252 
257 int
259 {
260  (void) domain;
261  return (severity <= log_global_min_severity_);
262 }
263 
270 void
271 tor_log_string(int severity, log_domain_mask_t domain,
272  const char *function, const char *string)
273 {
274  log_fn_(severity, domain, function, "%s", string);
275 }
276 
278 static int log_time_granularity = 1;
279 
282 void
283 set_log_time_granularity(int granularity_msec)
284 {
285  log_time_granularity = granularity_msec;
286  tor_log_sigsafe_err_set_granularity(granularity_msec);
287 }
288 
292 static inline size_t
293 log_prefix_(char *buf, size_t buf_len, int severity)
294 {
295  time_t t;
296  struct timeval now;
297  struct tm tm;
298  size_t n;
299  int r, ms;
300 
301  tor_gettimeofday(&now);
302  t = (time_t)now.tv_sec;
303  ms = (int)now.tv_usec / 1000;
304  if (log_time_granularity >= 1000) {
305  t -= t % (log_time_granularity / 1000);
306  ms = 0;
307  } else {
308  ms -= ((int)now.tv_usec / 1000) % log_time_granularity;
309  }
310 
311  n = strftime(buf, buf_len, "%b %d %H:%M:%S",
312  tor_localtime_r_msg(&t, &tm, NULL));
313  r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ", ms,
314  sev_to_string(severity));
315 
316  if (r<0)
317  return buf_len-1;
318  else
319  return n+r;
320 }
321 
327 static int
328 log_tor_version(logfile_t *lf, int reset)
329 {
330  char buf[256];
331  size_t n;
332  int is_new;
333 
334  if (!lf->needs_close)
335  /* If it doesn't get closed, it isn't really a file. */
336  return 0;
337  if (lf->is_temporary)
338  /* If it's temporary, it isn't really a file. */
339  return 0;
340 
341  is_new = lf->fd >= 0 && tor_fd_getpos(lf->fd) == 0;
342 
343  if (reset && !is_new)
344  /* We are resetting, but we aren't at the start of the file; no
345  * need to log again. */
346  return 0;
347  n = log_prefix_(buf, sizeof(buf), LOG_NOTICE);
348  if (appname) {
349  tor_snprintf(buf+n, sizeof(buf)-n,
350  "%s opening %slog file.\n", appname, is_new?"new ":"");
351  } else {
352  tor_snprintf(buf+n, sizeof(buf)-n,
353  "Tor %s opening %slog file.\n", VERSION, is_new?"new ":"");
354  }
355  if (write_all_to_fd_minimal(lf->fd, buf, strlen(buf)) < 0) /* error */
356  return -1; /* failed */
357  return 0;
358 }
359 
365 static inline char *
366 format_msg(char *buf, size_t buf_len,
367  log_domain_mask_t domain, int severity, const char *funcname,
368  const char *suffix,
369  const char *format, va_list ap, size_t *msg_len_out)
370 {
371  size_t n;
372  int r;
373  char *end_of_prefix;
374  char *buf_end;
375 
376  raw_assert(buf_len >= 16); /* prevent integer underflow and stupidity */
377  buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */
378  buf_end = buf+buf_len; /* point *after* the last char we can write to */
379 
380  n = log_prefix_(buf, buf_len, severity);
381  end_of_prefix = buf+n;
382 
384  char *cp = buf+n;
385  if (cp == buf_end) goto format_msg_no_room_for_domains;
386  *cp++ = '{';
387  if (cp == buf_end) goto format_msg_no_room_for_domains;
388  cp = domain_to_string(domain, cp, (buf+buf_len-cp));
389  if (cp == buf_end) goto format_msg_no_room_for_domains;
390  *cp++ = '}';
391  if (cp == buf_end) goto format_msg_no_room_for_domains;
392  *cp++ = ' ';
393  if (cp == buf_end) goto format_msg_no_room_for_domains;
394  end_of_prefix = cp;
395  n = cp-buf;
396  format_msg_no_room_for_domains:
397  /* This will leave end_of_prefix and n unchanged, and thus cause
398  * whatever log domain string we had written to be clobbered. */
399  ;
400  }
401 
402  if (funcname && should_log_function_name(domain, severity)) {
403  r = tor_snprintf(buf+n, buf_len-n,
404  pretty_fn_has_parens ? "%s: " : "%s(): ",
405  funcname);
406  if (r<0)
407  n = strlen(buf);
408  else
409  n += r;
410  }
411 
412  if (domain == LD_BUG && buf_len-n > 6) {
413  memcpy(buf+n, "Bug: ", 6);
414  n += 5;
415  }
416 
417  r = tor_vsnprintf(buf+n,buf_len-n,format,ap);
418  if (r < 0) {
419  /* The message was too long; overwrite the end of the buffer with
420  * "[...truncated]" */
421  if (buf_len >= TRUNCATED_STR_LEN) {
422  size_t offset = buf_len-TRUNCATED_STR_LEN;
423  /* We have an extra 2 characters after buf_len to hold the \n\0,
424  * so it's safe to add 1 to the size here. */
425  strlcpy(buf+offset, TRUNCATED_STR, buf_len-offset+1);
426  }
427  /* Set 'n' to the end of the buffer, where we'll be writing \n\0.
428  * Since we already subtracted 2 from buf_len, this is safe.*/
429  n = buf_len;
430  } else {
431  n += r;
432  if (suffix) {
433  size_t suffix_len = strlen(suffix);
434  if (buf_len-n >= suffix_len) {
435  memcpy(buf+n, suffix, suffix_len);
436  n += suffix_len;
437  }
438  }
439  }
440 
441  if (domain == LD_BUG &&
442  buf_len - n > strlen(tor_bug_suffix)+1) {
443  memcpy(buf+n, tor_bug_suffix, strlen(tor_bug_suffix));
444  n += strlen(tor_bug_suffix);
445  }
446 
447  buf[n]='\n';
448  buf[n+1]='\0';
449  *msg_len_out = n+1;
450  return end_of_prefix;
451 }
452 
453 /* Create a new pending_log_message_t with appropriate values */
454 static pending_log_message_t *
455 pending_log_message_new(int severity, log_domain_mask_t domain,
456  const char *fullmsg, const char *shortmsg)
457 {
458  pending_log_message_t *m = tor_malloc(sizeof(pending_log_message_t));
459  m->severity = severity;
460  m->domain = domain;
461  m->fullmsg = fullmsg ? tor_strdup(fullmsg) : NULL;
462  m->msg = tor_strdup(shortmsg);
463  return m;
464 }
465 
466 #define pending_log_message_free(msg) \
467  FREE_AND_NULL(pending_log_message_t, pending_log_message_free_, (msg))
468 
470 static void
472 {
473  if (!msg)
474  return;
475  tor_free(msg->msg);
476  tor_free(msg->fullmsg);
477  tor_free(msg);
478 }
479 
483 static inline int
485 {
486  raw_assert(lf);
487  return lf->is_syslog || lf->is_android || lf->callback;
488 }
489 
493 static inline int
494 logfile_wants_message(const logfile_t *lf, int severity,
495  log_domain_mask_t domain)
496 {
497  if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
498  return 0;
499  }
500  if (! (lf->fd >= 0 || logfile_is_external(lf))) {
501  return 0;
502  }
503  if (lf->seems_dead) {
504  return 0;
505  }
506 
507  return 1;
508 }
509 
516 static inline void
517 logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len,
518  const char *msg_after_prefix, log_domain_mask_t domain,
519  int severity, int *callbacks_deferred)
520 {
521 
522  if (lf->is_syslog) {
523 #ifdef HAVE_SYSLOG_H
524 #ifdef MAXLINE
525  /* Some syslog implementations have limits on the length of what you can
526  * pass them, and some very old ones do not detect overflow so well.
527  * Regrettably, they call their maximum line length MAXLINE. */
528 #if MAXLINE < 64
529 #warn "MAXLINE is a very low number; it might not be from syslog.h after all"
530 #endif
531  char *m = msg_after_prefix;
532  if (msg_len >= MAXLINE)
533  m = tor_strndup(msg_after_prefix, MAXLINE-1);
534  syslog(severity, "%s", m);
535  if (m != msg_after_prefix) {
536  tor_free(m);
537  }
538 #else /* !(defined(MAXLINE)) */
539  /* We have syslog but not MAXLINE. That's promising! */
540  syslog(severity, "%s", msg_after_prefix);
541 #endif /* defined(MAXLINE) */
542 #endif /* defined(HAVE_SYSLOG_H) */
543  } else if (lf->is_android) {
544 #ifdef HAVE_ANDROID_LOG_H
545  int priority = severity_to_android_log_priority(severity);
546  __android_log_write(priority, lf->android_tag, msg_after_prefix);
547 #endif // HAVE_ANDROID_LOG_H.
548  } else if (lf->callback) {
549  if (domain & LD_NOCB) {
550  if (!*callbacks_deferred && pending_cb_messages) {
552  pending_log_message_new(severity,domain,NULL,msg_after_prefix));
553  *callbacks_deferred = 1;
554  if (smartlist_len(pending_cb_messages) == 1 && pending_cb_cb) {
555  pending_cb_cb();
556  }
557  }
558  } else {
559  lf->callback(severity, domain, msg_after_prefix);
560  }
561  } else {
562  if (write_all_to_fd_minimal(lf->fd, buf, msg_len) < 0) { /* error */
563  /* don't log the error! mark this log entry to be blown away, and
564  * continue. */
565  lf->seems_dead = 1;
566  }
567  }
568 }
569 
574 MOCK_IMPL(STATIC void,
575 logv,(int severity, log_domain_mask_t domain, const char *funcname,
576  const char *suffix, const char *format, va_list ap))
577 {
578  char buf[10240];
579  size_t msg_len = 0;
580  int formatted = 0;
581  logfile_t *lf;
582  char *end_of_prefix=NULL;
583  int callbacks_deferred = 0;
584 
585  /* Call raw_assert, not tor_assert, since tor_assert calls log on failure. */
586  raw_assert(format);
587  /* check that severity is sane. Overrunning the masks array leads to
588  * interesting and hard to diagnose effects */
589  raw_assert(severity >= LOG_ERR && severity <= LOG_DEBUG);
590  /* check that we've initialised the log mutex before we try to lock it */
591  raw_assert(log_mutex_initialized);
592  LOCK_LOGS();
593 
594  if ((! (domain & LD_NOCB)) && pending_cb_messages
595  && smartlist_len(pending_cb_messages))
597 
600  end_of_prefix =
601  format_msg(buf, sizeof(buf), domain, severity, funcname, suffix,
602  format, ap, &msg_len);
603  formatted = 1;
604 
606  pending_log_message_new(severity,domain,buf,end_of_prefix));
607  pending_startup_messages_len += msg_len;
608  }
609 
610  for (lf = logfiles; lf; lf = lf->next) {
611  if (! logfile_wants_message(lf, severity, domain))
612  continue;
613 
614  if (!formatted) {
615  end_of_prefix =
616  format_msg(buf, sizeof(buf), domain, severity, funcname, suffix,
617  format, ap, &msg_len);
618  formatted = 1;
619  }
620 
621  logfile_deliver(lf, buf, msg_len, end_of_prefix, domain, severity,
622  &callbacks_deferred);
623  }
624  UNLOCK_LOGS();
625 }
626 
631 void
632 tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
633 {
634  va_list ap;
635  if (severity > log_global_min_severity_)
636  return;
637  va_start(ap,format);
638 #ifdef TOR_UNIT_TESTS
639  if (domain & LD_NO_MOCK)
640  logv__real(severity, domain, NULL, NULL, format, ap);
641  else
642 #endif
643  logv(severity, domain, NULL, NULL, format, ap);
644  va_end(ap);
645 }
646 
649 static int
650 int_array_contains(const int *array, int n, int item)
651 {
652  int j;
653  for (j = 0; j < n; ++j) {
654  if (array[j] == item)
655  return 1;
656  }
657  return 0;
658 }
659 
662 void
664 {
665  const logfile_t *lf;
666  int found_real_stderr = 0;
667 
668  int fds[TOR_SIGSAFE_LOG_MAX_FDS];
669  int n_fds;
670 
671  LOCK_LOGS();
672  /* Reserve the first one for stderr. This is safe because when we daemonize,
673  * we dup2 /dev/null to stderr, */
674  fds[0] = STDERR_FILENO;
675  n_fds = 1;
676 
677  for (lf = logfiles; lf; lf = lf->next) {
678  /* Don't try callback to the control port, or syslogs: We can't
679  * do them from a signal handler. Don't try stdout: we always do stderr.
680  */
681  if (lf->is_temporary || logfile_is_external(lf)
682  || lf->seems_dead || lf->fd < 0)
683  continue;
685  (LD_BUG|LD_GENERAL)) {
686  if (lf->fd == STDERR_FILENO)
687  found_real_stderr = 1;
688  /* Avoid duplicates */
689  if (int_array_contains(fds, n_fds, lf->fd))
690  continue;
691  fds[n_fds++] = lf->fd;
692  if (n_fds == TOR_SIGSAFE_LOG_MAX_FDS)
693  break;
694  }
695  }
696 
697  if (!found_real_stderr &&
698  int_array_contains(fds, n_fds, STDOUT_FILENO)) {
699  /* Don't use a virtual stderr when we're also logging to stdout. */
700  raw_assert(n_fds >= 2); /* Don't tor_assert inside log fns */
701  fds[0] = fds[--n_fds];
702  }
703 
704  UNLOCK_LOGS();
705 
706  tor_log_set_sigsafe_err_fds(fds, n_fds);
707 }
708 
711 void
713 {
714  logfile_t *lf;
715  raw_assert(out);
716 
717  LOCK_LOGS();
718 
719  for (lf = logfiles; lf; lf = lf->next) {
720  if (lf->is_temporary || logfile_is_external(lf))
721  continue;
722  if (lf->filename == NULL)
723  continue;
724  smartlist_add_strdup(out, lf->filename);
725  }
726 
727  UNLOCK_LOGS();
728 }
729 
733 void
734 log_fn_(int severity, log_domain_mask_t domain, const char *fn,
735  const char *format, ...)
736 {
737  va_list ap;
738  if (severity > log_global_min_severity_)
739  return;
740  va_start(ap,format);
741  logv(severity, domain, fn, NULL, format, ap);
742  va_end(ap);
743 }
744 void
745 log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain,
746  const char *fn, const char *format, ...)
747 {
748  va_list ap;
749  char *m;
750  if (severity > log_global_min_severity_)
751  return;
752  m = rate_limit_log(ratelim, approx_time());
753  if (m == NULL)
754  return;
755  va_start(ap, format);
756  logv(severity, domain, fn, m, format, ap);
757  va_end(ap);
758  tor_free(m);
759 }
760 
762 static void
764 {
765  if (!victim)
766  return;
767  tor_free(victim->severities);
768  tor_free(victim->filename);
769  tor_free(victim->android_tag);
770  tor_free(victim);
771 }
772 
774 void
776 {
777  logfile_t *victim, *next;
778  smartlist_t *messages, *messages2;
779  LOCK_LOGS();
780  next = logfiles;
781  logfiles = NULL;
782  messages = pending_cb_messages;
783  pending_cb_messages = NULL;
784  pending_cb_cb = NULL;
785  messages2 = pending_startup_messages;
787  UNLOCK_LOGS();
788  while (next) {
789  victim = next;
790  next = next->next;
791  close_log(victim);
792  log_free(victim);
793  }
794  tor_free(appname);
795 
796  SMARTLIST_FOREACH(messages, pending_log_message_t *, msg, {
797  pending_log_message_free(msg);
798  });
799  smartlist_free(messages);
800 
801  if (messages2) {
802  SMARTLIST_FOREACH(messages2, pending_log_message_t *, msg, {
803  pending_log_message_free(msg);
804  });
805  smartlist_free(messages2);
806  }
807 
808  /* We _could_ destroy the log mutex here, but that would screw up any logs
809  * that happened between here and the end of execution. */
810 }
811 
820 static void
822 {
823  logfile_t *tmpl;
824  if (victim == logfiles)
825  logfiles = victim->next;
826  else {
827  for (tmpl = logfiles; tmpl && tmpl->next != victim; tmpl=tmpl->next) ;
828 // raw_assert(tmpl);
829 // raw_assert(tmpl->next == victim);
830  if (!tmpl)
831  return;
832  tmpl->next = victim->next;
833  }
834  log_free(victim);
835 }
836 
839 static void
841 {
842  if (victim->needs_close && victim->fd >= 0) {
843  close(victim->fd);
844  victim->fd = -1;
845  } else if (victim->is_syslog) {
846 #ifdef HAVE_SYSLOG_H
847  if (--syslog_count == 0) {
848  /* There are no other syslogs; close the logging facility. */
849  closelog();
850  }
851 #endif /* defined(HAVE_SYSLOG_H) */
852  }
853 }
854 
858 void
859 set_log_severity_config(int loglevelMin, int loglevelMax,
860  log_severity_list_t *severity_out)
861 {
862  int i;
863  raw_assert(loglevelMin >= loglevelMax);
864  raw_assert(loglevelMin >= LOG_ERR && loglevelMin <= LOG_DEBUG);
865  raw_assert(loglevelMax >= LOG_ERR && loglevelMax <= LOG_DEBUG);
866  memset(severity_out, 0, sizeof(log_severity_list_t));
867  for (i = loglevelMin; i >= loglevelMax; --i) {
868  severity_out->masks[SEVERITY_MASK_IDX(i)] = ~0u;
869  }
870 }
871 
874 static void
876  const char *name, int fd)
877 {
878  logfile_t *lf;
879  lf = tor_malloc_zero(sizeof(logfile_t));
880  lf->fd = fd;
881  lf->filename = tor_strdup(name);
882  lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
883  lf->next = logfiles;
884 
885  logfiles = lf;
887 }
888 
892 void
893 add_stream_log(const log_severity_list_t *severity, const char *name, int fd)
894 {
895  LOCK_LOGS();
896  add_stream_log_impl(severity, name, fd);
897  UNLOCK_LOGS();
898 }
899 
901 void
902 init_logging(int disable_startup_queue)
903 {
904  if (!log_mutex_initialized) {
907  }
908 #ifdef __GNUC__
909  if (strchr(__PRETTY_FUNCTION__, '(')) {
911  }
912 #endif
913  if (pending_cb_messages == NULL)
914  pending_cb_messages = smartlist_new();
915  if (disable_startup_queue)
918  pending_startup_messages = smartlist_new();
919  }
920 }
921 
924 void
926 {
927  LOCK_LOGS();
928  log_domains_are_logged = enabled;
929  UNLOCK_LOGS();
930 }
931 
935 void
936 add_temp_log(int min_severity)
937 {
938  log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t));
939  set_log_severity_config(min_severity, LOG_ERR, s);
940  LOCK_LOGS();
941  add_stream_log_impl(s, "<temp>", fileno(stdout));
942  tor_free(s);
943  logfiles->is_temporary = 1;
944  UNLOCK_LOGS();
945 }
946 
959 void
960 logs_set_pending_callback_callback(pending_callback_callback cb)
961 {
962  pending_cb_cb = cb;
963 }
964 
969 int
971 {
972  logfile_t *lf;
973  lf = tor_malloc_zero(sizeof(logfile_t));
974  lf->fd = -1;
975  lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
976  lf->filename = tor_strdup("<callback>");
977  lf->callback = cb;
978  lf->next = logfiles;
979 
980  LOCK_LOGS();
981  logfiles = lf;
983  UNLOCK_LOGS();
984  return 0;
985 }
986 
989 void
990 change_callback_log_severity(int loglevelMin, int loglevelMax,
991  log_callback cb)
992 {
993  logfile_t *lf;
994  log_severity_list_t severities;
995  set_log_severity_config(loglevelMin, loglevelMax, &severities);
996  LOCK_LOGS();
997  for (lf = logfiles; lf; lf = lf->next) {
998  if (lf->callback == cb) {
999  memcpy(lf->severities, &severities, sizeof(severities));
1000  }
1001  }
1003  UNLOCK_LOGS();
1004 }
1005 
1008 void
1010 {
1011  logfile_t *lf;
1012  smartlist_t *messages, *messages_tmp;
1013 
1014  LOCK_LOGS();
1015  if (!pending_cb_messages || 0 == smartlist_len(pending_cb_messages)) {
1016  UNLOCK_LOGS();
1017  return;
1018  }
1019 
1020  messages = pending_cb_messages;
1021  pending_cb_messages = smartlist_new();
1022  do {
1024  const int severity = msg->severity;
1025  const int domain = msg->domain;
1026  for (lf = logfiles; lf; lf = lf->next) {
1027  if (! lf->callback || lf->seems_dead ||
1028  ! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
1029  continue;
1030  }
1031  lf->callback(severity, domain, msg->msg);
1032  }
1033  pending_log_message_free(msg);
1034  } SMARTLIST_FOREACH_END(msg);
1035  smartlist_clear(messages);
1036 
1037  messages_tmp = pending_cb_messages;
1038  pending_cb_messages = messages;
1039  messages = messages_tmp;
1040  } while (smartlist_len(messages));
1041 
1042  smartlist_free(messages);
1043 
1044  UNLOCK_LOGS();
1045 }
1046 
1050 void
1052 {
1053  logfile_t *lf;
1054 
1055  LOCK_LOGS();
1059  goto out;
1060 
1062  msg) {
1063  int callbacks_deferred = 0;
1064  for (lf = logfiles; lf; lf = lf->next) {
1065  if (! logfile_wants_message(lf, msg->severity, msg->domain))
1066  continue;
1067 
1068  /* We configure a temporary startup log that goes to stdout, so we
1069  * shouldn't replay to stdout/stderr*/
1070  if (lf->fd == STDOUT_FILENO || lf->fd == STDERR_FILENO) {
1071  continue;
1072  }
1073 
1074  logfile_deliver(lf, msg->fullmsg, strlen(msg->fullmsg), msg->msg,
1075  msg->domain, msg->severity, &callbacks_deferred);
1076  }
1077  pending_log_message_free(msg);
1078  } SMARTLIST_FOREACH_END(msg);
1079  smartlist_free(pending_startup_messages);
1080  pending_startup_messages = NULL;
1081 
1082  out:
1083  UNLOCK_LOGS();
1084 }
1085 
1088 void
1090 {
1091  logfile_t *lf, **p;
1092 
1093  LOCK_LOGS();
1094  for (p = &logfiles; *p; ) {
1095  if ((*p)->is_temporary) {
1096  lf = *p;
1097  /* we use *p here to handle the edge case of the head of the list */
1098  *p = (*p)->next;
1099  close_log(lf);
1100  log_free(lf);
1101  } else {
1102  p = &((*p)->next);
1103  }
1104  }
1105 
1107  UNLOCK_LOGS();
1108 }
1109 
1112 void
1114 {
1115  logfile_t *lf;
1116  LOCK_LOGS();
1117  for (lf = logfiles; lf; lf = lf->next)
1118  lf->is_temporary = ! lf->is_temporary;
1119  UNLOCK_LOGS();
1120  close_temp_logs();
1121 }
1122 
1124 void
1126 {
1127  logfile_t *lf;
1128  LOCK_LOGS();
1129  for (lf = logfiles; lf; lf = lf->next)
1130  lf->is_temporary = 1;
1131  UNLOCK_LOGS();
1132 }
1133 
1139 int
1141  const char *filename,
1142  int fd)
1143 {
1144  logfile_t *lf;
1145 
1146  if (fd<0)
1147  return -1;
1148  if (tor_fd_seekend(fd)<0) {
1149  close(fd);
1150  return -1;
1151  }
1152 
1153  LOCK_LOGS();
1154  add_stream_log_impl(severity, filename, fd);
1155  logfiles->needs_close = 1;
1156  lf = logfiles;
1158 
1159  if (log_tor_version(lf, 0) < 0) {
1160  delete_log(lf);
1161  }
1162  UNLOCK_LOGS();
1163 
1164  return 0;
1165 }
1166 
1167 #ifdef HAVE_SYSLOG_H
1168 
1174 int
1175 add_syslog_log(const log_severity_list_t *severity,
1176  const char* syslog_identity_tag)
1177 {
1178  logfile_t *lf;
1179  if (syslog_count++ == 0) {
1180  /* This is the first syslog. */
1181  static char buf[256];
1182  if (syslog_identity_tag) {
1183  tor_snprintf(buf, sizeof(buf), "Tor-%s", syslog_identity_tag);
1184  } else {
1185  tor_snprintf(buf, sizeof(buf), "Tor");
1186  }
1187  openlog(buf, LOG_PID | LOG_NDELAY, LOGFACILITY);
1188  }
1189 
1190  lf = tor_malloc_zero(sizeof(logfile_t));
1191  lf->fd = -1;
1192  lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
1193  lf->filename = tor_strdup("<syslog>");
1194  lf->is_syslog = 1;
1195 
1196  LOCK_LOGS();
1197  lf->next = logfiles;
1198  logfiles = lf;
1200  UNLOCK_LOGS();
1201  return 0;
1202 }
1203 #endif /* defined(HAVE_SYSLOG_H) */
1204 
1205 #ifdef HAVE_ANDROID_LOG_H
1206 
1209 int
1210 add_android_log(const log_severity_list_t *severity,
1211  const char *android_tag)
1212 {
1213  logfile_t *lf = NULL;
1214 
1215  lf = tor_malloc_zero(sizeof(logfile_t));
1216  lf->fd = -1;
1217  lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
1218  lf->filename = tor_strdup("<android>");
1219  lf->is_android = 1;
1220 
1221  if (android_tag == NULL)
1222  lf->android_tag = tor_strdup("Tor");
1223  else {
1224  char buf[256];
1225  tor_snprintf(buf, sizeof(buf), "Tor-%s", android_tag);
1226  lf->android_tag = tor_strdup(buf);
1227  }
1228 
1229  LOCK_LOGS();
1230  lf->next = logfiles;
1231  logfiles = lf;
1233  UNLOCK_LOGS();
1234  return 0;
1235 }
1236 #endif // HAVE_ANDROID_LOG_H.
1237 
1240 int
1241 parse_log_level(const char *level)
1242 {
1243  if (!strcasecmp(level, "err"))
1244  return LOG_ERR;
1245  if (!strcasecmp(level, "warn"))
1246  return LOG_WARN;
1247  if (!strcasecmp(level, "notice"))
1248  return LOG_NOTICE;
1249  if (!strcasecmp(level, "info"))
1250  return LOG_INFO;
1251  if (!strcasecmp(level, "debug"))
1252  return LOG_DEBUG;
1253  return -1;
1254 }
1255 
1257 const char *
1259 {
1260  return sev_to_string(level);
1261 }
1262 
1268 static const char *domain_list[] = {
1269  "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
1270  "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
1271  "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL",
1272  "SCHED", "GUARD", "CONSDIFF", "DOS", "PROCESS", "PT", "BTRACK", "MESG",
1273  NULL
1274 };
1275 
1276 CTASSERT(ARRAY_LENGTH(domain_list) == N_LOGGING_DOMAINS + 1);
1277 
1280 static log_domain_mask_t
1281 parse_log_domain(const char *domain)
1282 {
1283  int i;
1284  for (i=0; domain_list[i]; ++i) {
1285  if (!strcasecmp(domain, domain_list[i]))
1286  return (1u<<i);
1287  }
1288  return 0;
1289 }
1290 
1292 static char *
1293 domain_to_string(log_domain_mask_t domain, char *buf, size_t buflen)
1294 {
1295  char *cp = buf;
1296  char *eos = buf+buflen;
1297 
1298  buf[0] = '\0';
1299  if (! domain)
1300  return buf;
1301  while (1) {
1302  const char *d;
1303  int bit = tor_log2(domain);
1304  size_t n;
1305  if ((unsigned)bit >= ARRAY_LENGTH(domain_list)-1 ||
1306  bit >= N_LOGGING_DOMAINS) {
1307  tor_snprintf(buf, buflen, "<BUG:Unknown domain %lx>", (long)domain);
1308  return buf+strlen(buf);
1309  }
1310  d = domain_list[bit];
1311  n = strlcpy(cp, d, eos-cp);
1312  if (n >= buflen) {
1313  tor_snprintf(buf, buflen, "<BUG:Truncating domain %lx>", (long)domain);
1314  return buf+strlen(buf);
1315  }
1316  cp += n;
1317  domain &= ~(1<<bit);
1318 
1319  if (domain == 0 || (eos-cp) < 2)
1320  return cp;
1321 
1322  memcpy(cp, ",", 2); /*Nul-terminated ,"*/
1323  cp++;
1324  }
1325 }
1326 
1344 int
1345 parse_log_severity_config(const char **cfg_ptr,
1346  log_severity_list_t *severity_out)
1347 {
1348  const char *cfg = *cfg_ptr;
1349  int got_anything = 0;
1350  int got_an_unqualified_range = 0;
1351  memset(severity_out, 0, sizeof(*severity_out));
1352 
1353  cfg = eat_whitespace(cfg);
1354  while (*cfg) {
1355  const char *dash, *space;
1356  char *sev_lo, *sev_hi;
1357  int low, high, i;
1358  log_domain_mask_t domains = ~0u;
1359 
1360  if (*cfg == '[') {
1361  int err = 0;
1362  char *domains_str;
1363  smartlist_t *domains_list;
1364  log_domain_mask_t neg_domains = 0;
1365  const char *closebracket = strchr(cfg, ']');
1366  if (!closebracket)
1367  return -1;
1368  domains = 0;
1369  domains_str = tor_strndup(cfg+1, closebracket-cfg-1);
1370  domains_list = smartlist_new();
1371  smartlist_split_string(domains_list, domains_str, ",", SPLIT_SKIP_SPACE,
1372  -1);
1373  tor_free(domains_str);
1374  SMARTLIST_FOREACH_BEGIN(domains_list, const char *, domain) {
1375  if (!strcmp(domain, "*")) {
1376  domains = ~0u;
1377  } else {
1378  int d;
1379  int negate=0;
1380  if (*domain == '~') {
1381  negate = 1;
1382  ++domain;
1383  }
1384  d = parse_log_domain(domain);
1385  if (!d) {
1386  log_warn(LD_CONFIG, "No such logging domain as %s", domain);
1387  err = 1;
1388  } else {
1389  if (negate)
1390  neg_domains |= d;
1391  else
1392  domains |= d;
1393  }
1394  }
1395  } SMARTLIST_FOREACH_END(domain);
1396  SMARTLIST_FOREACH(domains_list, char *, d, tor_free(d));
1397  smartlist_free(domains_list);
1398  if (err)
1399  return -1;
1400  if (domains == 0 && neg_domains)
1401  domains = ~neg_domains;
1402  else
1403  domains &= ~neg_domains;
1404  cfg = eat_whitespace(closebracket+1);
1405  } else {
1406  ++got_an_unqualified_range;
1407  }
1408  if (!strcasecmpstart(cfg, "file") ||
1409  !strcasecmpstart(cfg, "stderr") ||
1410  !strcasecmpstart(cfg, "stdout") ||
1411  !strcasecmpstart(cfg, "syslog") ||
1412  !strcasecmpstart(cfg, "android")) {
1413  goto done;
1414  }
1415  if (got_an_unqualified_range > 1)
1416  return -1;
1417 
1418  space = find_whitespace(cfg);
1419  dash = strchr(cfg, '-');
1420  if (dash && dash < space) {
1421  sev_lo = tor_strndup(cfg, dash-cfg);
1422  sev_hi = tor_strndup(dash+1, space-(dash+1));
1423  } else {
1424  sev_lo = tor_strndup(cfg, space-cfg);
1425  sev_hi = tor_strdup("ERR");
1426  }
1427  low = parse_log_level(sev_lo);
1428  high = parse_log_level(sev_hi);
1429  tor_free(sev_lo);
1430  tor_free(sev_hi);
1431  if (low == -1)
1432  return -1;
1433  if (high == -1)
1434  return -1;
1435 
1436  got_anything = 1;
1437  for (i=low; i >= high; --i)
1438  severity_out->masks[SEVERITY_MASK_IDX(i)] |= domains;
1439 
1440  cfg = eat_whitespace(space);
1441  }
1442 
1443  done:
1444  *cfg_ptr = cfg;
1445  return got_anything ? 0 : -1;
1446 }
1447 
1449 int
1451 {
1452  logfile_t *lf;
1453  int i;
1454  int min = LOG_ERR;
1455  for (lf = logfiles; lf; lf = lf->next) {
1456  for (i = LOG_DEBUG; i > min; --i)
1457  if (lf->severities->masks[SEVERITY_MASK_IDX(i)])
1458  min = i;
1459  }
1460  return min;
1461 }
1462 
1464 void
1466 {
1467  logfile_t *lf;
1468  int i;
1469  LOCK_LOGS();
1470  for (lf = logfiles; lf; lf=lf->next) {
1471  for (i = LOG_DEBUG; i >= LOG_ERR; --i)
1472  lf->severities->masks[SEVERITY_MASK_IDX(i)] = ~0u;
1473  }
1475  UNLOCK_LOGS();
1476 }
1477 
1479 void
1481 {
1482  logfile_t *lf;
1483  for (lf = logfiles; lf; lf = lf->next) {
1484  if (lf->fd >= 0) {
1485  tor_ftruncate(lf->fd);
1486  }
1487  }
1488 }
static const char * domain_list[]
Definition: log.c:1268
static void logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len, const char *msg_after_prefix, log_domain_mask_t domain, int severity, int *callbacks_deferred)
Definition: log.c:517
log_domain_mask_t masks[LOG_DEBUG-LOG_ERR+1]
Definition: log.h:141
char * filename
Definition: log.c:79
int add_file_log(const log_severity_list_t *severity, const char *filename, int fd)
Definition: log.c:1140
int write_all_to_fd_minimal(int fd, const char *buf, size_t count)
Definition: fdio.c:107
void init_logging(int disable_startup_queue)
Definition: log.c:902
Header for printf.c.
static void delete_log(logfile_t *victim)
Definition: log.c:821
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
void logs_free_all(void)
Definition: log.c:775
uint32_t log_domain_mask_t
Definition: log.h:134
char * fullmsg
Definition: log.c:181
static int queue_startup_messages
Definition: log.c:202
int tor_fd_seekend(int fd)
Definition: fdio.c:60
void rollback_log_changes(void)
Definition: log.c:1113
int add_callback_log(const log_severity_list_t *severity, log_callback cb)
Definition: log.c:970
Summarize similar messages that would otherwise flood the logs.
static int pretty_fn_has_parens
Definition: log.c:205
Definition: log.c:77
Definitions for timing-related constants.
#define CTASSERT(x)
Definition: ctassert.h:44
const int LOG_WARN_
Definition: log.c:71
int needs_close
Definition: log.c:82
void smartlist_add_strdup(struct smartlist_t *sl, const char *string)
#define LD_GENERAL
Definition: log.h:59
void(* log_callback)(int severity, uint32_t domain, const char *msg)
Definition: log.h:145
void tor_log_sigsafe_err_set_granularity(int ms)
Definition: torerr.c:140
static void close_log(logfile_t *victim)
Definition: log.c:840
#define LOG_INFO
Definition: log.h:42
Header for fdio.c.
void tor_log(int severity, log_domain_mask_t domain, const char *format,...)
Definition: log.c:632
void smartlist_add(smartlist_t *sl, void *element)
static const char * sev_to_string(int severity)
Definition: log.c:98
struct pending_log_message_t pending_log_message_t
static int should_log_function_name(log_domain_mask_t domain, int severity)
Definition: log.c:114
int get_min_log_level(void)
Definition: log.c:1450
struct tm * tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
Definition: time_to_tm.c:133
char * rate_limit_log(ratelim_t *lim, time_t now)
Definition: ratelim.c:41
void tor_log_get_logfile_names(smartlist_t *out)
Definition: log.c:712
void tor_log_string(int severity, log_domain_mask_t domain, const char *function, const char *string)
Definition: log.c:271
#define SEVERITY_MASK_IDX(sev)
Definition: log.c:60
Header for smartlist_split.c.
const char * find_whitespace(const char *s)
Definition: util_string.c:344
void tor_log_set_sigsafe_err_fds(const int *fds, int n)
Definition: torerr.c:115
off_t tor_fd_getpos(int fd)
Definition: fdio.c:47
static char * format_msg(char *buf, size_t buf_len, log_domain_mask_t domain, int severity, const char *funcname, const char *suffix, const char *format, va_list ap, size_t *msg_len_out) CHECK_PRINTF(7
Definition: log.c:366
int log_global_min_severity_
Definition: log.c:224
void set_log_time_granularity(int granularity_msec)
Definition: log.c:283
void logs_set_pending_callback_callback(pending_callback_callback cb)
Definition: log.c:960
#define tor_free(p)
Definition: malloc.h:52
Header for util_string.c.
static size_t pending_startup_messages_len
Definition: log.c:198
#define LOG_NOTICE
Definition: log.h:47
Header for time_to_tm.c.
static char * domain_to_string(log_domain_mask_t domain, char *buf, size_t buflen)
Definition: log.c:1293
void add_temp_log(int min_severity)
Definition: log.c:936
static char static char * appname
Definition: log.c:239
Headers for util_malloc.c.
Top-level declarations for the smartlist_t dynamic array type.
static int log_time_granularity
Definition: log.c:278
void tor_log_update_sigsafe_err_fds(void)
Definition: log.c:663
#define MAX_STARTUP_MSG_LEN
Definition: log.c:209
static int logfile_is_external(const logfile_t *lf)
Definition: log.c:484
static int log_tor_version(logfile_t *lf, int reset)
Definition: log.c:328
static smartlist_t * pending_startup_messages
Definition: log.c:193
#define LD_NOFUNCNAME
Definition: log.h:126
Macros for iterating over the elements of a smartlist_t.
void flush_pending_log_callbacks(void)
Definition: log.c:1009
void mark_logs_temp(void)
Definition: log.c:1125
static logfile_t * logfiles
Definition: log.c:166
struct logfile_t logfile_t
int tor_ftruncate(int fd)
Definition: fdio.c:91
void log_set_application_name(const char *name)
Definition: log.c:247
MOCK_IMPL(STATIC void, logv,(int severity, log_domain_mask_t domain, const char *funcname, const char *suffix, const char *format, va_list ap))
Definition: log.c:574
#define TOR_SIGSAFE_LOG_MAX_FDS
Definition: torerr.h:37
static log_domain_mask_t parse_log_domain(const char *domain)
Definition: log.c:1281
#define LD_NOCB
Definition: log.h:123
void change_callback_log_severity(int loglevelMin, int loglevelMax, log_callback cb)
Definition: log.c:990
const char * eat_whitespace(const char *s)
Definition: util_string.c:268
struct logfile_t * next
Definition: log.c:78
Declare subsystem object for the logging module.
void tor_mutex_init(tor_mutex_t *m)
#define LOG_WARN
Definition: log.h:50
log_severity_list_t * severities
Definition: log.c:88
void log_fn_(int severity, log_domain_mask_t domain, const char *fn, const char *format,...)
Definition: log.c:734
Header for approx_time.c.
void flush_log_messages_from_startup(void)
Definition: log.c:1051
int parse_log_severity_config(const char **cfg_ptr, log_severity_list_t *severity_out)
Definition: log.c:1345
void truncate_logs(void)
Definition: log.c:1480
void logs_set_domain_logging(int enabled)
Definition: log.c:925
char * android_tag
Definition: log.c:86
static tor_mutex_t log_mutex
Definition: log.c:161
int tor_log2(uint64_t u64)
Definition: bits.c:16
#define LOG_ERR
Definition: log.h:53
static int log_domains_are_logged
Definition: log.c:168
log_domain_mask_t domain
Definition: log.c:180
log_callback callback
Definition: log.c:87
Compile-time assertions: CTASSERT(expression).
static size_t log_prefix_(char *buf, size_t buf_len, int severity)
Definition: log.c:293
static pending_callback_callback pending_cb_cb
Definition: log.c:189
int seems_dead
Definition: log.c:81
int parse_log_level(const char *level)
Definition: log.c:1241
#define LOCK_LOGS()
Definition: log.c:212
#define TRUNCATED_STR
Definition: log.c:65
int tor_snprintf(char *str, size_t size, const char *format,...)
Definition: printf.c:27
static void add_stream_log_impl(const log_severity_list_t *severity, const char *name, int fd)
Definition: log.c:875
#define SMARTLIST_FOREACH(sl, type, var, cmd)
Header for compat_mutex.c.
void close_temp_logs(void)
Definition: log.c:1089
int log_message_is_interesting(int severity, log_domain_mask_t domain)
Definition: log.c:258
#define ARRAY_LENGTH(x)
void add_stream_log(const log_severity_list_t *severity, const char *name, int fd)
Definition: log.c:893
void switch_logs_debug(void)
Definition: log.c:1465
static int log_mutex_initialized
Definition: log.c:163
int fd
Definition: log.c:80
Header for bits.c.
const char * log_level_to_string(int level)
Definition: log.c:1258
#define UNLOCK_LOGS()
Definition: log.c:217
void set_log_severity_config(int loglevelMin, int loglevelMax, log_severity_list_t *severity_out)
Definition: log.c:859
Header for compat_string.c.
Headers for torerr.c.
time_t approx_time(void)
Definition: approx_time.c:32
int is_syslog
Definition: log.c:84
int is_temporary
Definition: log.c:83
#define LOG_DEBUG
Definition: log.h:39
#define LD_NET
Definition: log.h:63
Headers for log.c.
int is_android
Definition: log.c:85
Header for tor_gettimeofday.c.
void smartlist_clear(smartlist_t *sl)
static smartlist_t * pending_cb_messages
Definition: log.c:186
int strcasecmpstart(const char *s1, const char *s2)
Definition: util_string.c:216
int tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
Definition: printf.c:41
static void log_free_(logfile_t *victim)
Definition: log.c:763
#define LD_BUG
Definition: log.h:83
#define LD_CONFIG
Definition: log.h:65
static int int_array_contains(const int *array, int n, int item)
Definition: log.c:650
static void pending_log_message_free_(pending_log_message_t *msg)
Definition: log.c:471
static int logfile_wants_message(const logfile_t *lf, int severity, log_domain_mask_t domain)
Definition: log.c:494
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, int flags, int max)