/* * $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" /* 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. */ /*------------------------------------------------------------------------------ * 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. */ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) { size_t result; VTY_LOCK() ; result = uquagga_timestamp(timestamp_precision, buf, buflen); VTY_UNLOCK() ; return result; } /*------------------------------------------------------------------------------ * unprotected version for when mutex already held */ size_t uquagga_timestamp(int timestamp_precision, char *buf, size_t buflen) { static struct { time_t last; size_t len; char buf[timestamp_buffer_len]; } cache; struct timeval clock; size_t len ; int left ; assert((buflen > 1) && (buf != NULL)) ; /* 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), "%Y/%m/%d %H:%M:%S", &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 */ len = cache.len ; /* NB: asserted cache.len > 0 */ left = (buflen - (len + 1)) ; /* what would be left */ if (left < 0) len = buflen - 1 ; /* NB: asserted buflen > 1 */ memcpy(buf, cache.buf, len) ; /* Can do decimal part if there is room for the '.' character */ if ((timestamp_precision > 0) && (left > 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; char *p ; prec = timestamp_precision ; if ((1 + prec) > left) prec = left - 1 ; /* NB: left > 0 */ len += 1 + prec ; p = buf + prec ; /* point at last decimal digit */ while (prec > 6) /* this is unlikely to happen, but protect anyway */ { *p-- = '0'; --prec ; } ; clock.tv_usec /= divisor[prec]; while (prec > 0) /* could have been reduced to 0 */ { *p-- = '0'+(clock.tv_usec % 10); clock.tv_usec /= 10; --prec ; } ; *p = '.'; } ; buf[len] = '\0'; return len ; } ; /*============================================================================== * 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, 0) ; 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, 0) ; write(fileno(zl->fp), ll.line, ll.len) ; } /* stdout output. */ if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) { uvzlog_line(&ll, zl, priority, format, va, 0) ; 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, int crlf) { char* p ; p = ll->p_nl ; if (p != NULL) { /* we have the line -- just need to worry about the crlf state */ if ((crlf && ll->crlf) || (!crlf && !ll->crlf)) return ; /* exit here if all set */ } else { /* must construct the line */ const char* q ; char* e ; size_t len ; va_list vac ; p = ll->line = ll->buf ; e = p + sizeof(ll->buf) - 3 ; /* leave space for '\r', '\n' and '\0' */ /* "