/* * $Id$ * * Logging of zebra * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "log.h" #include "vty.h" #include "uty.h" #include "memory.h" #include "command.h" #ifndef SUNOS_5 #include #endif /* for printstack on solaris */ #ifdef HAVE_UCONTEXT_H #include #endif #include "qpthreads.h" #include "qfstring.h" #include "sigevent.h" /* log is protected by the same mutext as vty, see comments in vty.c */ /* prototypes */ static int uzlog_reset_file (struct zlog *zl); static void zlog_abort (const char *mess) __attribute__ ((noreturn)); static void vzlog (struct zlog *zl, int priority, const char *format, va_list args); static void uzlog_backtrace(int priority); static void uvzlog (struct zlog *zl, int priority, const char *format, va_list args); static int logfile_fd = -1; /* Used in signal handler. */ struct zlog *zlog_default = NULL; const char *zlog_proto_names[] = { "NONE", "DEFAULT", "ZEBRA", "RIP", "BGP", "OSPF", "RIPNG", "OSPF6", "ISIS", "MASC", NULL, }; const char *zlog_priority[] = { "emergencies", "alerts", "critical", "errors", "warnings", "notifications", "informational", "debugging", NULL, }; /*============================================================================== * Time stamp handling -- gettimeofday(), localtime() and strftime(). * * Maintains a cached form of the current time (under the vty/log mutex), so * that can avoid multiple calls of localtime() and strftime() per second. * * The value from gettimeofday() is in micro-seconds. Can provide timestamp * with any number of decimal digits, but at most 6 will be significant. */ static void uquagga_timestamp(qf_str qfs, int timestamp_precision) ; /*------------------------------------------------------------------------------ * Fill buffer with current time, to given number of decimal digits. * * If given buffer is too small, provides as many characters as possible. * * Returns: number of characters in buffer, not including trailing '\0'. * * NB: does no rounding. * * NB: buflen MUST be > 1 and buf MUST NOT be NULL. */ extern size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) { qf_str_t qfs ; VTY_LOCK() ; qfs_init(&qfs, buf, buflen) ; uquagga_timestamp(&qfs, timestamp_precision) ; VTY_UNLOCK() ; return qfs_len(&qfs) ; } /*------------------------------------------------------------------------------ * unprotected version for when mutex already held */ static void uquagga_timestamp(qf_str qfs, int timestamp_precision) { static struct { time_t last; size_t len; char buf[timestamp_buffer_len]; } cache; struct timeval clock; /* would it be sufficient to use global 'recent_time' here? I fear not... */ gettimeofday(&clock, NULL); /* first, we update the cache if the time has changed */ if (cache.last != clock.tv_sec) { struct tm tm; cache.last = clock.tv_sec; localtime_r(&cache.last, &tm); cache.len = strftime(cache.buf, sizeof(cache.buf), TIMESTAMP_FORM, &tm) ; assert(cache.len > 0) ; } /* note: it's not worth caching the subsecond part, because * chances are that back-to-back calls are not sufficiently close together * for the clock not to have ticked forward */ qfs_append_n(qfs, cache.buf, cache.len) ; /* Add decimal part as required. */ if (timestamp_precision > 0) { /* should we worry about locale issues? */ static const int divisor[] = { 1, /* 0 */ 100000, 10000, 1000, /* 1, 2, 3 */ 100, 10, 1}; /* 4, 5, 6 */ int prec; prec = timestamp_precision ; if (prec > 6) prec = 6 ; qfs_append_n(qfs, ".", 1) ; qfs_unsigned(qfs, clock.tv_usec / divisor[prec], 0, 0, prec) ; if (prec < timestamp_precision) qfs_append_ch_x_n(qfs, '0', timestamp_precision - prec) ; } ; } ; /*============================================================================== * va_list version of zlog */ static void vzlog (struct zlog *zl, int priority, const char *format, va_list args) { VTY_LOCK() ; uvzlog(zl, priority, format, args); VTY_UNLOCK() ; } /* va_list version of zlog. Unprotected assumes mutex already held*/ static void uvzlog (struct zlog *zl, int priority, const char *format, va_list va) { struct logline ll ; /* prepares line for output, here */ VTY_ASSERT_LOCKED() ; ll.p_nl = NULL ; /* Nothing generated, yet */ /* If zlog is not specified, use default one. */ if (zl == NULL) zl = zlog_default ; /* When zlog_default is also NULL, use stderr for logging. */ if (zl == NULL) { uvzlog_line(&ll, zl, priority, format, va, llt_lf) ; write(fileno(stderr), ll.line, ll.len) ; } else { /* Syslog output */ if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) { va_list ac; va_copy(ac, va); vsyslog (priority|zlog_default->facility, format, ac); va_end(ac); } /* File output. */ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) { uvzlog_line(&ll, zl, priority, format, va, llt_lf) ; write(fileno(zl->fp), ll.line, ll.len) ; } /* stdout output. */ if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) { uvzlog_line(&ll, zl, priority, format, va, llt_lf) ; write(fileno(zl->fp), ll.line, ll.len) ; } /* Terminal monitor. */ if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) uty_log(&ll, zl, priority, format, va) ; } } /*------------------------------------------------------------------------------ * Preparation of line to send to logging: file, stdout or "monitor" terminals. * * Takes copy of va_list before using it, so the va_list is unchanged. */ extern void uvzlog_line(struct logline* ll, struct zlog *zl, int priority, const char *format, va_list va, enum ll_term term) { char* p ; const char* q ; p = ll->p_nl ; if (p != NULL) { /* we have the line -- just need to worry about the crlf state */ if (term == ll->term) return ; /* exit here if all set */ } else { /* must construct the line */ qf_str_t qfs ; va_list vac ; qfs_init(&qfs, ll->line, sizeof(ll->line) - 2) ; /* leave space for '\n' or '\r''\n' */ /* "