log.c 18.1 KB
Newer Older
1
/* Copyright (c) 2001, Matej Pfajfar.
Roger Dingledine's avatar
Roger Dingledine committed
2
 * Copyright (c) 2001-2004, Roger Dingledine.
3
4
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
 * Copyright (c) 2007, The Tor Project, Inc. */
5
6
/* See LICENSE for licensing information */
/* $Id$ */
7
const char log_c_id[] = "$Id$";
Roger Dingledine's avatar
Roger Dingledine committed
8

Nick Mathewson's avatar
Nick Mathewson committed
9
10
11
/**
 * \file log.c
 * \brief Functions to send messages to log files or the console.
12
 **/
Nick Mathewson's avatar
Nick Mathewson committed
13

14
#include "orconfig.h"
15
#include <stdarg.h>
16
#include <assert.h>
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <string.h>
20
21
22
23
24
25
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
26
27
28
#include "./util.h"
#include "./log.h"

29
30
#include <event.h>

31
32
#define TRUNCATED_STR "[...truncated]"
#define TRUNCATED_STR_LEN 14
33

Nick Mathewson's avatar
Nick Mathewson committed
34
/** Information for a single logfile; only used in log.c */
35
typedef struct logfile_t {
36
  struct logfile_t *next; /**< Next logfile_t in the linked list. */
37
  char *filename; /**< Filename to open. */
38
  FILE *file; /**< Stream to receive log messages. */
39
  int seems_dead; /**< Boolean: true if the stream seems to be kaput. */
Nick Mathewson's avatar
Nick Mathewson committed
40
  int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */
41
  int min_loglevel; /**< Lowest severity level to send to this stream. */
Nick Mathewson's avatar
Nick Mathewson committed
42
  int max_loglevel; /**< Highest severity level to send to this stream. */
43
  int is_temporary; /**< Boolean: close after initializing logging subsystem.*/
44
  int is_syslog; /**< Boolean: send messages to syslog. */
Nick Mathewson's avatar
Nick Mathewson committed
45
  log_callback callback; /**< If not NULL, send messages to this function. */
46
47
} logfile_t;

48
/** Helper: map a log severity to descriptive string. */
49
50
51
static INLINE const char *
sev_to_string(int severity)
{
52
  switch (severity) {
53
54
    case LOG_DEBUG:   return "debug";
    case LOG_INFO:    return "info";
Roger Dingledine's avatar
Roger Dingledine committed
55
    case LOG_NOTICE:  return "notice";
Roger Dingledine's avatar
Roger Dingledine committed
56
    case LOG_WARN:    return "warn";
57
    case LOG_ERR:     return "err";
58
    default:          assert(0); return "UNKNOWN";
59
60
61
  }
}

62
/** Helper: decide whether to include the function name in the log message. */
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
static INLINE int
should_log_function_name(uint32_t domain, int severity)
{
  switch (severity) {
    case LOG_DEBUG:
    case LOG_INFO:
      /* All debugging messages occur in interesting places. */
      return 1;
    case LOG_NOTICE:
    case LOG_WARN:
    case LOG_ERR:
      /* We care about places where bugs occur. */
      return (domain == LD_BUG);
    default:
      assert(0); return 0;
  }
}

81
82
/* XXXX We should really have this protected by a mutex or something;
 * XXXX see bug 222. */
83
/** Linked list of logfile_t. */
84
static logfile_t *logfiles = NULL;
85
86
87
#ifdef HAVE_SYSLOG_H
static int syslog_count = 0;
#endif
88

89
90
91
/* What's the lowest log level anybody cares about? */
int _log_global_min_severity = LOG_NOTICE;

Roger Dingledine's avatar
Roger Dingledine committed
92
static void delete_log(logfile_t *victim);
93
static void close_log(logfile_t *victim);
Roger Dingledine's avatar
Roger Dingledine committed
94

95
96
/** Name of the application: used to generate the message we write at the
 * start of each new log. */
97
98
static char *appname = NULL;

99
100
101
102
103
/** Set the "application name" for the logs to <b>name</b>: we'll use this
 * name in the message we write when starting up, and at the start of each new
 * log.
 *
 * Tor uses this string to write the version number to the log file. */
104
105
106
107
108
109
110
void
log_set_application_name(const char *name)
{
  tor_free(appname);
  appname = name ? tor_strdup(name) : NULL;
}

