From 9470cb2c32eab220f796b1438b787528272cbe84 Mon Sep 17 00:00:00 2001 From: Chris Hall Date: Mon, 21 Mar 2011 01:16:05 +0000 Subject: Upgrade of "pipework" -- including piping to/from shell commands Version 0.99.15ex11p A major overhaul. --- lib/log.c | 1358 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 783 insertions(+), 575 deletions(-) (limited to 'lib/log.c') diff --git a/lib/log.c b/lib/log.c index 4da1e5e8..e3296d3a 100644 --- a/lib/log.c +++ b/lib/log.c @@ -25,13 +25,14 @@ #include #include "log.h" -#include "vty.h" +#include "log_local.h" +#include "vty_log.h" #include "memory.h" -#include "command_local.h" + #ifndef SUNOS_5 #include #endif -/* for printstack on solaris */ +/* for printstack on solaris */ #ifdef HAVE_UCONTEXT_H #include #endif @@ -39,18 +40,11 @@ #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); +/*------------------------------------------------------------------------------ + * Prototypes + */ +static void zlog_abort (const char *mess) __attribute__ ((noreturn)) ; 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[] = { @@ -80,23 +74,310 @@ const char *zlog_priority[] = NULL, }; +/*------------------------------------------------------------------------------ + * Static variables + */ +struct zlog* zlog_default = NULL; + +struct zlog* zlog_list = NULL ; + +static volatile sig_atomic_t max_maxlvl = INT_MAX ; + +qpt_mutex_t log_mutex ; + +int log_lock_count = 0 ; +int log_assert_fail = 0 ; + /*============================================================================== - * Time stamp handling -- gettimeofday(), localtime() and strftime(). + * Log locking and relationship with VTY. * - * 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. + * For pthreads purposes there is a LOG_LOCK() mutex. * - * The value from gettimeofday() is in micro-seconds. Can provide timestamp - * with any number of decimal digits, but at most 6 will be significant. + * 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_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) +{ + qpt_mutex_init(log_mutex, qpt_mutex_recursive); +} ; + +/*------------------------------------------------------------------------------ + * Close down for qpthreads. + */ +extern void +log_finish(void) +{ + qpt_mutex_destroy(log_mutex, 0); +} ; + +/*============================================================================== + * The actual logging function and creation of the logging lines. + * + */ +#define TIMESTAMP_FORM "%Y/%m/%d %H:%M:%S" + +/* 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. + */ +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. So, we assume that: + * + * a) writing to max_maxlvl is atomic wrt all forms of interrupt. + * So the variable cannot exist in a partly written state (with, + * say, some bytes of the old value and some of the new). + * + * b) reading max_maxlvl will either collect the state before some + * change to the logging levels, or after. + * + * If passes the initial test, immediately acquires the LOG_LOCK(). + * + * So, if the logging facilities are being changed, then: + * + * a) if the level is about to be increased, so the current + * priority would pass, then that change is just too late + * for this particular logging operation. + * + * b) if the level is about to be reduced, will get past the + * initial test, but will fail the later tests, under the + * LOG_LOCK(). + * + * NB: max_maxlvl is statically initialised to INT_MAX ! + */ + 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) ; + + return ; + } ; + } ; + + /* 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_log(priority, ll->line, ll->len - 1) ; /* less the '\n' */ + } ; + + LOG_UNLOCK() ; +} ; + +/*------------------------------------------------------------------------------ + * Preparation of line to send to logging: file, stdout or "monitor" terminals. + */ +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) - 1) ; /* leave space for '\n' */ + /* "