Back to index

tor  0.2.3.19-rc
log.c
Go to the documentation of this file.
00001 /* Copyright (c) 2001, Matej Pfajfar.
00002  * Copyright (c) 2001-2004, Roger Dingledine.
00003  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
00004  * Copyright (c) 2007-2012, The Tor Project, Inc. */
00005 /* See LICENSE for licensing information */
00006 
00012 #include "orconfig.h"
00013 #include <stdarg.h>
00014 #include <assert.h>
00015 // #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #ifdef HAVE_SYS_TIME_H
00019 #include <sys/time.h>
00020 #endif
00021 #ifdef HAVE_TIME_H
00022 #include <time.h>
00023 #endif
00024 #ifdef HAVE_UNISTD_H
00025 #include <unistd.h>
00026 #endif
00027 #ifdef HAVE_SYS_TYPES_H
00028 #include <sys/types.h>
00029 #endif
00030 #ifdef HAVE_FCNTL_H
00031 #include <fcntl.h>
00032 #endif
00033 #include "compat.h"
00034 #include "util.h"
00035 #define LOG_PRIVATE
00036 #include "torlog.h"
00037 #include "container.h"
00038 
00042 #define TRUNCATED_STR "[...truncated]"
00043 #define TRUNCATED_STR_LEN 14
00044 
00047 typedef struct logfile_t {
00048   struct logfile_t *next; 
00049   char *filename; 
00050   int fd; 
00051   int seems_dead; 
00052   int needs_close; 
00053   int is_temporary; 
00054   int is_syslog; 
00055   log_callback callback; 
00056   log_severity_list_t *severities; 
00058 } logfile_t;
00059 
00060 static void log_free(logfile_t *victim);
00061 
00063 static INLINE const char *
00064 sev_to_string(int severity)
00065 {
00066   switch (severity) {
00067     case LOG_DEBUG:   return "debug";
00068     case LOG_INFO:    return "info";
00069     case LOG_NOTICE:  return "notice";
00070     case LOG_WARN:    return "warn";
00071     case LOG_ERR:     return "err";
00072     default:          /* Call assert, not tor_assert, since tor_assert
00073                        * calls log on failure. */
00074                       assert(0); return "UNKNOWN";
00075   }
00076 }
00077 
00079 static INLINE int
00080 should_log_function_name(log_domain_mask_t domain, int severity)
00081 {
00082   switch (severity) {
00083     case LOG_DEBUG:
00084     case LOG_INFO:
00085       /* All debugging messages occur in interesting places. */
00086       return 1;
00087     case LOG_NOTICE:
00088     case LOG_WARN:
00089     case LOG_ERR:
00090       /* We care about places where bugs occur. */
00091       return (domain == LD_BUG);
00092     default:
00093       /* Call assert, not tor_assert, since tor_assert calls log on failure. */
00094       assert(0); return 0;
00095   }
00096 }
00097 
00099 static tor_mutex_t log_mutex;
00101 static int log_mutex_initialized = 0;
00102 
00104 static logfile_t *logfiles = NULL;
00106 static int log_domains_are_logged = 0;
00107 
00108 #ifdef HAVE_SYSLOG_H
00109 
00111 static int syslog_count = 0;
00112 #endif
00113 
00116 typedef struct pending_cb_message_t {
00117   int severity; 
00118   log_domain_mask_t domain; 
00119   char *msg; 
00120 } pending_cb_message_t;
00121 
00123 static smartlist_t *pending_cb_messages = NULL;
00124 
00126 #define LOCK_LOGS() STMT_BEGIN                                          \
00127   tor_mutex_acquire(&log_mutex);                                        \
00128   STMT_END
00129 
00130 #define UNLOCK_LOGS() STMT_BEGIN tor_mutex_release(&log_mutex); STMT_END
00131 
00134 int _log_global_min_severity = LOG_NOTICE;
00135 
00136 static void delete_log(logfile_t *victim);
00137 static void close_log(logfile_t *victim);
00138 
00139 static char *domain_to_string(log_domain_mask_t domain,
00140                              char *buf, size_t buflen);
00141 static INLINE char *format_msg(char *buf, size_t buf_len,
00142            log_domain_mask_t domain, int severity, const char *funcname,
00143            const char *format, va_list ap, size_t *msg_len_out)
00144   CHECK_PRINTF(6,0);
00145 static void logv(int severity, log_domain_mask_t domain, const char *funcname,
00146                  const char *format, va_list ap)
00147   CHECK_PRINTF(4,0);
00148 
00151 static char *appname = NULL;
00152 
00158 void
00159 log_set_application_name(const char *name)
00160 {
00161   tor_free(appname);
00162   appname = name ? tor_strdup(name) : NULL;
00163 }
00164 
00166 static int log_time_granularity = 1;
00167 
00170 void
00171 set_log_time_granularity(int granularity_msec)
00172 {
00173   log_time_granularity = granularity_msec;
00174 }
00175 
00179 static INLINE size_t
00180 _log_prefix(char *buf, size_t buf_len, int severity)
00181 {
00182   time_t t;
00183   struct timeval now;
00184   struct tm tm;
00185   size_t n;
00186   int r, ms;
00187 
00188   tor_gettimeofday(&now);
00189   t = (time_t)now.tv_sec;
00190   ms = (int)now.tv_usec / 1000;
00191   if (log_time_granularity >= 1000) {
00192     t -= t % (log_time_granularity / 1000);
00193     ms = 0;
00194   } else {
00195     ms -= ((int)now.tv_usec / 1000) % log_time_granularity;
00196   }
00197 
00198   n = strftime(buf, buf_len, "%b %d %H:%M:%S", tor_localtime_r(&t, &tm));
00199   r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ", ms,
00200                    sev_to_string(severity));
00201 
00202   if (r<0)
00203     return buf_len-1;
00204   else
00205     return n+r;
00206 }
00207 
00213 static int
00214 log_tor_version(logfile_t *lf, int reset)
00215 {
00216   char buf[256];
00217   size_t n;
00218   int is_new;
00219 
00220   if (!lf->needs_close)
00221     /* If it doesn't get closed, it isn't really a file. */
00222     return 0;
00223   if (lf->is_temporary)
00224     /* If it's temporary, it isn't really a file. */
00225     return 0;
00226 
00227   is_new = lf->fd >= 0 && tor_fd_getpos(lf->fd) == 0;
00228 
00229   if (reset && !is_new)
00230     /* We are resetting, but we aren't at the start of the file; no
00231      * need to log again. */
00232     return 0;
00233   n = _log_prefix(buf, sizeof(buf), LOG_NOTICE);
00234   if (appname) {
00235     tor_snprintf(buf+n, sizeof(buf)-n,
00236                  "%s opening %slog file.\n", appname, is_new?"new ":"");
00237   } else {
00238     tor_snprintf(buf+n, sizeof(buf)-n,
00239                  "Tor %s opening %slog file.\n", VERSION, is_new?"new ":"");
00240   }
00241   if (write_all(lf->fd, buf, strlen(buf), 0) < 0) /* error */
00242     return -1; /* failed */
00243   return 0;
00244 }
00245 
00251 static INLINE char *
00252 format_msg(char *buf, size_t buf_len,
00253            log_domain_mask_t domain, int severity, const char *funcname,
00254            const char *format, va_list ap, size_t *msg_len_out)
00255 {
00256   size_t n;
00257   int r;
00258   char *end_of_prefix;
00259   char *buf_end;
00260 
00261   assert(buf_len >= 16); /* prevent integer underflow and general stupidity */
00262   buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */
00263   buf_end = buf+buf_len; /* point *after* the last char we can write to */
00264 
00265   n = _log_prefix(buf, buf_len, severity);
00266   end_of_prefix = buf+n;
00267 
00268   if (log_domains_are_logged) {
00269     char *cp = buf+n;
00270     if (cp == buf_end) goto format_msg_no_room_for_domains;
00271     *cp++ = '{';
00272     if (cp == buf_end) goto format_msg_no_room_for_domains;
00273     cp = domain_to_string(domain, cp, (buf+buf_len-cp));
00274     if (cp == buf_end) goto format_msg_no_room_for_domains;
00275     *cp++ = '}';
00276     if (cp == buf_end) goto format_msg_no_room_for_domains;
00277     *cp++ = ' ';
00278     if (cp == buf_end) goto format_msg_no_room_for_domains;
00279     end_of_prefix = cp;
00280     n = cp-buf;
00281   format_msg_no_room_for_domains:
00282     /* This will leave end_of_prefix and n unchanged, and thus cause
00283      * whatever log domain string we had written to be clobbered. */
00284     ;
00285   }
00286 
00287   if (funcname && should_log_function_name(domain, severity)) {
00288     r = tor_snprintf(buf+n, buf_len-n, "%s(): ", funcname);
00289     if (r<0)
00290       n = strlen(buf);
00291     else
00292       n += r;
00293   }
00294 
00295   if (domain == LD_BUG && buf_len-n > 6) {
00296     memcpy(buf+n, "Bug: ", 6);
00297     n += 5;
00298   }
00299 
00300   r = tor_vsnprintf(buf+n,buf_len-n,format,ap);
00301   if (r < 0) {
00302     /* The message was too long; overwrite the end of the buffer with
00303      * "[...truncated]" */
00304     if (buf_len >= TRUNCATED_STR_LEN) {
00305       size_t offset = buf_len-TRUNCATED_STR_LEN;
00306       /* We have an extra 2 characters after buf_len to hold the \n\0,
00307        * so it's safe to add 1 to the size here. */
00308       strlcpy(buf+offset, TRUNCATED_STR, buf_len-offset+1);
00309     }
00310     /* Set 'n' to the end of the buffer, where we'll be writing \n\0.
00311      * Since we already subtracted 2 from buf_len, this is safe.*/
00312     n = buf_len;
00313   } else {
00314     n += r;
00315   }
00316   buf[n]='\n';
00317   buf[n+1]='\0';
00318   *msg_len_out = n+1;
00319   return end_of_prefix;
00320 }
00321 
00326 static void
00327 logv(int severity, log_domain_mask_t domain, const char *funcname,
00328      const char *format, va_list ap)
00329 {
00330   char buf[10024];
00331   size_t msg_len = 0;
00332   int formatted = 0;
00333   logfile_t *lf;
00334   char *end_of_prefix=NULL;
00335   int callbacks_deferred = 0;
00336 
00337   /* Call assert, not tor_assert, since tor_assert calls log on failure. */
00338   assert(format);
00339   /* check that severity is sane.  Overrunning the masks array leads to
00340    * interesting and hard to diagnose effects */
00341   assert(severity >= LOG_ERR && severity <= LOG_DEBUG);
00342   LOCK_LOGS();
00343 
00344   if ((! (domain & LD_NOCB)) && smartlist_len(pending_cb_messages))
00345     flush_pending_log_callbacks();
00346 
00347   lf = logfiles;
00348   while (lf) {
00349     if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
00350       lf = lf->next;
00351       continue;
00352     }
00353     if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) {
00354       lf = lf->next;
00355       continue;
00356     }
00357     if (lf->seems_dead) {
00358       lf = lf->next;
00359       continue;
00360     }
00361 
00362     if (!formatted) {
00363       end_of_prefix =
00364         format_msg(buf, sizeof(buf), domain, severity, funcname, format, ap,
00365                    &msg_len);
00366       formatted = 1;
00367     }
00368 
00369     if (lf->is_syslog) {
00370 #ifdef HAVE_SYSLOG_H
00371       char *m = end_of_prefix;
00372 #ifdef MAXLINE
00373       /* Some syslog implementations have limits on the length of what you can
00374        * pass them, and some very old ones do not detect overflow so well.
00375        * Regrettably, they call their maximum line length MAXLINE. */
00376 #if MAXLINE < 64
00377 #warn "MAXLINE is a very low number; it might not be from syslog.h after all"
00378 #endif
00379       if (msg_len >= MAXLINE)
00380         m = tor_strndup(end_of_prefix, MAXLINE-1);
00381 #endif
00382       syslog(severity, "%s", m);
00383 #ifdef MAXLINE
00384       if (m != end_of_prefix) {
00385         tor_free(m);
00386       }
00387 #endif
00388 #endif
00389       lf = lf->next;
00390       continue;
00391     } else if (lf->callback) {
00392       if (domain & LD_NOCB) {
00393         if (!callbacks_deferred && pending_cb_messages) {
00394           pending_cb_message_t *msg = tor_malloc(sizeof(pending_cb_message_t));
00395           msg->severity = severity;
00396           msg->domain = domain;
00397           msg->msg = tor_strdup(end_of_prefix);
00398           smartlist_add(pending_cb_messages, msg);
00399 
00400           callbacks_deferred = 1;
00401         }
00402       } else {
00403         lf->callback(severity, domain, end_of_prefix);
00404       }
00405       lf = lf->next;
00406       continue;
00407     }
00408     if (write_all(lf->fd, buf, msg_len, 0) < 0) { /* error */
00409       /* don't log the error! mark this log entry to be blown away, and
00410        * continue. */
00411       lf->seems_dead = 1;
00412     }
00413     lf = lf->next;
00414   }
00415   UNLOCK_LOGS();
00416 }
00417 
00422 void
00423 tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
00424 {
00425   va_list ap;
00426   if (severity > _log_global_min_severity)
00427     return;
00428   va_start(ap,format);
00429   logv(severity, domain, NULL, format, ap);
00430   va_end(ap);
00431 }
00432 
00434 #ifdef __GNUC__
00435 
00438 void
00439 _log_fn(int severity, log_domain_mask_t domain, const char *fn,
00440         const char *format, ...)
00441 {
00442   va_list ap;
00443   if (severity > _log_global_min_severity)
00444     return;
00445   va_start(ap,format);
00446   logv(severity, domain, fn, format, ap);
00447   va_end(ap);
00448 }
00449 #else
00450 
00455 const char *_log_fn_function_name=NULL;
00456 void
00457 _log_fn(int severity, log_domain_mask_t domain, const char *format, ...)
00458 {
00459   va_list ap;
00460   if (severity > _log_global_min_severity)
00461     return;
00462   va_start(ap,format);
00463   logv(severity, domain, _log_fn_function_name, format, ap);
00464   va_end(ap);
00465   _log_fn_function_name = NULL;
00466 }
00467 void
00468 _log_debug(log_domain_mask_t domain, const char *format, ...)
00469 {
00470   va_list ap;
00471   /* For GCC we do this check in the macro. */
00472   if (PREDICT_LIKELY(LOG_DEBUG > _log_global_min_severity))
00473     return;
00474   va_start(ap,format);
00475   logv(LOG_DEBUG, domain, _log_fn_function_name, format, ap);
00476   va_end(ap);
00477   _log_fn_function_name = NULL;
00478 }
00479 void
00480 _log_info(log_domain_mask_t domain, const char *format, ...)
00481 {
00482   va_list ap;
00483   if (LOG_INFO > _log_global_min_severity)
00484     return;
00485   va_start(ap,format);
00486   logv(LOG_INFO, domain, _log_fn_function_name, format, ap);
00487   va_end(ap);
00488   _log_fn_function_name = NULL;
00489 }
00490 void
00491 _log_notice(log_domain_mask_t domain, const char *format, ...)
00492 {
00493   va_list ap;
00494   if (LOG_NOTICE > _log_global_min_severity)
00495     return;
00496   va_start(ap,format);
00497   logv(LOG_NOTICE, domain, _log_fn_function_name, format, ap);
00498   va_end(ap);
00499   _log_fn_function_name = NULL;
00500 }
00501 void
00502 _log_warn(log_domain_mask_t domain, const char *format, ...)
00503 {
00504   va_list ap;
00505   if (LOG_WARN > _log_global_min_severity)
00506     return;
00507   va_start(ap,format);
00508   logv(LOG_WARN, domain, _log_fn_function_name, format, ap);
00509   va_end(ap);
00510   _log_fn_function_name = NULL;
00511 }
00512 void
00513 _log_err(log_domain_mask_t domain, const char *format, ...)
00514 {
00515   va_list ap;
00516   if (LOG_ERR > _log_global_min_severity)
00517     return;
00518   va_start(ap,format);
00519   logv(LOG_ERR, domain, _log_fn_function_name, format, ap);
00520   va_end(ap);
00521   _log_fn_function_name = NULL;
00522 }
00524 #endif
00525 
00527 static void
00528 log_free(logfile_t *victim)
00529 {
00530   if (!victim)
00531     return;
00532   tor_free(victim->severities);
00533   tor_free(victim->filename);
00534   tor_free(victim);
00535 }
00536 
00538 void
00539 logs_free_all(void)
00540 {
00541   logfile_t *victim, *next;
00542   smartlist_t *messages;
00543   LOCK_LOGS();
00544   next = logfiles;
00545   logfiles = NULL;
00546   messages = pending_cb_messages;
00547   pending_cb_messages = NULL;
00548   UNLOCK_LOGS();
00549   while (next) {
00550     victim = next;
00551     next = next->next;
00552     close_log(victim);
00553     log_free(victim);
00554   }
00555   tor_free(appname);
00556 
00557   SMARTLIST_FOREACH(messages, pending_cb_message_t *, msg, {
00558       tor_free(msg->msg);
00559       tor_free(msg);
00560     });
00561   smartlist_free(messages);
00562 
00563   /* We _could_ destroy the log mutex here, but that would screw up any logs
00564    * that happened between here and the end of execution. */
00565 }
00566 
00575 static void
00576 delete_log(logfile_t *victim)
00577 {
00578   logfile_t *tmpl;
00579   if (victim == logfiles)
00580     logfiles = victim->next;
00581   else {
00582     for (tmpl = logfiles; tmpl && tmpl->next != victim; tmpl=tmpl->next) ;
00583 //    tor_assert(tmpl);
00584 //    tor_assert(tmpl->next == victim);
00585     if (!tmpl)
00586       return;
00587     tmpl->next = victim->next;
00588   }
00589   log_free(victim);
00590 }
00591 
00594 static void
00595 close_log(logfile_t *victim)
00596 {
00597   if (victim->needs_close && victim->fd >= 0) {
00598     close(victim->fd);
00599     victim->fd = -1;
00600   } else if (victim->is_syslog) {
00601 #ifdef HAVE_SYSLOG_H
00602     if (--syslog_count == 0) {
00603       /* There are no other syslogs; close the logging facility. */
00604       closelog();
00605     }
00606 #endif
00607   }
00608 }
00609 
00613 void
00614 set_log_severity_config(int loglevelMin, int loglevelMax,
00615                         log_severity_list_t *severity_out)
00616 {
00617   int i;
00618   tor_assert(loglevelMin >= loglevelMax);
00619   tor_assert(loglevelMin >= LOG_ERR && loglevelMin <= LOG_DEBUG);
00620   tor_assert(loglevelMax >= LOG_ERR && loglevelMax <= LOG_DEBUG);
00621   memset(severity_out, 0, sizeof(log_severity_list_t));
00622   for (i = loglevelMin; i >= loglevelMax; --i) {
00623     severity_out->masks[SEVERITY_MASK_IDX(i)] = ~0u;
00624   }
00625 }
00626 
00629 static void
00630 add_stream_log_impl(const log_severity_list_t *severity,
00631                     const char *name, int fd)
00632 {
00633   logfile_t *lf;
00634   lf = tor_malloc_zero(sizeof(logfile_t));
00635   lf->fd = fd;
00636   lf->filename = tor_strdup(name);
00637   lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
00638   lf->next = logfiles;
00639 
00640   logfiles = lf;
00641   _log_global_min_severity = get_min_log_level();
00642 }
00643 
00647 void
00648 add_stream_log(const log_severity_list_t *severity, const char *name, int fd)
00649 {
00650   LOCK_LOGS();
00651   add_stream_log_impl(severity, name, fd);
00652   UNLOCK_LOGS();
00653 }
00654 
00656 void
00657 init_logging(void)
00658 {
00659   if (!log_mutex_initialized) {
00660     tor_mutex_init(&log_mutex);
00661     log_mutex_initialized = 1;
00662   }
00663   if (pending_cb_messages == NULL)
00664     pending_cb_messages = smartlist_new();
00665 }
00666 
00669 void
00670 logs_set_domain_logging(int enabled)
00671 {
00672   LOCK_LOGS();
00673   log_domains_are_logged = enabled;
00674   UNLOCK_LOGS();
00675 }
00676 
00680 void
00681 add_temp_log(int min_severity)
00682 {
00683   log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t));
00684   set_log_severity_config(min_severity, LOG_ERR, s);
00685   LOCK_LOGS();
00686   add_stream_log_impl(s, "<temp>", fileno(stdout));
00687   tor_free(s);
00688   logfiles->is_temporary = 1;
00689   UNLOCK_LOGS();
00690 }
00691 
00696 int
00697 add_callback_log(const log_severity_list_t *severity, log_callback cb)
00698 {
00699   logfile_t *lf;
00700   lf = tor_malloc_zero(sizeof(logfile_t));
00701   lf->fd = -1;
00702   lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
00703   lf->filename = tor_strdup("<callback>");
00704   lf->callback = cb;
00705   lf->next = logfiles;
00706 
00707   LOCK_LOGS();
00708   logfiles = lf;
00709   _log_global_min_severity = get_min_log_level();
00710   UNLOCK_LOGS();
00711   return 0;
00712 }
00713 
00716 void
00717 change_callback_log_severity(int loglevelMin, int loglevelMax,
00718                              log_callback cb)
00719 {
00720   logfile_t *lf;
00721   log_severity_list_t severities;
00722   set_log_severity_config(loglevelMin, loglevelMax, &severities);
00723   LOCK_LOGS();
00724   for (lf = logfiles; lf; lf = lf->next) {
00725     if (lf->callback == cb) {
00726       memcpy(lf->severities, &severities, sizeof(severities));
00727     }
00728   }
00729   _log_global_min_severity = get_min_log_level();
00730   UNLOCK_LOGS();
00731 }
00732 
00735 void
00736 flush_pending_log_callbacks(void)
00737 {
00738   logfile_t *lf;
00739   smartlist_t *messages, *messages_tmp;
00740 
00741   LOCK_LOGS();
00742   if (0 == smartlist_len(pending_cb_messages)) {
00743     UNLOCK_LOGS();
00744     return;
00745   }
00746 
00747   messages = pending_cb_messages;
00748   pending_cb_messages = smartlist_new();
00749   do {
00750     SMARTLIST_FOREACH_BEGIN(messages, pending_cb_message_t *, msg) {
00751       const int severity = msg->severity;
00752       const int domain = msg->domain;
00753       for (lf = logfiles; lf; lf = lf->next) {
00754         if (! lf->callback || lf->seems_dead ||
00755             ! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
00756           continue;
00757         }
00758         lf->callback(severity, domain, msg->msg);
00759       }
00760       tor_free(msg->msg);
00761       tor_free(msg);
00762     } SMARTLIST_FOREACH_END(msg);
00763     smartlist_clear(messages);
00764 
00765     messages_tmp = pending_cb_messages;
00766     pending_cb_messages = messages;
00767     messages = messages_tmp;
00768   } while (smartlist_len(messages));
00769 
00770   smartlist_free(messages);
00771 
00772   UNLOCK_LOGS();
00773 }
00774 
00777 void
00778 close_temp_logs(void)
00779 {
00780   logfile_t *lf, **p;
00781 
00782   LOCK_LOGS();
00783   for (p = &logfiles; *p; ) {
00784     if ((*p)->is_temporary) {
00785       lf = *p;
00786       /* we use *p here to handle the edge case of the head of the list */
00787       *p = (*p)->next;
00788       close_log(lf);
00789       log_free(lf);
00790     } else {
00791       p = &((*p)->next);
00792     }
00793   }
00794 
00795   _log_global_min_severity = get_min_log_level();
00796   UNLOCK_LOGS();
00797 }
00798 
00801 void
00802 rollback_log_changes(void)
00803 {
00804   logfile_t *lf;
00805   LOCK_LOGS();
00806   for (lf = logfiles; lf; lf = lf->next)
00807     lf->is_temporary = ! lf->is_temporary;
00808   UNLOCK_LOGS();
00809   close_temp_logs();
00810 }
00811 
00813 void
00814 mark_logs_temp(void)
00815 {
00816   logfile_t *lf;
00817   LOCK_LOGS();
00818   for (lf = logfiles; lf; lf = lf->next)
00819     lf->is_temporary = 1;
00820   UNLOCK_LOGS();
00821 }
00822 
00827 int
00828 add_file_log(const log_severity_list_t *severity, const char *filename)
00829 {
00830   int fd;
00831   logfile_t *lf;
00832 
00833   fd = tor_open_cloexec(filename, O_WRONLY|O_CREAT|O_APPEND, 0644);
00834   if (fd<0)
00835     return -1;
00836   if (tor_fd_seekend(fd)<0)
00837     return -1;
00838 
00839   LOCK_LOGS();
00840   add_stream_log_impl(severity, filename, fd);
00841   logfiles->needs_close = 1;
00842   lf = logfiles;
00843   _log_global_min_severity = get_min_log_level();
00844 
00845   if (log_tor_version(lf, 0) < 0) {
00846     delete_log(lf);
00847   }
00848   UNLOCK_LOGS();
00849 
00850   return 0;
00851 }
00852 
00853 #ifdef HAVE_SYSLOG_H
00854 
00857 int
00858 add_syslog_log(const log_severity_list_t *severity)
00859 {
00860   logfile_t *lf;
00861   if (syslog_count++ == 0)
00862     /* This is the first syslog. */
00863     openlog("Tor", LOG_PID | LOG_NDELAY, LOGFACILITY);
00864 
00865   lf = tor_malloc_zero(sizeof(logfile_t));
00866   lf->fd = -1;
00867   lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
00868   lf->filename = tor_strdup("<syslog>");
00869   lf->is_syslog = 1;
00870 
00871   LOCK_LOGS();
00872   lf->next = logfiles;
00873   logfiles = lf;
00874   _log_global_min_severity = get_min_log_level();
00875   UNLOCK_LOGS();
00876   return 0;
00877 }
00878 #endif
00879 
00882 int
00883 parse_log_level(const char *level)
00884 {
00885   if (!strcasecmp(level, "err"))
00886     return LOG_ERR;
00887   if (!strcasecmp(level, "warn"))
00888     return LOG_WARN;
00889   if (!strcasecmp(level, "notice"))
00890     return LOG_NOTICE;
00891   if (!strcasecmp(level, "info"))
00892     return LOG_INFO;
00893   if (!strcasecmp(level, "debug"))
00894     return LOG_DEBUG;
00895   return -1;
00896 }
00897 
00899 const char *
00900 log_level_to_string(int level)
00901 {
00902   return sev_to_string(level);
00903 }
00904 
00907 static const char *domain_list[] = {
00908   "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
00909   "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
00910   "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", NULL
00911 };
00912 
00915 static log_domain_mask_t
00916 parse_log_domain(const char *domain)
00917 {
00918   int i;
00919   for (i=0; domain_list[i]; ++i) {
00920     if (!strcasecmp(domain, domain_list[i]))
00921       return (1u<<i);
00922   }
00923   return 0;
00924 }
00925 
00927 static char *
00928 domain_to_string(log_domain_mask_t domain, char *buf, size_t buflen)
00929 {
00930   char *cp = buf;
00931   char *eos = buf+buflen;
00932 
00933   buf[0] = '\0';
00934   if (! domain)
00935     return buf;
00936   while (1) {
00937     const char *d;
00938     int bit = tor_log2(domain);
00939     size_t n;
00940     if (bit >= N_LOGGING_DOMAINS) {
00941       tor_snprintf(buf, buflen, "<BUG:Unknown domain %lx>", (long)domain);
00942       return buf+strlen(buf);
00943     }
00944     d = domain_list[bit];
00945     n = strlcpy(cp, d, eos-cp);
00946     if (n >= buflen) {
00947       tor_snprintf(buf, buflen, "<BUG:Truncating domain %lx>", (long)domain);
00948       return buf+strlen(buf);
00949     }
00950     cp += n;
00951     domain &= ~(1<<bit);
00952 
00953     if (domain == 0 || (eos-cp) < 2)
00954       return cp;
00955 
00956     memcpy(cp, ",", 2); /*Nul-terminated ,"*/
00957     cp++;
00958   }
00959 }
00960 
00978 int
00979 parse_log_severity_config(const char **cfg_ptr,
00980                           log_severity_list_t *severity_out)
00981 {
00982   const char *cfg = *cfg_ptr;
00983   int got_anything = 0;
00984   int got_an_unqualified_range = 0;
00985   memset(severity_out, 0, sizeof(*severity_out));
00986 
00987   cfg = eat_whitespace(cfg);
00988   while (*cfg) {
00989     const char *dash, *space;
00990     char *sev_lo, *sev_hi;
00991     int low, high, i;
00992     log_domain_mask_t domains = ~0u;
00993 
00994     if (*cfg == '[') {
00995       int err = 0;
00996       char *domains_str;
00997       smartlist_t *domains_list;
00998       log_domain_mask_t neg_domains = 0;
00999       const char *closebracket = strchr(cfg, ']');
01000       if (!closebracket)
01001         return -1;
01002       domains = 0;
01003       domains_str = tor_strndup(cfg+1, closebracket-cfg-1);
01004       domains_list = smartlist_new();
01005       smartlist_split_string(domains_list, domains_str, ",", SPLIT_SKIP_SPACE,
01006                              -1);
01007       tor_free(domains_str);
01008       SMARTLIST_FOREACH(domains_list, const char *, domain,
01009           {
01010             if (!strcmp(domain, "*")) {
01011               domains = ~0u;
01012             } else {
01013               int d;
01014               int negate=0;
01015               if (*domain == '~') {
01016                 negate = 1;
01017                 ++domain;
01018               }
01019               d = parse_log_domain(domain);
01020               if (!d) {
01021                 log_warn(LD_CONFIG, "No such logging domain as %s", domain);
01022                 err = 1;
01023               } else {
01024                 if (negate)
01025                   neg_domains |= d;
01026                 else
01027                   domains |= d;
01028               }
01029             }
01030           });
01031       SMARTLIST_FOREACH(domains_list, char *, d, tor_free(d));
01032       smartlist_free(domains_list);
01033       if (err)
01034         return -1;
01035       if (domains == 0 && neg_domains)
01036         domains = ~neg_domains;
01037       else
01038         domains &= ~neg_domains;
01039       cfg = eat_whitespace(closebracket+1);
01040     } else {
01041       ++got_an_unqualified_range;
01042     }
01043     if (!strcasecmpstart(cfg, "file") ||
01044         !strcasecmpstart(cfg, "stderr") ||
01045         !strcasecmpstart(cfg, "stdout") ||
01046         !strcasecmpstart(cfg, "syslog")) {
01047       goto done;
01048     }
01049     if (got_an_unqualified_range > 1)
01050       return -1;
01051 
01052     space = strchr(cfg, ' ');
01053     dash = strchr(cfg, '-');
01054     if (!space)
01055       space = strchr(cfg, '\0');
01056     if (dash && dash < space) {
01057       sev_lo = tor_strndup(cfg, dash-cfg);
01058       sev_hi = tor_strndup(dash+1, space-(dash+1));
01059     } else {
01060       sev_lo = tor_strndup(cfg, space-cfg);
01061       sev_hi = tor_strdup("ERR");
01062     }
01063     low = parse_log_level(sev_lo);
01064     high = parse_log_level(sev_hi);
01065     tor_free(sev_lo);
01066     tor_free(sev_hi);
01067     if (low == -1)
01068       return -1;
01069     if (high == -1)
01070       return -1;
01071 
01072     got_anything = 1;
01073     for (i=low; i >= high; --i)
01074       severity_out->masks[SEVERITY_MASK_IDX(i)] |= domains;
01075 
01076     cfg = eat_whitespace(space);
01077   }
01078 
01079  done:
01080   *cfg_ptr = cfg;
01081   return got_anything ? 0 : -1;
01082 }
01083 
01085 int
01086 get_min_log_level(void)
01087 {
01088   logfile_t *lf;
01089   int i;
01090   int min = LOG_ERR;
01091   for (lf = logfiles; lf; lf = lf->next) {
01092     for (i = LOG_DEBUG; i > min; --i)
01093       if (lf->severities->masks[SEVERITY_MASK_IDX(i)])
01094         min = i;
01095   }
01096   return min;
01097 }
01098 
01100 void
01101 switch_logs_debug(void)
01102 {
01103   logfile_t *lf;
01104   int i;
01105   LOCK_LOGS();
01106   for (lf = logfiles; lf; lf=lf->next) {
01107     for (i = LOG_DEBUG; i >= LOG_ERR; --i)
01108       lf->severities->masks[SEVERITY_MASK_IDX(i)] = ~0u;
01109   }
01110   _log_global_min_severity = get_min_log_level();
01111   UNLOCK_LOGS();
01112 }
01113 
01114 #if 0
01115 static void
01116 dump_log_info(logfile_t *lf)
01117 {
01118   const char *tp;
01119 
01120   if (lf->filename) {
01121     printf("=== log into \"%s\" (%s-%s) (%stemporary)\n", lf->filename,
01122            sev_to_string(lf->min_loglevel),
01123            sev_to_string(lf->max_loglevel),
01124            lf->is_temporary?"":"not ");
01125   } else if (lf->is_syslog) {
01126     printf("=== syslog (%s-%s) (%stemporary)\n",
01127            sev_to_string(lf->min_loglevel),
01128            sev_to_string(lf->max_loglevel),
01129            lf->is_temporary?"":"not ");
01130   } else {
01131     printf("=== log (%s-%s) (%stemporary)\n",
01132            sev_to_string(lf->min_loglevel),
01133            sev_to_string(lf->max_loglevel),
01134            lf->is_temporary?"":"not ");
01135   }
01136 }
01137 
01138 void
01139 describe_logs(void)
01140 {
01141   logfile_t *lf;
01142   printf("==== BEGIN LOGS ====\n");
01143   for (lf = logfiles; lf; lf = lf->next)
01144     dump_log_info(lf);
01145   printf("==== END LOGS ====\n");
01146 }
01147 #endif
01148