diff --git a/.cvsignore b/.cvsignore index 75286771eb..e99de92461 100644 --- a/.cvsignore +++ b/.cvsignore @@ -8,6 +8,8 @@ orconfig.h.in config.cache config.log config.status +config.guess +config.sub conftest* stamp-h stamp-h1 diff --git a/src/common/log.c b/src/common/log.c index 136ea66fc4..0581bd792a 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "orconfig.h" #include "./util.h" #include "./log.h" @@ -162,14 +163,14 @@ void reset_logs() /** Add a log handler to send all messages of severity loglevel * or higher to stream. */ -void add_stream_log(int loglevel, const char *name, FILE *stream) +void add_stream_log(int loglevelMin, int loglevelMax, const char *name, FILE *stream) { logfile_t *lf; lf = tor_malloc(sizeof(logfile_t)); lf->filename = name; lf->needs_close = 0; - lf->loglevel = loglevel; - lf->max_loglevel = LOG_ERR; + lf->loglevel = loglevelMin; + lf->max_loglevel = loglevelMax; lf->file = stream; lf->next = logfiles; logfiles = lf; @@ -180,16 +181,43 @@ void add_stream_log(int loglevel, const char *name, FILE *stream) * the logfile fails, -1 is returned and errno is set appropriately * (by fopen). */ -int add_file_log(int loglevel, const char *filename) +int add_file_log(int loglevelMin, int loglevelMax, const char *filename) { FILE *f; f = fopen(filename, "a"); if (!f) return -1; - add_stream_log(loglevel, filename, f); + add_stream_log(loglevelMin, loglevelMax, filename, f); logfiles->needs_close = 1; return 0; } +/** If level is a valid log severity, return the corresponding + * numeric value. Otherwise, return -1. */ +int parse_log_level(const char *level) { + if (!strcasecmp(level, "err")) + return LOG_ERR; + else if (!strcasecmp(level, "notice")) + return LOG_NOTICE; + else if (!strcasecmp(level, "info")) + return LOG_INFO; + else if (!strcasecmp(level, "debug")) + return LOG_DEBUG; + else + return -1; +} + +int get_min_log_level(void) +{ + logfile_t *lf; + int min = LOG_ERR; + for (lf = logfiles; lf; lf = lf->next) { + if (lf->loglevel < min) + min = lf->loglevel; + } + return min; +} + + /* Local Variables: mode:c diff --git a/src/common/log.h b/src/common/log.h index fa830a9b46..786e7fcc12 100644 --- a/src/common/log.h +++ b/src/common/log.h @@ -46,8 +46,10 @@ #define CHECK_PRINTF(formatIdx, firstArg) #endif -void add_stream_log(int loglevel, const char *name, FILE *stream); -int add_file_log(int severity, const char *filename); +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); +int get_min_log_level(void); void close_logs(); void reset_logs(); diff --git a/src/or/config.c b/src/or/config.c index 7f30416d3f..2910ec7a0d 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -28,7 +28,7 @@ static int config_close(FILE *f); static struct config_line_t *config_get_commandlines(int argc, char **argv); static struct config_line_t *config_get_lines(FILE *f); static void config_free_lines(struct config_line_t *front); -static int config_compare(struct config_line_t *c, char *key, config_type_t type, void *arg); +static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg); static int config_assign(or_options_t *options, struct config_line_t *list); /** Open a configuration file for reading */ @@ -131,12 +131,17 @@ static void config_free_lines(struct config_line_t *front) { * the result in arg. If the option is misformatted, log a warning and * skip it. */ -static int config_compare(struct config_line_t *c, char *key, config_type_t type, void *arg) { +static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg) { int i; if(strncasecmp(c->key,key,strlen(c->key))) return 0; + if(strcasecmp(c->key,key)) { + tor_free(c->key); + c->key = tor_strdup(key); + } + /* it's a match. cast and assign. */ log_fn(LOG_DEBUG,"Recognized keyword '%s' as %s, using value '%s'.",c->key,key,c->value); @@ -204,8 +209,8 @@ static int config_assign(or_options_t *options, struct config_line_t *list) { config_compare(list, "KeepalivePeriod",CONFIG_TYPE_INT, &options->KeepalivePeriod) || - config_compare(list, "LogLevel", CONFIG_TYPE_STRING, &options->LogLevel) || - config_compare(list, "LogFile", CONFIG_TYPE_STRING, &options->LogFile) || + config_compare(list, "LogLevel", CONFIG_TYPE_LINELIST, &options->LogOptions) || + config_compare(list, "LogFile", CONFIG_TYPE_LINELIST, &options->LogOptions) || config_compare(list, "LinkPadding", CONFIG_TYPE_BOOL, &options->LinkPadding) || config_compare(list, "MaxConn", CONFIG_TYPE_INT, &options->MaxConn) || @@ -460,8 +465,7 @@ static int resolve_my_address(or_options_t *options) { /** Release storage held by options */ static void free_options(or_options_t *options) { - tor_free(options->LogLevel); - tor_free(options->LogFile); + config_free_lines(options->LogOptions); tor_free(options->DebugLogFile); tor_free(options->DataDirectory); tor_free(options->RouterFile); @@ -487,7 +491,7 @@ static void free_options(or_options_t *options) { static void init_options(or_options_t *options) { /* give reasonable values for each option. Defaults to zero. */ memset(options,0,sizeof(or_options_t)); - options->LogLevel = tor_strdup("notice"); + options->LogOptions = NULL; options->ExitNodes = tor_strdup(""); options->EntryNodes = tor_strdup(""); options->ExcludeNodes = tor_strdup(""); @@ -498,7 +502,6 @@ static void init_options(or_options_t *options) { options->ORBindAddress = tor_strdup("0.0.0.0"); options->DirBindAddress = tor_strdup("0.0.0.0"); options->RecommendedVersions = NULL; - options->loglevel = LOG_INFO; options->PidFile = NULL; // tor_strdup("tor.pid"); options->DataDirectory = NULL; options->PathlenCoinWeight = 0.3; @@ -616,23 +619,6 @@ int getconfig(int argc, char **argv, or_options_t *options) { return -1; } - if(options->LogLevel) { - if(!strcmp(options->LogLevel,"err")) - options->loglevel = LOG_ERR; - else if(!strcmp(options->LogLevel,"warn")) - options->loglevel = LOG_WARN; - else if(!strcmp(options->LogLevel,"notice")) - options->loglevel = LOG_NOTICE; - else if(!strcmp(options->LogLevel,"info")) - options->loglevel = LOG_INFO; - else if(!strcmp(options->LogLevel,"debug")) - options->loglevel = LOG_DEBUG; - else { - log(LOG_WARN,"LogLevel must be one of err|warn|notice|info|debug."); - result = -1; - } - } - if(options->ORPort < 0) { log(LOG_WARN,"ORPort option can't be negative."); result = -1; @@ -727,6 +713,101 @@ int getconfig(int argc, char **argv, or_options_t *options) { return result; } +static void add_single_log(struct config_line_t *level_opt, + struct config_line_t *file_opt, + int isDaemon) +{ + int levelMin=-1, levelMax=-1; + char *cp, *tmp_sev; + + if (level_opt) { + cp = strchr(level_opt->value, '-'); + if (cp) { + tmp_sev = tor_strndup(level_opt->value, cp - level_opt->value); + levelMin = parse_log_level(tmp_sev); + if (levelMin<0) { + log_fn(LOG_WARN, "Unrecognized log severity %s: must be one of err|warn|notice|info|debug", tmp_sev); + } + tor_free(tmp_sev); + levelMax = parse_log_level(cp+1); + if (levelMax<0) { + log_fn(LOG_WARN, "Unrecognized log severity %s: must be one of err|warn|notice|info|debug", cp+1); + } + } else { + levelMin = parse_log_level(level_opt->value); + if (levelMin<0) { + log_fn(LOG_WARN, "Unrecognized log severity %s: must be one of err|warn|notice|info|debug", level_opt->value); + } + } + } + if (levelMin < 0 && levelMax < 0) { + levelMin = LOG_NOTICE; + levelMax = LOG_ERR; + } else if (levelMin < 0) { + levelMin = levelMax; + } else { + levelMax = LOG_ERR; + } + if (file_opt) { + if (add_file_log(levelMin, levelMax, file_opt->value) < 0) { + /* opening the log file failed! Use stderr and log a warning */ + add_stream_log(levelMin, levelMax, "", stderr); + log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", file_opt->value, + strerror(errno)); + } + log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.", + file_opt->value); + } else if (!isDaemon) { + add_stream_log(levelMin, levelMax, "", stderr); + } +} + +/** + * Initialize the logs based on the configuration file. + */ +void config_init_logs(or_options_t *options) +{ + /* The order of options is: Level? (File Level?)+ + */ + struct config_line_t *opt = options->LogOptions; + + /* Special case for if first option is LogLevel. */ + if (opt && !strcasecmp(opt->key, "LogLevel")) { + if (opt->next && !strcasecmp(opt->next->key, "LogFile")) { + add_single_log(opt, opt->next, options->RunAsDaemon); + opt = opt->next->next; + } else if (!opt->next) { + add_single_log(opt, NULL, options->RunAsDaemon); + return; + } else { + opt = opt->next; + } + } + + while (opt) { + if (!strcasecmp(opt->key, "LogLevel")) { + log_fn(LOG_WARN, "Two LogLevel options in a row without intervening LogFile"); + opt = opt->next; + } else { + assert(!strcasecmp(opt->key, "LogFile")); + if (opt->next && !strcasecmp(opt->next->key, "LogLevel")) { + /* LogFile followed by LogLevel */ + add_single_log(opt->next, opt, options->RunAsDaemon); + opt = opt->next->next; + } else { + /* LogFile followed by LogFile or end of list. */ + add_single_log(NULL, opt, options->RunAsDaemon); + opt = opt->next; + } + } + } + + if (options->DebugLogFile) { + log_fn(LOG_WARN, "DebugLogFile is deprecated; use LogFile and LogLevel instead"); + add_file_log(LOG_DEBUG, LOG_ERR, options->DebugLogFile); + } +} + /* Local Variables: mode:c diff --git a/src/or/main.c b/src/or/main.c index b5631eb753..996dea0c84 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -566,22 +566,7 @@ static int init_from_config(int argc, char **argv) { } /* Configure the log(s) */ - if(!options.LogFile && !options.RunAsDaemon) - add_stream_log(options.loglevel, "", stdout); - if(options.LogFile) { - if (add_file_log(options.loglevel, options.LogFile) != 0) { - /* opening the log file failed! Use stderr and log a warning */ - add_stream_log(options.loglevel, "", stderr); - log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", options.LogFile, strerror(errno)); - } - log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.", - options.LogFile); - } - if(options.DebugLogFile) { - if (add_file_log(LOG_DEBUG, options.DebugLogFile) != 0) - log_fn(LOG_WARN, "Cannot write to DebugLogFile '%s': %s.", options.DebugLogFile, strerror(errno)); - log_fn(LOG_DEBUG, "Successfully opened DebugLogFile '%s'.", options.DebugLogFile); - } + config_init_logs(&options); /* Set up our buckets */ connection_bucket_init(); @@ -696,7 +681,7 @@ static int do_main_loop(void) { #ifndef MS_WINDOWS /* do signal stuff only on unix */ if(please_dumpstats) { /* prefer to log it at INFO, but make sure we always see it */ - dumpstats(options.loglevel>LOG_INFO ? options.loglevel : LOG_INFO); + dumpstats(get_min_log_level()>LOG_INFO ? get_min_log_level() : LOG_INFO); please_dumpstats = 0; } if(please_reset) { @@ -867,7 +852,7 @@ void exit_function(void) int tor_main(int argc, char *argv[]) { /* give it somewhere to log to initially */ - add_stream_log(LOG_INFO, "", stdout); + add_stream_log(LOG_INFO, LOG_ERR, "", stdout); log_fn(LOG_NOTICE,"Tor v%s. This is experimental software. Do not use it if you need anonymity.",VERSION); if (network_init()<0) { diff --git a/src/or/or.h b/src/or/or.h index 601ee6c19d..3422f8adbb 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -753,6 +753,9 @@ typedef struct circuit_t circuit_t; /** Configuration options for a Tor process */ typedef struct { + struct config_line_t *LogOptions; /**< List of configuration lines + * for logfiles */ + char *LogLevel; /**< Verbosity of log: minimal level of messages to report. */ char *LogFile; /**< Where to send normal log messages. */ char *DebugLogFile; /**< Where to send verbose log messages. */ @@ -811,8 +814,6 @@ typedef struct { int BandwidthBurst; /**< How much bandwidth, at maximum, are we willing to * use in a second? */ int NumCpus; /**< How many CPUs should we try to use? */ - int loglevel; /**< How verbose should we be? Log messages less severe than - * this will be ignored. */ int RunTesting; /**< If true, create testing circuits to measure how well the * other ORs are running. */ struct config_line_t *RendConfigLines; /**< List of configuration lines @@ -951,6 +952,7 @@ struct config_line_t { int config_assign_default_dirservers(void); int getconfig(int argc, char **argv, or_options_t *options); +void config_init_logs(or_options_t *options); /********************************* connection.c ***************************/