diff options
Diffstat (limited to 'src/common/log.c')
-rw-r--r-- | src/common/log.c | 186 |
1 files changed, 165 insertions, 21 deletions
diff --git a/src/common/log.c b/src/common/log.c index 4db1c9f0d0..ebd50f62d3 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -35,6 +35,9 @@ #define LOG_PRIVATE #include "torlog.h" #include "container.h" +#ifdef HAVE_ANDROID_LOG_H +#include <android/log.h> +#endif // HAVE_ANDROID_LOG_H. /** Given a severity, yields an index into log_severity_list_t.masks to use * for that severity. */ @@ -49,6 +52,13 @@ #define raw_assert(x) assert(x) // assert OK +/** Defining compile-time constants for Tor log levels (used by the Rust + * log wrapper at src/rust/tor_log) */ +const int LOG_WARN_ = LOG_WARN; +const int LOG_NOTICE_ = LOG_NOTICE; +const log_domain_mask_t LD_GENERAL_ = LD_GENERAL; +const log_domain_mask_t LD_NET_ = LD_NET; + /** Information for a single logfile; only used in log.c */ typedef struct logfile_t { struct logfile_t *next; /**< Next logfile_t in the linked list. */ @@ -58,12 +68,16 @@ typedef struct logfile_t { int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */ int is_temporary; /**< Boolean: close after initializing logging subsystem.*/ int is_syslog; /**< Boolean: send messages to syslog. */ + int is_android; /**< Boolean: send messages to Android's log subsystem. */ + char *android_tag; /**< Identity Tag used in Android's log subsystem. */ log_callback callback; /**< If not NULL, send messages to this function. */ log_severity_list_t *severities; /**< Which severity of messages should we * log for each log domain? */ } logfile_t; -static void log_free(logfile_t *victim); +static void log_free_(logfile_t *victim); +#define log_free(lg) \ + FREE_AND_NULL(logfile_t, log_free_, (lg)) /** Helper: map a log severity to descriptive string. */ static inline const char * @@ -101,6 +115,33 @@ should_log_function_name(log_domain_mask_t domain, int severity) } } +#ifdef HAVE_ANDROID_LOG_H +/** Helper function to convert Tor's log severity into the matching + * Android log priority. + */ +static int +severity_to_android_log_priority(int severity) +{ + switch (severity) { + case LOG_DEBUG: + return ANDROID_LOG_VERBOSE; + case LOG_INFO: + return ANDROID_LOG_DEBUG; + case LOG_NOTICE: + return ANDROID_LOG_INFO; + case LOG_WARN: + return ANDROID_LOG_WARN; + case LOG_ERR: + return ANDROID_LOG_ERROR; + default: + // LCOV_EXCL_START + raw_assert(0); + return 0; + // LCOV_EXCL_STOP + } +} +#endif // HAVE_ANDROID_LOG_H. + /** A mutex to guard changes to logfiles and logging. */ static tor_mutex_t log_mutex; /** True iff we have initialized log_mutex */ @@ -129,6 +170,9 @@ typedef struct pending_log_message_t { /** Log messages waiting to be replayed onto callback-based logs */ static smartlist_t *pending_cb_messages = NULL; +/** Callback to invoke when pending_cb_messages becomes nonempty. */ +static pending_callback_callback pending_cb_cb = NULL; + /** Log messages waiting to be replayed once the logging system is initialized. */ static smartlist_t *pending_startup_messages = NULL; @@ -191,6 +235,30 @@ log_set_application_name(const char *name) appname = name ? tor_strdup(name) : NULL; } +/** Return true if some of the running logs might be interested in a log + * message of the given severity in the given domains. If this function + * returns true, the log message might be ignored anyway, but if it returns + * false, it is definitely_ safe not to log the message. */ +int +log_message_is_interesting(int severity, log_domain_mask_t domain) +{ + (void) domain; + return (severity <= log_global_min_severity_); +} + +/** + * As tor_log, but takes an optional function name, and does not treat its + * <b>string</b> as a printf format. + * + * For use by Rust integration. + */ +void +tor_log_string(int severity, log_domain_mask_t domain, + const char *function, const char *string) +{ + log_fn_(severity, domain, function, "%s", string); +} + /** Log time granularity in milliseconds. */ static int log_time_granularity = 1; @@ -385,9 +453,12 @@ pending_log_message_new(int severity, log_domain_mask_t domain, return m; } +#define pending_log_message_free(msg) \ + FREE_AND_NULL(pending_log_message_t, pending_log_message_free_, (msg)) + /** Release all storage held by <b>msg</b>. */ static void -pending_log_message_free(pending_log_message_t *msg) +pending_log_message_free_(pending_log_message_t *msg) { if (!msg) return; @@ -396,6 +467,16 @@ pending_log_message_free(pending_log_message_t *msg) tor_free(msg); } +/** Helper function: returns true iff the log file, given in <b>lf</b>, is + * handled externally via the system log API, the Android logging API, or is an + * external callback function. */ +static inline int +logfile_is_external(const logfile_t *lf) +{ + raw_assert(lf); + return lf->is_syslog || lf->is_android || lf->callback; +} + /** Return true iff <b>lf</b> would like to receive a message with the * specified <b>severity</b> in the specified <b>domain</b>. */ @@ -406,7 +487,7 @@ logfile_wants_message(const logfile_t *lf, int severity, if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) { return 0; } - if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) { + if (! (lf->fd >= 0 || logfile_is_external(lf))) { return 0; } if (lf->seems_dead) { @@ -444,17 +525,25 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len, if (m != msg_after_prefix) { tor_free(m); } -#else +#else /* !(defined(MAXLINE)) */ /* We have syslog but not MAXLINE. That's promising! */ syslog(severity, "%s", msg_after_prefix); -#endif -#endif +#endif /* defined(MAXLINE) */ +#endif /* defined(HAVE_SYSLOG_H) */ + } else if (lf->is_android) { +#ifdef HAVE_ANDROID_LOG_H + int priority = severity_to_android_log_priority(severity); + __android_log_write(priority, lf->android_tag, msg_after_prefix); +#endif // HAVE_ANDROID_LOG_H. } else if (lf->callback) { if (domain & LD_NOCB) { if (!*callbacks_deferred && pending_cb_messages) { smartlist_add(pending_cb_messages, pending_log_message_new(severity,domain,NULL,msg_after_prefix)); *callbacks_deferred = 1; + if (smartlist_len(pending_cb_messages) == 1 && pending_cb_cb) { + pending_cb_cb(); + } } } else { lf->callback(severity, domain, msg_after_prefix); @@ -641,8 +730,8 @@ tor_log_update_sigsafe_err_fds(void) /* Don't try callback to the control port, or syslogs: We can't * do them from a signal handler. Don't try stdout: we always do stderr. */ - if (lf->is_temporary || lf->is_syslog || - lf->callback || lf->seems_dead || lf->fd < 0) + if (lf->is_temporary || logfile_is_external(lf) + || lf->seems_dead || lf->fd < 0) continue; if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] & (LD_BUG|LD_GENERAL)) { @@ -678,11 +767,11 @@ tor_log_get_logfile_names(smartlist_t *out) LOCK_LOGS(); for (lf = logfiles; lf; lf = lf->next) { - if (lf->is_temporary || lf->is_syslog || lf->callback) + if (lf->is_temporary || logfile_is_external(lf)) continue; if (lf->filename == NULL) continue; - smartlist_add(out, tor_strdup(lf->filename)); + smartlist_add_strdup(out, lf->filename); } UNLOCK_LOGS(); @@ -721,12 +810,13 @@ log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain, /** Free all storage held by <b>victim</b>. */ static void -log_free(logfile_t *victim) +log_free_(logfile_t *victim) { if (!victim) return; tor_free(victim->severities); tor_free(victim->filename); + tor_free(victim->android_tag); tor_free(victim); } @@ -741,6 +831,7 @@ logs_free_all(void) logfiles = NULL; messages = pending_cb_messages; pending_cb_messages = NULL; + pending_cb_cb = NULL; messages2 = pending_startup_messages; pending_startup_messages = NULL; UNLOCK_LOGS(); @@ -807,7 +898,7 @@ close_log(logfile_t *victim) /* There are no other syslogs; close the logging facility. */ closelog(); } -#endif +#endif /* defined(HAVE_SYSLOG_H) */ } } @@ -904,6 +995,24 @@ add_temp_log(int min_severity) } /** + * Register "cb" as the callback to call when there are new pending log + * callbacks to be flushed with flush_pending_log_callbacks(). + * + * Note that this callback, if present, can be invoked from any thread. + * + * This callback must not log. + * + * It is intentional that this function contains the name "callback" twice: it + * sets a "callback" to be called on the condition that there is a "pending + * callback". + **/ +void +logs_set_pending_callback_callback(pending_callback_callback cb) +{ + pending_cb_cb = cb; +} + +/** * Add a log handler to send messages in <b>severity</b> * to the function <b>cb</b>. */ @@ -1086,7 +1195,7 @@ add_file_log(const log_severity_list_t *severity, const char *filename, int open_flags = O_WRONLY|O_CREAT; open_flags |= truncate_log ? O_TRUNC : O_APPEND; - fd = tor_open_cloexec(filename, open_flags, 0644); + fd = tor_open_cloexec(filename, open_flags, 0640); if (fd<0) return -1; if (tor_fd_seekend(fd)<0) { @@ -1144,7 +1253,40 @@ add_syslog_log(const log_severity_list_t *severity, UNLOCK_LOGS(); return 0; } -#endif +#endif /* defined(HAVE_SYSLOG_H) */ + +#ifdef HAVE_ANDROID_LOG_H +/** + * Add a log handler to send messages to the Android platform log facility. + */ +int +add_android_log(const log_severity_list_t *severity, + const char *android_tag) +{ + logfile_t *lf = NULL; + + lf = tor_malloc_zero(sizeof(logfile_t)); + lf->fd = -1; + lf->severities = tor_memdup(severity, sizeof(log_severity_list_t)); + lf->filename = tor_strdup("<android>"); + lf->is_android = 1; + + if (android_tag == NULL) + lf->android_tag = tor_strdup("Tor"); + else { + char buf[256]; + tor_snprintf(buf, sizeof(buf), "Tor-%s", android_tag); + lf->android_tag = tor_strdup(buf); + } + + LOCK_LOGS(); + lf->next = logfiles; + logfiles = lf; + log_global_min_severity_ = get_min_log_level(); + UNLOCK_LOGS(); + return 0; +} +#endif // HAVE_ANDROID_LOG_H. /** If <b>level</b> is a valid log severity, return the corresponding * numeric value. Otherwise, return -1. */ @@ -1172,12 +1314,15 @@ log_level_to_string(int level) } /** NULL-terminated array of names for log domains such that domain_list[dom] - * is a description of <b>dom</b>. */ + * is a description of <b>dom</b>. + * + * Remember to update doc/tor.1.txt if you modify this list. + * */ static const char *domain_list[] = { "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM", "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV", "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL", - "SCHED", "DOS", NULL + "SCHED", "GUARD", "CONSDIFF", "DOS", NULL }; /** Return a bitmask for the log domain for which <b>domain</b> is the name, @@ -1313,16 +1458,15 @@ parse_log_severity_config(const char **cfg_ptr, if (!strcasecmpstart(cfg, "file") || !strcasecmpstart(cfg, "stderr") || !strcasecmpstart(cfg, "stdout") || - !strcasecmpstart(cfg, "syslog")) { + !strcasecmpstart(cfg, "syslog") || + !strcasecmpstart(cfg, "android")) { goto done; } if (got_an_unqualified_range > 1) return -1; - space = strchr(cfg, ' '); + space = find_whitespace(cfg); dash = strchr(cfg, '-'); - if (!space) - space = strchr(cfg, '\0'); if (dash && dash < space) { sev_lo = tor_strndup(cfg, dash-cfg); sev_hi = tor_strndup(dash+1, space-(dash+1)); |