log.c 6.81 KB
Newer Older
1 2 3
/*
 *	BIRD Library -- Logging Functions
 *
4
 *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
5 6 7 8
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

9 10 11 12
/**
 * DOC: Logging
 *
 * The Logging module offers a simple set of functions for writing
13 14 15
 * messages to system logs and to the debug output. Message classes
 * used by this module are described in |birdlib.h| and also in the
 * user's manual.
16 17
 */

18 19 20
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
21
#include <time.h>
22
#include <unistd.h>
23 24

#include "nest/bird.h"
25
#include "nest/cli.h"
26
#include "nest/mrtdump.h"
27
#include "lib/string.h"
28 29
#include "lib/lists.h"
#include "lib/unix.h"
30

31
static FILE *dbgf;
32
static list *current_log_list;
33
static char *current_syslog_name; /* NULL -> syslog closed */
34

35

36
#ifdef USE_PTHREADS
37 38

#include <pthread.h>
39

40 41 42 43
static pthread_mutex_t log_mutex;
static inline void log_lock(void) { pthread_mutex_lock(&log_mutex); }
static inline void log_unlock(void) { pthread_mutex_unlock(&log_mutex); }

44 45 46 47
static pthread_t main_thread;
void main_thread_init(void) { main_thread = pthread_self(); }
static int main_thread_self(void) { return pthread_equal(pthread_self(), main_thread); }

48 49 50 51
#else

static inline void log_lock(void) {  }
static inline void log_unlock(void) {  }
52 53
void main_thread_init(void) { }
static int main_thread_self(void) { return 1; }
54 55 56

#endif

57

58 59 60 61
#ifdef HAVE_SYSLOG
#include <sys/syslog.h>

static int syslog_priorities[] = {
62 63
  LOG_DEBUG,
  LOG_DEBUG,
64 65
  LOG_DEBUG,
  LOG_INFO,
66
  LOG_ERR,
67 68
  LOG_WARNING,
  LOG_ERR,
69 70
  LOG_ERR,
  LOG_CRIT,
71 72 73 74 75 76 77
  LOG_CRIT
};
#endif

static char *class_names[] = {
  "???",
  "DBG",
78
  "TRACE",
79
  "INFO",
80
  "RMT",
81 82 83
  "WARN",
  "ERR",
  "AUTH",
84 85
  "FATAL",
  "BUG"
86 87
};

88

89 90 91 92 93 94 95 96 97 98 99 100 101
/**
 * log_commit - commit a log message
 * @class: message class information (%L_DEBUG to %L_BUG, see |lib/birdlib.h|)
 *
 * This function writes a message prepared in the log buffer to the
 * log file (as specified in the configuration). The log buffer is
 * reset after that. The log message is a full line, log_commit()
 * terminates it.
 *
 * The message class is an integer, not a first char of a string like
 * in log(), so it should be written like *L_INFO.
 */
void
102
log_commit(int class, buffer *buf)
103 104
{
  struct log_config *l;
105

106 107 108 109
  if (buf->pos == buf->end)
    strcpy(buf->end - 100, " ... <too long>");

  log_lock();
110
  WALK_LIST(l, *current_log_list)
111
    {
112 113 114 115 116 117 118 119
      if (!(l->mask & (1 << class)))
	continue;
      if (l->fh)
	{
	  if (l->terminal_flag)
	    fputs("bird: ", l->fh);
	  else
	    {
120 121 122
	      byte tbuf[TM_DATETIME_BUFFER_SIZE];
	      tm_format_datetime(tbuf, &config->tf_log, now);
	      fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]);
123
	    }
124
	  fputs(buf->start, l->fh);
125 126 127
	  fputc('\n', l->fh);
	  fflush(l->fh);
	}
128
#ifdef HAVE_SYSLOG
129
      else
130
	syslog(syslog_priorities[class], "%s", buf->start);
131 132
#endif
    }
133
  log_unlock();
134

135 136 137
  /* cli_echo is not thread-safe, so call it just from the main thread */
  if (main_thread_self())
    cli_echo(class, buf->start);
138 139

  buf->pos = buf->start;
140 141
}

142
int buffer_vprint(buffer *buf, const char *fmt, va_list args);
143 144 145 146

static void
vlog(int class, const char *msg, va_list args)
{
147 148 149 150
  buffer buf;
  LOG_BUFFER_INIT(buf);
  buffer_vprint(&buf, msg, args);
  log_commit(class, &buf);
151 152 153
}


154 155 156 157 158 159
/**
 * log - log a message
 * @msg: printf-like formatting string with message class information
 * prepended (%L_DEBUG to %L_BUG, see |lib/birdlib.h|)
 *
 * This function formats a message according to the format string @msg
160
 * and writes it to the corresponding log file (as specified in the
161 162
 * configuration). Please note that the message is automatically
 * formatted as a full line, no need to include |\n| inside.
163
 * It is essentially a sequence of log_reset(), logn() and log_commit().
164
 */
