diff options
Diffstat (limited to 'lib/log.c')
-rw-r--r-- | lib/log.c | 1352 |
1 files changed, 780 insertions, 572 deletions
@@ -25,13 +25,14 @@ #include <zebra.h> #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 <sys/un.h> #endif -/* for printstack on solaris */ +/* for printstack on solaris */ #ifdef HAVE_UCONTEXT_H #include <ucontext.h> #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' */ + /* "<time stamp>" */ + uquagga_timestamp(qfs, (zl != NULL) ? zl->timestamp_precision : 0) ; + + qfs_append_n(qfs, " ", 1) ; + + /* "<priority>: " if required */ + if ((zl != NULL) && zl->record_priority) + { + qfs_append(qfs, zlog_priority[priority]) ; + qfs_append(qfs, ": ") ; + } ; + + /* "<protocol>: " or "unknown: " */ + if (zl != NULL) + q = zlog_proto_names[zl->protocol] ; + else + q = "unknown" ; + + qfs_append(qfs, q) ; + qfs_append(qfs, ": ") ; + + /* Now the log line itself (uses a *copy* of the va_list) */ + qfs_vprintf(qfs, format, va) ; + + /* Set pointer to where the '\n' is going */ + qfs_append_n(qfs, "\n", 1) ; + ll->len = qfs_len(qfs) ; +} ; + /*------------------------------------------------------------------------------ * Fill buffer with current time, to given number of decimal digits. * * If given buffer is too small, provides as many characters as possible. + + * 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. + * + * Puts a current timestamp in buf and returns the number of characters + * written (not including the terminating NUL). The purpose of + * this function is to avoid calls to localtime appearing all over the code. + * It caches the most recent localtime result and can therefore + * avoid multiple calls within the same second. * + * The buflen MUST be > 1 and the buffer address MUST NOT be NULL. + * + * If buflen is too small, writes buflen-1 characters followed by '\0'. + * + * Time stamp is rendered in the form: %Y/%m/%d %H:%M:%S + * + * This has a fixed length (leading zeros are included) of 19 characters + * (unless this code is still in use beyond the year 9999 !) + * + * Which may be followed by "." and a number of decimal digits, usually 1..6. + * + * So the maximum time stamp is 19 + 1 + 6 = 26. Adding the trailing '\n', and + * rounding up for good measure -- buffer size = 32. + + * Returns: number of characters in buffer, not including trailing '\0'. * * NB: does no rounding. @@ -108,18 +389,21 @@ quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) { qf_str_t qfs ; - VTY_LOCK() ; + LOG_LOCK() ; - qfs_init(&qfs, buf, buflen) ; - uquagga_timestamp(&qfs, timestamp_precision) ; + qfs_init(qfs, buf, buflen) ; + uquagga_timestamp(qfs, timestamp_precision) ; - VTY_UNLOCK() ; - return qfs_len(&qfs) ; + LOG_UNLOCK() ; + return qfs_len(qfs) ; } /*------------------------------------------------------------------------------ * unprotected version for when mutex already held */ + +// used in uvzlog_line + static void uquagga_timestamp(qf_str qfs, int timestamp_precision) { @@ -134,7 +418,7 @@ uquagga_timestamp(qf_str qfs, int timestamp_precision) /* 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 */ + /* first, we update the cache if the seconds time has changed */ if (cache.last != clock.tv_sec) { struct tm tm; @@ -173,137 +457,54 @@ uquagga_timestamp(qf_str qfs, int timestamp_precision) } ; /*============================================================================== - * va_list version of zlog + * */ -static void -vzlog (struct zlog *zl, int priority, const char *format, va_list args) + +void +_zlog_assert_failed (const char *assertion, const char *file, + unsigned int line, const char *function) { - VTY_LOCK() ; - uvzlog(zl, priority, format, args); - VTY_UNLOCK() ; + const static size_t buff_size = 1024; + char buff[buff_size]; + snprintf(buff, buff_size, + "Assertion `%s' failed in file %s, line %u, function %s", + assertion, file, line, (function ? function : "?")); + zlog_abort(buff); } -/* va_list version of zlog. Unprotected assumes mutex already held*/ -static void -uvzlog (struct zlog *zl, int priority, const char *format, va_list va) +/* Abort with message */ +void +_zlog_abort_mess (const char *mess, const char *file, + unsigned int line, const char *function) { - 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) ; - } + const static size_t buff_size = 1024; + char buff[buff_size]; + snprintf(buff, buff_size, "%s, in file %s, line %u, function %s", + mess, file, line, (function ? function : "?")); + zlog_abort(buff); } -/*------------------------------------------------------------------------------ - * 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) +/* Abort with message and errno and strerror() thereof */ +void +_zlog_abort_errno (const char *mess, const char *file, + unsigned int line, const char *function) { - 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' */ - /* "<time stamp>" */ - uquagga_timestamp(&qfs, (zl != NULL) ? zl->timestamp_precision : 0) ; - - qfs_append_n(&qfs, " ", 1) ; - - /* "<priority>: " if required */ - if ((zl != NULL) && (zl->record_priority)) - { - qfs_append(&qfs, zlog_priority[priority]) ; - qfs_append(&qfs, ": ") ; - } ; - - /* "<protocol>: " or "unknown: " */ - if (zl != NULL) - q = zlog_proto_names[zl->protocol] ; - else - q = "unknown" ; - - qfs_append(&qfs, q) ; - qfs_append(&qfs, ": ") ; - - /* Now the log line itself */ - va_copy(vac, va); - qfs_vprintf(&qfs, format, vac) ; - va_end(vac); - - /* Set pointer to where the '\0' is. */ - p = ll->p_nl = qfs_ptr(&qfs) ; - } ; - - /* finish off with '\r''\n''\0' or '\n''\0' as required */ - if (term == llt_crlf) - *p++ = '\r' ; - - if (term != llt_nul) - *p++ = '\n' ; - - *p = '\0' ; + _zlog_abort_err(mess, errno, file, line, function); +} - ll->len = p - ll->line ; - ll->term = term ; -} ; +/* Abort with message and given error and strerror() thereof */ +void +_zlog_abort_err (const char *mess, int err, const char *file, + unsigned int line, const char *function) +{ + const static size_t buff_size = 1024; + char buff[buff_size]; + snprintf(buff, buff_size, + "%s, in file %s, line %u, function %s, %s", + mess, file, line, (function ? function : "?"), + errtoa(err, 0).str); + zlog_abort(buff); +} /*============================================================================*/ @@ -444,18 +645,18 @@ open_crashlog(void) #undef CRASHLOG_PREFIX } -/* Note: the goal here is to use only async-signal-safe functions. */ -void -zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , siginfo_t *siginfo, void *program_counter -#endif - ) +/*------------------------------------------------------------------------------ + * Note: the goal here is to use only async-signal-safe functions. + */ +extern void +zlog_signal(int signo, const char *action, siginfo_t *siginfo, + void *program_counter) { time_t now; char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")+100]; char *s = buf; char *msgstart = buf; + int file_fd ; #define LOC s,buf+sizeof(buf)-s time(&now); @@ -489,10 +690,16 @@ zlog_signal(int signo, const char *action /* N.B. implicit priority is most severe */ #define PRI LOG_CRIT -#define DUMP(FD) write(FD, buf, s-buf); +#define DUMP(FD) write(FD, buf, s - buf); + /* If no file logging configured, try to write to fallback log file. */ - if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) - DUMP(logfile_fd) + file_fd = (zlog_default != NULL) ? zlog_default->file_fd : -1 ; + if (file_fd < 0) + file_fd = open_crashlog() ; + + if (file_fd >= 0) + DUMP(file_fd) + if (!zlog_default) DUMP(STDERR_FILENO) else @@ -502,7 +709,7 @@ zlog_signal(int signo, const char *action /* Remove trailing '\n' for monitor and syslog */ *--s = '\0'; if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf,s-buf); + vty_log_fixed(buf, s - buf); if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart); } @@ -537,6 +744,7 @@ zlog_backtrace_sigsafe(int priority, void *program_counter) int size; char buf[100]; char *s, **bt = NULL; + int file_fd ; #define LOC s,buf+sizeof(buf)-s #ifdef HAVE_GLIBC_BACKTRACE @@ -567,8 +775,13 @@ zlog_backtrace_sigsafe(int priority, void *program_counter) s = num_append(LOC,size); s = str_append(LOC," stack frames:\n"); - if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) - DUMP(logfile_fd) + file_fd = (zlog_default != NULL) ? zlog_default->file_fd : -1 ; + if (file_fd < 0) + file_fd = open_crashlog() ; + + if (file_fd >= 0) + DUMP(file_fd) + if (!zlog_default) DUMP(STDERR_FILENO) else @@ -616,16 +829,16 @@ zlog_backtrace_sigsafe(int priority, void *program_counter) void zlog_backtrace(int priority) { - VTY_LOCK() ; + LOG_LOCK() ; uzlog_backtrace(priority); - VTY_UNLOCK() ; + LOG_UNLOCK() ; } static void uzlog_backtrace(int priority) { #ifndef HAVE_GLIBC_BACKTRACE - uzlog(NULL, priority, "No backtrace available on this platform."); + zlog(NULL, priority, "No backtrace available on this platform."); #else void *array[20]; int size, i; @@ -634,161 +847,79 @@ uzlog_backtrace(int priority) if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) || ((size_t)size > sizeof(array)/sizeof(array[0]))) { - uzlog(NULL, LOG_ERR, "Cannot get backtrace, returned invalid # of frames %d " + zlog(NULL, LOG_ERR, "Cannot get backtrace, returned invalid # of frames %d " "(valid range is between 1 and %lu)", size, (unsigned long)(sizeof(array)/sizeof(array[0]))); return; } - uzlog(NULL, priority, "Backtrace for %d stack frames:", size); + zlog(NULL, priority, "Backtrace for %d stack frames:", size); if (!(strings = backtrace_symbols(array, size))) { - uzlog(NULL, LOG_ERR, "Cannot get backtrace symbols (out of memory?)"); + zlog(NULL, LOG_ERR, "Cannot get backtrace symbols (out of memory?)"); for (i = 0; i < size; i++) - uzlog(NULL, priority, "[bt %d] %p",i,array[i]); + zlog(NULL, priority, "[bt %d] %p",i,array[i]); } else { for (i = 0; i < size; i++) - uzlog(NULL, priority, "[bt %d] %s",i,strings[i]); + zlog(NULL, priority, "[bt %d] %s",i,strings[i]); free(strings); } #endif /* HAVE_GLIBC_BACKTRACE */ } -/* unlocked version */ -void -uzlog (struct zlog *zl, int priority, const char *format, ...) -{ - va_list args; - - va_start(args, format); - uvzlog (zl, priority, format, args); - va_end (args); -} - -void -zlog (struct zlog *zl, int priority, const char *format, ...) -{ - va_list args; - - va_start(args, format); - vzlog (zl, priority, format, args); - va_end (args); -} - -#define ZLOG_FUNC(FUNCNAME,PRIORITY) \ -void \ -FUNCNAME(const char *format, ...) \ -{ \ - va_list args; \ - va_start(args, format); \ - vzlog (NULL, PRIORITY, format, args); \ - va_end(args); \ -} - -ZLOG_FUNC(zlog_err, LOG_ERR) - -ZLOG_FUNC(zlog_warn, LOG_WARNING) - -ZLOG_FUNC(zlog_info, LOG_INFO) - -ZLOG_FUNC(zlog_notice, LOG_NOTICE) - -ZLOG_FUNC(zlog_debug, LOG_DEBUG) - -#undef ZLOG_FUNC - -#define PLOG_FUNC(FUNCNAME,PRIORITY) \ -void \ -FUNCNAME(struct zlog *zl, const char *format, ...) \ -{ \ - va_list args; \ - va_start(args, format); \ - vzlog (zl, PRIORITY, format, args); \ - va_end(args); \ -} - -PLOG_FUNC(plog_err, LOG_ERR) - -PLOG_FUNC(plog_warn, LOG_WARNING) - -PLOG_FUNC(plog_info, LOG_INFO) - -PLOG_FUNC(plog_notice, LOG_NOTICE) - -PLOG_FUNC(plog_debug, LOG_DEBUG) - -#undef PLOG_FUNC - -void -_zlog_assert_failed (const char *assertion, const char *file, - unsigned int line, const char *function) -{ - const static size_t buff_size = 1024; - char buff[buff_size]; - snprintf(buff, buff_size, - "Assertion `%s' failed in file %s, line %u, function %s", - assertion, file, line, (function ? function : "?")); - zlog_abort(buff); -} - -/* Abort with message */ -void -_zlog_abort_mess (const char *mess, const char *file, - unsigned int line, const char *function) -{ - const static size_t buff_size = 1024; - char buff[buff_size]; - snprintf(buff, buff_size, "%s, in file %s, line %u, function %s", - mess, file, line, (function ? function : "?")); - zlog_abort(buff); -} - -/* Abort with message and errno and strerror() thereof */ -void -_zlog_abort_errno (const char *mess, const char *file, - unsigned int line, const char *function) -{ - _zlog_abort_err(mess, errno, file, line, function); -} - -/* Abort with message and given error and strerror() thereof */ -void -_zlog_abort_err (const char *mess, int err, const char *file, - unsigned int line, const char *function) -{ - const static size_t buff_size = 1024; - char buff[buff_size]; - snprintf(buff, buff_size, - "%s, in file %s, line %u, function %s, %s", - mess, file, line, (function ? function : "?"), - errtoa(err, 0).str); - zlog_abort(buff); -} +static void uzlog_set_effective_level(struct zlog* zl) ; static void zlog_abort (const char *mess) { -#if VTY_DEBUG +#if LOG_DEBUG /* May not be locked -- but that doesn't matter any more */ - ++vty_lock_count ; + ++log_lock_count ; #endif - /* Force fallback file logging? */ - if (zlog_default && !zlog_default->fp && - ((logfile_fd = open_crashlog()) >= 0) && - ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL)) - zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR; + if (zlog_default != NULL) + { + if (zlog_default->file_fd < 0) + zlog_default->file_fd = open_crashlog() ; + + if (zlog_default->file_fd >= 0) + { + zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR; + uzlog_set_effective_level(zlog_default) ; + } ; + } ; - uzlog(NULL, LOG_CRIT, "%s", mess); + zlog(NULL, LOG_CRIT, "%s", mess); uzlog_backtrace(LOG_CRIT); zabort_abort(); } +/*============================================================================== + * Opening, closing, setting log levels etc. + * + */ +static int uzlog_set_file(struct zlog *zl, const char *filename, int level) ; +static int uzlog_reset_file(struct zlog *zl) ; +static void uzlog_set_effective_level(struct zlog* zl) ; + +/*------------------------------------------------------------------------------ + * Get the effective zlog stream. + */ +static inline struct zlog* zlog_actual(struct zlog* zl) +{ + return zl != NULL ? zl : zlog_default ; +} ; -/* Open log stream */ -struct zlog * +/*------------------------------------------------------------------------------ + * Open logging -- create and initialise a struct zlog object. + * + * Opens a connection to syslog. + * + * This must be done very early in the morning, before any pthreading starts. + */ +extern struct zlog * openzlog (const char *progname, zlog_proto_t protocol, int syslog_flags, int syslog_facility) { @@ -797,438 +928,515 @@ openzlog (const char *progname, zlog_proto_t protocol, zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog)); - zl->ident = progname; - zl->protocol = protocol; - zl->facility = syslog_facility; + zl->ident = progname; + zl->protocol = protocol; + zl->facility = syslog_facility; zl->syslog_options = syslog_flags; - /* Set default logging levels. */ - for (i = 0; i < sizeof(zl->maxlvl)/sizeof(zl->maxlvl[0]); i++) - zl->maxlvl[i] = ZLOG_DISABLED; - zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG; - zl->default_lvl = LOG_DEBUG; + /* Set default logging levels. */ + for (i = 0 ; i < ZLOG_DEST_COUNT ; ++i) + zl->maxlvl[i] = ZLOG_DISABLED ; + + zl->default_lvl = LOG_DEBUG ; openlog (progname, syslog_flags, zl->facility); + zl->syslog = true ; /* have syslog */ + zl->stdout_fd = fileno(stdout) ; /* assume have stdout */ + zl->file_fd = -1 ; /* no file, yet */ + zl->monitors = 0 ; /* no monitors, yet */ + + assert(zlog_list == NULL) ; /* can do this once ! */ + zlog_list = zl ; + + uzlog_set_effective_level(zl) ; + return zl; -} +} ; -void +/*------------------------------------------------------------------------------ + * Close logging -- destroy struct zlog object. + * + * Closes connection to syslog and any log file. + */ +extern void closezlog (struct zlog *zl) { + assert((zl == zlog_list) && (zl->next == NULL)) ; /* pro tem */ + closelog(); + if (zl->file_fd >= 0) + close (zl->file_fd) ; - if (zl->fp != NULL) - fclose (zl->fp); + if (zl->filename != NULL) + free (zl->filename) ; + + zl->syslog = false ; + zl->file_fd = -1 ; + zl->stdout_fd = -1 ; + zl->monitors = 0 ; /* TODO... state of VTY ?? */ + + uzlog_set_effective_level(zl) ; XFREE (MTYPE_ZLOG, zl); } -/* Called from command.c. */ -void -zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level) +/*------------------------------------------------------------------------------ + * Set new logging level for the given destination. + * + * Update the effective maxlvl for this zlog, and the max_maxlvl for all zlog. + */ +extern void +zlog_set_level (struct zlog *zl, zlog_dest_t dest, int level) { - VTY_LOCK() ; - - if (zl == NULL) - zl = zlog_default; + LOG_LOCK() ; - if (zl != NULL) + if ((zl = zlog_actual(zl)) != NULL) { - zl->maxlvl[dest] = log_level; + zl->maxlvl[dest] = level ; + uzlog_set_effective_level(zl) ; } - VTY_UNLOCK() ; + LOG_UNLOCK() ; } -int -zlog_set_file (struct zlog *zl, const char *filename, int log_level) +/*------------------------------------------------------------------------------ + * Set new log file: name and level. + * + * Note that this closes any existing file + * + * Returns: 0 => OK + * errno otherwise. + */ +extern int +zlog_set_file(struct zlog *zl, const char *filename, int level) { - FILE *fp; - mode_t oldumask; - int result = 1; + int err ; - VTY_LOCK() ; + LOG_LOCK() ; - /* There is opend file. */ - uzlog_reset_file (zl); + err = uzlog_set_file(zl, filename, level) ; - /* Set default zl. */ - if (zl == NULL) - zl = zlog_default; + LOG_UNLOCK() ; + return err ; +} - if (zl != NULL) +static int +uzlog_set_file(struct zlog *zl, const char *filename, int level) +{ + int err ; + + LOG_ASSERT_LOCKED() ; + + err = 0 ; + if ((zl = zlog_actual(zl)) != NULL) { - /* Open file. */ + mode_t oldumask; + int fd ; + + /* Close any existing file */ + uzlog_reset_file(zl); + + /* Open file making damn sure we get the mode we want ! */ oldumask = umask (0777 & ~LOGFILE_MASK); - fp = fopen (filename, "a"); - umask(oldumask); - if (fp == NULL) - result = 0; + fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, LOGFILE_MASK); + if (fd < 0) + err = errno ; else { - /* Set flags. */ - zl->filename = strdup (filename); - zl->maxlvl[ZLOG_DEST_FILE] = log_level; - zl->fp = fp; - logfile_fd = fileno(fp); - } - } + /* Set flags. */ + zl->filename = strdup (filename) ; + zl->file_fd = fd ; + zl->maxlvl[ZLOG_DEST_FILE] = level; + + uzlog_set_effective_level(zl) ; + } ; + + umask(oldumask); + } ; - VTY_UNLOCK() ; - return result; + return err ; } -/* Reset opend file. */ -int -zlog_reset_file (struct zlog *zl) +/*------------------------------------------------------------------------------ + * Close any existing log file. Set level to ZLOG_DISABLED. + * + * Note that this closes any existing file + * + * Returns: 1 + */ +extern int +zlog_reset_file(struct zlog *zl) { - int result; - VTY_LOCK() ; - result = uzlog_reset_file(zl); - VTY_UNLOCK() ; - return result; + int ret ; + LOG_LOCK() ; + + ret = uzlog_reset_file(zl) ; + + LOG_UNLOCK() ; + return ret ; } static int -uzlog_reset_file (struct zlog *zl) - { - if (zl == NULL) - zl = zlog_default; +uzlog_reset_file(struct zlog *zl) +{ + LOG_ASSERT_LOCKED() ; - if (zl != NULL) + if ((zl = zlog_actual(zl)) != NULL) { - if (zl->fp) - fclose (zl->fp); - zl->fp = NULL; - logfile_fd = -1; + if (zl->file_fd >= 0) + close (zl->file_fd) ; + + zl->file_fd = -1 ; zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; - if (zl->filename) - free (zl->filename); - zl->filename = NULL; - } + if (zl->filename != NULL) + free (zl->filename) ; + + zl->filename = NULL ; + + uzlog_set_effective_level(zl) ; + } ; return 1; } -/* Reopen log file. */ -int +/*------------------------------------------------------------------------------ + * Close and reopen log file -- TODO and the point ?? + */ +extern int zlog_rotate (struct zlog *zl) { - int level; - int result = 1; + int err ; + char* filename ; - VTY_LOCK() ; + filename = NULL ; + err = 0 ; - if (zl == NULL) - zl = zlog_default; + LOG_LOCK() ; - if (zl != NULL) + if ((zl = zlog_actual(zl)) != NULL) { - if (zl->fp) - fclose (zl->fp); - zl->fp = NULL; - logfile_fd = -1; - level = zl->maxlvl[ZLOG_DEST_FILE]; - zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; - - if (zl->filename) + if (zl->file_fd >= 0) { - mode_t oldumask; - int save_errno; - - oldumask = umask (0777 & ~LOGFILE_MASK); - zl->fp = fopen (zl->filename, "a"); - save_errno = errno; - umask(oldumask); - if (zl->fp == NULL) - { - /* can't call logging while locked */ - char *fname = strdup(zl->filename); - uzlog(NULL, LOG_ERR, - "Log rotate failed: cannot open file %s for append: %s", - fname, errtoa(save_errno, 0).str); - free(fname); - result = -1; - } - else - { - logfile_fd = fileno(zl->fp); - zl->maxlvl[ZLOG_DEST_FILE] = level; - } - } - } - VTY_UNLOCK() ; - return result; + filename = zl->filename ; + zl->filename = NULL ; /* co-opt the name */ + + err = uzlog_set_file(zl, filename, zl->maxlvl[ZLOG_DEST_FILE]) ; + } ; + } ; + + LOG_UNLOCK() ; + + if (err != 0) + zlog(NULL, LOG_ERR, + "Log rotate failed: cannot open file %s for append: %s", + filename, errtoa(err, 0).str) ; + if (filename != NULL) + free(filename) ; /* discard old copy */ + + return (err == 0) ? 1 : -1 ; } -int -zlog_get_default_lvl (struct zlog *zl) +/*------------------------------------------------------------------------------ + * Increment or decrement the number of monitor terminals. + */ +extern void +uzlog_add_monitor(struct zlog *zl, int count) { - int result = LOG_DEBUG; + if ((zl = zlog_actual(zl)) != NULL) + { + zl->monitors += count ; + uzlog_set_effective_level(zl) ; + } ; +} - VTY_LOCK() ; - if (zl == NULL) - zl = zlog_default; +/*------------------------------------------------------------------------------ + * get the current default level + */ +extern int +zlog_get_default_lvl (struct zlog *zl) +{ + int level ; - if (zl != NULL) - { - result = zl->default_lvl; - } + LOG_LOCK() ; + + zl = zlog_actual(zl) ; + level = (zl != NULL) ? zl->default_lvl : LOG_DEBUG ; - VTY_UNLOCK() ; - return result; + LOG_UNLOCK() ; + return level ; } -void +/*------------------------------------------------------------------------------ + * set the default level + */ +extern void zlog_set_default_lvl (struct zlog *zl, int level) { - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; + if ((zl = zlog_actual(zl)) != NULL) + zl->default_lvl = level; - if (zl != NULL) - { - zl->default_lvl = level; - } - - VTY_UNLOCK() ; + LOG_UNLOCK() ; } -/* Set logging level and default for all destinations */ -void +/*------------------------------------------------------------------------------ + * Set default logging level and the level for any destination not ZLOG_DISABLED + * + * Note that on openzlog(), all destinations are set ZLOG_DISABLED. + */ +extern void zlog_set_default_lvl_dest (struct zlog *zl, int level) { - int i; + LOG_LOCK() ; - VTY_LOCK() ; - - if (zl == NULL) - zl = zlog_default; - - if (zl != NULL) + if ((zl = zlog_actual(zl)) != NULL) { + int i; + zl->default_lvl = level; - for (i = 0; i < ZLOG_NUM_DESTS; i++) + for (i = 0; i < ZLOG_DEST_COUNT; i++) if (zl->maxlvl[i] != ZLOG_DISABLED) - zl->maxlvl[i] = level; - } + zl->maxlvl[i] = level ; - VTY_UNLOCK() ; -} + uzlog_set_effective_level(zl) ; + } ; -int + LOG_UNLOCK() ; +} ; + +/*------------------------------------------------------------------------------ + * Get the current level for the given destination. + */ +extern int zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest) { - int result = ZLOG_DISABLED; + int level ; - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; - - if (zl != NULL) - { - result = zl->maxlvl[dest]; - } + zl = zlog_actual(zl) ; + level = (zl != NULL) ? zl->maxlvl[dest] : ZLOG_DISABLED ; - VTY_UNLOCK() ; - return result; + LOG_UNLOCK() ; + return level; } -int +/*------------------------------------------------------------------------------ + * Get the current facility setting for syslog + */ +extern int zlog_get_facility (struct zlog *zl) { - int result = LOG_DAEMON; + int facility ; - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; - - if (zl != NULL) - { - result = zl->facility; - } + zl = zlog_actual(zl) ; + facility = (zl != NULL) ? zl->facility : LOG_DAEMON ; - VTY_UNLOCK() ; - return result; -} + LOG_UNLOCK() ; + return facility ; +} ; -void +/*------------------------------------------------------------------------------ + * Set the current facility setting for syslog + */ +extern void zlog_set_facility (struct zlog *zl, int facility) { - VTY_LOCK() ; - - if (zl == NULL) - zl = zlog_default; + LOG_LOCK() ; - if (zl != NULL) - { - zl->facility = facility; - } + if ((zl = zlog_actual(zl)) != NULL) + zl->facility = facility ; - VTY_UNLOCK() ; + LOG_UNLOCK() ; } -int +/*------------------------------------------------------------------------------ + * Get the record priority setting + */ +extern bool zlog_get_record_priority (struct zlog *zl) { - int result = 0; + bool priority ; - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; + zl = zlog_actual(zl) ; + priority = (zl != NULL) ? zl->record_priority : false ; - if (zl != NULL) - { - result = zl->record_priority; - } - - VTY_UNLOCK() ; - return result; + LOG_UNLOCK() ; + return priority ; } -void -zlog_set_record_priority (struct zlog *zl, int record_priority) +/*------------------------------------------------------------------------------ + * Set the record priority setting + */ +extern void +zlog_set_record_priority (struct zlog *zl, bool record_priority) { - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; + if ((zl = zlog_actual(zl)) != NULL) + zl->record_priority = record_priority ; - if (zl != NULL) - { - zl->record_priority = record_priority; - } - VTY_UNLOCK() ; + LOG_UNLOCK() ; } -int +/*------------------------------------------------------------------------------ + * Get the timestamp precision setting + */ +extern int zlog_get_timestamp_precision (struct zlog *zl) { - int result = 0; + int precision = 0; - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; + zl = zlog_actual(zl) ; + precision = (zl != NULL) ? zl->timestamp_precision : 0 ; - if (zl != NULL) - { - result = zl->timestamp_precision; - } - VTY_UNLOCK() ; - return result; + LOG_UNLOCK() ; + return precision ; } -void +/*------------------------------------------------------------------------------ + * Get the timestamp precision setting + */ +extern void zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision) { - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; - - if (zl != NULL) - { - zl->timestamp_precision = timestamp_precision; - } + if ((zl = zlog_actual(zl)) != NULL) + zl->timestamp_precision = timestamp_precision; - VTY_UNLOCK() ; + LOG_UNLOCK() ; } -/* returns name of ZLOG_NONE if no zlog given and no default set */ -const char * +/*------------------------------------------------------------------------------ + * Get name of the protocol -- name of ZLOG_NONE if no zlog set up yet. + */ +extern const char * zlog_get_proto_name (struct zlog *zl) { - const char * result; - VTY_LOCK() ; - result = uzlog_get_proto_name(zl); - VTY_UNLOCK() ; - return result; + const char* name ; + LOG_LOCK() ; + + zl = zlog_actual(zl) ; + name = zlog_proto_names[(zl != NULL) ? zl->protocol : ZLOG_NONE] ; + + LOG_UNLOCK() ; + return name ; } -/* unprotected version, assumes mutex held */ -const char * -uzlog_get_proto_name (struct zlog *zl) +/*------------------------------------------------------------------------------ + * caller must free result + */ +extern char * +zlog_get_filename (struct zlog *zl) { - zlog_proto_t protocol = ZLOG_NONE; + char* name = NULL; - if (zl == NULL) - zl = zlog_default; + LOG_LOCK() ; - if (zl != NULL) - { - protocol = zl->protocol; - } - - return zlog_proto_names[protocol]; + zl = zlog_actual(zl) ; + name = ((zl != NULL) && (zl->filename != NULL)) ? strdup(zl->filename) + : NULL ; + LOG_UNLOCK() ; + return name ; } -/* caller must free result */ -char * -zlog_get_filename (struct zlog *zl) +/*------------------------------------------------------------------------------ + * Get address of ident string + */ +extern const char * +zlog_get_ident (struct zlog *zl) { - char * result = NULL; + const char* ident ; - VTY_LOCK() ; - - if (zl == NULL) - zl = zlog_default; + LOG_LOCK() ; - if (zl != NULL && zl->filename != NULL) - { - result = strdup(zl->filename); - } + zl = zlog_actual(zl) ; + ident = (zl != NULL) ? zl->ident : NULL ; - VTY_UNLOCK() ; - return result; + LOG_UNLOCK() ; + return ident ; } -const char * -zlog_get_ident (struct zlog *zl) +/*------------------------------------------------------------------------------ + * logging to a file? + */ +extern bool +zlog_is_file (struct zlog *zl) { - const char * result = NULL; + bool is_file ; - VTY_LOCK() ; + LOG_LOCK() ; - if (zl == NULL) - zl = zlog_default; + zl = zlog_actual(zl) ; + is_file = (zl != NULL) ? (zl->file_fd >= 0) : false ; - if (zl != NULL) - { - result = zl->ident; - } - - VTY_UNLOCK() ; - return result; + LOG_UNLOCK() ;; + return is_file ; } -/* logging to a file? */ -int -zlog_is_file (struct zlog *zl) +/*------------------------------------------------------------------------------ + * Setting of emaxlvl for given destination. + */ +inline static void +uzlog_set_emaxlvl(struct zlog *zl, zlog_dest_t dest, bool test) +{ + zl->emaxlvl[dest] = test ? zl->maxlvl[dest] : ZLOG_DISABLED ; +}; + +/*------------------------------------------------------------------------------ + * Update effective logging level for the given destination, and max_maxlvl. + * + * The effective logging level takes into account whether the destination is + * enabled or not. + */ +static void +uzlog_set_effective_level(struct zlog *zl) { - int result = 0; + int emaxlvl ; - VTY_LOCK() ; + LOG_ASSERT_LOCKED() ; - if (zl == NULL) - zl = zlog_default; + /* Re-establish the emaxlvl for this logging stream. */ + uzlog_set_emaxlvl(zl, ZLOG_DEST_SYSLOG, zl->syslog ) ; + uzlog_set_emaxlvl(zl, ZLOG_DEST_FILE, zl->file_fd >= 0) ; + uzlog_set_emaxlvl(zl, ZLOG_DEST_STDOUT, zl->stdout_fd >= 0) ; + uzlog_set_emaxlvl(zl, ZLOG_DEST_MONITOR, zl->monitors > 0) ; + confirm(ZLOG_DEST_COUNT == 4) ; - if (zl != NULL) + /* Scan all known logging streams, and re-establish max_maxlvl. + * + * Do not expect there to be many of these, or that we will change them very + * often -- so nothing clever, here. + */ + emaxlvl = ZLOG_DISABLED ; + zl = zlog_list ; + while (zl != NULL) { - result = (zl->fp != NULL); - } + uint i ; + for (i = 0 ; i < ZLOG_DEST_COUNT ; ++i) + if (emaxlvl < zl->emaxlvl[i]) + emaxlvl = zl->emaxlvl[i] ; + zl = zl->next ; + } ; - VTY_UNLOCK() ;; - return result; -} + max_maxlvl = emaxlvl ; +} ; + +/*============================================================================== + * + */ /* Message lookup function. */ const char * |