111
112
113
/** Helper: Write the standard prefix for log lines to a
 * <b>buf_len</b> character buffer in <b>buf</b>.
 */
Roger Dingledine's avatar
Roger Dingledine committed
114
static INLINE size_t
115
_log_prefix(char *buf, size_t buf_len, int severity)
Roger Dingledine's avatar
Roger Dingledine committed
116
{
117
  time_t t;
118
  struct timeval now;
119
  struct tm tm;
120
  size_t n;
121
  int r;
122

123
  tor_gettimeofday(&now);
124
  t = (time_t)now.tv_sec;
125

126
  n = strftime(buf, buf_len, "%b %d %H:%M:%S", tor_localtime_r(&t, &tm));
Roger Dingledine's avatar
Roger Dingledine committed
127
  r = tor_snprintf(buf+n, buf_len-n, ".%.3ld [%s] ",
128
                   (long)now.tv_usec / 1000, sev_to_string(severity));
129
130
131
132
  if (r<0)
    return buf_len-1;
  else
    return n+r;
133
134
135
}

/** If lf refers to an actual file that we have just opened, and the file
136
137
138
139
 * contains no data, log an "opening new logfile" message at the top.
 *
 * Return -1 if the log is broken and needs to be deleted, else return 0.
 */
140
141
static int
log_tor_version(logfile_t *lf, int reset)
142
143
144
{
  char buf[256];
  size_t n;
145
  int is_new;
146
147
148

  if (!lf->needs_close)
    /* If it doesn't get closed, it isn't really a file. */
149
    return 0;
150
151
  if (lf->is_temporary)
    /* If it's temporary, it isn't really a file. */
152
    return 0;
153
#ifdef HAVE_FTELLO
154
155
  is_new = (ftello(lf->file) == 0);
#else
156
  is_new = (ftell(lf->file) == 0);
157
#endif
158
159
160
  if (reset && !is_new)
    /* We are resetting, but we aren't at the start of the file; no
     * need to log again. */
161
    return 0;
162
  n = _log_prefix(buf, sizeof(buf), LOG_NOTICE);
163
164
165
166
167
168
169
  if (appname) {
    tor_snprintf(buf+n, sizeof(buf)-n,
                 "%s opening %slog file.\n", appname, is_new?"new ":"");
  } else {
    tor_snprintf(buf+n, sizeof(buf)-n,
                 "Tor %s opening %slog file.\n", VERSION, is_new?"new ":"");
  }
170
  if (fputs(buf, lf->file) == EOF ||
171
      fflush(lf->file) == EOF) /* error */
172
173
    return -1; /* failed */
  return 0;
174
175
176
177
}

/** 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
178
179
 * than once.)  Return a pointer to the first character of the message
 * portion of the formatted string.
180
 */
181
182
static INLINE char *
format_msg(char *buf, size_t buf_len,
183
           uint32_t domain, int severity, const char *funcname,
184
           const char *format, va_list ap)
185
186
{
  size_t n;
187
  int r;
188
  char *end_of_prefix;
189
190

  tor_assert(buf_len >= 2); /* prevent integer underflow */
191
192
  buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */

193
  n = _log_prefix(buf, buf_len, severity);
194
  end_of_prefix = buf+n;
195

196
  if (funcname && should_log_function_name(domain, severity)) {
197
198
199
200
201
    r = tor_snprintf(buf+n, buf_len-n, "%s(): ", funcname);
    if (r<0)
      n = strlen(buf);
    else
      n += r;
202
  }
203

204
205
206
207
208
  if (domain == LD_BUG && buf_len-n > 6) {
    memcpy(buf+n, "Bug: ", 6);
    n += 5;
  }

209
  r = tor_vsnprintf(buf+n,buf_len-n,format,ap);
210
  if (r < 0) {
211
212
213
    /* The message was too long; overwrite the end of the buffer with
     * "[...truncated]" */
    if (buf_len >= TRUNCATED_STR_LEN) {
214
215
216
217
      int offset = buf_len-TRUNCATED_STR_LEN;
      /* We have an extra 2 characters after buf_len to hold the \n\0,
       * so it's safe to add 1 to the size here. */
      strlcpy(buf+offset, TRUNCATED_STR, buf_len-offset+1);
218
219
220
221
    }
    /* Set 'n' to the end of the buffer, where we'll be writing \n\0.
     * Since we already subtracted 2 from buf_len, this is safe.*/
    n = buf_len;
222
223
  } else {
    n += r;
224
  }
225
226
  buf[n]='\n';
  buf[n+1]='\0';
227
  return end_of_prefix;
228
229
}