165
void
166
log_msg(const char *msg, ...)
167 168 169 170 171
{
  int class = 1;
  va_list args;

  va_start(args, msg);
172
  if (*msg >= 1 && *msg <= 8)
173 174 175 176 177
    class = *msg++;
  vlog(class, msg, args);
  va_end(args);
}

178
void
179
log_rl(struct tbf *f, const char *msg, ...)
180
{
181
  int last_hit = f->mark;
182 183 184
  int class = 1;
  va_list args;

185 186
  /* Rate limiting is a bit tricky here as it also logs '...' during the first hit */
  if (tbf_limit(f) && last_hit)
187 188 189 190
    return;

  if (*msg >= 1 && *msg <= 8)
    class = *msg++;
191 192 193

  va_start(args, msg);
  vlog(class, (f->mark ? "..." : msg), args);
194 195 196
  va_end(args);
}

197 198 199 200 201 202 203
/**
 * bug - report an internal error
 * @msg: a printf-like error message
 *
 * This function logs an internal error and aborts execution
 * of the program.
 */
204
void
205
bug(const char *msg, ...)
206 207 208 209 210
{
  va_list args;

  va_start(args, msg);
  vlog(L_BUG[0], msg, args);
211
  abort();
212 213
}

214 215 216 217 218 219 220
/**
 * bug - report a fatal error
 * @msg: a printf-like error message
 *
 * This function logs a fatal error and aborts execution
 * of the program.
 */
221
void
222
die(const char *msg, ...)
223 224 225 226
{
  va_list args;

  va_start(args, msg);
227
  vlog(L_FATAL[0], msg, args);
228 229 230
  exit(1);
}

231 232 233 234 235 236 237
/**
 * debug - write to debug output
 * @msg: a printf-like message
 *
 * This function formats the message @msg and prints it out
 * to the debugging output. No newline character is appended.
 */
238
void
239
debug(const char *msg, ...)
240 241
{
  va_list args;
242
  char buf[1024];
243 244 245

  va_start(args, msg);
  if (dbgf)
246 247 248 249 250
    {
      if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
	bsprintf(buf + sizeof(buf) - 100, " ... <too long>\n");
      fputs(buf, dbgf);
    }
251 252 253
  va_end(args);
}

254 255
static list *
default_log_list(int debug, int init, char **syslog_name)
256
{
257
  static list init_log_list;
258
  init_list(&init_log_list);
259
  *syslog_name = NULL;
260 261

#ifdef HAVE_SYSLOG
262
  if (!debug)
263
    {
264
      static struct log_config lc_syslog = { .mask = ~0 };
265 266
      add_tail(&init_log_list, &lc_syslog.n);
      *syslog_name = bird_name;
267
      if (!init)
268
	return &init_log_list;
269
    }
270 271
#endif

272
  static struct log_config lc_stderr = { .mask = ~0, .terminal_flag = 1 };
273
  lc_stderr.fh = stderr;
274 275
  add_tail(&init_log_list, &lc_stderr.n);
  return &init_log_list;
276 277 278
}

void
279
log_switch(int debug, list *l, char *new_syslog_name)
280
{
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
  if (!l || EMPTY_LIST(*l))
    l = default_log_list(debug, !l, &new_syslog_name);

  current_log_list = l;

#ifdef HAVE_SYSLOG
  if (current_syslog_name && new_syslog_name &&
      !strcmp(current_syslog_name, new_syslog_name))
    return;

  if (current_syslog_name)
    closelog();

  if (new_syslog_name)
    openlog(new_syslog_name, LOG_CONS | LOG_NDELAY, LOG_DAEMON);

  current_syslog_name = new_syslog_name;
#endif
299 300
}

301 302


303 304 305 306 307 308 309
void
log_init_debug(char *f)
{
  if (dbgf && dbgf != stderr)
    fclose(dbgf);
  if (!f)
    dbgf = NULL;
310 311
  else if (!*f)
    dbgf = stderr;
312 313
  else if (!(dbgf = fopen(f, "a")))
    log(L_ERR "Error opening debug file `%s': %m", f);
314 315
  if (dbgf)
    setvbuf(dbgf, NULL, _IONBF, 0);
316
}
317 318 319 320 321 322 323 324 325 326 327 328 329

void
mrt_dump_message(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len)
{
  /* Prepare header */
  put_u32(buf+0, now_real);
  put_u16(buf+4, type);
  put_u16(buf+6, subtype);
  put_u32(buf+8, len - MRTDUMP_HDR_LENGTH);

  if (p->cf->global->mrtdump_file != -1)
    write(p->cf->global->mrtdump_file, buf, len);
}