瀏覽代碼

Add SysLog option to direct log messages to the system log instead of a FILE*.

svn:r2591
Nick Mathewson 19 年之前
父節點
當前提交
26f3cb8652
共有 7 個文件被更改,包括 139 次插入39 次删除
  1. 1 1
      configure.in
  2. 4 0
      doc/tor.1.in
  3. 84 24
      src/common/log.c
  4. 23 7
      src/common/log.h
  5. 4 0
      src/common/util.c
  6. 3 0
      src/config/torrc.sample.in
  7. 20 7
      src/or/config.c

+ 1 - 1
configure.in

@@ -143,7 +143,7 @@ AC_CHECK_HEADERS(unistd.h string.h signal.h netdb.h ctype.h poll.h sys/stat.h sy
 
 dnl These headers are not essential
 
-AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h)
+AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h syslog.h)
 
 AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime)
 AC_REPLACE_FUNCS(strlcat strlcpy)

+ 4 - 0
doc/tor.1.in

@@ -32,6 +32,10 @@ Set the verboseness level of the primary log. (Default: warn)
 \fBlogfile \fR\fIFILE\fP
 Rather than logging to stdout, log to FILE.
 .TP
+\fBsyslog\fP
+Rather than logging to stdout, send messages to the system log. (Not
+supported on all platforms)
+.TP
 \fBbandwidthrate \fR\fINUM\fP
 A token bucket limits the average incoming bandwidth on this node to NUM bytes per second. (Default: 800000)
 .TP

+ 84 - 24
src/common/log.c

@@ -34,6 +34,7 @@ typedef struct logfile_t {
   int loglevel; /**< Lowest severity level to send to this stream. */
   int max_loglevel; /**< Highest severity level to send to this stream. */
   int is_temporary; /**< Boolean: close after initializing logging subsystem.*/
+  int is_syslog; /**< Boolean: send messages to syslog. */
 } logfile_t;
 
 /** Helper: map a log severity to descriptive string. */
