/** 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. */
- const char *filename; /**< Filename to open. */
+ char *filename; /**< Filename to open. */
FILE *file; /**< Stream to receive log messages. */
int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */
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.*/
} logfile_t;
/** Helper: map a log severity to descriptive string. */
logfiles = logfiles->next;
if (victim->needs_close)
fclose(victim->file);
- free(victim);
+ tor_free(victim->filename);
+ tor_free(victim);
}
}
{
logfile_t *lf;
lf = tor_malloc(sizeof(logfile_t));
- lf->filename = name;
+ 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;
}
+/** Add a log handler to receive messages during startup (before the real
+ * logs are initialized).
+ */
+void add_temp_log(void)
+{
+ add_stream_log(LOG_INFO, LOG_ERR, "<temp>", stdout);
+ logfiles->is_temporary = 1;
+}
+
+void close_temp_logs(void)
+{
+ logfile_t *lf, **p;
+ for (p = &logfiles; *p; ) {
+ if ((*p)->is_temporary) {
+ lf = *p;
+ *p = (*p)->next;
+ if (lf->needs_close)
+ fclose(lf->file);
+ tor_free(lf->filename);
+ tor_free(lf);
+ } else {
+ p = &((*p)->next);
+ }
+ }
+}
+
/**
* Add a log handler to send messages to <b>filename</b>. If opening
* the logfile fails, -1 is returned and errno is set appropriately
return result;
}
-static void add_single_log(struct config_line_t *level_opt,
+static int add_single_log(struct config_line_t *level_opt,
struct config_line_t *file_opt,
int isDaemon)
{
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);
+ log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", tmp_sev);
+ return -1;
}
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);
+ log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", cp+1);
+ return -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);
+ log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", level_opt->value);
+ return -1;
+
}
}
}
}
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>", stderr);
log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", file_opt->value,
strerror(errno));
- return;
+ return -1;
}
log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
file_opt->value);
} else if (!isDaemon) {
add_stream_log(levelMin, levelMax, "<stdout>", stdout);
+ close_temp_logs();
}
+ return 0;
}
/**
* Initialize the logs based on the configuration file.
*/
-void config_init_logs(or_options_t *options)
+int config_init_logs(or_options_t *options)
{
/* The order of options is: Level? (File Level?)+
*/
struct config_line_t *opt = options->LogOptions;
- /* Special case if nothing is specified. */
- if(!opt) {
- add_single_log(NULL, NULL, options->RunAsDaemon);
+ /* Special case if no options are given. */
+ if (!opt) {
+ add_stream_log(LOG_NOTICE, LOG_ERR, "<stdout>", stdout);
+ close_temp_logs();
/* don't return yet, in case we want to do a debuglogfile below */
}
/* 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);
+ if (add_single_log(opt, opt->next, options->RunAsDaemon)<0)
+ return -1;
opt = opt->next->next;
} else if (!opt->next) {
- add_single_log(opt, NULL, options->RunAsDaemon);
- return;
- } else {
+ if (add_single_log(opt, NULL, options->RunAsDaemon)<0)
+ return -1;
opt = opt->next;
+ } else {
+ ; /* give warning below */
}
}
tor_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);
+ 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. */
- add_single_log(NULL, opt, options->RunAsDaemon);
+ if (add_single_log(NULL, opt, options->RunAsDaemon)<0)
+ return -1;
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);
+ if (add_file_log(LOG_DEBUG, LOG_ERR, options->DebugLogFile)<0)
+ return -1;
}
+ return 0;
}
void