Nick Mathewson's avatar
Nick Mathewson committed
230
231
/** Helper: sends a message to the appropriate logfiles, at loglevel
 * <b>severity</b>.  If provided, <b>funcname</b> is prepended to the
232
 * message.  The actual message is derived as from tor_snprintf(format,ap).
Nick Mathewson's avatar
Nick Mathewson committed
233
 */
234
static void
235
logv(int severity, uint32_t domain, const char *funcname, const char *format,
236
     va_list ap)
237
{
238
  char buf[10024];
239
240
  int formatted = 0;
  logfile_t *lf;
241
  char *end_of_prefix=NULL;
242

243
  assert(format);
244
  lf = logfiles;
245
  while (lf) {
246
    if (severity > lf->min_loglevel || severity < lf->max_loglevel) {
247
      lf = lf->next;
248
      continue;
249
    }
250
    if (! (lf->file || lf->is_syslog || lf->callback)) {
251
      lf = lf->next;
252
      continue;
253
    }
254
255
256
257
    if (lf->seems_dead) {
      lf = lf->next;
      continue;
    }
258

259
    if (!formatted) {
260
      end_of_prefix =
261
        format_msg(buf, sizeof(buf), domain, severity, funcname, format, ap);
262
263
      formatted = 1;
    }
264
265
    if (lf->is_syslog) {
#ifdef HAVE_SYSLOG_H
266
267
      /* XXXX Some syslog implementations have scary limits on the length of
       * what you can pass them.  Can/should we detect this? */
268
269
270
271
      syslog(severity, "%s", end_of_prefix);
#endif
      lf = lf->next;
      continue;
272
    } else if (lf->callback) {
273
      lf->callback(severity, domain, end_of_prefix);
274
275
      lf = lf->next;
      continue;
276
    }
277
    if (fputs(buf, lf->file) == EOF ||
278
        fflush(lf->file) == EOF) { /* error */
279
280
281
      /* don't log the error! mark this log entry to be blown away, and
       * continue. */
      lf->seems_dead = 1;
Roger Dingledine's avatar
Roger Dingledine committed
282
    }
283
    lf = lf->next;
284
  }
285
}
286

Nick Mathewson's avatar
Nick Mathewson committed
287
/** Output a message to the log. */
288
void
289
_log(int severity, uint32_t domain, const char *format, ...)
290
291
292
{
  va_list ap;
  va_start(ap,format);
293
  logv(severity, domain, NULL, format, ap);
294
  va_end(ap);
Roger Dingledine's avatar
Roger Dingledine committed
295
}
296

297
/** Output a message to the log, prefixed with a function name <b>fn</b>. */
298
#ifdef __GNUC__
299
void
300
_log_fn(int severity, uint32_t domain, const char *fn, const char *format, ...)
301
302
303
{
  va_list ap;
  va_start(ap,format);
304
  logv(severity, domain, fn, format, ap);
305
306
  va_end(ap);
}
307
308
#else
const char *_log_fn_function_name=NULL;
309
void
310
_log_fn(int severity, uint32_t domain, const char *format, ...)
311
312
313
{
  va_list ap;
  va_start(ap,format);
314
315
316
317
318
  logv(severity, domain, _log_fn_function_name, format, ap);
  va_end(ap);
  _log_fn_function_name = NULL;
}
void
319
_log_debug(uint32_t domain, const char *format, ...)
320
321
322
{
  va_list ap;
  va_start(ap,format);
323
  logv(LOG_DEBUG, domain, _log_fn_function_name, format, ap);
324
325
326
327
  va_end(ap);
  _log_fn_function_name = NULL;
}
void
328
_log_info(uint32_t domain, const char *format, ...)
329
330
331
{
  va_list ap;
  va_start(ap,format);
332
  logv(LOG_INFO, domain, _log_fn_function_name, format, ap);
333
334
335
336
  va_end(ap);
  _log_fn_function_name = NULL;
}
void
337
_log_notice(uint32_t domain, const char *format, ...)
338
339
340
{
  va_list ap;
  va_start(ap,format);
341
  logv(LOG_NOTICE, domain, _log_fn_function_name, format, ap);
342
343
344
345
  va_end(ap);
  _log_fn_function_name = NULL;
}
void
346
_log_warn(uint32_t domain, const char *format, ...)
347
348
349
{
  va_list ap;
  va_start(ap,format);
350
  logv(LOG_WARN, domain, _log_fn_function_name, format, ap);
351
352
353
354
  va_end(ap);
  _log_fn_function_name = NULL;
}
void
355
_log_err(uint32_t domain, const char *format, ...)
356
357
358
{
  va_list ap;
  va_start(ap,format);
359
  logv(LOG_ERR, domain, _log_fn_function_name, format, ap);
360
361
362
363
  va_end(ap);
  _log_fn_function_name = NULL;
}
#endif
364

