/* * 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. */ #define QUAGGA_DEFINE_DESC_TABLE #include #include "log.h" #include "log_local.h" #include "vty_log.h" #include "memory.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" /*------------------------------------------------------------------------------ * Logging is managed using struct zlog, which represents a set of logging * destinations, which are output to in parallel, each with its own log * priority. * * Notionally, there may be more than one strct zlog, but in practice there is * only one and that is the one pointed at by zlog_default. */ struct zlog { struct zlog* next ; /* To support multiple logging streams */ const char *ident; /* daemon name (first arg to openlog) */ zlog_proto_t protocol ; int maxlvl[ZLOG_DEST_COUNT]; /* maximum priority set */ int monitor_lvl ; /* last monitor level specified */ int default_lvl ; /* maxlvl to use if none is specified */ int emaxlvl[ZLOG_DEST_COUNT]; /* effective maximum priority */ bool syslog ; /* have active syslog */ int file_fd ; /* fd for ZLOG_DEST_FILE (if any) */ int stdout_fd ; /* fd for ZLOG_DEST_STDOUT */ char *filename; /* for ZLOG_DEST_FILE */ int facility; /* as per syslog facility */ int syslog_options; /* 2nd arg to openlog */ int timestamp_precision; /* # of digits of subsecond precision */ bool record_priority; /* should messages logged through stdio include the priority of the message? */ } ; /*------------------------------------------------------------------------------ * Tables of protocol and log priority names. */ const char *zlog_proto_names[] = { [ZLOG_NONE] = "NONE", [ZLOG_DEFAULT] = "DEFAULT", [ZLOG_ZEBRA] = "ZEBRA", [ZLOG_RIP] = "RIP", [ZLOG_BGP] = "BGP", [ZLOG_OSPF] = "OSPF", [ZLOG_RIPNG] = "RIPNG", [ZLOG_OSPF6] = "OSPF6", [ZLOG_ISIS] = "ISIS", [ZLOG_MASC] = "MASC", NULL, } ; const char *zlog_priority[] = { [LOG_EMERG] = "emergencies", [LOG_ALERT] = "alerts", [LOG_CRIT] = "critical", [LOG_ERR] = "errors", [LOG_WARNING] = "warnings", [LOG_NOTICE] = "notifications", [LOG_INFO] = "informational", [LOG_DEBUG] = "debugging", NULL, }; /*------------------------------------------------------------------------------ * Static variables */ struct zlog* zlog_default = NULL; struct zlog* zlog_list = NULL ; static volatile sig_atomic_t max_maxlvl = ZLOG_DISABLED ; qpt_mutex log_mutex ; int log_lock_count = 0 ; int log_assert_fail = 0 ; /*------------------------------------------------------------------------------ * Prototypes */ static void zlog_abort (const char *mess) __attribute__ ((noreturn)) ; static void uzlog_backtrace(int priority); /*============================================================================== * Log locking and relationship with VTY. * * For pthreads purposes there is a LOG_LOCK() mutex. * * To support interaction with the VTY: * * a. setting logging configuration. * * b. issuing log messages within the VTY * * the LOG_LOCK() may be collected while VTY_LOCK() -- BUT NOT vice versa ! * This is not too difficult, because the logging has no need to call VTY * functions... except for "vty monitor" VTY, so, for that purpose: * * a. for each monitor VTY there is a "monitor_fifo", into which log messages * are place if required. This buffer and some related flags must be * handled under the LOG_LOCK(). * * The vty_monitor_log() function takes care of this, and takes care of * prompting the vty(s) to output the contents of the fifo. * * This will be called under LOG_LOCK(). It will NOT VTY_LOCK(). It * will return promptly. * * b. once any logging has been handed to the VTY, the logging can forget * about it. * * c. the interface from logging to VTY is in vty_log.h * * The logging system supports some logging once a signal (in particular, * the hard exceptions, such as SIGSEGV) has gone off and the system is being * brought to a dead stop. A separate mechanism is provided for outputting * directly to any vty monitor VTY -- at a file descriptor level ! * * The VTY may call logging functions in the same was as any other part of the * system. When interacting with configuration etc, may need to acquire the * LOG_LOCK, etc. The interface from VTY to logging is in log_local.h. */ /*------------------------------------------------------------------------------ * Further initialisation for qpthreads. * * This is done during "second stage" initialisation, when all nexuses have * been set up and the qpthread_enabled state established. * * Initialise mutex. * * NB: may be called once and once only. */ extern void log_init_r(void) { log_mutex = qpt_mutex_new(qpt_mutex_recursive, "Logging"); } ; /*------------------------------------------------------------------------------ * Close down for qpthreads. */ extern void log_finish(void) { log_mutex = qpt_mutex_destroy(log_mutex); } ; /*============================================================================== * The actual logging function and creation of the logging lines. */ /* Structure used to hold line for log output -- so that need be generated * just once even if output to multiple destinations. * * Note that the buffer length is a hard limit (including terminating "\n") * Do not wish to malloc any larger buffer while logging. */ enum { logline_buffer_len = 1008 } ; struct logline { size_t len ; /* length including either '\r''\n' or '\n' */ char line[logline_buffer_len]; /* buffer */ } ; typedef struct logline logline_t[1] ; typedef struct logline* logline ; static void uvzlog_line(logline ll, struct zlog *zl, int priority, const char *format, va_list va) ; static void uquagga_timestamp(qf_str qfs, int timestamp_precision) ; /*============================================================================== * The main logging function. * * This writes the logging information directly to all logging destinations, * except the vty_monitor_log(), so that it is externally visible as soon as * possible. * * This assumes that syslog and file output is essentially instantaneous, and * will not block. * * Does not attempt to pick up or report any I/O errors. * * For vty_monitor_log(), any logging is buffered and dealt with by the vty. * * So, having acquired the LOG_LOCK() the logging will proceed without * requiring any further locks. Indeed, apart from vty_monitor_log() and * uquagga_timestamp() (TODO) the process is async_signal_safe, even. */ extern void zlog (struct zlog *zl, int priority, const char *format, ...) { logline_t ll ; /* Decide whether any logging at all is required. * * NB: the max_maxlvl is established every time any change is made to * logging facilities. Those changes are made under LOG_LOCK(), so * only one thread will write to max_max_lvl at any time, and that * will be consistent with the state of all known logging at the * time. * * The max_maxlvl is sig_atomic_t and volatile, and we assume that: * * writing to max_maxlvl is atomic wrt all forms of interrupt, * and wrt any processor cache invalidation. * * That is: * * the variable cannot be read in a partly written state (with, * say, some bytes of the old value and some of the new). * * Hence, reading max_maxlvl will either collect the state before some * change to the logging levels, or after. * * If the given priority > max_maxlvl, this function exits, otherwise it * immediately acquires the LOG_LOCK(). * * So, if the logging facilities are being changed, then: * * a) if the level is being increased, so the current priority would * would pass, then that change is just too late for this logging * operation. * * b) if the level is about to be reduced, then will get past the * initial test, but after acquiring the LOG_LOCK(), will find that * there is no logging to be done after all. * * NB: max_maxlvl is statically initialised to ZLOG_DISABLED. */ if (priority > max_maxlvl) return ; /* Decide where we are logging to. */ LOG_LOCK() ; if (zl == NULL) { zl = zlog_default ; if (zl == NULL) { /* Have to get up very early in the morning to get to here, because * zlog_default should be initialised -- by openzlog() -- very early * indeed. * * So... "log" to stderr. */ va_list va; va_start(va, format); uvzlog_line(ll, zl, priority, format, va) ; va_end (va); write(fileno(stderr), ll->line, ll->len) ; goto unlock ; } ; } ; /* Step through the logging destinations and log as required. */ ll->len = 0 ; /* Nothing generated, yet */ /* Syslog output */ if (priority <= zl->emaxlvl[ZLOG_DEST_SYSLOG]) { va_list va; va_start(va, format); vsyslog (priority|zlog_default->facility, format, va); va_end(va); } /* File output. */ if (priority <= zl->emaxlvl[ZLOG_DEST_FILE]) { if (ll->len == 0) { va_list va; va_start(va, format); uvzlog_line(ll, zl, priority, format, va) ; va_end (va); } ; write(zl->file_fd, ll->line, ll->len) ; } /* stdout output. */ if (priority <= zl->emaxlvl[ZLOG_DEST_STDOUT]) { if (ll->len == 0) { va_list va; va_start(va, format); uvzlog_line(ll, zl, priority, format, va) ; va_end (va); } ; write(zl->stdout_fd, ll->line, ll->len) ; } /* Terminal monitor. */ if (priority <= zl->emaxlvl[ZLOG_DEST_MONITOR]) { if (ll->len == 0) { va_list va; va_start(va, format); uvzlog_line(ll, zl, priority, format, va) ; va_end (va); } ; vty_monitor_log(priority, ll->line, ll->len - 1) ; /* less the '\n' */ } ; unlock: LOG_UNLOCK() ; } ; /*------------------------------------------------------------------------------ * Preparation of line to send to logging: file, stdout or "monitor" terminals. * * Line ends in '\n', but no terminating '\0'. * * TODO: apart from uquagga_timestamp, this is all async_signal_safe. * */ static void uvzlog_line(logline ll, struct zlog *zl, int priority, const char *format, va_list va) { const char* q ; qf_str_t qfs ; qfs_init(qfs, ll->line, sizeof(ll->line)) ; /* "