@@ -50,8 +51,13 @@ static INLINE const char *sev_to_string(int severity) {
 
 /** Linked list of logfile_t. */
 static logfile_t *logfiles = NULL;
+#ifdef HAVE_SYSLOG_H
+static int syslog_count = 0;
+#endif
 
 static void delete_log(logfile_t *victim);
+static void close_log(logfile_t *victim);
+static int reset_log(logfile_t *lf);
 
 static INLINE size_t
 _log_prefix(char *buf, size_t buf_len, int severity)
@@ -104,16 +110,19 @@ static void log_tor_version(logfile_t *lf, int reset)
 
 /** Helper: Format a log message into a fixed-sized buffer. (This is
  * factored out of <b>logv</b> so that we never format a message more
- * than once.)
+ * than once.)  Return a pointer to the first character of the message
+ * portion of the formatted string.
  */
-static INLINE void format_msg(char *buf, size_t buf_len,
+static INLINE char *format_msg(char *buf, size_t buf_len,
                               int severity, const char *funcname,
                               const char *format, va_list ap)
 {
   size_t n;
+  char *end_of_prefix;
   buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */
 
   n = _log_prefix(buf, buf_len, severity);
+  end_of_prefix = buf+n;
 
   if (funcname) {
     n += snprintf(buf+n, buf_len-n, "%s(): ", funcname);
@@ -128,6 +137,7 @@ static INLINE void format_msg(char *buf, size_t buf_len,
   }
   buf[n]='\n';
   buf[n+1]='\0';
+  return end_of_prefix;
 }
 
 /** Helper: sends a message to the appropriate logfiles, at loglevel
@@ -140,23 +150,32 @@ logv(int severity, const char *funcname, const char *format, va_list ap)
   char buf[10024];
   int formatted = 0;
   logfile_t *lf;
+  char *end_of_prefix=NULL;
 
   assert(format);
   lf = logfiles;
   while(lf) {
-    if (severity < lf->loglevel || severity > lf->max_loglevel) {
+    if (severity > lf->loglevel || severity < lf->max_loglevel) {
       lf = lf->next;
       continue;
     }
-    if (!lf->file) {
+    if (! (lf->file || lf->is_syslog)) {
       lf = lf->next;
       continue;
     }
 
     if (!formatted) {
-      format_msg(buf, sizeof(buf), severity, funcname, format, ap);
+      end_of_prefix =
+        format_msg(buf, sizeof(buf), severity, funcname, format, ap);
       formatted = 1;
     }
+    if (lf->is_syslog) {
+#ifdef HAVE_SYSLOG_H
+      syslog(severity, "%s", end_of_prefix);
+#endif
+      lf = lf->next;
+      continue;
+    }
     if(fputs(buf, lf->file) == EOF ||
        fflush(lf->file) == EOF) { /* error */
       /* don't log the error! Blow away this log entry and continue. */
@@ -194,8 +213,7 @@ void close_logs()
   while(logfiles) {
     victim = logfiles;
     logfiles = logfiles->next;
-    if (victim->needs_close)
-      fclose(victim->file);
+    close_log(victim);
     tor_free(victim->filename);
     tor_free(victim);
   }
@@ -206,17 +224,12 @@ void reset_logs()
 {
   logfile_t *lf = logfiles;
   while(lf) {
-    if (lf->needs_close) {
-      if(fclose(lf->file)==EOF ||
-        !(lf->file = fopen(lf->filename, "a"))) {
-        /* error. don't log it. delete the log entry and continue. */
-        logfile_t *victim = lf;
-        lf = victim->next;
-        delete_log(victim);
-        continue;
-      } else {
-        log_tor_version(lf, 1);
-      }
+    if (reset_log(lf)) {
+      /* error. don't log it. delete the log entry and continue. */
+      logfile_t *victim = lf;
+      lf = victim->next;
+      delete_log(victim);
+      continue;
     }
     lf = lf->next;
   }
@@ -241,19 +254,43 @@ static void delete_log(logfile_t *victim) {
   tor_free(victim);
 }
 
+static void close_log(logfile_t *victim)
+{
+  if (victim->needs_close && victim->file) {
+    fclose(victim->file);
+  } else if (victim->is_syslog) {
+#ifdef HAVE_SYSLOG_H
+    if (--syslog_count == 0)
+      /* There are no other syslogs; close the logging facility. */
+      closelog();
+#endif
+  }
+}
+
+static int reset_log(logfile_t *lf)
+{
+  if (lf->needs_close) {
+    if(fclose(lf->file)==EOF ||
+       !(lf->file = fopen(lf->filename, "a"))) {
+      return -1;
+    } else {
+      log_tor_version(lf, 1);
+    }
+  }
+  return 0;
+}
+
 /** Add a log handler to send all messages of severity <b>loglevel</b>
  * or higher to <b>stream</b>. */
 void add_stream_log(int loglevelMin, int loglevelMax, const char *name, FILE *stream)
 {
   logfile_t *lf;
-  lf = tor_malloc(sizeof(logfile_t));
+  lf = tor_malloc_zero(sizeof(logfile_t));
   lf->filename = tor_strdup(name);
-  lf->needs_close = 0;
   lf->loglevel = loglevelMin;
   lf->max_loglevel = loglevelMax;
   lf->file = stream;
   lf->next = logfiles;
-  lf->is_temporary = 0;
   logfiles = lf;
 }
 
@@ -266,6 +303,7 @@ void add_temp_log(void)
   logfiles->is_temporary = 1;
 }
 
+/** Close any log handlers added by add_temp_log or marked by mark_logs_temp */
 void close_temp_logs(void)
 {
   logfile_t *lf, **p;
@@ -273,8 +311,7 @@ void close_temp_logs(void)
     if ((*p)->is_temporary) {
       lf = *p;
       *p = (*p)->next;
-      if (lf->needs_close)
-        fclose(lf->file);
+      close_log(lf);
       tor_free(lf->filename);
       tor_free(lf);
     } else {
@@ -283,6 +320,7 @@ void close_temp_logs(void)
   }
 }
 
+/** Configure all log handles to be closed by close_temp_logs */
 void mark_logs_temp(void)
 {
   logfile_t *lf;
@@ -306,6 +344,28 @@ int add_file_log(int loglevelMin, int loglevelMax, const char *filename)
   return 0;
 }
 
+#ifdef HAVE_SYSLOG_H
+/**
+ * Add a log handler to send messages to they system log facility.
+ */
+int add_syslog_log(int loglevelMin, int loglevelMax)
+{
+  logfile_t *lf;
+  if (syslog_count++ == 0)
+    /* This is the the first syslog. */
+    openlog("Tor", LOG_NDELAY, LOG_DAEMON);
+
+  lf = tor_malloc_zero(sizeof(logfile_t));
+  lf->loglevel = loglevelMin;
+  lf->filename = tor_strdup("<syslog>");
+  lf->max_loglevel = loglevelMax;
+  lf->is_syslog = 1;
+  lf->next = logfiles;
+  logfiles = lf;
+  return 0;
+}
+#endif
+
 /** If <b>level</b> is a valid log severity, return the corresponding
  * numeric value.  Otherwise, return -1. */
 int parse_log_level(const char *level) {
@@ -327,7 +387,7 @@ int get_min_log_level(void)
   logfile_t *lf;
   int min = LOG_ERR;
   for (lf = logfiles; lf; lf = lf->next) {
-    if (lf->loglevel < min)
+    if (lf->loglevel > min)
       min = lf->loglevel;
   }
   return min;

+ 23 - 7
src/common/log.h

@@ -18,24 +18,37 @@
 #ifdef HAVE_SYSLOG_H
 #include <syslog.h>
 #define LOG_WARN LOG_WARNING
+#if LOG_DEBUG < LOG_ERR
+#error "Your syslog.h thinks high numbers are more important.  We aren't prepared to deal with that."
+#endif
 #else
+/* XXXX Note: The code was originally written to refer to severities,
+ * with 0 being the least severe; while syslog's logging code refers to
+ * priorities, with 0 being the most important.  Thus, all our comparisons
+ * needed to be reversed when we added syslog support.  
+ *
+ * The upshot of this is that comments about log levels may be messed
+ * up: for "maximum severity" read "most severe" and "numerically
+ * *lowest* severity".
+ */
+
 /** Debug-level severity: for hyper-verbose messages of no interest to
  * anybody but developers. */
-#define LOG_DEBUG   0
+#define LOG_DEBUG   7
 /** Info-level severity: for messages that appear frequently during normal
  * operation. */
-#define LOG_INFO    1
+#define LOG_INFO    6
 /** Notice-level severity: for messages that appear infrequently
  * during normal operation; that the user will probably care about;
  * and that are not errors.
  */
-#define LOG_NOTICE  2
+#define LOG_NOTICE  5
 /** Warn-level severity: for messages that only appear when something has gone
  * wrong. */
-#define LOG_WARN    3
+#define LOG_WARN    4
 /** Error-level severity: for messages that only appear when something has gone
  * very wrong, and the Tor process can no longer proceed. */
-#define LOG_ERR     4
+#define LOG_ERR     3
 #endif
 
 /* magic to make GCC check for proper format strings. */
@@ -49,9 +62,12 @@
 int parse_log_level(const char *level);
 void add_stream_log(int severityMin, int severityMax, const char *name, FILE *stream);
 int add_file_log(int severityMin, int severityMax, const char *filename);
+#ifdef HAVE_SYSLOG_H
+int add_syslog_log(int loglevelMin, int loglevelMax);
+#endif
 int get_min_log_level(void);
-void close_logs();
-void reset_logs();
+void close_logs(void);
+void reset_logs(void);
 void add_temp_log(void);
 void close_temp_logs(void);
 void mark_logs_temp(void);

+ 4 - 0
src/common/util.c

@@ -1740,13 +1740,17 @@ try_next_line:
   while(*value && isspace((int)*value))
     value++;
 
+#if 0
   if(!*end || !*value) { /* only a key on this line. no value. */
     *end = 0;
     log_fn(LOG_WARN,"Line has keyword '%s' but no value. Failing.",key);
     return -1;
   }
+#endif
   *end = 0; /* null it out */
 
+  tor_assert(key);
+  tor_assert(value);
   log_fn(LOG_DEBUG,"got keyword '%s', value '%s'", key, value);
   *key_out = key, *value_out = value;
   return 1;

+ 3 - 0
src/config/torrc.sample.in

@@ -29,6 +29,9 @@ AllowUnverifiedNodes middle,rendezvous
 ### Send all debug messages ONLY to /var/log/tor/debug
 #LogFile /var/log/tor/debug
 #LogLevel debug-debug
+### To use the system log instead of Tor's logfiles, uncomment these lines:
+#SysLog
+#LogLevel notice
 
 # Uncomment this to start the process in the background
 #RunAsDaemon 1

+ 20 - 7
src/or/config.c

@@ -292,7 +292,7 @@ config_assign(or_options_t *options, struct config_line_t *list)
       config_compare(list, "SocksPort",      CONFIG_TYPE_UINT, &options->SocksPort) ||
       config_compare(list, "SocksBindAddress",CONFIG_TYPE_LINELIST,&options->SocksBindAddress) ||
       config_compare(list, "SocksPolicy",     CONFIG_TYPE_LINELIST,&options->SocksPolicy) ||
-
+      config_compare(list, "SysLog",          CONFIG_TYPE_LINELIST,&options->LogOptions) ||
       config_compare(list, "TrafficShaping", CONFIG_TYPE_OBSOLETE, NULL) ||
 
       config_compare(list, "User",           CONFIG_TYPE_STRING, &options->User)
@@ -964,7 +964,8 @@ add_single_log(struct config_line_t *level_opt,
   } else {
     levelMax = LOG_ERR;
   }
-  if (file_opt) {
+
+  if (file_opt && !strcasecmp(file_opt->key, "LogFile")) {
     if (add_file_log(levelMin, levelMax, file_opt->value) < 0) {
       log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", file_opt->value,
              strerror(errno));
@@ -972,6 +973,16 @@ add_single_log(struct config_line_t *level_opt,
     }
     log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
            file_opt->value);
+  } else if (file_opt && !strcasecmp(file_opt->key, "SysLog")) {
+#ifdef HAVE_SYSLOG_H
+    if (add_syslog_log(levelMin, levelMax) < 0) {
+      log_fn(LOG_WARN, "Cannot open system log facility");
+      return -1;
+    }
+    log_fn(LOG_NOTICE, "Successfully opened system log, redirecting output.");
+#else
+    log_fn(LOG_WARN, "Tor was compiled without system logging enabled; can't enable SysLog.");
+#endif
   } else if (!isDaemon) {
     add_stream_log(levelMin, levelMax, "<stdout>", stdout);
     close_temp_logs();
@@ -998,7 +1009,8 @@ config_init_logs(or_options_t *options)
 
   /* Special case for if first option is LogLevel. */
   if (opt && !strcasecmp(opt->key, "LogLevel")) {
-    if (opt->next && !strcasecmp(opt->next->key, "LogFile")) {
+    if (opt->next && (!strcasecmp(opt->next->key, "LogFile") ||
+                      !strcasecmp(opt->next->key, "SysLog"))) {
       if (add_single_log(opt, opt->next, options->RunAsDaemon) < 0)
         return -1;
       opt = opt->next->next;
@@ -1013,17 +1025,18 @@ config_init_logs(or_options_t *options)
 
   while (opt) {
     if (!strcasecmp(opt->key, "LogLevel")) {
-      log_fn(LOG_WARN, "Two LogLevel options in a row without intervening LogFile");
+      log_fn(LOG_WARN, "Two LogLevel options in a row without intervening LogFile or SysLog");
       opt = opt->next;
     } else {
-      tor_assert(!strcasecmp(opt->key, "LogFile"));
+      tor_assert(!strcasecmp(opt->key, "LogFile") ||
+                 !strcasecmp(opt->key, "SysLog"));
       if (opt->next && !strcasecmp(opt->next->key, "LogLevel")) {
-        /* LogFile followed by LogLevel */
+        /* LogFile/SysLog followed by LogLevel */
         if (add_single_log(opt->next, opt, options->RunAsDaemon) < 0)
           return -1;
         opt = opt->next->next;
       } else {
-        /* LogFile followed by LogFile or end of list. */
+        /* LogFile/SysLog followed by LogFile/SysLog or end of list. */
         if (add_single_log(NULL, opt, options->RunAsDaemon) < 0)
           return -1;
         opt = opt->next;