365
/** Close all open log files, and free other static memory. */
366
void
367
logs_free_all(void)
368
{
369
370
371
372
373
374
  logfile_t *victim, *next;
  next = logfiles;
  logfiles = NULL;
  while (next) {
    victim = next;
    next = next->next;
375
    close_log(victim);
376
377
    tor_free(victim->filename);
    tor_free(victim);
378
  }
379
  tor_free(appname);
380
}
381

Roger Dingledine's avatar
Roger Dingledine committed
382
/** Remove and free the log entry <b>victim</b> from the linked-list
383
384
385
386
387
388
 * logfiles (it is probably present, but it might not be due to thread
 * racing issues). After this function is called, the caller shouldn't
 * refer to <b>victim</b> anymore.
 *
 * Long-term, we need to do something about races in the log subsystem
 * in general. See bug 222 for more details.
Roger Dingledine's avatar
Roger Dingledine committed
389
 */
390
391
392
static void
delete_log(logfile_t *victim)
{
Roger Dingledine's avatar
Roger Dingledine committed
393
  logfile_t *tmpl;
394
  if (victim == logfiles)
Roger Dingledine's avatar
Roger Dingledine committed
395
396
    logfiles = victim->next;
  else {
397
    for (tmpl = logfiles; tmpl && tmpl->next != victim; tmpl=tmpl->next) ;
398
399
400
401
//    tor_assert(tmpl);
//    tor_assert(tmpl->next == victim);
    if (!tmpl)
      return;
Roger Dingledine's avatar
Roger Dingledine committed
402
403
404
405
406
407
    tmpl->next = victim->next;
  }
  tor_free(victim->filename);
  tor_free(victim);
}

408
409
/** Helper: release system resources (but not memory) held by a single
 * logfile_t. */
