/*
 *	BIRD Library -- Logging Functions
 *
 *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

/**
 * DOC: Logging
 *
 * The Logging module offers a simple set of functions for writing
 * messages to system logs and to the debug output.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/time.h>

#include "nest/bird.h"
#include "nest/cli.h"
#include "lib/string.h"
#include "lib/lists.h"
#include "lib/unix.h"

static FILE *dbgf = NULL;
static list *current_log_list;
static list init_log_list;

#ifdef HAVE_SYSLOG
#include <sys/syslog.h>

static int syslog_priorities[] = {
  LOG_DEBUG,
  LOG_DEBUG,
  LOG_DEBUG,
  LOG_INFO,
  LOG_ERR,
  LOG_WARNING,
  LOG_ERR,
  LOG_ERR,
  LOG_CRIT,
  LOG_CRIT
};
#endif

static char *class_names[] = {
  "???",
  "DBG",
  "TRACE",
  "INFO",
  "RMT",
  "WARN",
  "ERR",
  "AUTH",
  "FATAL",
  "BUG"
};

static void
vlog(int class, char *msg, va_list args)
{
  char buf[1024];
  struct log_config *l;

  if (bvsnprintf(buf, sizeof(buf)-1, msg, args) < 0)
    bsprintf(buf + sizeof(buf) - 100, " ... <too long>");

  WALK_LIST(l, *current_log_list)
    {
      if (!(l->mask & (1 << class)))
	continue;
      if (l->fh)
	{
	  time_t now = time(NULL);
	  struct tm *tm = localtime(&now);

	  if (l->terminal_flag)
	    fputs("bird: ", l->fh);
	  else
	    {
	      fprintf(l->fh, "%02d-%02d-%04d %02d:%02d:%02d <%s> ",
		       tm->tm_mday,
		       tm->tm_mon+1,
		       tm->tm_year+1900,
		       tm->tm_hour,
		       tm->tm_min,
		       tm->tm_sec,
		       class_names[class]);
	    }
	  fputs(buf, l->fh);
	  fputc('\n', l->fh);
	  fflush(l->fh);
	}
#ifdef HAVE_SYSLOG
      else
	syslog(syslog_priorities[class], "%s", buf);
#endif
    }
  cli_echo(class, buf);
}

/**
 * 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
 * and writes it to the corresponding log file (as specified in the
 * configuration). Please note that the message is automatically
 * formatted as a full line, no need to include |\n| inside.
 */
void
log(char *msg, ...)
{
  int class = 1;
  va_list args;

  va_start(args, msg);
  if (*msg >= 1 && *msg <= 8)
    class = *msg++;
  vlog(class, msg, args);
  va_end(args);
}

/**
 * bug - report an internal error
 * @msg: a printf-like error message
 *
 * This function logs an internal error and aborts execution
 * of the program.
 */
void
bug(char *msg, ...)
{
  va_list args;

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

/**
 * bug - report a fatal error
 * @msg: a printf-like error message
 *
 * This function logs a fatal error and aborts execution
 * of the program.
 */
void
die(char *msg, ...)
{
  va_list args;

  va_start(args, msg);
  vlog(L_FATAL[0], msg, args);
  exit(1);
}

/**
 * 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.
 */
void
debug(char *msg, ...)
{
  va_list args;
  char buf[1024];

  va_start(args, msg);
  if (dbgf)
    {
      if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
	bsprintf(buf + sizeof(buf) - 100, " ... <too long>\n");
      fputs(buf, dbgf);
    }
  va_end(args);
}

void
log_init(int debug)
{
  static struct log_config lc_stderr = { mask: ~0, terminal_flag: 1 };

  init_list(&init_log_list);
  current_log_list = &init_log_list;

#ifdef HAVE_SYSLOG
  if (!debug)
    {
      static struct log_config lc_syslog = { mask: ~0 };
      openlog("bird", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
      add_tail(current_log_list, &lc_syslog.n);
    }
#endif

  lc_stderr.fh = stderr;
  add_tail(current_log_list, &lc_stderr.n);
}

void
log_switch(list *l)
{
  if (EMPTY_LIST(*l))
    current_log_list = &init_log_list;
  else
    current_log_list = l;
}

void
log_init_debug(char *f)
{
  if (dbgf && dbgf != stderr)
    fclose(dbgf);
  if (!f)
    dbgf = NULL;
  else if (!*f)
    dbgf = stderr;
  else if (!(dbgf = fopen(f, "a")))
    log(L_ERR "Error opening debug file `%s': %m", f);
  if (dbgf)
    setvbuf(dbgf, NULL, _IONBF, 0);
}