410
411
static void
close_log(logfile_t *victim)
412
413
414
415
416
{
  if (victim->needs_close && victim->file) {
    fclose(victim->file);
  } else if (victim->is_syslog) {
#ifdef HAVE_SYSLOG_H
417
    if (--syslog_count == 0) {
418
419
      /* There are no other syslogs; close the logging facility. */
      closelog();
420
    }
421
422
423
424
#endif
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
425
426
/** Add a log handler to send all messages of severity <b>loglevel</b>
 * or higher to <b>stream</b>. */
427
void
428
429
add_stream_log(int loglevelMin, int loglevelMax,
               const char *name, FILE *stream)
430
431
{
  logfile_t *lf;
432
  lf = tor_malloc_zero(sizeof(logfile_t));
433
  lf->filename = tor_strdup(name);
434
  lf->min_loglevel = loglevelMin;
435
  lf->max_loglevel = loglevelMax;
436
437
438
  lf->file = stream;
  lf->next = logfiles;
  logfiles = lf;
439
440

  _log_global_min_severity = get_min_log_level();
441
442
}

443
444
445
/** Add a log handler to receive messages during startup (before the real
 * logs are initialized).
 */
446
447
void
add_temp_log(void)
448
{
449
  add_stream_log(LOG_NOTICE, LOG_ERR, "<temp>", stdout);
450
  logfiles->is_temporary = 1;
451
452

  _log_global_min_severity = get_min_log_level();
453
454
}

455
456
457
458
459
/**
 * Add a log handler to send messages of severity between
 * <b>logLevelmin</b> and <b>logLevelMax</b> to the function
 * <b>cb</b>.
 */
460
461
int
add_callback_log(int loglevelMin, int loglevelMax, log_callback cb)
462
463
464
{
  logfile_t *lf;
  lf = tor_malloc_zero(sizeof(logfile_t));
465
  lf->min_loglevel = loglevelMin;
466
  lf->max_loglevel = loglevelMax;
Nick Mathewson's avatar
Nick Mathewson committed
467
  lf->filename = tor_strdup("<callback>");
468
469
470
  lf->callback = cb;
  lf->next = logfiles;
  logfiles = lf;
471
472

  _log_global_min_severity = get_min_log_level();
473
474
475
  return 0;
}

476
477
/** Adjust the configured severity of any logs whose callback function is
 * <b>cb</b>. */
478
479
480
void
change_callback_log_severity(int loglevelMin, int loglevelMax,
                             log_callback cb)
481
482
483
484
{
  logfile_t *lf;
  for (lf = logfiles; lf; lf = lf->next) {
    if (lf->callback == cb) {
485
      lf->min_loglevel = loglevelMin;
486
487
488
      lf->max_loglevel = loglevelMax;
    }
  }
489
490

  _log_global_min_severity = get_min_log_level();
491
492
}

493
/** Close any log handlers added by add_temp_log or marked by mark_logs_temp */
494
495
void
close_temp_logs(void)
496
{
497
498
499
500
  logfile_t *lf, **p;
  for (p = &logfiles; *p; ) {
    if ((*p)->is_temporary) {
      lf = *p;
501
      /* we use *p here to handle the edge case of the head of the list */
502
503
504
505
      *p = (*p)->next;
      close_log(lf);
      tor_free(lf->filename);
      tor_free(lf);
506
    } else {
507
      p = &((*p)->next);
508
509
    }
  }
510
511

  _log_global_min_severity = get_min_log_level();
512
513
}

514
515
516
517
518
519
520
521
522
523
524
/** Make all currently temporary logs (set to be closed by close_temp_logs)
 * live again, and close all non-temporary logs. */
void
rollback_log_changes(void)
{
  logfile_t *lf;
  for (lf = logfiles; lf; lf = lf->next)
    lf->is_temporary = ! lf->is_temporary;
  close_temp_logs();
}

525
/** Configure all log handles to be closed by close_temp_logs */
526
527
void
mark_logs_temp(void)
528
529
530
531
532
533
{
  logfile_t *lf;
  for (lf = logfiles; lf; lf = lf->next)
    lf->is_temporary = 1;
}

Nick Mathewson's avatar
Nick Mathewson committed
534
535
536
/**
 * Add a log handler to send messages to <b>filename</b>. If opening
 * the logfile fails, -1 is returned and errno is set appropriately
537
 * (by fopen).
538
 */
539
540
int
add_file_log(int loglevelMin, int loglevelMax, const char *filename)
541
542
543
{
  FILE *f;
  f = fopen(filename, "a");
544
  if (!f) return -1;
545
  add_stream_log(loglevelMin, loglevelMax, filename, f);
546
  logfiles->needs_close = 1;
547
  if (log_tor_version(logfiles, 0) < 0) {
548
    delete_log(logfiles);
549
  }
550
  _log_global_min_severity = get_min_log_level();
551
  return 0;
552
}
553

554
555
556
557
#ifdef HAVE_SYSLOG_H
/**
 * Add a log handler to send messages to they system log facility.
 */
558
559
int
add_syslog_log(int loglevelMin, int loglevelMax)
560
561
562
{
  logfile_t *lf;
  if (syslog_count++ == 0)
Roger Dingledine's avatar
Roger Dingledine committed
563
    /* This is the first syslog. */
564
    openlog("Tor", LOG_PID | LOG_NDELAY, LOGFACILITY);
565
566

  lf = tor_malloc_zero(sizeof(logfile_t));
567
  lf->min_loglevel = loglevelMin;
568
569
570
571
572
  lf->filename = tor_strdup("<syslog>");
  lf->max_loglevel = loglevelMax;
  lf->is_syslog = 1;
  lf->next = logfiles;
  logfiles = lf;
573
574

  _log_global_min_severity = get_min_log_level();
575
576
577
578
  return 0;
}
#endif

579
580
/** If <b>level</b> is a valid log severity, return the corresponding
 * numeric value.  Otherwise, return -1. */
581
582
583
int
parse_log_level(const char *level)
{
584
585
  if (!strcasecmp(level, "err"))
    return LOG_ERR;
586
587
588
  if (!strcasecmp(level, "warn"))
    return LOG_WARN;
  if (!strcasecmp(level, "notice"))
589
    return LOG_NOTICE;
590
  if (!strcasecmp(level, "info"))
591
    return LOG_INFO;
592
  if (!strcasecmp(level, "debug"))
593
    return LOG_DEBUG;
594
  return -1;
595
596
}

597
/** Return the string equivalent of a given log level. */
598
599
const char *
log_level_to_string(int level)
600
601
602
603
{
  return sev_to_string(level);
}

604
/** Return the least severe log level that any current log is interested in. */
605
606
int
get_min_log_level(void)
607
608
609
610
{
  logfile_t *lf;
  int min = LOG_ERR;
  for (lf = logfiles; lf; lf = lf->next) {
611
612
    if (lf->min_loglevel > min)
      min = lf->min_loglevel;
613
614
615
616
  }
  return min;
}

617
/** Switch all logs to output at most verbose level. */
618
619
void
switch_logs_debug(void)
620
621
622
{
  logfile_t *lf;
  for (lf = logfiles; lf; lf=lf->next) {
623
    lf->min_loglevel = LOG_DEBUG;
624
625
626
  }
}

627
#ifdef HAVE_EVENT_SET_LOG_CALLBACK
628
/** A string which, if it appears in a libevent log, should be ignored. */
629
static const char *suppress_msg = NULL;
630
631
/** Callback function passed to event_set_log() so we can intercept
 * log messages from libevent. */
632
633
static void
libevent_logging_callback(int severity, const char *msg)
634
{
635
636
  char buf[1024];
  size_t n;
637
638
  if (suppress_msg && strstr(msg, suppress_msg))
    return;
639
640
641
642
  n = strlcpy(buf, msg, sizeof(buf));
  if (n && n < sizeof(buf) && buf[n-1] == '\n') {
    buf[n-1] = '\0';
  }
643
644
  switch (severity) {
    case _EVENT_LOG_DEBUG:
645
      log(LOG_DEBUG, LD_NET, "Message from libevent: %s", buf);
646
647
      break;
    case _EVENT_LOG_MSG:
648
      log(LOG_INFO, LD_NET, "Message from libevent: %s", buf);
649
650
      break;
    case _EVENT_LOG_WARN:
651
      log(LOG_WARN, LD_GENERAL, "Warning from libevent: %s", buf);
652
653
      break;
    case _EVENT_LOG_ERR:
654
      log(LOG_ERR, LD_GENERAL, "Error from libevent: %s", buf);
655
656
      break;
    default:
657
658
      log(LOG_WARN, LD_GENERAL, "Message [%d] from libevent: %s",
          severity, buf);
659
660
661
      break;
  }
}
662
/** Set hook to intercept log messages from libevent. */
663
664
void
configure_libevent_logging(void)
665
666
667
{
  event_set_log_callback(libevent_logging_callback);
}
Roger Dingledine's avatar
Roger Dingledine committed
668
/** Ignore any libevent log message that contains <b>msg</b>. */
669
670
void
suppress_libevent_log_msg(const char *msg)
671
672
673
{
  suppress_msg = msg;
}
674
#else
675
676
677
678
679
680
681
void
configure_libevent_logging(void)
{
}
void
suppress_libevent_log_msg(const char *msg)
{
682
  (void)msg;
683
}
684
#endif
685

686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
#if 0
static void
dump_log_info(logfile_t *lf)
{
  const char *tp;

  if (lf->filename) {
    printf("=== log into \"%s\" (%s-%s) (%stemporary)\n", lf->filename,
           sev_to_string(lf->min_loglevel),
           sev_to_string(lf->max_loglevel),
           lf->is_temporary?"":"not ");
  } else if (lf->is_syslog) {
    printf("=== syslog (%s-%s) (%stemporary)\n",
           sev_to_string(lf->min_loglevel),
           sev_to_string(lf->max_loglevel),
           lf->is_temporary?"":"not ");
  } else {
    printf("=== log (%s-%s) (%stemporary)\n",
           sev_to_string(lf->min_loglevel),
           sev_to_string(lf->max_loglevel),
           lf->is_temporary?"":"not ");
  }
}

void
describe_logs(void)
{
  logfile_t *lf;
  printf("==== BEGIN LOGS ====\n");
  for (lf = logfiles; lf; lf = lf->next)
    dump_log_info(lf);
  printf("==== END LOGS ====\n");
}
#endif