forked from Mirror/frr
lib: rewrite zlog lock-free & TLS-buffered
This is a full rewrite of the "back end" logging code. It now uses a lock-free list to iterate over logging targets, and the targets themselves are as lock-free as possible. (syslog() may have a hidden internal mutex in the C library; the file/fd targets use a single write() call which should ensure atomicity kernel-side.) Note that some functionality is lost in this patch: - Solaris printstack() backtraces are ditched (unlikely to come back) - the `log-filter` machinery is gone (re-added in followup commit) - `terminal monitor` is temporarily stubbed out. The old code had a race condition with VTYs going away. It'll likely come back rewritten and with vtysh support. - The `zebra_ext_log` hook is gone. Instead, it's now much easier to add a "proper" logging target. v2: TLS buffer to get some actual performance Signed-off-by: David Lamparter <equinox@diac24.net>
This commit is contained in:
parent
6f00dd6658
commit
0bdeb5e58d
|
@ -25,6 +25,7 @@
|
||||||
#include "bfd.h"
|
#include "bfd.h"
|
||||||
#include "bfdd_nb.h"
|
#include "bfdd_nb.h"
|
||||||
#include "lib/version.h"
|
#include "lib/version.h"
|
||||||
|
#include "lib/command.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#include "lib/memory.h"
|
#include "lib/memory.h"
|
||||||
#include "lib/routemap.h"
|
#include "lib/routemap.h"
|
||||||
#include "lib/log.h"
|
#include "lib/log.h"
|
||||||
#include "lib/log_int.h"
|
|
||||||
#include "lib/linklist.h"
|
#include "lib/linklist.h"
|
||||||
#include "lib/command.h"
|
#include "lib/command.h"
|
||||||
|
|
||||||
|
@ -371,7 +370,7 @@ int rfapiStream2Vty(void *stream, /* input */
|
||||||
*fp = (int (*)(void *, const char *, ...))rfapiDebugPrintf;
|
*fp = (int (*)(void *, const char *, ...))rfapiDebugPrintf;
|
||||||
*outstream = NULL;
|
*outstream = NULL;
|
||||||
*vty_newline = str_vty_newline(*vty);
|
*vty_newline = str_vty_newline(*vty);
|
||||||
return (vzlog_test(LOG_DEBUG));
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((uintptr_t)stream == (uintptr_t)1)
|
if (((uintptr_t)stream == (uintptr_t)1)
|
||||||
|
|
18
configure.ac
18
configure.ac
|
@ -959,7 +959,7 @@ int main(int argc, char **argv) {
|
||||||
AC_CHECK_HEADERS([pthread_np.h],,, [
|
AC_CHECK_HEADERS([pthread_np.h],,, [
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
])
|
])
|
||||||
AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np])
|
AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np pthread_getthreadid_np])
|
||||||
|
|
||||||
needsync=true
|
needsync=true
|
||||||
|
|
||||||
|
@ -1202,7 +1202,11 @@ dnl other functions
|
||||||
dnl ---------------
|
dnl ---------------
|
||||||
AC_CHECK_FUNCS([ \
|
AC_CHECK_FUNCS([ \
|
||||||
strlcat strlcpy \
|
strlcat strlcpy \
|
||||||
getgrouplist])
|
getgrouplist \
|
||||||
|
openat \
|
||||||
|
unlinkat \
|
||||||
|
posix_fallocate \
|
||||||
|
])
|
||||||
|
|
||||||
dnl ##########################################################################
|
dnl ##########################################################################
|
||||||
dnl LARGE if block spans a lot of "configure"!
|
dnl LARGE if block spans a lot of "configure"!
|
||||||
|
@ -2196,15 +2200,6 @@ if test "$enable_backtrace" != "no" ; then
|
||||||
])
|
])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$backtrace_ok" = "no"; then
|
|
||||||
case "$host_os" in
|
|
||||||
sunos* | solaris2*)
|
|
||||||
AC_CHECK_FUNCS([printstack], [
|
|
||||||
AC_DEFINE([HAVE_PRINTSTACK], [1], [Solaris printstack])
|
|
||||||
backtrace_ok=yes
|
|
||||||
])
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
if test "$backtrace_ok" = "no"; then
|
if test "$backtrace_ok" = "no"; then
|
||||||
AC_CHECK_HEADER([execinfo.h], [
|
AC_CHECK_HEADER([execinfo.h], [
|
||||||
AC_SEARCH_LIBS([backtrace], [execinfo], [
|
AC_SEARCH_LIBS([backtrace], [execinfo], [
|
||||||
|
@ -2213,7 +2208,6 @@ if test "$enable_backtrace" != "no" ; then
|
||||||
],, [-lm])
|
],, [-lm])
|
||||||
])
|
])
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
if test "$enable_backtrace" = "yes" -a "$backtrace_ok" = "no"; then
|
if test "$enable_backtrace" = "yes" -a "$backtrace_ok" = "no"; then
|
||||||
dnl user explicitly requested backtrace but we failed to find support
|
dnl user explicitly requested backtrace but we failed to find support
|
||||||
|
|
19
ldpd/ldpd.c
19
ldpd/ldpd.c
|
@ -308,9 +308,15 @@ main(int argc, char *argv[])
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lflag || eflag)
|
if (lflag || eflag) {
|
||||||
openzlog(ldpd_di.progname, "LDP", 0,
|
struct zprivs_ids_t ids;
|
||||||
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
|
||||||
|
zprivs_preinit(&ldpd_privs);
|
||||||
|
zprivs_get_ids(&ids);
|
||||||
|
|
||||||
|
zlog_init(ldpd_di.progname, "LDP", 0,
|
||||||
|
ids.uid_normal, ids.gid_normal);
|
||||||
|
}
|
||||||
if (lflag)
|
if (lflag)
|
||||||
lde();
|
lde();
|
||||||
else if (eflag)
|
else if (eflag)
|
||||||
|
@ -486,7 +492,7 @@ ldpd_shutdown(void)
|
||||||
static pid_t
|
static pid_t
|
||||||
start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync)
|
start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync)
|
||||||
{
|
{
|
||||||
char *argv[3];
|
char *argv[7];
|
||||||
int argc = 0, nullfd;
|
int argc = 0, nullfd;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
|
@ -529,6 +535,11 @@ start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync)
|
||||||
argv[argc++] = (char *)"-E";
|
argv[argc++] = (char *)"-E";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
argv[argc++] = (char *)"-u";
|
||||||
|
argv[argc++] = (char *)ldpd_privs.user;
|
||||||
|
argv[argc++] = (char *)"-g";
|
||||||
|
argv[argc++] = (char *)ldpd_privs.group;
|
||||||
argv[argc++] = NULL;
|
argv[argc++] = NULL;
|
||||||
|
|
||||||
execvp(argv0, argv);
|
execvp(argv0, argv);
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include <lib/log.h>
|
#include <lib/log.h>
|
||||||
#include <lib/log_int.h>
|
|
||||||
|
|
||||||
const char *log_procname;
|
const char *log_procname;
|
||||||
|
|
||||||
|
|
18
lib/clippy.c
18
lib/clippy.c
|
@ -107,22 +107,12 @@ int main(int argc, char **argv)
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "zassert.h"
|
#include "zassert.h"
|
||||||
|
|
||||||
#define ZLOG_FUNC(FUNCNAME) \
|
void vzlog(int prio, const char *format, va_list args)
|
||||||
void FUNCNAME(const char *format, ...) \
|
{
|
||||||
{ \
|
vfprintf(stderr, format, args);
|
||||||
va_list args; \
|
fputs("\n", stderr);
|
||||||
va_start(args, format); \
|
|
||||||
vfprintf(stderr, format, args); \
|
|
||||||
fputs("\n", stderr); \
|
|
||||||
va_end(args); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ZLOG_FUNC(zlog_err)
|
|
||||||
ZLOG_FUNC(zlog_warn)
|
|
||||||
ZLOG_FUNC(zlog_info)
|
|
||||||
ZLOG_FUNC(zlog_notice)
|
|
||||||
ZLOG_FUNC(zlog_debug)
|
|
||||||
|
|
||||||
void _zlog_assert_failed(const char *assertion, const char *file,
|
void _zlog_assert_failed(const char *assertion, const char *file,
|
||||||
unsigned int line, const char *function)
|
unsigned int line, const char *function)
|
||||||
{
|
{
|
||||||
|
|
487
lib/command.c
487
lib/command.c
|
@ -31,7 +31,7 @@
|
||||||
#include "frrstr.h"
|
#include "frrstr.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "log_int.h"
|
#include "log_vty.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "linklist.h"
|
#include "linklist.h"
|
||||||
|
@ -198,65 +198,6 @@ static struct cmd_node enable_node = {
|
||||||
|
|
||||||
static struct cmd_node config_node = {CONFIG_NODE, "%s(config)# ", 1};
|
static struct cmd_node config_node = {CONFIG_NODE, "%s(config)# ", 1};
|
||||||
|
|
||||||
static const struct facility_map {
|
|
||||||
int facility;
|
|
||||||
const char *name;
|
|
||||||
size_t match;
|
|
||||||
} syslog_facilities[] = {
|
|
||||||
{LOG_KERN, "kern", 1},
|
|
||||||
{LOG_USER, "user", 2},
|
|
||||||
{LOG_MAIL, "mail", 1},
|
|
||||||
{LOG_DAEMON, "daemon", 1},
|
|
||||||
{LOG_AUTH, "auth", 1},
|
|
||||||
{LOG_SYSLOG, "syslog", 1},
|
|
||||||
{LOG_LPR, "lpr", 2},
|
|
||||||
{LOG_NEWS, "news", 1},
|
|
||||||
{LOG_UUCP, "uucp", 2},
|
|
||||||
{LOG_CRON, "cron", 1},
|
|
||||||
#ifdef LOG_FTP
|
|
||||||
{LOG_FTP, "ftp", 1},
|
|
||||||
#endif
|
|
||||||
{LOG_LOCAL0, "local0", 6},
|
|
||||||
{LOG_LOCAL1, "local1", 6},
|
|
||||||
{LOG_LOCAL2, "local2", 6},
|
|
||||||
{LOG_LOCAL3, "local3", 6},
|
|
||||||
{LOG_LOCAL4, "local4", 6},
|
|
||||||
{LOG_LOCAL5, "local5", 6},
|
|
||||||
{LOG_LOCAL6, "local6", 6},
|
|
||||||
{LOG_LOCAL7, "local7", 6},
|
|
||||||
{0, NULL, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *facility_name(int facility)
|
|
||||||
{
|
|
||||||
const struct facility_map *fm;
|
|
||||||
|
|
||||||
for (fm = syslog_facilities; fm->name; fm++)
|
|
||||||
if (fm->facility == facility)
|
|
||||||
return fm->name;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
static int facility_match(const char *str)
|
|
||||||
{
|
|
||||||
const struct facility_map *fm;
|
|
||||||
|
|
||||||
for (fm = syslog_facilities; fm->name; fm++)
|
|
||||||
if (!strncmp(str, fm->name, fm->match))
|
|
||||||
return fm->facility;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int level_match(const char *s)
|
|
||||||
{
|
|
||||||
int level;
|
|
||||||
|
|
||||||
for (level = 0; zlog_priority[level] != NULL; level++)
|
|
||||||
if (!strncmp(s, zlog_priority[level], 2))
|
|
||||||
return level;
|
|
||||||
return ZLOG_DISABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is called from main when a daemon is invoked with -v or --version. */
|
/* This is called from main when a daemon is invoked with -v or --version. */
|
||||||
void print_version(const char *progname)
|
void print_version(const char *progname)
|
||||||
{
|
{
|
||||||
|
@ -493,6 +434,8 @@ static char *zencrypt(const char *passwd)
|
||||||
return crypt(passwd, salt);
|
return crypt(passwd, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool full_cli;
|
||||||
|
|
||||||
/* This function write configuration of this host. */
|
/* This function write configuration of this host. */
|
||||||
static int config_write_host(struct vty *vty)
|
static int config_write_host(struct vty *vty)
|
||||||
{
|
{
|
||||||
|
@ -508,7 +451,7 @@ static int config_write_host(struct vty *vty)
|
||||||
* which would cause other daemons to then switch to syslog when they
|
* which would cause other daemons to then switch to syslog when they
|
||||||
* parse frr.conf.
|
* parse frr.conf.
|
||||||
*/
|
*/
|
||||||
if (strcmp(zlog_default->protoname, "WATCHFRR")) {
|
if (full_cli) {
|
||||||
if (host.encrypt) {
|
if (host.encrypt) {
|
||||||
if (host.password_encrypt)
|
if (host.password_encrypt)
|
||||||
vty_out(vty, "password 8 %s\n",
|
vty_out(vty, "password 8 %s\n",
|
||||||
|
@ -523,59 +466,7 @@ static int config_write_host(struct vty *vty)
|
||||||
vty_out(vty, "enable password %s\n",
|
vty_out(vty, "enable password %s\n",
|
||||||
host.enable);
|
host.enable);
|
||||||
}
|
}
|
||||||
|
log_config_write(vty);
|
||||||
if (host.logfile
|
|
||||||
&& (zlog_default->maxlvl[ZLOG_DEST_FILE]
|
|
||||||
!= ZLOG_DISABLED)) {
|
|
||||||
vty_out(vty, "log file %s", host.logfile);
|
|
||||||
if (zlog_default->maxlvl[ZLOG_DEST_FILE]
|
|
||||||
!= zlog_default->default_lvl)
|
|
||||||
vty_out(vty, " %s",
|
|
||||||
zlog_priority
|
|
||||||
[zlog_default->maxlvl
|
|
||||||
[ZLOG_DEST_FILE]]);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
|
|
||||||
vty_out(vty, "log stdout");
|
|
||||||
if (zlog_default->maxlvl[ZLOG_DEST_STDOUT]
|
|
||||||
!= zlog_default->default_lvl)
|
|
||||||
vty_out(vty, " %s",
|
|
||||||
zlog_priority
|
|
||||||
[zlog_default->maxlvl
|
|
||||||
[ZLOG_DEST_STDOUT]]);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
|
|
||||||
vty_out(vty, "no log monitor\n");
|
|
||||||
else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
|
|
||||||
!= zlog_default->default_lvl)
|
|
||||||
vty_out(vty, "log monitor %s\n",
|
|
||||||
zlog_priority[zlog_default->maxlvl
|
|
||||||
[ZLOG_DEST_MONITOR]]);
|
|
||||||
|
|
||||||
if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
|
|
||||||
vty_out(vty, "log syslog");
|
|
||||||
if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG]
|
|
||||||
!= zlog_default->default_lvl)
|
|
||||||
vty_out(vty, " %s",
|
|
||||||
zlog_priority[zlog_default->maxlvl
|
|
||||||
[ZLOG_DEST_SYSLOG]]);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zlog_default->facility != LOG_DAEMON)
|
|
||||||
vty_out(vty, "log facility %s\n",
|
|
||||||
facility_name(zlog_default->facility));
|
|
||||||
|
|
||||||
if (zlog_default->record_priority == 1)
|
|
||||||
vty_out(vty, "log record-priority\n");
|
|
||||||
|
|
||||||
if (zlog_default->timestamp_precision > 0)
|
|
||||||
vty_out(vty, "log timestamp precision %d\n",
|
|
||||||
zlog_default->timestamp_precision);
|
|
||||||
|
|
||||||
if (host.advanced)
|
if (host.advanced)
|
||||||
vty_out(vty, "service advanced-vty\n");
|
vty_out(vty, "service advanced-vty\n");
|
||||||
|
@ -2273,7 +2164,8 @@ DEFUN (config_logmsg,
|
||||||
int level;
|
int level;
|
||||||
char *message;
|
char *message;
|
||||||
|
|
||||||
if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
|
level = log_level_match(argv[idx_log_level]->arg);
|
||||||
|
if (level == ZLOG_DISABLED)
|
||||||
return CMD_ERR_NO_MATCH;
|
return CMD_ERR_NO_MATCH;
|
||||||
|
|
||||||
zlog(level, "%s",
|
zlog(level, "%s",
|
||||||
|
@ -2284,348 +2176,6 @@ DEFUN (config_logmsg,
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN (show_logging,
|
|
||||||
show_logging_cmd,
|
|
||||||
"show logging",
|
|
||||||
SHOW_STR
|
|
||||||
"Show current logging configuration\n")
|
|
||||||
{
|
|
||||||
struct zlog *zl = zlog_default;
|
|
||||||
|
|
||||||
vty_out(vty, "Syslog logging: ");
|
|
||||||
if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
|
|
||||||
vty_out(vty, "disabled");
|
|
||||||
else
|
|
||||||
vty_out(vty, "level %s, facility %s, ident %s",
|
|
||||||
zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
|
|
||||||
facility_name(zl->facility), zl->ident);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
|
|
||||||
vty_out(vty, "Stdout logging: ");
|
|
||||||
if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
|
|
||||||
vty_out(vty, "disabled");
|
|
||||||
else
|
|
||||||
vty_out(vty, "level %s",
|
|
||||||
zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
|
|
||||||
vty_out(vty, "Monitor logging: ");
|
|
||||||
if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
|
|
||||||
vty_out(vty, "disabled");
|
|
||||||
else
|
|
||||||
vty_out(vty, "level %s",
|
|
||||||
zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
|
|
||||||
vty_out(vty, "File logging: ");
|
|
||||||
if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
|
|
||||||
vty_out(vty, "disabled");
|
|
||||||
else
|
|
||||||
vty_out(vty, "level %s, filename %s",
|
|
||||||
zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
|
|
||||||
zl->filename);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
|
|
||||||
vty_out(vty, "Protocol name: %s\n", zl->protoname);
|
|
||||||
vty_out(vty, "Record priority: %s\n",
|
|
||||||
(zl->record_priority ? "enabled" : "disabled"));
|
|
||||||
vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (config_log_stdout,
|
|
||||||
config_log_stdout_cmd,
|
|
||||||
"log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
|
||||||
"Logging control\n"
|
|
||||||
"Set stdout logging level\n"
|
|
||||||
LOG_LEVEL_DESC)
|
|
||||||
{
|
|
||||||
int idx_log_level = 2;
|
|
||||||
|
|
||||||
if (argc == idx_log_level) {
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
int level;
|
|
||||||
|
|
||||||
if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
|
|
||||||
return CMD_ERR_NO_MATCH;
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, level);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (no_config_log_stdout,
|
|
||||||
no_config_log_stdout_cmd,
|
|
||||||
"no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
|
||||||
NO_STR
|
|
||||||
"Logging control\n"
|
|
||||||
"Cancel logging to stdout\n"
|
|
||||||
LOG_LEVEL_DESC)
|
|
||||||
{
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (config_log_monitor,
|
|
||||||
config_log_monitor_cmd,
|
|
||||||
"log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
|
||||||
"Logging control\n"
|
|
||||||
"Set terminal line (monitor) logging level\n"
|
|
||||||
LOG_LEVEL_DESC)
|
|
||||||
{
|
|
||||||
int idx_log_level = 2;
|
|
||||||
|
|
||||||
if (argc == idx_log_level) {
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, zlog_default->default_lvl);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
int level;
|
|
||||||
|
|
||||||
if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
|
|
||||||
return CMD_ERR_NO_MATCH;
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, level);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (no_config_log_monitor,
|
|
||||||
no_config_log_monitor_cmd,
|
|
||||||
"no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
|
||||||
NO_STR
|
|
||||||
"Logging control\n"
|
|
||||||
"Disable terminal line (monitor) logging\n"
|
|
||||||
LOG_LEVEL_DESC)
|
|
||||||
{
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int set_log_file(struct vty *vty, const char *fname, int loglevel)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
char *p = NULL;
|
|
||||||
const char *fullpath;
|
|
||||||
|
|
||||||
/* Path detection. */
|
|
||||||
if (!IS_DIRECTORY_SEP(*fname)) {
|
|
||||||
char cwd[MAXPATHLEN + 1];
|
|
||||||
cwd[MAXPATHLEN] = '\0';
|
|
||||||
|
|
||||||
if (getcwd(cwd, MAXPATHLEN) == NULL) {
|
|
||||||
flog_err_sys(EC_LIB_SYSTEM_CALL,
|
|
||||||
"config_log_file: Unable to alloc mem!");
|
|
||||||
return CMD_WARNING_CONFIG_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2);
|
|
||||||
sprintf(p, "%s/%s", cwd, fname);
|
|
||||||
fullpath = p;
|
|
||||||
} else
|
|
||||||
fullpath = fname;
|
|
||||||
|
|
||||||
ret = zlog_set_file(fullpath, loglevel);
|
|
||||||
|
|
||||||
XFREE(MTYPE_TMP, p);
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
if (vty)
|
|
||||||
vty_out(vty, "can't open logfile %s\n", fname);
|
|
||||||
return CMD_WARNING_CONFIG_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
XFREE(MTYPE_HOST, host.logfile);
|
|
||||||
|
|
||||||
host.logfile = XSTRDUP(MTYPE_HOST, fname);
|
|
||||||
|
|
||||||
#if defined(HAVE_CUMULUS)
|
|
||||||
if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
|
|
||||||
#endif
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void command_setup_early_logging(const char *dest, const char *level)
|
|
||||||
{
|
|
||||||
char *token;
|
|
||||||
|
|
||||||
if (level) {
|
|
||||||
int nlevel = level_match(level);
|
|
||||||
|
|
||||||
if (nlevel != ZLOG_DISABLED)
|
|
||||||
zlog_default->default_lvl = nlevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dest)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (strcmp(dest, "stdout") == 0) {
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(dest, "syslog") == 0) {
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
token = strstr(dest, ":");
|
|
||||||
if (token == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
token++;
|
|
||||||
|
|
||||||
set_log_file(NULL, token, zlog_default->default_lvl);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (config_log_file,
|
|
||||||
config_log_file_cmd,
|
|
||||||
"log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
|
||||||
"Logging control\n"
|
|
||||||
"Logging to file\n"
|
|
||||||
"Logging filename\n"
|
|
||||||
LOG_LEVEL_DESC)
|
|
||||||
{
|
|
||||||
int idx_filename = 2;
|
|
||||||
int idx_log_levels = 3;
|
|
||||||
if (argc == 4) {
|
|
||||||
int level;
|
|
||||||
if ((level = level_match(argv[idx_log_levels]->arg))
|
|
||||||
== ZLOG_DISABLED)
|
|
||||||
return CMD_ERR_NO_MATCH;
|
|
||||||
return set_log_file(vty, argv[idx_filename]->arg, level);
|
|
||||||
} else
|
|
||||||
return set_log_file(vty, argv[idx_filename]->arg,
|
|
||||||
zlog_default->default_lvl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disable_log_file(void)
|
|
||||||
{
|
|
||||||
zlog_reset_file();
|
|
||||||
|
|
||||||
XFREE(MTYPE_HOST, host.logfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (no_config_log_file,
|
|
||||||
no_config_log_file_cmd,
|
|
||||||
"no log file [FILENAME [LEVEL]]",
|
|
||||||
NO_STR
|
|
||||||
"Logging control\n"
|
|
||||||
"Cancel logging to file\n"
|
|
||||||
"Logging file name\n"
|
|
||||||
"Logging level\n")
|
|
||||||
{
|
|
||||||
disable_log_file();
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (config_log_syslog,
|
|
||||||
config_log_syslog_cmd,
|
|
||||||
"log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
|
||||||
"Logging control\n"
|
|
||||||
"Set syslog logging level\n"
|
|
||||||
LOG_LEVEL_DESC)
|
|
||||||
{
|
|
||||||
int idx_log_levels = 2;
|
|
||||||
|
|
||||||
if (argc == 3) {
|
|
||||||
int level;
|
|
||||||
if ((level = level_match(argv[idx_log_levels]->arg))
|
|
||||||
== ZLOG_DISABLED)
|
|
||||||
return CMD_ERR_NO_MATCH;
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, level);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
} else {
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (no_config_log_syslog,
|
|
||||||
no_config_log_syslog_cmd,
|
|
||||||
"no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
|
||||||
NO_STR
|
|
||||||
"Logging control\n"
|
|
||||||
"Cancel logging to syslog\n"
|
|
||||||
LOG_FACILITY_DESC
|
|
||||||
LOG_LEVEL_DESC)
|
|
||||||
{
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (config_log_facility,
|
|
||||||
config_log_facility_cmd,
|
|
||||||
"log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
|
|
||||||
"Logging control\n"
|
|
||||||
"Facility parameter for syslog messages\n"
|
|
||||||
LOG_FACILITY_DESC)
|
|
||||||
{
|
|
||||||
int idx_target = 2;
|
|
||||||
int facility = facility_match(argv[idx_target]->arg);
|
|
||||||
|
|
||||||
zlog_default->facility = facility;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (no_config_log_facility,
|
|
||||||
no_config_log_facility_cmd,
|
|
||||||
"no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
|
|
||||||
NO_STR
|
|
||||||
"Logging control\n"
|
|
||||||
"Reset syslog facility to default (daemon)\n"
|
|
||||||
LOG_FACILITY_DESC)
|
|
||||||
{
|
|
||||||
zlog_default->facility = LOG_DAEMON;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (config_log_record_priority,
|
|
||||||
config_log_record_priority_cmd,
|
|
||||||
"log record-priority",
|
|
||||||
"Logging control\n"
|
|
||||||
"Log the priority of the message within the message\n")
|
|
||||||
{
|
|
||||||
zlog_default->record_priority = 1;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (no_config_log_record_priority,
|
|
||||||
no_config_log_record_priority_cmd,
|
|
||||||
"no log record-priority",
|
|
||||||
NO_STR
|
|
||||||
"Logging control\n"
|
|
||||||
"Do not log the priority of the message within the message\n")
|
|
||||||
{
|
|
||||||
zlog_default->record_priority = 0;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (config_log_timestamp_precision,
|
|
||||||
config_log_timestamp_precision_cmd,
|
|
||||||
"log timestamp precision (0-6)",
|
|
||||||
"Logging control\n"
|
|
||||||
"Timestamp configuration\n"
|
|
||||||
"Set the timestamp precision\n"
|
|
||||||
"Number of subsecond digits\n")
|
|
||||||
{
|
|
||||||
int idx_number = 3;
|
|
||||||
zlog_default->timestamp_precision =
|
|
||||||
strtoul(argv[idx_number]->arg, NULL, 10);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (no_config_log_timestamp_precision,
|
|
||||||
no_config_log_timestamp_precision_cmd,
|
|
||||||
"no log timestamp precision",
|
|
||||||
NO_STR
|
|
||||||
"Logging control\n"
|
|
||||||
"Timestamp configuration\n"
|
|
||||||
"Reset the timestamp precision to the default value of 0\n")
|
|
||||||
{
|
|
||||||
zlog_default->timestamp_precision = 0;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (debug_memstats,
|
DEFUN (debug_memstats,
|
||||||
debug_memstats_cmd,
|
debug_memstats_cmd,
|
||||||
"[no] debug memstats-at-exit",
|
"[no] debug memstats-at-exit",
|
||||||
|
@ -2875,7 +2425,6 @@ void cmd_init(int terminal)
|
||||||
#endif
|
#endif
|
||||||
host.password = NULL;
|
host.password = NULL;
|
||||||
host.enable = NULL;
|
host.enable = NULL;
|
||||||
host.logfile = NULL;
|
|
||||||
host.config = NULL;
|
host.config = NULL;
|
||||||
host.noconfig = (terminal < 0);
|
host.noconfig = (terminal < 0);
|
||||||
host.lines = -1;
|
host.lines = -1;
|
||||||
|
@ -2903,7 +2452,6 @@ void cmd_init(int terminal)
|
||||||
install_element(VIEW_NODE, &config_enable_cmd);
|
install_element(VIEW_NODE, &config_enable_cmd);
|
||||||
install_element(VIEW_NODE, &config_terminal_length_cmd);
|
install_element(VIEW_NODE, &config_terminal_length_cmd);
|
||||||
install_element(VIEW_NODE, &config_terminal_no_length_cmd);
|
install_element(VIEW_NODE, &config_terminal_no_length_cmd);
|
||||||
install_element(VIEW_NODE, &show_logging_cmd);
|
|
||||||
install_element(VIEW_NODE, &show_commandtree_cmd);
|
install_element(VIEW_NODE, &show_commandtree_cmd);
|
||||||
install_element(VIEW_NODE, &echo_cmd);
|
install_element(VIEW_NODE, &echo_cmd);
|
||||||
install_element(VIEW_NODE, &autocomplete_cmd);
|
install_element(VIEW_NODE, &autocomplete_cmd);
|
||||||
|
@ -2930,6 +2478,8 @@ void cmd_init(int terminal)
|
||||||
install_element(CONFIG_NODE, &no_domainname_cmd);
|
install_element(CONFIG_NODE, &no_domainname_cmd);
|
||||||
|
|
||||||
if (terminal > 0) {
|
if (terminal > 0) {
|
||||||
|
full_cli = true;
|
||||||
|
|
||||||
install_element(CONFIG_NODE, &debug_memstats_cmd);
|
install_element(CONFIG_NODE, &debug_memstats_cmd);
|
||||||
|
|
||||||
install_element(CONFIG_NODE, &password_cmd);
|
install_element(CONFIG_NODE, &password_cmd);
|
||||||
|
@ -2937,23 +2487,6 @@ void cmd_init(int terminal)
|
||||||
install_element(CONFIG_NODE, &enable_password_cmd);
|
install_element(CONFIG_NODE, &enable_password_cmd);
|
||||||
install_element(CONFIG_NODE, &no_enable_password_cmd);
|
install_element(CONFIG_NODE, &no_enable_password_cmd);
|
||||||
|
|
||||||
install_element(CONFIG_NODE, &config_log_stdout_cmd);
|
|
||||||
install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
|
|
||||||
install_element(CONFIG_NODE, &config_log_monitor_cmd);
|
|
||||||
install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
|
|
||||||
install_element(CONFIG_NODE, &config_log_file_cmd);
|
|
||||||
install_element(CONFIG_NODE, &no_config_log_file_cmd);
|
|
||||||
install_element(CONFIG_NODE, &config_log_syslog_cmd);
|
|
||||||
install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
|
|
||||||
install_element(CONFIG_NODE, &config_log_facility_cmd);
|
|
||||||
install_element(CONFIG_NODE, &no_config_log_facility_cmd);
|
|
||||||
install_element(CONFIG_NODE, &config_log_record_priority_cmd);
|
|
||||||
install_element(CONFIG_NODE,
|
|
||||||
&no_config_log_record_priority_cmd);
|
|
||||||
install_element(CONFIG_NODE,
|
|
||||||
&config_log_timestamp_precision_cmd);
|
|
||||||
install_element(CONFIG_NODE,
|
|
||||||
&no_config_log_timestamp_precision_cmd);
|
|
||||||
install_element(CONFIG_NODE, &service_password_encrypt_cmd);
|
install_element(CONFIG_NODE, &service_password_encrypt_cmd);
|
||||||
install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
|
install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
|
||||||
install_element(CONFIG_NODE, &banner_motd_default_cmd);
|
install_element(CONFIG_NODE, &banner_motd_default_cmd);
|
||||||
|
@ -2963,6 +2496,7 @@ void cmd_init(int terminal)
|
||||||
install_element(CONFIG_NODE, &service_terminal_length_cmd);
|
install_element(CONFIG_NODE, &service_terminal_length_cmd);
|
||||||
install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
|
install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
|
||||||
|
|
||||||
|
log_cmd_init();
|
||||||
vrf_install_commands();
|
vrf_install_commands();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3000,7 +2534,6 @@ void cmd_terminate(void)
|
||||||
XFREE(MTYPE_HOST, host.password_encrypt);
|
XFREE(MTYPE_HOST, host.password_encrypt);
|
||||||
XFREE(MTYPE_HOST, host.enable);
|
XFREE(MTYPE_HOST, host.enable);
|
||||||
XFREE(MTYPE_HOST, host.enable_encrypt);
|
XFREE(MTYPE_HOST, host.enable_encrypt);
|
||||||
XFREE(MTYPE_HOST, host.logfile);
|
|
||||||
XFREE(MTYPE_HOST, host.motdfile);
|
XFREE(MTYPE_HOST, host.motdfile);
|
||||||
XFREE(MTYPE_HOST, host.config);
|
XFREE(MTYPE_HOST, host.config);
|
||||||
XFREE(MTYPE_HOST, host.motd);
|
XFREE(MTYPE_HOST, host.motd);
|
||||||
|
|
|
@ -66,9 +66,6 @@ struct host {
|
||||||
/* System wide terminal lines. */
|
/* System wide terminal lines. */
|
||||||
int lines;
|
int lines;
|
||||||
|
|
||||||
/* Log filename. */
|
|
||||||
char *logfile;
|
|
||||||
|
|
||||||
/* config file name of this host */
|
/* config file name of this host */
|
||||||
char *config;
|
char *config;
|
||||||
int noconfig;
|
int noconfig;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "frr_pthread.h"
|
#include "frr_pthread.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "linklist.h"
|
#include "linklist.h"
|
||||||
|
#include "zlog.h"
|
||||||
|
|
||||||
DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread")
|
DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread")
|
||||||
DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives")
|
DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives")
|
||||||
|
@ -273,6 +274,8 @@ static void *fpt_run(void *arg)
|
||||||
struct frr_pthread *fpt = arg;
|
struct frr_pthread *fpt = arg;
|
||||||
fpt->master->owner = pthread_self();
|
fpt->master->owner = pthread_self();
|
||||||
|
|
||||||
|
zlog_tls_buffer_init();
|
||||||
|
|
||||||
int sleeper[2];
|
int sleeper[2];
|
||||||
pipe(sleeper);
|
pipe(sleeper);
|
||||||
thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
|
thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
|
||||||
|
@ -294,5 +297,7 @@ static void *fpt_run(void *arg)
|
||||||
close(sleeper[1]);
|
close(sleeper[1]);
|
||||||
close(sleeper[0]);
|
close(sleeper[0]);
|
||||||
|
|
||||||
|
zlog_tls_buffer_fini();
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,11 +45,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
master = thread_master_create(NULL);
|
master = thread_master_create(NULL);
|
||||||
|
|
||||||
openzlog("grammar_sandbox", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||||
LOG_DAEMON);
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG);
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
|
|
||||||
|
|
||||||
/* Library inits. */
|
/* Library inits. */
|
||||||
cmd_init(1);
|
cmd_init(1);
|
||||||
|
|
14
lib/libfrr.c
14
lib/libfrr.c
|
@ -33,7 +33,6 @@
|
||||||
#include "lib_vty.h"
|
#include "lib_vty.h"
|
||||||
#include "log_vty.h"
|
#include "log_vty.h"
|
||||||
#include "zclient.h"
|
#include "zclient.h"
|
||||||
#include "log_int.h"
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "lib_errors.h"
|
#include "lib_errors.h"
|
||||||
|
@ -630,6 +629,7 @@ struct thread_master *frr_init(void)
|
||||||
{
|
{
|
||||||
struct option_chain *oc;
|
struct option_chain *oc;
|
||||||
struct frrmod_runtime *module;
|
struct frrmod_runtime *module;
|
||||||
|
struct zprivs_ids_t ids;
|
||||||
char moderr[256];
|
char moderr[256];
|
||||||
char p_instance[16] = "", p_pathspace[256] = "";
|
char p_instance[16] = "", p_pathspace[256] = "";
|
||||||
const char *dir;
|
const char *dir;
|
||||||
|
@ -657,9 +657,11 @@ struct thread_master *frr_init(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
zprivs_preinit(di->privs);
|
zprivs_preinit(di->privs);
|
||||||
|
zprivs_get_ids(&ids);
|
||||||
|
|
||||||
openzlog(di->progname, di->logname, di->instance,
|
zlog_init(di->progname, di->logname, di->instance,
|
||||||
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
ids.uid_normal, ids.gid_normal);
|
||||||
|
zlog_tls_buffer_init();
|
||||||
|
|
||||||
command_setup_early_logging(di->early_logging, di->early_loglevel);
|
command_setup_early_logging(di->early_logging, di->early_loglevel);
|
||||||
|
|
||||||
|
@ -709,7 +711,6 @@ struct thread_master *frr_init(void)
|
||||||
|
|
||||||
vty_init(master, di->log_always);
|
vty_init(master, di->log_always);
|
||||||
lib_cmd_init();
|
lib_cmd_init();
|
||||||
log_filter_cmd_init();
|
|
||||||
|
|
||||||
frr_pthread_init();
|
frr_pthread_init();
|
||||||
|
|
||||||
|
@ -1086,7 +1087,7 @@ void frr_run(struct thread_master *master)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* end fixed stderr startup logging */
|
/* end fixed stderr startup logging */
|
||||||
zlog_startup_stderr = false;
|
zlog_startup_end();
|
||||||
|
|
||||||
struct thread thread;
|
struct thread thread;
|
||||||
while (thread_fetch(master, &thread))
|
while (thread_fetch(master, &thread))
|
||||||
|
@ -1119,7 +1120,8 @@ void frr_fini(void)
|
||||||
/* signal_init -> nothing needed */
|
/* signal_init -> nothing needed */
|
||||||
thread_master_free(master);
|
thread_master_free(master);
|
||||||
master = NULL;
|
master = NULL;
|
||||||
closezlog();
|
zlog_tls_buffer_fini();
|
||||||
|
zlog_fini();
|
||||||
/* frrmod_init -> nothing needed / hooks */
|
/* frrmod_init -> nothing needed / hooks */
|
||||||
rcu_shutdown();
|
rcu_shutdown();
|
||||||
|
|
||||||
|
|
683
lib/log.c
683
lib/log.c
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
#include "zclient.h"
|
#include "zclient.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "log_int.h"
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "lib_errors.h"
|
#include "lib_errors.h"
|
||||||
|
@ -33,154 +32,12 @@
|
||||||
#include "printfrr.h"
|
#include "printfrr.h"
|
||||||
#include "frr_pthread.h"
|
#include "frr_pthread.h"
|
||||||
|
|
||||||
#ifndef SUNOS_5
|
|
||||||
#include <sys/un.h>
|
|
||||||
#endif
|
|
||||||
/* for printstack on solaris */
|
|
||||||
#ifdef HAVE_UCONTEXT_H
|
|
||||||
#include <ucontext.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBUNWIND
|
#ifdef HAVE_LIBUNWIND
|
||||||
#define UNW_LOCAL_ONLY
|
#define UNW_LOCAL_ONLY
|
||||||
#include <libunwind.h>
|
#include <libunwind.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DEFINE_MTYPE_STATIC(LIB, ZLOG, "Logging")
|
|
||||||
|
|
||||||
/* hook for external logging */
|
|
||||||
DEFINE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args),
|
|
||||||
(priority, format, args));
|
|
||||||
|
|
||||||
static int logfile_fd = -1; /* Used in signal handler. */
|
|
||||||
|
|
||||||
struct zlog *zlog_default = NULL;
|
|
||||||
bool zlog_startup_stderr = true;
|
|
||||||
|
|
||||||
/* lock protecting zlog_default for mt-safe zlog */
|
|
||||||
static pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
const char *zlog_priority[] = {
|
|
||||||
"emergencies", "alerts", "critical", "errors", "warnings",
|
|
||||||
"notifications", "informational", "debugging", NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
static char zlog_filters[ZLOG_FILTERS_MAX][ZLOG_FILTER_LENGTH_MAX + 1];
|
|
||||||
static uint8_t zlog_filter_count;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* look for a match on the filter in the current filters, loglock must be held
|
|
||||||
*/
|
|
||||||
static int zlog_filter_lookup(const char *lookup)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < zlog_filter_count; i++) {
|
|
||||||
if (strncmp(lookup, zlog_filters[i], sizeof(zlog_filters[0]))
|
|
||||||
== 0)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void zlog_filter_clear(void)
|
|
||||||
{
|
|
||||||
frr_with_mutex(&loglock) {
|
|
||||||
zlog_filter_count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int zlog_filter_add(const char *filter)
|
|
||||||
{
|
|
||||||
frr_with_mutex(&loglock) {
|
|
||||||
if (zlog_filter_count >= ZLOG_FILTERS_MAX)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (zlog_filter_lookup(filter) != -1)
|
|
||||||
/* Filter already present */
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
strlcpy(zlog_filters[zlog_filter_count], filter,
|
|
||||||
sizeof(zlog_filters[0]));
|
|
||||||
|
|
||||||
if (zlog_filters[zlog_filter_count][0] == '\0')
|
|
||||||
/* Filter was either empty or didn't get copied
|
|
||||||
* correctly
|
|
||||||
*/
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
zlog_filter_count++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int zlog_filter_del(const char *filter)
|
|
||||||
{
|
|
||||||
frr_with_mutex(&loglock) {
|
|
||||||
int found_idx = zlog_filter_lookup(filter);
|
|
||||||
int last_idx = zlog_filter_count - 1;
|
|
||||||
|
|
||||||
if (found_idx == -1)
|
|
||||||
/* Didn't find the filter to delete */
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Adjust the filter array */
|
|
||||||
memmove(zlog_filters[found_idx], zlog_filters[found_idx + 1],
|
|
||||||
(last_idx - found_idx) * sizeof(zlog_filters[0]));
|
|
||||||
|
|
||||||
zlog_filter_count--;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dump all filters to buffer, delimited by new line */
|
|
||||||
int zlog_filter_dump(char *buf, size_t max_size)
|
|
||||||
{
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
frr_with_mutex(&loglock) {
|
|
||||||
for (int i = 0; i < zlog_filter_count; i++) {
|
|
||||||
int ret;
|
|
||||||
ret = snprintf(buf + len, max_size - len, " %s\n",
|
|
||||||
zlog_filters[i]);
|
|
||||||
len += ret;
|
|
||||||
if ((ret < 0) || ((size_t)len >= max_size))
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* write_wrapper
|
|
||||||
*
|
|
||||||
* glibc has declared that the return value from write *must* not be
|
|
||||||
* ignored.
|
|
||||||
* gcc see's this problem and issues a warning for the line.
|
|
||||||
*
|
|
||||||
* Why is this a big deal you say? Because both of them are right
|
|
||||||
* and if you have -Werror enabled then all calls to write
|
|
||||||
* generate a build error and the build stops.
|
|
||||||
*
|
|
||||||
* clang has helpfully allowed this construct:
|
|
||||||
* (void)write(...)
|
|
||||||
* to tell the compiler yeah I know it has a return value
|
|
||||||
* I don't care about it at this time.
|
|
||||||
* gcc doesn't have this ability.
|
|
||||||
*
|
|
||||||
* This code was written such that it didn't care about the
|
|
||||||
* return value from write. At this time do I want
|
|
||||||
* to go through and fix and test this code for correctness.
|
|
||||||
* So just wrapper the bad behavior and move on.
|
|
||||||
*/
|
|
||||||
static void write_wrapper(int fd, const void *buf, size_t count)
|
|
||||||
{
|
|
||||||
if (write(fd, buf, count) <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up a message in a message list by key.
|
* Looks up a message in a message list by key.
|
||||||
*
|
*
|
||||||
|
@ -264,274 +121,12 @@ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void timestamp_control_render(struct timestamp_control *ctl)
|
|
||||||
{
|
|
||||||
if (!ctl->already_rendered) {
|
|
||||||
ctl->len = quagga_timestamp(ctl->precision, ctl->buf,
|
|
||||||
sizeof(ctl->buf));
|
|
||||||
ctl->already_rendered = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Utility routine for current time printing. */
|
|
||||||
static void time_print(FILE *fp, struct timestamp_control *ctl)
|
|
||||||
{
|
|
||||||
timestamp_control_render(ctl);
|
|
||||||
fprintf(fp, "%s ", ctl->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int time_print_buf(char *buf, int len, int max_size,
|
|
||||||
struct timestamp_control *ctl)
|
|
||||||
{
|
|
||||||
timestamp_control_render(ctl);
|
|
||||||
|
|
||||||
if (ctl->len + 1 >= (unsigned long)max_size)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return snprintf(buf + len, max_size - len, "%s ", ctl->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl,
|
|
||||||
const char *proto_str, int record_priority, int priority,
|
|
||||||
FILE *fp, const char *msg)
|
|
||||||
{
|
|
||||||
time_print(fp, tsctl);
|
|
||||||
if (record_priority)
|
|
||||||
fprintf(fp, "%s: ", zlog_priority[priority]);
|
|
||||||
|
|
||||||
fprintf(fp, "%s%s\n", proto_str, msg);
|
|
||||||
fflush(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search a buf for the filter strings, loglock must be held */
|
|
||||||
static int search_buf(const char *buf)
|
|
||||||
{
|
|
||||||
char *found = NULL;
|
|
||||||
|
|
||||||
for (int i = 0; i < zlog_filter_count; i++) {
|
|
||||||
found = strstr(buf, zlog_filters[i]);
|
|
||||||
if (found != NULL)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Filter out a log */
|
|
||||||
static int vzlog_filter(struct zlog *zl, struct timestamp_control *tsctl,
|
|
||||||
const char *proto_str, int priority, const char *msg)
|
|
||||||
{
|
|
||||||
int len = 0;
|
|
||||||
int ret = 0;
|
|
||||||
char buf[1024] = "";
|
|
||||||
|
|
||||||
ret = time_print_buf(buf, len, sizeof(buf), tsctl);
|
|
||||||
|
|
||||||
len += ret;
|
|
||||||
if ((ret < 0) || ((size_t)len >= sizeof(buf)))
|
|
||||||
goto search;
|
|
||||||
|
|
||||||
if (zl && zl->record_priority)
|
|
||||||
snprintf(buf + len, sizeof(buf) - len, "%s: %s: %s",
|
|
||||||
zlog_priority[priority], proto_str, msg);
|
|
||||||
else
|
|
||||||
snprintf(buf + len, sizeof(buf) - len, "%s: %s", proto_str,
|
|
||||||
msg);
|
|
||||||
|
|
||||||
search:
|
|
||||||
return search_buf(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* va_list version of zlog. */
|
|
||||||
void vzlog(int priority, const char *format, va_list args)
|
|
||||||
{
|
|
||||||
frr_mutex_lock_autounlock(&loglock);
|
|
||||||
|
|
||||||
char proto_str[32] = "";
|
|
||||||
int original_errno = errno;
|
|
||||||
struct timestamp_control tsctl = {};
|
|
||||||
tsctl.already_rendered = 0;
|
|
||||||
struct zlog *zl = zlog_default;
|
|
||||||
char buf[256], *msg;
|
|
||||||
|
|
||||||
if (zl == NULL) {
|
|
||||||
tsctl.precision = 0;
|
|
||||||
} else {
|
|
||||||
tsctl.precision = zl->timestamp_precision;
|
|
||||||
if (zl->instance)
|
|
||||||
sprintf(proto_str, "%s[%d]: ", zl->protoname,
|
|
||||||
zl->instance);
|
|
||||||
else
|
|
||||||
sprintf(proto_str, "%s: ", zl->protoname);
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), format, args);
|
|
||||||
|
|
||||||
/* If it doesn't match on a filter, do nothing with the debug log */
|
|
||||||
if ((priority == LOG_DEBUG) && zlog_filter_count
|
|
||||||
&& vzlog_filter(zl, &tsctl, proto_str, priority, msg))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* call external hook */
|
|
||||||
hook_call(zebra_ext_log, priority, format, args);
|
|
||||||
|
|
||||||
/* When zlog_default is also NULL, use stderr for logging. */
|
|
||||||
if (zl == NULL) {
|
|
||||||
time_print(stderr, &tsctl);
|
|
||||||
fprintf(stderr, "%s: %s\n", "unknown", msg);
|
|
||||||
fflush(stderr);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Syslog output */
|
|
||||||
if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
|
|
||||||
syslog(priority | zlog_default->facility, "%s", msg);
|
|
||||||
|
|
||||||
/* File output. */
|
|
||||||
if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
|
|
||||||
vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority,
|
|
||||||
zl->fp, msg);
|
|
||||||
|
|
||||||
/* fixed-config logging to stderr while we're stating up & haven't
|
|
||||||
* daemonized / reached mainloop yet
|
|
||||||
*
|
|
||||||
* note the "else" on stdout output -- we don't want to print the same
|
|
||||||
* message to both stderr and stdout. */
|
|
||||||
if (zlog_startup_stderr && priority <= LOG_WARNING)
|
|
||||||
vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, msg);
|
|
||||||
else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
|
|
||||||
vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority,
|
|
||||||
stdout, msg);
|
|
||||||
|
|
||||||
/* Terminal monitor. */
|
|
||||||
if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
|
|
||||||
vty_log((zl->record_priority ? zlog_priority[priority] : NULL),
|
|
||||||
proto_str, msg, &tsctl);
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (msg != buf)
|
|
||||||
XFREE(MTYPE_TMP, msg);
|
|
||||||
errno = original_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vzlog_test(int priority)
|
|
||||||
{
|
|
||||||
frr_mutex_lock_autounlock(&loglock);
|
|
||||||
|
|
||||||
struct zlog *zl = zlog_default;
|
|
||||||
|
|
||||||
/* When zlog_default is also NULL, use stderr for logging. */
|
|
||||||
if (zl == NULL)
|
|
||||||
return 1;
|
|
||||||
/* Syslog output */
|
|
||||||
else if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
|
|
||||||
return 1;
|
|
||||||
/* File output. */
|
|
||||||
else if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
|
|
||||||
return 1;
|
|
||||||
/* stdout output. */
|
|
||||||
else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
|
|
||||||
return 1;
|
|
||||||
/* Terminal monitor. */
|
|
||||||
else if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* crash handling
|
* crash handling
|
||||||
*
|
*
|
||||||
* NB: only AS-Safe (async-signal) functions can be used here!
|
* NB: only AS-Safe (async-signal) functions can be used here!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Needs to be enhanced to support Solaris. */
|
|
||||||
static int syslog_connect(void)
|
|
||||||
{
|
|
||||||
#ifdef SUNOS_5
|
|
||||||
return -1;
|
|
||||||
#else
|
|
||||||
int fd;
|
|
||||||
struct sockaddr_un addr;
|
|
||||||
|
|
||||||
if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
|
|
||||||
return -1;
|
|
||||||
addr.sun_family = AF_UNIX;
|
|
||||||
#ifdef _PATH_LOG
|
|
||||||
#define SYSLOG_SOCKET_PATH _PATH_LOG
|
|
||||||
#else
|
|
||||||
#define SYSLOG_SOCKET_PATH "/dev/log"
|
|
||||||
#endif
|
|
||||||
strlcpy(addr.sun_path, SYSLOG_SOCKET_PATH, sizeof(addr.sun_path));
|
|
||||||
#undef SYSLOG_SOCKET_PATH
|
|
||||||
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void syslog_sigsafe(int priority, const char *msg, size_t msglen)
|
|
||||||
{
|
|
||||||
static int syslog_fd = -1;
|
|
||||||
char buf[sizeof("<1234567890>ripngd[1234567890]: ") + msglen + 50];
|
|
||||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
|
|
||||||
|
|
||||||
if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* forget about the timestamp, too difficult in a signal handler */
|
|
||||||
bprintfrr(&fb, "<%d>%s", priority, zlog_default->ident);
|
|
||||||
if (zlog_default->syslog_options & LOG_PID)
|
|
||||||
bprintfrr(&fb, "[%ld]", (long)getpid());
|
|
||||||
bprintfrr(&fb, ": %s", msg);
|
|
||||||
write_wrapper(syslog_fd, fb.buf, fb.pos - fb.buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int open_crashlog(void)
|
|
||||||
{
|
|
||||||
char crashlog_buf[PATH_MAX];
|
|
||||||
const char *crashlog_default = "/var/tmp/frr.crashlog", *crashlog;
|
|
||||||
|
|
||||||
if (!zlog_default || !zlog_default->ident)
|
|
||||||
crashlog = crashlog_default;
|
|
||||||
else {
|
|
||||||
snprintfrr(crashlog_buf, sizeof(crashlog_buf),
|
|
||||||
"/var/tmp/frr.%s.crashlog", zlog_default->ident);
|
|
||||||
crashlog = crashlog_buf;
|
|
||||||
}
|
|
||||||
return open(crashlog, O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* N.B. implicit priority is most severe */
|
|
||||||
#define PRI LOG_CRIT
|
|
||||||
|
|
||||||
static void crash_write(struct fbuf *fb, char *msgstart)
|
|
||||||
{
|
|
||||||
if (fb->pos == fb->buf)
|
|
||||||
return;
|
|
||||||
if (!msgstart)
|
|
||||||
msgstart = fb->buf;
|
|
||||||
|
|
||||||
/* If no file logging configured, try to write to fallback log file. */
|
|
||||||
if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
|
|
||||||
write(logfile_fd, fb->buf, fb->pos - fb->buf);
|
|
||||||
if (!zlog_default)
|
|
||||||
write(STDERR_FILENO, fb->buf, fb->pos - fb->buf);
|
|
||||||
else {
|
|
||||||
if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
|
|
||||||
write(STDOUT_FILENO, fb->buf, fb->pos - fb->buf);
|
|
||||||
/* Remove trailing '\n' for monitor and syslog */
|
|
||||||
fb->pos--;
|
|
||||||
if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
|
|
||||||
vty_log_fixed(fb->buf, fb->pos - fb->buf);
|
|
||||||
if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
|
|
||||||
syslog_sigsafe(PRI | zlog_default->facility, msgstart,
|
|
||||||
fb->pos - msgstart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note: the goal here is to use only async-signal-safe functions. */
|
/* Note: the goal here is to use only async-signal-safe functions. */
|
||||||
void zlog_signal(int signo, const char *action, void *siginfo_v,
|
void zlog_signal(int signo, const char *action, void *siginfo_v,
|
||||||
void *program_counter)
|
void *program_counter)
|
||||||
|
@ -540,14 +135,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
|
||||||
time_t now;
|
time_t now;
|
||||||
char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")
|
char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")
|
||||||
+ 100];
|
+ 100];
|
||||||
char *msgstart;
|
|
||||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
|
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
|
||||||
|
|
||||||
time(&now);
|
time(&now);
|
||||||
if (zlog_default)
|
|
||||||
bprintfrr(&fb, "%s: ", zlog_default->protoname);
|
|
||||||
|
|
||||||
msgstart = fb.pos;
|
|
||||||
|
|
||||||
bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now);
|
bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now);
|
||||||
if (program_counter)
|
if (program_counter)
|
||||||
|
@ -559,9 +149,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
|
||||||
(ptrdiff_t)siginfo->si_addr);
|
(ptrdiff_t)siginfo->si_addr);
|
||||||
bprintfrr(&fb, "; %s\n", action);
|
bprintfrr(&fb, "; %s\n", action);
|
||||||
|
|
||||||
crash_write(&fb, msgstart);
|
zlog_sigsafe(fb.buf, fb.pos - fb.buf);
|
||||||
|
|
||||||
zlog_backtrace_sigsafe(PRI, program_counter);
|
zlog_backtrace_sigsafe(LOG_CRIT, program_counter);
|
||||||
|
|
||||||
fb.pos = buf;
|
fb.pos = buf;
|
||||||
|
|
||||||
|
@ -574,7 +164,7 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
|
||||||
bprintfrr(&fb, "in thread %s scheduled from %s:%d\n",
|
bprintfrr(&fb, "in thread %s scheduled from %s:%d\n",
|
||||||
tc->funcname, tc->schedfrom, tc->schedfrom_line);
|
tc->funcname, tc->schedfrom, tc->schedfrom_line);
|
||||||
|
|
||||||
crash_write(&fb, NULL);
|
zlog_sigsafe(fb.buf, fb.pos - fb.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Log a backtrace using only async-signal-safe functions.
|
/* Log a backtrace using only async-signal-safe functions.
|
||||||
|
@ -609,64 +199,24 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter)
|
||||||
bprintfrr(&fb, " %s (mapped at %p)",
|
bprintfrr(&fb, " %s (mapped at %p)",
|
||||||
dlinfo.dli_fname, dlinfo.dli_fbase);
|
dlinfo.dli_fname, dlinfo.dli_fbase);
|
||||||
bprintfrr(&fb, "\n");
|
bprintfrr(&fb, "\n");
|
||||||
crash_write(&fb, NULL);
|
zlog_sigsafe(fb.buf, fb.pos - fb.buf);
|
||||||
}
|
}
|
||||||
#elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK)
|
#elif defined(HAVE_GLIBC_BACKTRACE)
|
||||||
static const char pclabel[] = "Program counter: ";
|
|
||||||
void *array[64];
|
void *array[64];
|
||||||
int size;
|
int size, i;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
|
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
|
||||||
char **bt = NULL;
|
char **bt = NULL;
|
||||||
|
|
||||||
#ifdef HAVE_GLIBC_BACKTRACE
|
|
||||||
size = backtrace(array, array_size(array));
|
size = backtrace(array, array_size(array));
|
||||||
if (size <= 0 || (size_t)size > array_size(array))
|
if (size <= 0 || (size_t)size > array_size(array))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#define DUMP(FD) \
|
bprintfrr(&fb, "Backtrace for %d stack frames:", size);
|
||||||
{ \
|
zlog_sigsafe(fb.pos, fb.buf - fb.pos);
|
||||||
if (program_counter) { \
|
|
||||||
write_wrapper(FD, pclabel, sizeof(pclabel) - 1); \
|
|
||||||
backtrace_symbols_fd(&program_counter, 1, FD); \
|
|
||||||
} \
|
|
||||||
write_wrapper(FD, fb.buf, fb.pos - fb.buf); \
|
|
||||||
backtrace_symbols_fd(array, size, FD); \
|
|
||||||
}
|
|
||||||
#elif defined(HAVE_PRINTSTACK)
|
|
||||||
size = 0;
|
|
||||||
|
|
||||||
#define DUMP(FD) \
|
|
||||||
{ \
|
|
||||||
if (program_counter) \
|
|
||||||
write_wrapper((FD), pclabel, sizeof(pclabel) - 1); \
|
|
||||||
write_wrapper((FD), fb.buf, fb.pos - fb.buf); \
|
|
||||||
printstack((FD)); \
|
|
||||||
}
|
|
||||||
#endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */
|
|
||||||
|
|
||||||
bprintfrr(&fb, "Backtrace for %d stack frames:\n", size);
|
|
||||||
|
|
||||||
if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
|
|
||||||
DUMP(logfile_fd)
|
|
||||||
if (!zlog_default)
|
|
||||||
DUMP(STDERR_FILENO)
|
|
||||||
else {
|
|
||||||
if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
|
|
||||||
DUMP(STDOUT_FILENO)
|
|
||||||
/* Remove trailing '\n' for monitor and syslog */
|
|
||||||
fb.pos--;
|
|
||||||
if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
|
|
||||||
vty_log_fixed(fb.buf, fb.pos - fb.buf);
|
|
||||||
if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
|
|
||||||
syslog_sigsafe(priority | zlog_default->facility,
|
|
||||||
fb.buf, fb.pos - fb.buf);
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
#ifdef HAVE_GLIBC_BACKTRACE
|
|
||||||
bt = backtrace_symbols(array, size);
|
bt = backtrace_symbols(array, size);
|
||||||
#endif
|
|
||||||
/* Just print the function addresses. */
|
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
fb.pos = buf;
|
fb.pos = buf;
|
||||||
if (bt)
|
if (bt)
|
||||||
|
@ -674,20 +224,10 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter)
|
||||||
else
|
else
|
||||||
bprintfrr(&fb, "[bt %d] 0x%tx", i,
|
bprintfrr(&fb, "[bt %d] 0x%tx", i,
|
||||||
(ptrdiff_t)(array[i]));
|
(ptrdiff_t)(array[i]));
|
||||||
if (priority
|
zlog_sigsafe(fb.buf, fb.pos - fb.buf);
|
||||||
<= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
|
|
||||||
vty_log_fixed(fb.buf, fb.pos - fb.buf);
|
|
||||||
if (priority
|
|
||||||
<= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
|
|
||||||
syslog_sigsafe(priority
|
|
||||||
| zlog_default->facility,
|
|
||||||
fb.buf, fb.pos - fb.buf);
|
|
||||||
}
|
}
|
||||||
if (bt)
|
if (bt)
|
||||||
free(bt);
|
free(bt);
|
||||||
}
|
|
||||||
}
|
|
||||||
#undef DUMP
|
|
||||||
#endif /* HAVE_STRACK_TRACE */
|
#endif /* HAVE_STRACK_TRACE */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,36 +294,6 @@ void zlog_backtrace(int priority)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void zlog(int priority, const char *format, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, format);
|
|
||||||
vzlog(priority, format, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ZLOG_FUNC(FUNCNAME, PRIORITY) \
|
|
||||||
void FUNCNAME(const char *format, ...) \
|
|
||||||
{ \
|
|
||||||
va_list args; \
|
|
||||||
va_start(args, format); \
|
|
||||||
vzlog(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
|
|
||||||
|
|
||||||
void zlog_thread_info(int log_level)
|
void zlog_thread_info(int log_level)
|
||||||
{
|
{
|
||||||
struct thread *tc;
|
struct thread *tc;
|
||||||
|
@ -801,11 +311,6 @@ void zlog_thread_info(int log_level)
|
||||||
void _zlog_assert_failed(const char *assertion, const char *file,
|
void _zlog_assert_failed(const char *assertion, const char *file,
|
||||||
unsigned int line, const char *function)
|
unsigned int line, const char *function)
|
||||||
{
|
{
|
||||||
/* 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;
|
|
||||||
zlog(LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
|
zlog(LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
|
||||||
assertion, file, line, (function ? function : "?"));
|
assertion, file, line, (function ? function : "?"));
|
||||||
zlog_backtrace(LOG_CRIT);
|
zlog_backtrace(LOG_CRIT);
|
||||||
|
@ -816,174 +321,14 @@ void _zlog_assert_failed(const char *assertion, const char *file,
|
||||||
|
|
||||||
void memory_oom(size_t size, const char *name)
|
void memory_oom(size_t size, const char *name)
|
||||||
{
|
{
|
||||||
flog_err_sys(EC_LIB_SYSTEM_CALL,
|
zlog(LOG_CRIT,
|
||||||
"out of memory: failed to allocate %zu bytes for %s"
|
"out of memory: failed to allocate %zu bytes for %s object",
|
||||||
"object",
|
|
||||||
size, name);
|
size, name);
|
||||||
zlog_backtrace(LOG_ERR);
|
zlog_backtrace(LOG_CRIT);
|
||||||
|
log_memstats(stderr, "log");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open log stream */
|
|
||||||
void openzlog(const char *progname, const char *protoname,
|
|
||||||
unsigned short instance, int syslog_flags, int syslog_facility)
|
|
||||||
{
|
|
||||||
struct zlog *zl;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
zl = XCALLOC(MTYPE_ZLOG, sizeof(struct zlog));
|
|
||||||
|
|
||||||
zl->ident = progname;
|
|
||||||
zl->protoname = protoname;
|
|
||||||
zl->instance = instance;
|
|
||||||
zl->facility = syslog_facility;
|
|
||||||
zl->syslog_options = syslog_flags;
|
|
||||||
|
|
||||||
/* Set default logging levels. */
|
|
||||||
for (i = 0; i < array_size(zl->maxlvl); i++)
|
|
||||||
zl->maxlvl[i] = ZLOG_DISABLED;
|
|
||||||
zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG;
|
|
||||||
zl->default_lvl = LOG_DEBUG;
|
|
||||||
|
|
||||||
openlog(progname, syslog_flags, zl->facility);
|
|
||||||
|
|
||||||
frr_with_mutex(&loglock) {
|
|
||||||
zlog_default = zl;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_GLIBC_BACKTRACE
|
|
||||||
/* work around backtrace() using lazily resolved dynamically linked
|
|
||||||
* symbols, which will otherwise cause funny breakage in the SEGV
|
|
||||||
* handler.
|
|
||||||
* (particularly, the dynamic linker can call malloc(), which uses locks
|
|
||||||
* in programs linked with -pthread, thus can deadlock.) */
|
|
||||||
void *bt[4];
|
|
||||||
backtrace(bt, array_size(bt));
|
|
||||||
free(backtrace_symbols(bt, 0));
|
|
||||||
backtrace_symbols_fd(bt, 0, 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void closezlog(void)
|
|
||||||
{
|
|
||||||
frr_mutex_lock_autounlock(&loglock);
|
|
||||||
|
|
||||||
struct zlog *zl = zlog_default;
|
|
||||||
|
|
||||||
closelog();
|
|
||||||
|
|
||||||
if (zl->fp != NULL)
|
|
||||||
fclose(zl->fp);
|
|
||||||
|
|
||||||
XFREE(MTYPE_ZLOG, zl->filename);
|
|
||||||
|
|
||||||
XFREE(MTYPE_ZLOG, zl);
|
|
||||||
zlog_default = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called from command.c. */
|
|
||||||
void zlog_set_level(zlog_dest_t dest, int log_level)
|
|
||||||
{
|
|
||||||
frr_with_mutex(&loglock) {
|
|
||||||
zlog_default->maxlvl[dest] = log_level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int zlog_set_file(const char *filename, int log_level)
|
|
||||||
{
|
|
||||||
struct zlog *zl;
|
|
||||||
FILE *fp;
|
|
||||||
mode_t oldumask;
|
|
||||||
int ret = 1;
|
|
||||||
|
|
||||||
/* There is opend file. */
|
|
||||||
zlog_reset_file();
|
|
||||||
|
|
||||||
/* Open file. */
|
|
||||||
oldumask = umask(0777 & ~LOGFILE_MASK);
|
|
||||||
fp = fopen(filename, "a");
|
|
||||||
umask(oldumask);
|
|
||||||
if (fp == NULL) {
|
|
||||||
ret = 0;
|
|
||||||
} else {
|
|
||||||
frr_with_mutex(&loglock) {
|
|
||||||
zl = zlog_default;
|
|
||||||
|
|
||||||
/* Set flags. */
|
|
||||||
zl->filename = XSTRDUP(MTYPE_ZLOG, filename);
|
|
||||||
zl->maxlvl[ZLOG_DEST_FILE] = log_level;
|
|
||||||
zl->fp = fp;
|
|
||||||
logfile_fd = fileno(fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset opend file. */
|
|
||||||
int zlog_reset_file(void)
|
|
||||||
{
|
|
||||||
frr_mutex_lock_autounlock(&loglock);
|
|
||||||
|
|
||||||
struct zlog *zl = zlog_default;
|
|
||||||
|
|
||||||
if (zl->fp)
|
|
||||||
fclose(zl->fp);
|
|
||||||
zl->fp = NULL;
|
|
||||||
logfile_fd = -1;
|
|
||||||
zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
|
|
||||||
|
|
||||||
XFREE(MTYPE_ZLOG, zl->filename);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reopen log file. */
|
|
||||||
int zlog_rotate(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&loglock);
|
|
||||||
|
|
||||||
struct zlog *zl = zlog_default;
|
|
||||||
int level;
|
|
||||||
int ret = 1;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&loglock);
|
|
||||||
|
|
||||||
flog_err_sys(
|
|
||||||
EC_LIB_SYSTEM_CALL,
|
|
||||||
"Log rotate failed: cannot open file %s for append: %s",
|
|
||||||
zl->filename, safe_strerror(save_errno));
|
|
||||||
ret = -1;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&loglock);
|
|
||||||
} else {
|
|
||||||
logfile_fd = fileno(zl->fp);
|
|
||||||
zl->maxlvl[ZLOG_DEST_FILE] = level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&loglock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wrapper around strerror to handle case where it returns NULL. */
|
/* Wrapper around strerror to handle case where it returns NULL. */
|
||||||
const char *safe_strerror(int errnum)
|
const char *safe_strerror(int errnum)
|
||||||
{
|
{
|
||||||
|
|
63
lib/log.h
63
lib/log.h
|
@ -22,21 +22,21 @@
|
||||||
#ifndef _ZEBRA_LOG_H
|
#ifndef _ZEBRA_LOG_H
|
||||||
#define _ZEBRA_LOG_H
|
#define _ZEBRA_LOG_H
|
||||||
|
|
||||||
|
#include "zassert.h"
|
||||||
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "lib/hook.h"
|
#include "lib/hook.h"
|
||||||
|
#include "lib/zlog.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Hook for external logging function */
|
|
||||||
DECLARE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args),
|
|
||||||
(priority, format, args));
|
|
||||||
|
|
||||||
/* Here is some guidance on logging levels to use:
|
/* Here is some guidance on logging levels to use:
|
||||||
*
|
*
|
||||||
* LOG_DEBUG - For all messages that are enabled by optional debugging
|
* LOG_DEBUG - For all messages that are enabled by optional debugging
|
||||||
|
@ -53,19 +53,7 @@ DECLARE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args),
|
||||||
* please use LOG_ERR instead.
|
* please use LOG_ERR instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent
|
extern void zlog_rotate(void);
|
||||||
to that logging destination. */
|
|
||||||
#define ZLOG_DISABLED (LOG_EMERG-1)
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ZLOG_DEST_SYSLOG = 0,
|
|
||||||
ZLOG_DEST_STDOUT,
|
|
||||||
ZLOG_DEST_MONITOR,
|
|
||||||
ZLOG_DEST_FILE
|
|
||||||
} zlog_dest_t;
|
|
||||||
#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1)
|
|
||||||
|
|
||||||
extern bool zlog_startup_stderr;
|
|
||||||
|
|
||||||
/* Message structure. */
|
/* Message structure. */
|
||||||
struct message {
|
struct message {
|
||||||
|
@ -73,22 +61,6 @@ struct message {
|
||||||
const char *str;
|
const char *str;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Open zlog function */
|
|
||||||
extern void openzlog(const char *progname, const char *protoname,
|
|
||||||
uint16_t instance, int syslog_options,
|
|
||||||
int syslog_facility);
|
|
||||||
|
|
||||||
/* Close zlog function. */
|
|
||||||
extern void closezlog(void);
|
|
||||||
|
|
||||||
/* Handy zlog functions. */
|
|
||||||
extern void zlog_err(const char *format, ...) PRINTFRR(1, 2);
|
|
||||||
extern void zlog_warn(const char *format, ...) PRINTFRR(1, 2);
|
|
||||||
extern void zlog_info(const char *format, ...) PRINTFRR(1, 2);
|
|
||||||
extern void zlog_notice(const char *format, ...) PRINTFRR(1, 2);
|
|
||||||
extern void zlog_debug(const char *format, ...) PRINTFRR(1, 2);
|
|
||||||
extern void zlog(int priority, const char *format, ...) PRINTFRR(2, 3);
|
|
||||||
|
|
||||||
/* For logs which have error codes associated with them */
|
/* For logs which have error codes associated with them */
|
||||||
#define flog_err(ferr_id, format, ...) \
|
#define flog_err(ferr_id, format, ...) \
|
||||||
zlog_err("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__)
|
zlog_err("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__)
|
||||||
|
@ -101,29 +73,6 @@ extern void zlog(int priority, const char *format, ...) PRINTFRR(2, 3);
|
||||||
|
|
||||||
extern void zlog_thread_info(int log_level);
|
extern void zlog_thread_info(int log_level);
|
||||||
|
|
||||||
/* Set logging level for the given destination. If the log_level
|
|
||||||
argument is ZLOG_DISABLED, then the destination is disabled.
|
|
||||||
This function should not be used for file logging (use zlog_set_file
|
|
||||||
or zlog_reset_file instead). */
|
|
||||||
extern void zlog_set_level(zlog_dest_t, int log_level);
|
|
||||||
|
|
||||||
/* Set logging to the given filename at the specified level. */
|
|
||||||
extern int zlog_set_file(const char *filename, int log_level);
|
|
||||||
/* Disable file logging. */
|
|
||||||
extern int zlog_reset_file(void);
|
|
||||||
|
|
||||||
/* Rotate log. */
|
|
||||||
extern int zlog_rotate(void);
|
|
||||||
|
|
||||||
#define ZLOG_FILTERS_MAX 100 /* Max # of filters at once */
|
|
||||||
#define ZLOG_FILTER_LENGTH_MAX 80 /* 80 character filter limit */
|
|
||||||
|
|
||||||
/* Add/Del/Dump log filters */
|
|
||||||
extern void zlog_filter_clear(void);
|
|
||||||
extern int zlog_filter_add(const char *filter);
|
|
||||||
extern int zlog_filter_del(const char *filter);
|
|
||||||
extern int zlog_filter_dump(char *buf, size_t max_size);
|
|
||||||
|
|
||||||
const char *lookup_msg(const struct message *mz, int kz, const char *nf);
|
const char *lookup_msg(const struct message *mz, int kz, const char *nf);
|
||||||
|
|
||||||
/* Safe version of strerror -- never returns NULL. */
|
/* Safe version of strerror -- never returns NULL. */
|
||||||
|
@ -176,8 +125,6 @@ extern int proto_redistnum(int afi, const char *s);
|
||||||
extern const char *zserv_command_string(unsigned int command);
|
extern const char *zserv_command_string(unsigned int command);
|
||||||
|
|
||||||
|
|
||||||
extern int vzlog_test(int priority);
|
|
||||||
|
|
||||||
/* structure useful for avoiding repeated rendering of the same timestamp */
|
/* structure useful for avoiding repeated rendering of the same timestamp */
|
||||||
struct timestamp_control {
|
struct timestamp_control {
|
||||||
size_t len; /* length of rendered timestamp */
|
size_t len; /* length of rendered timestamp */
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* Zebra logging funcions.
|
|
||||||
* 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 this program; see the file COPYING; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ZEBRA_LOG_PRIVATE_H
|
|
||||||
#define _ZEBRA_LOG_PRIVATE_H
|
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct zlog {
|
|
||||||
const char *ident; /* daemon name (first arg to openlog) */
|
|
||||||
const char *protoname;
|
|
||||||
unsigned short instance;
|
|
||||||
int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated
|
|
||||||
logging destination */
|
|
||||||
int default_lvl; /* maxlvl to use if none is specified */
|
|
||||||
FILE *fp;
|
|
||||||
char *filename;
|
|
||||||
int facility; /* as per syslog facility */
|
|
||||||
int record_priority; /* should messages logged through stdio include the
|
|
||||||
priority of the message? */
|
|
||||||
int syslog_options; /* 2nd arg to openlog */
|
|
||||||
int timestamp_precision; /* # of digits of subsecond precision */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Default logging strucutre. */
|
|
||||||
extern struct zlog *zlog_default;
|
|
||||||
|
|
||||||
extern const char *zlog_priority[];
|
|
||||||
|
|
||||||
/* Generic function for zlog. */
|
|
||||||
extern void vzlog(int priority, const char *format, va_list args);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _ZEBRA_LOG_PRIVATE_H */
|
|
615
lib/log_vty.c
615
lib/log_vty.c
|
@ -22,76 +22,587 @@
|
||||||
|
|
||||||
#include "lib/log_vty.h"
|
#include "lib/log_vty.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "lib/vty.h"
|
|
||||||
#include "lib/log.h"
|
#include "lib/log.h"
|
||||||
|
#include "lib/zlog_targets.h"
|
||||||
|
#include "lib/lib_errors.h"
|
||||||
|
#include "lib/printfrr.h"
|
||||||
|
|
||||||
#ifndef VTYSH_EXTRACT_PL
|
#ifndef VTYSH_EXTRACT_PL
|
||||||
#include "lib/log_vty_clippy.c"
|
#include "lib/log_vty_clippy.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DEFPY (log_filter,
|
#define ZLOG_MAXLVL(a, b) MAX(a, b)
|
||||||
log_filter_cmd,
|
|
||||||
"[no] log-filter WORD$filter",
|
|
||||||
NO_STR
|
|
||||||
FILTER_LOG_STR
|
|
||||||
"String to filter by\n")
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (no)
|
DEFINE_HOOK(zlog_rotate, (), ())
|
||||||
ret = zlog_filter_del(filter);
|
|
||||||
|
static const int log_default_lvl = LOG_DEBUG;
|
||||||
|
|
||||||
|
static int log_config_stdout_lvl = ZLOG_DISABLED;
|
||||||
|
static int log_config_syslog_lvl = ZLOG_DISABLED;
|
||||||
|
static int log_cmdline_stdout_lvl = ZLOG_DISABLED;
|
||||||
|
static int log_cmdline_syslog_lvl = ZLOG_DISABLED;
|
||||||
|
|
||||||
|
static struct zlog_cfg_file zt_file_cmdline = {
|
||||||
|
.prio_min = ZLOG_DISABLED,
|
||||||
|
};
|
||||||
|
static struct zlog_cfg_file zt_file = {
|
||||||
|
.prio_min = ZLOG_DISABLED,
|
||||||
|
};
|
||||||
|
static struct zlog_cfg_file zt_stdout = {
|
||||||
|
.prio_min = ZLOG_DISABLED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *zlog_progname;
|
||||||
|
static const char *zlog_protoname;
|
||||||
|
|
||||||
|
static const struct facility_map {
|
||||||
|
int facility;
|
||||||
|
const char *name;
|
||||||
|
size_t match;
|
||||||
|
} syslog_facilities[] = {
|
||||||
|
{LOG_KERN, "kern", 1},
|
||||||
|
{LOG_USER, "user", 2},
|
||||||
|
{LOG_MAIL, "mail", 1},
|
||||||
|
{LOG_DAEMON, "daemon", 1},
|
||||||
|
{LOG_AUTH, "auth", 1},
|
||||||
|
{LOG_SYSLOG, "syslog", 1},
|
||||||
|
{LOG_LPR, "lpr", 2},
|
||||||
|
{LOG_NEWS, "news", 1},
|
||||||
|
{LOG_UUCP, "uucp", 2},
|
||||||
|
{LOG_CRON, "cron", 1},
|
||||||
|
#ifdef LOG_FTP
|
||||||
|
{LOG_FTP, "ftp", 1},
|
||||||
|
#endif
|
||||||
|
{LOG_LOCAL0, "local0", 6},
|
||||||
|
{LOG_LOCAL1, "local1", 6},
|
||||||
|
{LOG_LOCAL2, "local2", 6},
|
||||||
|
{LOG_LOCAL3, "local3", 6},
|
||||||
|
{LOG_LOCAL4, "local4", 6},
|
||||||
|
{LOG_LOCAL5, "local5", 6},
|
||||||
|
{LOG_LOCAL6, "local6", 6},
|
||||||
|
{LOG_LOCAL7, "local7", 6},
|
||||||
|
{0, NULL, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const zlog_priority[] = {
|
||||||
|
"emergencies", "alerts", "critical", "errors", "warnings",
|
||||||
|
"notifications", "informational", "debugging", NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *facility_name(int facility)
|
||||||
|
{
|
||||||
|
const struct facility_map *fm;
|
||||||
|
|
||||||
|
for (fm = syslog_facilities; fm->name; fm++)
|
||||||
|
if (fm->facility == facility)
|
||||||
|
return fm->name;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int facility_match(const char *str)
|
||||||
|
{
|
||||||
|
const struct facility_map *fm;
|
||||||
|
|
||||||
|
for (fm = syslog_facilities; fm->name; fm++)
|
||||||
|
if (!strncmp(str, fm->name, fm->match))
|
||||||
|
return fm->facility;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_level_match(const char *s)
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
|
||||||
|
for (level = 0; zlog_priority[level] != NULL; level++)
|
||||||
|
if (!strncmp(s, zlog_priority[level], 2))
|
||||||
|
return level;
|
||||||
|
return ZLOG_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_rotate(void)
|
||||||
|
{
|
||||||
|
zlog_file_rotate(&zt_file);
|
||||||
|
hook_call(zlog_rotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_show_syslog(struct vty *vty)
|
||||||
|
{
|
||||||
|
int level = zlog_syslog_get_prio_min();
|
||||||
|
|
||||||
|
vty_out(vty, "Syslog logging: ");
|
||||||
|
if (level == ZLOG_DISABLED)
|
||||||
|
vty_out(vty, "disabled\n");
|
||||||
else
|
else
|
||||||
ret = zlog_filter_add(filter);
|
vty_out(vty, "level %s, facility %s, ident %s\n",
|
||||||
|
zlog_priority[level],
|
||||||
if (ret == 1) {
|
facility_name(zlog_syslog_get_facility()),
|
||||||
vty_out(vty, "%% filter table full\n");
|
zlog_progname);
|
||||||
return CMD_WARNING;
|
|
||||||
} else if (ret != 0) {
|
|
||||||
vty_out(vty, "%% failed to %s log filter\n",
|
|
||||||
(no ? "remove" : "apply"));
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vty_out(vty, " %s\n", filter);
|
DEFUN (show_logging,
|
||||||
return CMD_SUCCESS;
|
show_logging_cmd,
|
||||||
}
|
"show logging",
|
||||||
|
|
||||||
/* Clear all log filters */
|
|
||||||
DEFPY (log_filter_clear,
|
|
||||||
log_filter_clear_cmd,
|
|
||||||
"clear log-filter",
|
|
||||||
CLEAR_STR
|
|
||||||
FILTER_LOG_STR)
|
|
||||||
{
|
|
||||||
zlog_filter_clear();
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show log filter */
|
|
||||||
DEFPY (show_log_filter,
|
|
||||||
show_log_filter_cmd,
|
|
||||||
"show log-filter",
|
|
||||||
SHOW_STR
|
SHOW_STR
|
||||||
FILTER_LOG_STR)
|
"Show current logging configuration\n")
|
||||||
{
|
{
|
||||||
char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = "";
|
log_show_syslog(vty);
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
len = zlog_filter_dump(log_filters, sizeof(log_filters));
|
vty_out(vty, "Stdout logging: ");
|
||||||
|
if (zt_stdout.prio_min == ZLOG_DISABLED)
|
||||||
|
vty_out(vty, "disabled");
|
||||||
|
else
|
||||||
|
vty_out(vty, "level %s",
|
||||||
|
zlog_priority[zt_stdout.prio_min]);
|
||||||
|
vty_out(vty, "\n");
|
||||||
|
|
||||||
if (len == -1) {
|
vty_out(vty, "File logging: ");
|
||||||
vty_out(vty, "%% failed to get filters\n");
|
if (zt_file.prio_min == ZLOG_DISABLED || !zt_file.filename)
|
||||||
return CMD_WARNING;
|
vty_out(vty, "disabled");
|
||||||
|
else
|
||||||
|
vty_out(vty, "level %s, filename %s",
|
||||||
|
zlog_priority[zt_file.prio_min], zt_file.filename);
|
||||||
|
vty_out(vty, "\n");
|
||||||
|
|
||||||
|
if (log_cmdline_syslog_lvl != ZLOG_DISABLED)
|
||||||
|
vty_out(vty,
|
||||||
|
"From command line: \"--log syslog --log-level %s\"\n",
|
||||||
|
zlog_priority[log_cmdline_syslog_lvl]);
|
||||||
|
if (log_cmdline_stdout_lvl != ZLOG_DISABLED)
|
||||||
|
vty_out(vty,
|
||||||
|
"From command line: \"--log stdout --log-level %s\"\n",
|
||||||
|
zlog_priority[log_cmdline_stdout_lvl]);
|
||||||
|
if (zt_file_cmdline.prio_min != ZLOG_DISABLED)
|
||||||
|
vty_out(vty,
|
||||||
|
"From command line: \"--log file:%s --log-level %s\"\n",
|
||||||
|
zt_file_cmdline.filename,
|
||||||
|
zlog_priority[zt_file_cmdline.prio_min]);
|
||||||
|
|
||||||
|
vty_out(vty, "Protocol name: %s\n", zlog_protoname);
|
||||||
|
vty_out(vty, "Record priority: %s\n",
|
||||||
|
(zt_file.record_priority ? "enabled" : "disabled"));
|
||||||
|
vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec);
|
||||||
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len != 0)
|
DEFPY (config_log_stdout,
|
||||||
vty_out(vty, "%s", log_filters);
|
config_log_stdout_cmd,
|
||||||
|
"log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
|
||||||
|
"Logging control\n"
|
||||||
|
"Set stdout logging level\n"
|
||||||
|
LOG_LEVEL_DESC)
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
|
||||||
|
if (levelarg) {
|
||||||
|
level = log_level_match(levelarg);
|
||||||
|
if (level == ZLOG_DISABLED)
|
||||||
|
return CMD_ERR_NO_MATCH;
|
||||||
|
} else
|
||||||
|
level = log_default_lvl;
|
||||||
|
|
||||||
|
log_config_stdout_lvl = level;
|
||||||
|
zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
|
||||||
|
log_cmdline_stdout_lvl);
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (no_config_log_stdout,
|
||||||
|
no_config_log_stdout_cmd,
|
||||||
|
"no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
||||||
|
NO_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Cancel logging to stdout\n"
|
||||||
|
LOG_LEVEL_DESC)
|
||||||
|
{
|
||||||
|
log_config_stdout_lvl = ZLOG_DISABLED;
|
||||||
|
zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
|
||||||
|
log_cmdline_stdout_lvl);
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN_HIDDEN (config_log_monitor,
|
||||||
|
config_log_monitor_cmd,
|
||||||
|
"log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
||||||
|
"Logging control\n"
|
||||||
|
"Set terminal line (monitor) logging level\n"
|
||||||
|
LOG_LEVEL_DESC)
|
||||||
|
{
|
||||||
|
vty_out(vty, "%% \"log monitor\" is deprecated and does nothing.\n");
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN_HIDDEN (no_config_log_monitor,
|
||||||
|
no_config_log_monitor_cmd,
|
||||||
|
"no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
||||||
|
NO_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Disable terminal line (monitor) logging\n"
|
||||||
|
LOG_LEVEL_DESC)
|
||||||
|
{
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_log_file(struct zlog_cfg_file *target, struct vty *vty,
|
||||||
|
const char *fname, int loglevel)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
const char *fullpath;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
/* Path detection. */
|
||||||
|
if (!IS_DIRECTORY_SEP(*fname)) {
|
||||||
|
char cwd[MAXPATHLEN + 1];
|
||||||
|
|
||||||
|
cwd[MAXPATHLEN] = '\0';
|
||||||
|
|
||||||
|
if (getcwd(cwd, MAXPATHLEN) == NULL) {
|
||||||
|
flog_err_sys(EC_LIB_SYSTEM_CALL,
|
||||||
|
"config_log_file: Unable to alloc mem!");
|
||||||
|
return CMD_WARNING_CONFIG_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2);
|
||||||
|
sprintf(p, "%s/%s", cwd, fname);
|
||||||
|
fullpath = p;
|
||||||
|
} else
|
||||||
|
fullpath = fname;
|
||||||
|
|
||||||
|
target->prio_min = loglevel;
|
||||||
|
ok = zlog_file_set_filename(target, fullpath);
|
||||||
|
|
||||||
|
XFREE(MTYPE_TMP, p);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
if (vty)
|
||||||
|
vty_out(vty, "can't open logfile %s\n", fname);
|
||||||
|
return CMD_WARNING_CONFIG_FAILED;
|
||||||
|
}
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void command_setup_early_logging(const char *dest, const char *level)
|
||||||
|
{
|
||||||
|
int nlevel;
|
||||||
|
char *sep;
|
||||||
|
int len;
|
||||||
|
char type[8];
|
||||||
|
|
||||||
|
if (level) {
|
||||||
|
nlevel = log_level_match(level);
|
||||||
|
|
||||||
|
if (nlevel == ZLOG_DISABLED) {
|
||||||
|
fprintf(stderr, "invalid log level \"%s\"\n", level);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
nlevel = log_default_lvl;
|
||||||
|
|
||||||
|
if (!dest)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sep = strchr(dest, ':');
|
||||||
|
len = sep ? (int)(sep - dest) : (int)strlen(dest);
|
||||||
|
|
||||||
|
snprintfrr(type, sizeof(type), "%.*s", len, dest);
|
||||||
|
|
||||||
|
if (strcmp(type, "stdout") == 0) {
|
||||||
|
log_cmdline_stdout_lvl = nlevel;
|
||||||
|
zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
|
||||||
|
log_cmdline_stdout_lvl);
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(type, "syslog") == 0) {
|
||||||
|
log_cmdline_syslog_lvl = nlevel;
|
||||||
|
zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
|
||||||
|
log_cmdline_syslog_lvl));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(type, "file") == 0 && sep) {
|
||||||
|
sep++;
|
||||||
|
set_log_file(&zt_file_cmdline, NULL, sep, nlevel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (clear_log_cmdline,
|
||||||
|
clear_log_cmdline_cmd,
|
||||||
|
"clear log cmdline-targets",
|
||||||
|
CLEAR_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Disable log targets specified at startup by --log option\n")
|
||||||
|
{
|
||||||
|
zt_file_cmdline.prio_min = ZLOG_DISABLED;
|
||||||
|
zlog_file_set_other(&zt_file_cmdline);
|
||||||
|
|
||||||
|
log_cmdline_syslog_lvl = ZLOG_DISABLED;
|
||||||
|
zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
|
||||||
|
log_cmdline_syslog_lvl));
|
||||||
|
|
||||||
|
log_cmdline_stdout_lvl = ZLOG_DISABLED;
|
||||||
|
zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
|
||||||
|
log_cmdline_stdout_lvl);
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_filter_cmd_init(void)
|
DEFPY (config_log_file,
|
||||||
|
config_log_file_cmd,
|
||||||
|
"log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
|
||||||
|
"Logging control\n"
|
||||||
|
"Logging to file\n"
|
||||||
|
"Logging filename\n"
|
||||||
|
LOG_LEVEL_DESC)
|
||||||
{
|
{
|
||||||
install_element(VIEW_NODE, &show_log_filter_cmd);
|
int level = log_default_lvl;
|
||||||
install_element(CONFIG_NODE, &log_filter_cmd);
|
|
||||||
install_element(CONFIG_NODE, &log_filter_clear_cmd);
|
if (levelarg) {
|
||||||
|
level = log_level_match(levelarg);
|
||||||
|
if (level == ZLOG_DISABLED)
|
||||||
|
return CMD_ERR_NO_MATCH;
|
||||||
|
}
|
||||||
|
return set_log_file(&zt_file, vty, filename, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (no_config_log_file,
|
||||||
|
no_config_log_file_cmd,
|
||||||
|
"no log file [FILENAME [LEVEL]]",
|
||||||
|
NO_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Cancel logging to file\n"
|
||||||
|
"Logging file name\n"
|
||||||
|
"Logging level\n")
|
||||||
|
{
|
||||||
|
zt_file.prio_min = ZLOG_DISABLED;
|
||||||
|
zlog_file_set_other(&zt_file);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFPY (config_log_syslog,
|
||||||
|
config_log_syslog_cmd,
|
||||||
|
"log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
|
||||||
|
"Logging control\n"
|
||||||
|
"Set syslog logging level\n"
|
||||||
|
LOG_LEVEL_DESC)
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
|
||||||
|
if (levelarg) {
|
||||||
|
level = log_level_match(levelarg);
|
||||||
|
|
||||||
|
if (level == ZLOG_DISABLED)
|
||||||
|
return CMD_ERR_NO_MATCH;
|
||||||
|
} else
|
||||||
|
level = log_default_lvl;
|
||||||
|
|
||||||
|
log_config_syslog_lvl = level;
|
||||||
|
zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
|
||||||
|
log_cmdline_syslog_lvl));
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (no_config_log_syslog,
|
||||||
|
no_config_log_syslog_cmd,
|
||||||
|
"no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
|
||||||
|
NO_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Cancel logging to syslog\n"
|
||||||
|
LOG_FACILITY_DESC
|
||||||
|
LOG_LEVEL_DESC)
|
||||||
|
{
|
||||||
|
log_config_syslog_lvl = ZLOG_DISABLED;
|
||||||
|
zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
|
||||||
|
log_cmdline_syslog_lvl));
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFPY (config_log_facility,
|
||||||
|
config_log_facility_cmd,
|
||||||
|
"log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>$facilityarg",
|
||||||
|
"Logging control\n"
|
||||||
|
"Facility parameter for syslog messages\n"
|
||||||
|
LOG_FACILITY_DESC)
|
||||||
|
{
|
||||||
|
int facility = facility_match(facilityarg);
|
||||||
|
|
||||||
|
zlog_syslog_set_facility(facility);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (no_config_log_facility,
|
||||||
|
no_config_log_facility_cmd,
|
||||||
|
"no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
|
||||||
|
NO_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Reset syslog facility to default (daemon)\n"
|
||||||
|
LOG_FACILITY_DESC)
|
||||||
|
{
|
||||||
|
zlog_syslog_set_facility(LOG_DAEMON);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (config_log_record_priority,
|
||||||
|
config_log_record_priority_cmd,
|
||||||
|
"log record-priority",
|
||||||
|
"Logging control\n"
|
||||||
|
"Log the priority of the message within the message\n")
|
||||||
|
{
|
||||||
|
zt_file.record_priority = true;
|
||||||
|
zlog_file_set_other(&zt_file);
|
||||||
|
zt_stdout.record_priority = true;
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (no_config_log_record_priority,
|
||||||
|
no_config_log_record_priority_cmd,
|
||||||
|
"no log record-priority",
|
||||||
|
NO_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Do not log the priority of the message within the message\n")
|
||||||
|
{
|
||||||
|
zt_file.record_priority = false;
|
||||||
|
zlog_file_set_other(&zt_file);
|
||||||
|
zt_stdout.record_priority = false;
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFPY (config_log_timestamp_precision,
|
||||||
|
config_log_timestamp_precision_cmd,
|
||||||
|
"log timestamp precision (0-6)",
|
||||||
|
"Logging control\n"
|
||||||
|
"Timestamp configuration\n"
|
||||||
|
"Set the timestamp precision\n"
|
||||||
|
"Number of subsecond digits\n")
|
||||||
|
{
|
||||||
|
zt_file.ts_subsec = precision;
|
||||||
|
zlog_file_set_other(&zt_file);
|
||||||
|
zt_stdout.ts_subsec = precision;
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN (no_config_log_timestamp_precision,
|
||||||
|
no_config_log_timestamp_precision_cmd,
|
||||||
|
"no log timestamp precision [(0-6)]",
|
||||||
|
NO_STR
|
||||||
|
"Logging control\n"
|
||||||
|
"Timestamp configuration\n"
|
||||||
|
"Reset the timestamp precision to the default value of 0\n"
|
||||||
|
"Number of subsecond digits\n")
|
||||||
|
{
|
||||||
|
zt_file.ts_subsec = 0;
|
||||||
|
zlog_file_set_other(&zt_file);
|
||||||
|
zt_stdout.ts_subsec = 0;
|
||||||
|
zlog_file_set_other(&zt_stdout);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_config_write(struct vty *vty)
|
||||||
|
{
|
||||||
|
bool show_cmdline_hint = false;
|
||||||
|
|
||||||
|
if (zt_file.prio_min != ZLOG_DISABLED && zt_file.filename) {
|
||||||
|
vty_out(vty, "log file %s", zt_file.filename);
|
||||||
|
|
||||||
|
if (zt_file.prio_min != log_default_lvl)
|
||||||
|
vty_out(vty, " %s", zlog_priority[zt_file.prio_min]);
|
||||||
|
vty_out(vty, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_config_stdout_lvl != ZLOG_DISABLED) {
|
||||||
|
vty_out(vty, "log stdout");
|
||||||
|
|
||||||
|
if (log_config_stdout_lvl != log_default_lvl)
|
||||||
|
vty_out(vty, " %s",
|
||||||
|
zlog_priority[log_config_stdout_lvl]);
|
||||||
|
vty_out(vty, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_config_syslog_lvl != ZLOG_DISABLED) {
|
||||||
|
vty_out(vty, "log syslog");
|
||||||
|
|
||||||
|
if (log_config_syslog_lvl != log_default_lvl)
|
||||||
|
vty_out(vty, " %s",
|
||||||
|
zlog_priority[log_config_syslog_lvl]);
|
||||||
|
vty_out(vty, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_cmdline_syslog_lvl != ZLOG_DISABLED) {
|
||||||
|
vty_out(vty,
|
||||||
|
"! \"log syslog %s\" enabled by \"--log\" startup option\n",
|
||||||
|
zlog_priority[log_cmdline_syslog_lvl]);
|
||||||
|
show_cmdline_hint = true;
|
||||||
|
}
|
||||||
|
if (log_cmdline_stdout_lvl != ZLOG_DISABLED) {
|
||||||
|
vty_out(vty,
|
||||||
|
"! \"log stdout %s\" enabled by \"--log\" startup option\n",
|
||||||
|
zlog_priority[log_cmdline_stdout_lvl]);
|
||||||
|
show_cmdline_hint = true;
|
||||||
|
}
|
||||||
|
if (zt_file_cmdline.prio_min != ZLOG_DISABLED) {
|
||||||
|
vty_out(vty,
|
||||||
|
"! \"log file %s %s\" enabled by \"--log\" startup option\n",
|
||||||
|
zt_file_cmdline.filename,
|
||||||
|
zlog_priority[zt_file_cmdline.prio_min]);
|
||||||
|
show_cmdline_hint = true;
|
||||||
|
}
|
||||||
|
if (show_cmdline_hint)
|
||||||
|
vty_out(vty,
|
||||||
|
"! use \"clear log cmdline-targets\" to remove this target\n");
|
||||||
|
|
||||||
|
if (zlog_syslog_get_facility() != LOG_DAEMON)
|
||||||
|
vty_out(vty, "log facility %s\n",
|
||||||
|
facility_name(zlog_syslog_get_facility()));
|
||||||
|
|
||||||
|
if (zt_file.record_priority == 1)
|
||||||
|
vty_out(vty, "log record-priority\n");
|
||||||
|
|
||||||
|
if (zt_file.ts_subsec > 0)
|
||||||
|
vty_out(vty, "log timestamp precision %d\n",
|
||||||
|
zt_file.ts_subsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log_vty_init(const char *progname, const char *protoname,
|
||||||
|
unsigned short instance, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
zlog_progname = progname;
|
||||||
|
zlog_protoname = protoname;
|
||||||
|
|
||||||
|
zlog_file_set_fd(&zt_stdout, STDOUT_FILENO);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((_CONSTRUCTOR(475))) static void log_vty_preinit(void)
|
||||||
|
{
|
||||||
|
hook_register(zlog_init, log_vty_init);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_cmd_init(void)
|
||||||
|
{
|
||||||
|
install_element(VIEW_NODE, &show_logging_cmd);
|
||||||
|
install_element(ENABLE_NODE, &clear_log_cmdline_cmd);
|
||||||
|
|
||||||
|
install_element(CONFIG_NODE, &config_log_stdout_cmd);
|
||||||
|
install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
|
||||||
|
install_element(CONFIG_NODE, &config_log_monitor_cmd);
|
||||||
|
install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
|
||||||
|
install_element(CONFIG_NODE, &config_log_file_cmd);
|
||||||
|
install_element(CONFIG_NODE, &no_config_log_file_cmd);
|
||||||
|
install_element(CONFIG_NODE, &config_log_syslog_cmd);
|
||||||
|
install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
|
||||||
|
install_element(CONFIG_NODE, &config_log_facility_cmd);
|
||||||
|
install_element(CONFIG_NODE, &no_config_log_facility_cmd);
|
||||||
|
install_element(CONFIG_NODE, &config_log_record_priority_cmd);
|
||||||
|
install_element(CONFIG_NODE, &no_config_log_record_priority_cmd);
|
||||||
|
install_element(CONFIG_NODE, &config_log_timestamp_precision_cmd);
|
||||||
|
install_element(CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,17 @@
|
||||||
|
|
||||||
#ifndef __LOG_VTY_H__
|
#ifndef __LOG_VTY_H__
|
||||||
#define __LOG_VTY_H__
|
#define __LOG_VTY_H__
|
||||||
extern void log_filter_cmd_init(void);
|
|
||||||
|
#include "lib/hook.h"
|
||||||
|
|
||||||
|
struct vty;
|
||||||
|
|
||||||
|
extern void log_cmd_init(void);
|
||||||
|
extern void log_config_write(struct vty *vty);
|
||||||
|
extern int log_level_match(const char *s);
|
||||||
|
extern void log_show_syslog(struct vty *vty);
|
||||||
|
|
||||||
|
DECLARE_HOOK(zlog_rotate, (), ())
|
||||||
|
extern void zlog_rotate(void);
|
||||||
|
|
||||||
#endif /* __LOG_VTY_H__ */
|
#endif /* __LOG_VTY_H__ */
|
||||||
|
|
|
@ -100,6 +100,8 @@ lib_libfrr_la_SOURCES = \
|
||||||
lib/yang_translator.c \
|
lib/yang_translator.c \
|
||||||
lib/yang_wrappers.c \
|
lib/yang_wrappers.c \
|
||||||
lib/zclient.c \
|
lib/zclient.c \
|
||||||
|
lib/zlog.c \
|
||||||
|
lib/zlog_targets.c \
|
||||||
lib/printf/printf-pos.c \
|
lib/printf/printf-pos.c \
|
||||||
lib/printf/vfprintf.c \
|
lib/printf/vfprintf.c \
|
||||||
lib/printf/glue.c \
|
lib/printf/glue.c \
|
||||||
|
@ -254,6 +256,8 @@ pkginclude_HEADERS += \
|
||||||
lib/zassert.h \
|
lib/zassert.h \
|
||||||
lib/zclient.h \
|
lib/zclient.h \
|
||||||
lib/zebra.h \
|
lib/zebra.h \
|
||||||
|
lib/zlog.h \
|
||||||
|
lib/zlog_targets.h \
|
||||||
lib/pbr.h \
|
lib/pbr.h \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
|
@ -265,7 +269,6 @@ nodist_pkginclude_HEADERS += \
|
||||||
|
|
||||||
noinst_HEADERS += \
|
noinst_HEADERS += \
|
||||||
lib/clippy.h \
|
lib/clippy.h \
|
||||||
lib/log_int.h \
|
|
||||||
lib/plist_int.h \
|
lib/plist_int.h \
|
||||||
lib/printf/printfcommon.h \
|
lib/printf/printfcommon.h \
|
||||||
lib/printf/printflocal.h \
|
lib/printf/printflocal.h \
|
||||||
|
|
|
@ -724,6 +724,7 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
|
||||||
< 0) // effect a poll (return immediately)
|
< 0) // effect a poll (return immediately)
|
||||||
timeout = 0;
|
timeout = 0;
|
||||||
|
|
||||||
|
zlog_tls_buffer_flush();
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
rcu_assert_read_unlocked();
|
rcu_assert_read_unlocked();
|
||||||
|
|
||||||
|
|
702
lib/zlog.c
Normal file
702
lib/zlog.c
Normal file
|
@ -0,0 +1,702 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "zebra.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* gettid() & co. */
|
||||||
|
#ifdef HAVE_PTHREAD_NP_H
|
||||||
|
#include <pthread_np.h>
|
||||||
|
#endif
|
||||||
|
#ifdef linux
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#include <sys/thr.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __NetBSD__
|
||||||
|
#include <lwp.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __DragonFly__
|
||||||
|
#include <sys/lwp.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <mach/mach_traps.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "atomlist.h"
|
||||||
|
#include "printfrr.h"
|
||||||
|
#include "frrcu.h"
|
||||||
|
#include "zlog.h"
|
||||||
|
|
||||||
|
DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message")
|
||||||
|
DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer")
|
||||||
|
|
||||||
|
DEFINE_HOOK(zlog_init, (const char *progname, const char *protoname,
|
||||||
|
unsigned short instance, uid_t uid, gid_t gid),
|
||||||
|
(progname, protoname, instance, uid, gid))
|
||||||
|
DEFINE_KOOH(zlog_fini, (), ())
|
||||||
|
DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
|
||||||
|
(prefix, prio_min))
|
||||||
|
|
||||||
|
char zlog_prefix[128];
|
||||||
|
size_t zlog_prefixsz;
|
||||||
|
int zlog_tmpdirfd = -1;
|
||||||
|
|
||||||
|
/* these are kept around because logging is initialized (and directories
|
||||||
|
* & files created) before zprivs code switches to the FRR user; therefore
|
||||||
|
* we need to chown() things so we don't get permission errors later when
|
||||||
|
* trying to delete things on shutdown
|
||||||
|
*/
|
||||||
|
static uid_t zlog_uid = -1;
|
||||||
|
static gid_t zlog_gid = -1;
|
||||||
|
|
||||||
|
DECLARE_ATOMLIST(zlog_targets, struct zlog_target, head);
|
||||||
|
static struct zlog_targets_head zlog_targets;
|
||||||
|
|
||||||
|
/* cf. zlog.h for additional comments on this struct.
|
||||||
|
*
|
||||||
|
* Note: you MUST NOT pass the format string + va_list to non-FRR format
|
||||||
|
* string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an
|
||||||
|
* extended prinf() with additional formats (%pI4 and the like).
|
||||||
|
*
|
||||||
|
* Also remember to use va_copy() on args.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct zlog_msg {
|
||||||
|
struct timespec ts;
|
||||||
|
int prio;
|
||||||
|
|
||||||
|
const char *fmt;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
char *stackbuf;
|
||||||
|
size_t stackbufsz;
|
||||||
|
char *text;
|
||||||
|
size_t textlen;
|
||||||
|
|
||||||
|
/* This is always ISO8601 with sub-second precision 9 here, it's
|
||||||
|
* converted for callers as needed. ts_dot points to the "."
|
||||||
|
* separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the
|
||||||
|
* local time offset.
|
||||||
|
*
|
||||||
|
* Valid if ZLOG_TS_ISO8601 is set.
|
||||||
|
* (0 if timestamp has not been formatted yet)
|
||||||
|
*/
|
||||||
|
uint32_t ts_flags;
|
||||||
|
char ts_str[32], *ts_dot, ts_zonetail[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* thread-local log message buffering
|
||||||
|
*
|
||||||
|
* This is strictly optional and set up by calling zlog_tls_buffer_init()
|
||||||
|
* on a particular thread.
|
||||||
|
*
|
||||||
|
* If in use, this will create a temporary file in /var/tmp which is used as
|
||||||
|
* memory-mapped MAP_SHARED log message buffer. The idea there is that buffer
|
||||||
|
* access doesn't require any syscalls, but in case of a crash the kernel
|
||||||
|
* knows to sync the memory back to disk. This way the user can still get the
|
||||||
|
* last log messages if there were any left unwritten in the buffer.
|
||||||
|
*
|
||||||
|
* Sizing this dynamically isn't particularly useful, so here's an 8k buffer
|
||||||
|
* with a message limit of 64 messages. Message metadata (e.g. priority,
|
||||||
|
* timestamp) aren't in the mmap region, so they're lost on crash, but we can
|
||||||
|
* live with that.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
|
||||||
|
#define CAN_DO_TLS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TLS_LOG_BUF_SIZE 8192
|
||||||
|
#define TLS_LOG_MAXMSG 64
|
||||||
|
|
||||||
|
struct zlog_tls {
|
||||||
|
char *mmbuf;
|
||||||
|
size_t bufpos;
|
||||||
|
|
||||||
|
size_t nmsgs;
|
||||||
|
struct zlog_msg msgs[TLS_LOG_MAXMSG];
|
||||||
|
struct zlog_msg *msgp[TLS_LOG_MAXMSG];
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void zlog_tls_free(void *arg);
|
||||||
|
|
||||||
|
/* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
|
||||||
|
* available we'll use it here
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
static pthread_key_t zlog_tls_key;
|
||||||
|
|
||||||
|
static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
|
||||||
|
static void zlog_tls_key_init(void)
|
||||||
|
{
|
||||||
|
pthread_key_create(&zlog_tls_key, zlog_tls_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
|
||||||
|
static void zlog_tls_key_fini(void)
|
||||||
|
{
|
||||||
|
pthread_key_delete(zlog_tls_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct zlog_tls *zlog_tls_get(void)
|
||||||
|
{
|
||||||
|
return pthread_getspecific(zlog_tls_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void zlog_tls_set(struct zlog_tls *val)
|
||||||
|
{
|
||||||
|
pthread_setspecific(zlog_tls_key, val);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# ifndef thread_local
|
||||||
|
# define thread_local __thread
|
||||||
|
# endif
|
||||||
|
|
||||||
|
static thread_local struct zlog_tls *zlog_tls_var
|
||||||
|
__attribute__((tls_model("initial-exec")));
|
||||||
|
|
||||||
|
static inline struct zlog_tls *zlog_tls_get(void)
|
||||||
|
{
|
||||||
|
return zlog_tls_var;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void zlog_tls_set(struct zlog_tls *val)
|
||||||
|
{
|
||||||
|
zlog_tls_var = val;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CAN_DO_TLS
|
||||||
|
static long zlog_gettid(void)
|
||||||
|
{
|
||||||
|
long rv = -1;
|
||||||
|
#ifdef HAVE_PTHREAD_GETTHREADID_NP
|
||||||
|
rv = pthread_getthreadid_np();
|
||||||
|
#elif defined(linux)
|
||||||
|
rv = syscall(__NR_gettid);
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
rv = _lwp_self();
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
thr_self(&rv);
|
||||||
|
#elif defined(__DragonFly__)
|
||||||
|
rv = lwp_gettid();
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
rv = getthrid();
|
||||||
|
#elif defined(__sun)
|
||||||
|
rv = pthread_self();
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
rv = mach_thread_self();
|
||||||
|
mach_port_deallocate(mach_task_self(), rv);
|
||||||
|
#endif
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_tls_buffer_init(void)
|
||||||
|
{
|
||||||
|
struct zlog_tls *zlog_tls;
|
||||||
|
char mmpath[MAXPATHLEN];
|
||||||
|
int mmfd;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
zlog_tls = zlog_tls_get();
|
||||||
|
|
||||||
|
if (zlog_tls || zlog_tmpdirfd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
zlog_tls = XCALLOC(MTYPE_LOG_TLSBUF, sizeof(*zlog_tls));
|
||||||
|
for (i = 0; i < array_size(zlog_tls->msgp); i++)
|
||||||
|
zlog_tls->msgp[i] = &zlog_tls->msgs[i];
|
||||||
|
|
||||||
|
snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
|
||||||
|
|
||||||
|
mmfd = openat(zlog_tmpdirfd, mmpath,
|
||||||
|
O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
|
||||||
|
fchown(mmfd, zlog_uid, zlog_gid);
|
||||||
|
|
||||||
|
if (mmfd < 0) {
|
||||||
|
zlog_err("failed to open thread log buffer \"%s\": %s",
|
||||||
|
mmpath, strerror(errno));
|
||||||
|
goto out_anon;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_POSIX_FALLOCATE
|
||||||
|
if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) < 0) {
|
||||||
|
#else
|
||||||
|
if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
|
||||||
|
#endif
|
||||||
|
zlog_err("failed to allocate thread log buffer \"%s\": %s",
|
||||||
|
mmpath, strerror(errno));
|
||||||
|
goto out_anon_unlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, mmfd, 0);
|
||||||
|
if (zlog_tls->mmbuf == MAP_FAILED) {
|
||||||
|
zlog_err("failed to mmap thread log buffer \"%s\": %s",
|
||||||
|
mmpath, strerror(errno));
|
||||||
|
goto out_anon_unlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(mmfd);
|
||||||
|
zlog_tls_set(zlog_tls);
|
||||||
|
return;
|
||||||
|
|
||||||
|
out_anon_unlink:
|
||||||
|
unlink(mmpath);
|
||||||
|
close(mmfd);
|
||||||
|
out_anon:
|
||||||
|
|
||||||
|
#ifndef MAP_ANONYMOUS
|
||||||
|
#define MAP_ANONYMOUS MAP_ANON
|
||||||
|
#endif
|
||||||
|
zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
|
|
||||||
|
if (!zlog_tls->mmbuf) {
|
||||||
|
zlog_err("failed to anonymous-mmap thread log buffer: %s",
|
||||||
|
strerror(errno));
|
||||||
|
XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
|
||||||
|
zlog_tls_set(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zlog_tls_set(zlog_tls);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_tls_buffer_fini(void)
|
||||||
|
{
|
||||||
|
char mmpath[MAXPATHLEN];
|
||||||
|
|
||||||
|
zlog_tls_buffer_flush();
|
||||||
|
|
||||||
|
zlog_tls_free(zlog_tls_get());
|
||||||
|
zlog_tls_set(NULL);
|
||||||
|
|
||||||
|
snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
|
||||||
|
if (unlinkat(zlog_tmpdirfd, mmpath, 0))
|
||||||
|
zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !CAN_DO_TLS */
|
||||||
|
void zlog_tls_buffer_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_tls_buffer_fini(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void zlog_tls_free(void *arg)
|
||||||
|
{
|
||||||
|
struct zlog_tls *zlog_tls = arg;
|
||||||
|
|
||||||
|
if (!zlog_tls)
|
||||||
|
return;
|
||||||
|
|
||||||
|
munmap(zlog_tls->mmbuf, TLS_LOG_BUF_SIZE);
|
||||||
|
XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_tls_buffer_flush(void)
|
||||||
|
{
|
||||||
|
struct zlog_target *zt;
|
||||||
|
struct zlog_tls *zlog_tls = zlog_tls_get();
|
||||||
|
|
||||||
|
if (!zlog_tls)
|
||||||
|
return;
|
||||||
|
if (!zlog_tls->nmsgs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
frr_each (zlog_targets, &zlog_targets, zt) {
|
||||||
|
if (!zt->logfn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
zt->logfn(zt, zlog_tls->msgp, zlog_tls->nmsgs);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
zlog_tls->bufpos = 0;
|
||||||
|
zlog_tls->nmsgs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void vzlog_notls(int prio, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
struct zlog_target *zt;
|
||||||
|
struct zlog_msg stackmsg = {
|
||||||
|
.prio = prio & LOG_PRIMASK,
|
||||||
|
.fmt = fmt,
|
||||||
|
}, *msg = &stackmsg;
|
||||||
|
char stackbuf[512];
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_REALTIME, &msg->ts);
|
||||||
|
va_copy(msg->args, ap);
|
||||||
|
msg->stackbuf = stackbuf;
|
||||||
|
msg->stackbufsz = sizeof(stackbuf);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
frr_each (zlog_targets, &zlog_targets, zt) {
|
||||||
|
if (prio > zt->prio_min)
|
||||||
|
continue;
|
||||||
|
if (!zt->logfn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
zt->logfn(zt, &msg, 1);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
va_end(msg->args);
|
||||||
|
if (msg->text && msg->text != stackbuf)
|
||||||
|
XFREE(MTYPE_LOG_MESSAGE, msg->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vzlog_tls(struct zlog_tls *zlog_tls, int prio,
|
||||||
|
const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
struct zlog_target *zt;
|
||||||
|
struct zlog_msg *msg;
|
||||||
|
char *buf;
|
||||||
|
bool ignoremsg = true;
|
||||||
|
bool immediate = false;
|
||||||
|
|
||||||
|
/* avoid further processing cost if no target wants this message */
|
||||||
|
rcu_read_lock();
|
||||||
|
frr_each (zlog_targets, &zlog_targets, zt) {
|
||||||
|
if (prio > zt->prio_min)
|
||||||
|
continue;
|
||||||
|
ignoremsg = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (ignoremsg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
msg = &zlog_tls->msgs[zlog_tls->nmsgs];
|
||||||
|
zlog_tls->nmsgs++;
|
||||||
|
if (zlog_tls->nmsgs == array_size(zlog_tls->msgs))
|
||||||
|
immediate = true;
|
||||||
|
|
||||||
|
memset(msg, 0, sizeof(*msg));
|
||||||
|
clock_gettime(CLOCK_REALTIME, &msg->ts);
|
||||||
|
va_copy(msg->args, ap);
|
||||||
|
msg->stackbuf = buf = zlog_tls->mmbuf + zlog_tls->bufpos;
|
||||||
|
msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1;
|
||||||
|
msg->fmt = fmt;
|
||||||
|
msg->prio = prio & LOG_PRIMASK;
|
||||||
|
if (msg->prio < LOG_INFO)
|
||||||
|
immediate = true;
|
||||||
|
|
||||||
|
if (!immediate) {
|
||||||
|
/* messages written later need to take the formatting cost
|
||||||
|
* immediately since we can't hold a reference on varargs
|
||||||
|
*/
|
||||||
|
zlog_msg_text(msg, NULL);
|
||||||
|
|
||||||
|
if (msg->text != buf)
|
||||||
|
/* zlog_msg_text called malloc() on us :( */
|
||||||
|
immediate = true;
|
||||||
|
else {
|
||||||
|
zlog_tls->bufpos += msg->textlen + 1;
|
||||||
|
/* write a second \0 to mark current end position
|
||||||
|
* (in case of crash this signals end of unwritten log
|
||||||
|
* messages in mmap'd logbuf file)
|
||||||
|
*/
|
||||||
|
zlog_tls->mmbuf[zlog_tls->bufpos] = '\0';
|
||||||
|
|
||||||
|
/* avoid malloc() for next message */
|
||||||
|
if (TLS_LOG_BUF_SIZE - zlog_tls->bufpos < 256)
|
||||||
|
immediate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (immediate)
|
||||||
|
zlog_tls_buffer_flush();
|
||||||
|
|
||||||
|
va_end(msg->args);
|
||||||
|
if (msg->text && msg->text != buf)
|
||||||
|
XFREE(MTYPE_LOG_MESSAGE, msg->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vzlog(int prio, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
struct zlog_tls *zlog_tls = zlog_tls_get();
|
||||||
|
|
||||||
|
if (zlog_tls)
|
||||||
|
vzlog_tls(zlog_tls, prio, fmt, ap);
|
||||||
|
else
|
||||||
|
vzlog_notls(prio, fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_sigsafe(const char *text, size_t len)
|
||||||
|
{
|
||||||
|
struct zlog_target *zt;
|
||||||
|
const char *end = text + len, *nlpos;
|
||||||
|
|
||||||
|
while (text < end) {
|
||||||
|
nlpos = memchr(text, '\n', end - text);
|
||||||
|
if (!nlpos)
|
||||||
|
nlpos = end;
|
||||||
|
|
||||||
|
frr_each (zlog_targets, &zlog_targets, zt) {
|
||||||
|
if (LOG_CRIT > zt->prio_min)
|
||||||
|
continue;
|
||||||
|
if (!zt->logfn_sigsafe)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
zt->logfn_sigsafe(zt, text, nlpos - text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nlpos == end)
|
||||||
|
break;
|
||||||
|
text = nlpos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int zlog_msg_prio(struct zlog_msg *msg)
|
||||||
|
{
|
||||||
|
return msg->prio;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen)
|
||||||
|
{
|
||||||
|
if (!msg->text) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_copy(args, msg->args);
|
||||||
|
msg->text = vasnprintfrr(MTYPE_LOG_MESSAGE, msg->stackbuf,
|
||||||
|
msg->stackbufsz, msg->fmt, args);
|
||||||
|
msg->textlen = strlen(msg->text);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
if (textlen)
|
||||||
|
*textlen = msg->textlen;
|
||||||
|
return msg->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
|
||||||
|
#define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
|
||||||
|
|
||||||
|
size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
|
||||||
|
uint32_t flags)
|
||||||
|
{
|
||||||
|
size_t len1;
|
||||||
|
|
||||||
|
if (!(flags & ZLOG_TS_FORMAT))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!(msg->ts_flags & ZLOG_TS_FORMAT) ||
|
||||||
|
((msg->ts_flags ^ flags) & ZLOG_TS_UTC)) {
|
||||||
|
struct tm tm;
|
||||||
|
|
||||||
|
if (flags & ZLOG_TS_UTC)
|
||||||
|
gmtime_r(&msg->ts.tv_sec, &tm);
|
||||||
|
else
|
||||||
|
localtime_r(&msg->ts.tv_sec, &tm);
|
||||||
|
|
||||||
|
strftime(msg->ts_str, sizeof(msg->ts_str),
|
||||||
|
"%Y-%m-%dT%H:%M:%S", &tm);
|
||||||
|
|
||||||
|
if (flags & ZLOG_TS_UTC) {
|
||||||
|
msg->ts_zonetail[0] = 'Z';
|
||||||
|
msg->ts_zonetail[1] = '\0';
|
||||||
|
} else
|
||||||
|
snprintfrr(msg->ts_zonetail, sizeof(msg->ts_zonetail),
|
||||||
|
"%+03d:%02d",
|
||||||
|
(int)(tm.tm_gmtoff / 3600),
|
||||||
|
(int)(labs(tm.tm_gmtoff) / 60) % 60);
|
||||||
|
|
||||||
|
msg->ts_dot = msg->ts_str + strlen(msg->ts_str);
|
||||||
|
snprintfrr(msg->ts_dot,
|
||||||
|
msg->ts_str + sizeof(msg->ts_str) - msg->ts_dot,
|
||||||
|
".%09lu", (unsigned long)msg->ts.tv_nsec);
|
||||||
|
|
||||||
|
msg->ts_flags = ZLOG_TS_ISO8601 | (flags & ZLOG_TS_UTC);
|
||||||
|
}
|
||||||
|
|
||||||
|
len1 = flags & ZLOG_TS_PREC;
|
||||||
|
len1 = (msg->ts_dot - msg->ts_str) + (len1 ? len1 + 1 : 0);
|
||||||
|
|
||||||
|
if (len1 > strlen(msg->ts_str))
|
||||||
|
len1 = strlen(msg->ts_str);
|
||||||
|
|
||||||
|
if (flags & ZLOG_TS_LEGACY) {
|
||||||
|
if (len1 + 1 > outsz)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* just swap out the formatting, faster than redoing it */
|
||||||
|
for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) {
|
||||||
|
switch (*p) {
|
||||||
|
case '-':
|
||||||
|
*out++ = '/';
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
*out++ = ' ';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*out++ = *p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out = '\0';
|
||||||
|
return len1;
|
||||||
|
} else {
|
||||||
|
size_t len2 = strlen(msg->ts_zonetail);
|
||||||
|
|
||||||
|
if (len1 + len2 + 1 > outsz)
|
||||||
|
return 0;
|
||||||
|
memcpy(out, msg->ts_str, len1);
|
||||||
|
memcpy(out + len1, msg->ts_zonetail, len2);
|
||||||
|
out[len1 + len2] = '\0';
|
||||||
|
return len1 + len2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup functions */
|
||||||
|
|
||||||
|
struct zlog_target *zlog_target_clone(struct memtype *mt,
|
||||||
|
struct zlog_target *oldzt, size_t size)
|
||||||
|
{
|
||||||
|
struct zlog_target *newzt;
|
||||||
|
|
||||||
|
newzt = XCALLOC(mt, size);
|
||||||
|
if (oldzt) {
|
||||||
|
newzt->prio_min = oldzt->prio_min;
|
||||||
|
newzt->logfn = oldzt->logfn;
|
||||||
|
newzt->logfn_sigsafe = oldzt->logfn_sigsafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newzt;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
|
||||||
|
struct zlog_target *newzt)
|
||||||
|
{
|
||||||
|
if (newzt)
|
||||||
|
zlog_targets_add_tail(&zlog_targets, newzt);
|
||||||
|
if (oldzt)
|
||||||
|
zlog_targets_del(&zlog_targets, oldzt);
|
||||||
|
return oldzt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* common init */
|
||||||
|
|
||||||
|
#define TMPBASEDIR "/var/tmp/frr"
|
||||||
|
|
||||||
|
static char zlog_tmpdir[MAXPATHLEN];
|
||||||
|
|
||||||
|
void zlog_aux_init(const char *prefix, int prio_min)
|
||||||
|
{
|
||||||
|
if (prefix)
|
||||||
|
strlcpy(zlog_prefix, prefix, sizeof(zlog_prefix));
|
||||||
|
|
||||||
|
hook_call(zlog_aux_init, prefix, prio_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_init(const char *progname, const char *protoname,
|
||||||
|
unsigned short instance, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
zlog_uid = uid;
|
||||||
|
zlog_gid = gid;
|
||||||
|
|
||||||
|
if (instance) {
|
||||||
|
snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
|
||||||
|
"/var/tmp/frr/%s-%d.%ld",
|
||||||
|
progname, instance, (long)getpid());
|
||||||
|
|
||||||
|
zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
|
||||||
|
"%s[%d]: ", protoname, instance);
|
||||||
|
} else {
|
||||||
|
snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
|
||||||
|
"/var/tmp/frr/%s.%ld",
|
||||||
|
progname, (long)getpid());
|
||||||
|
|
||||||
|
zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
|
||||||
|
"%s: ", protoname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mkdir(TMPBASEDIR, 0700) != 0) {
|
||||||
|
if (errno != EEXIST) {
|
||||||
|
zlog_err("failed to mkdir \"%s\": %s",
|
||||||
|
TMPBASEDIR, strerror(errno));
|
||||||
|
goto out_warn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chown(TMPBASEDIR, zlog_uid, zlog_gid);
|
||||||
|
|
||||||
|
if (mkdir(zlog_tmpdir, 0700) != 0) {
|
||||||
|
zlog_err("failed to mkdir \"%s\": %s",
|
||||||
|
zlog_tmpdir, strerror(errno));
|
||||||
|
goto out_warn;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef O_PATH
|
||||||
|
zlog_tmpdirfd = open(zlog_tmpdir,
|
||||||
|
O_PATH | O_RDONLY | O_CLOEXEC);
|
||||||
|
#else
|
||||||
|
zlog_tmpdirfd = open(zlog_tmpdir,
|
||||||
|
O_DIRECTORY | O_RDONLY | O_CLOEXEC);
|
||||||
|
#endif
|
||||||
|
if (zlog_tmpdirfd < 0) {
|
||||||
|
zlog_err("failed to open \"%s\": %s",
|
||||||
|
zlog_tmpdir, strerror(errno));
|
||||||
|
goto out_warn;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AT_EMPTY_PATH
|
||||||
|
fchownat(zlog_tmpdirfd, "", zlog_uid, zlog_gid, AT_EMPTY_PATH);
|
||||||
|
#else
|
||||||
|
chown(zlog_tmpdir, zlog_uid, zlog_gid);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
hook_call(zlog_init, progname, protoname, instance, uid, gid);
|
||||||
|
return;
|
||||||
|
|
||||||
|
out_warn:
|
||||||
|
zlog_err("crashlog and per-thread log buffering unavailable!");
|
||||||
|
hook_call(zlog_init, progname, protoname, instance, uid, gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_fini(void)
|
||||||
|
{
|
||||||
|
hook_call(zlog_fini);
|
||||||
|
|
||||||
|
if (zlog_tmpdirfd >= 0) {
|
||||||
|
close(zlog_tmpdirfd);
|
||||||
|
zlog_tmpdirfd = -1;
|
||||||
|
|
||||||
|
if (rmdir(zlog_tmpdir))
|
||||||
|
zlog_err("failed to rmdir \"%s\": %s",
|
||||||
|
zlog_tmpdir, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
186
lib/zlog.h
Normal file
186
lib/zlog.h
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FRR_ZLOG_H
|
||||||
|
#define _FRR_ZLOG_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
#include "atomlist.h"
|
||||||
|
#include "frrcu.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "hook.h"
|
||||||
|
|
||||||
|
extern char zlog_prefix[];
|
||||||
|
extern size_t zlog_prefixsz;
|
||||||
|
extern int zlog_tmpdirfd;
|
||||||
|
|
||||||
|
/* These functions are set up to write to stdout/stderr without explicit
|
||||||
|
* initialization and/or before config load. There is no need to call e.g.
|
||||||
|
* fprintf(stderr, ...) just because it's "too early" at startup. Depending
|
||||||
|
* on context, it may still be the right thing to use fprintf though -- try to
|
||||||
|
* determine wether something is a log message or something else.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void vzlog(int prio, const char *fmt, va_list ap);
|
||||||
|
|
||||||
|
__attribute__ ((format (printf, 2, 3)))
|
||||||
|
static inline void zlog(int prio, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vzlog(prio, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define zlog_err(...) zlog(LOG_ERR, __VA_ARGS__)
|
||||||
|
#define zlog_warn(...) zlog(LOG_WARNING, __VA_ARGS__)
|
||||||
|
#define zlog_info(...) zlog(LOG_INFO, __VA_ARGS__)
|
||||||
|
#define zlog_notice(...) zlog(LOG_NOTICE, __VA_ARGS__)
|
||||||
|
#define zlog_debug(...) zlog(LOG_DEBUG, __VA_ARGS__)
|
||||||
|
|
||||||
|
extern void zlog_sigsafe(const char *text, size_t len);
|
||||||
|
|
||||||
|
/* extra priority value to disable a target without deleting it */
|
||||||
|
#define ZLOG_DISABLED (LOG_EMERG-1)
|
||||||
|
|
||||||
|
/* zlog_msg encapsulates a particular logging call from somewhere in the code.
|
||||||
|
* The same struct is passed around to all zlog_targets.
|
||||||
|
*
|
||||||
|
* This is used to defer formatting the log message until it is actually
|
||||||
|
* requested by one of the targets. If none of the targets needs the message
|
||||||
|
* formatted, the formatting call is avoided entirely.
|
||||||
|
*
|
||||||
|
* This struct is opaque / private to the core zlog code. Logging targets
|
||||||
|
* should use zlog_msg_* functions to get text / timestamps / ... for a
|
||||||
|
* message.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct zlog_msg;
|
||||||
|
|
||||||
|
extern int zlog_msg_prio(struct zlog_msg *msg);
|
||||||
|
|
||||||
|
/* pass NULL as textlen if you don't need it. */
|
||||||
|
extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen);
|
||||||
|
|
||||||
|
/* timestamp formatting control flags */
|
||||||
|
|
||||||
|
/* sub-second digit count */
|
||||||
|
#define ZLOG_TS_PREC 0xfU
|
||||||
|
|
||||||
|
/* 8601: 0000-00-00T00:00:00Z (if used with ZLOG_TS_UTC)
|
||||||
|
* 0000-00-00T00:00:00+00:00 (otherwise)
|
||||||
|
* Legacy: 0000/00/00 00:00:00 (no TZ indicated!)
|
||||||
|
*/
|
||||||
|
#define ZLOG_TS_ISO8601 (1 << 8)
|
||||||
|
#define ZLOG_TS_LEGACY (1 << 9)
|
||||||
|
|
||||||
|
/* default is local time zone */
|
||||||
|
#define ZLOG_TS_UTC (1 << 10)
|
||||||
|
|
||||||
|
extern size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
|
||||||
|
uint32_t flags);
|
||||||
|
|
||||||
|
/* This list & struct implements the actual logging targets. It is accessed
|
||||||
|
* lock-free from all threads, and thus MUST only be changed atomically, i.e.
|
||||||
|
* RCU.
|
||||||
|
*
|
||||||
|
* Since there's no atomic replace, the replacement action is an add followed
|
||||||
|
* by a delete. This means that during logging config changes, log messages
|
||||||
|
* may be duplicated in the log target that is being changed. The old entry
|
||||||
|
* being changed MUST also at the very least not crash or do other stupid
|
||||||
|
* things.
|
||||||
|
*
|
||||||
|
* This list and struct are NOT related to config. Logging config is kept
|
||||||
|
* separately, and results in creating appropriate zlog_target(s) to realize
|
||||||
|
* the config. Log targets may also be created from varying sources, e.g.
|
||||||
|
* command line options, or VTY commands ("log monitor").
|
||||||
|
*
|
||||||
|
* struct zlog_target is intended to be embedded into a larger structure that
|
||||||
|
* contains additional field for the specific logging target, e.g. an fd or
|
||||||
|
* additional options. It MUST be the first field in that larger struct.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PREDECL_ATOMLIST(zlog_targets)
|
||||||
|
struct zlog_target {
|
||||||
|
struct zlog_targets_item head;
|
||||||
|
|
||||||
|
int prio_min;
|
||||||
|
|
||||||
|
void (*logfn)(struct zlog_target *zt, struct zlog_msg *msg[],
|
||||||
|
size_t nmsgs);
|
||||||
|
|
||||||
|
/* for crash handlers, set to NULL if log target can't write crash logs
|
||||||
|
* without possibly deadlocking (AS-Safe)
|
||||||
|
*
|
||||||
|
* text is not \0 terminated & split up into lines (e.g. no \n)
|
||||||
|
*/
|
||||||
|
void (*logfn_sigsafe)(struct zlog_target *zt, const char *text,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
struct rcu_head rcu_head;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* make a copy for RCUpdating. oldzt may be NULL to allocate a fresh one. */
|
||||||
|
extern struct zlog_target *zlog_target_clone(struct memtype *mt,
|
||||||
|
struct zlog_target *oldzt,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
/* update the zlog_targets list; both oldzt and newzt may be NULL. You
|
||||||
|
* still need to zlog_target_free() the old target afterwards if it wasn't
|
||||||
|
* NULL.
|
||||||
|
*
|
||||||
|
* Returns oldzt so you can zlog_target_free(zlog_target_replace(old, new));
|
||||||
|
* (Some log targets may need extra cleanup inbetween, but remember the old
|
||||||
|
* target MUST remain functional until the end of the current RCU cycle.)
|
||||||
|
*/
|
||||||
|
extern struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
|
||||||
|
struct zlog_target *newzt);
|
||||||
|
|
||||||
|
/* Mostly for symmetry for zlog_target_clone(), just rcu_free() internally. */
|
||||||
|
#define zlog_target_free(mt, zt) \
|
||||||
|
rcu_free(mt, zt, rcu_head)
|
||||||
|
|
||||||
|
extern void zlog_init(const char *progname, const char *protoname,
|
||||||
|
unsigned short instance, uid_t uid, gid_t gid);
|
||||||
|
DECLARE_HOOK(zlog_init, (const char *progname, const char *protoname,
|
||||||
|
unsigned short instance, uid_t uid, gid_t gid),
|
||||||
|
(progname, protoname, instance, uid, gid))
|
||||||
|
|
||||||
|
extern void zlog_fini(void);
|
||||||
|
DECLARE_KOOH(zlog_fini, (), ())
|
||||||
|
|
||||||
|
/* for tools & test programs, i.e. anything not a daemon.
|
||||||
|
* (no cleanup needed at exit)
|
||||||
|
*/
|
||||||
|
extern void zlog_aux_init(const char *prefix, int prio_min);
|
||||||
|
DECLARE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
|
||||||
|
(prefix, prio_min))
|
||||||
|
|
||||||
|
extern void zlog_startup_end(void);
|
||||||
|
|
||||||
|
extern void zlog_tls_buffer_init(void);
|
||||||
|
extern void zlog_tls_buffer_flush(void);
|
||||||
|
extern void zlog_tls_buffer_fini(void);
|
||||||
|
|
||||||
|
#endif /* _FRR_ZLOG_H */
|
566
lib/zlog_targets.c
Normal file
566
lib/zlog_targets.c
Normal file
|
@ -0,0 +1,566 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "zebra.h"
|
||||||
|
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "frrcu.h"
|
||||||
|
#include "frr_pthread.h"
|
||||||
|
#include "printfrr.h"
|
||||||
|
#include "zlog.h"
|
||||||
|
#include "zlog_targets.h"
|
||||||
|
|
||||||
|
DEFINE_MTYPE_STATIC(LIB, LOG_FD, "log file target")
|
||||||
|
DEFINE_MTYPE_STATIC(LIB, LOG_FD_NAME, "log file name")
|
||||||
|
DEFINE_MTYPE_STATIC(LIB, LOG_FD_ROTATE, "log file rotate helper")
|
||||||
|
DEFINE_MTYPE_STATIC(LIB, LOG_SYSL, "syslog target")
|
||||||
|
|
||||||
|
struct zlt_fd {
|
||||||
|
struct zlog_target zt;
|
||||||
|
|
||||||
|
atomic_uint_fast32_t fd;
|
||||||
|
|
||||||
|
char ts_subsec;
|
||||||
|
bool record_priority;
|
||||||
|
|
||||||
|
struct rcu_head_close head_close;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const prionames[] = {
|
||||||
|
[LOG_EMERG] = "emergencies: ",
|
||||||
|
[LOG_ALERT] = "alerts: ",
|
||||||
|
[LOG_CRIT] = "critical: ",
|
||||||
|
[LOG_ERR] = "errors: ",
|
||||||
|
[LOG_WARNING] = "warnings: ",
|
||||||
|
[LOG_NOTICE] = "notifications: ",
|
||||||
|
[LOG_INFO] = "informational: ",
|
||||||
|
[LOG_DEBUG] = "debugging: ",
|
||||||
|
};
|
||||||
|
|
||||||
|
static void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
|
||||||
|
{
|
||||||
|
struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
|
||||||
|
int fd;
|
||||||
|
size_t i, textlen, iovpos = 0;
|
||||||
|
size_t niov = MIN(4 * nmsgs + 1, IOV_MAX);
|
||||||
|
struct iovec iov[niov];
|
||||||
|
/* "\nYYYY-MM-DD HH:MM:SS.NNNNNNNNN+ZZ:ZZ " = 37 chars */
|
||||||
|
#define TS_LEN 40
|
||||||
|
char ts_buf[TS_LEN * nmsgs], *ts_pos = ts_buf;
|
||||||
|
|
||||||
|
fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
|
||||||
|
|
||||||
|
for (i = 0; i < nmsgs; i++) {
|
||||||
|
struct zlog_msg *msg = msgs[i];
|
||||||
|
int prio = zlog_msg_prio(msg);
|
||||||
|
|
||||||
|
if (prio > zt->prio_min)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
iov[iovpos].iov_base = ts_pos;
|
||||||
|
if (iovpos > 0)
|
||||||
|
*ts_pos++ = '\n';
|
||||||
|
ts_pos += zlog_msg_ts(msg, ts_pos, sizeof(ts_buf) - 1
|
||||||
|
- (ts_pos - ts_buf),
|
||||||
|
ZLOG_TS_LEGACY | zte->ts_subsec);
|
||||||
|
*ts_pos++ = ' ';
|
||||||
|
iov[iovpos].iov_len = ts_pos - (char *)iov[iovpos].iov_base;
|
||||||
|
|
||||||
|
iovpos++;
|
||||||
|
|
||||||
|
if (zte->record_priority) {
|
||||||
|
iov[iovpos].iov_base = (char *)prionames[prio];
|
||||||
|
iov[iovpos].iov_len = strlen(iov[iovpos].iov_base);
|
||||||
|
|
||||||
|
iovpos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
iov[iovpos].iov_base = zlog_prefix;
|
||||||
|
iov[iovpos].iov_len = zlog_prefixsz;
|
||||||
|
|
||||||
|
iovpos++;
|
||||||
|
|
||||||
|
iov[iovpos].iov_base = (char *)zlog_msg_text(msg, &textlen);
|
||||||
|
iov[iovpos].iov_len = textlen;
|
||||||
|
|
||||||
|
iovpos++;
|
||||||
|
|
||||||
|
if (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN
|
||||||
|
|| i + 1 == nmsgs
|
||||||
|
|| array_size(iov) - iovpos < 5) {
|
||||||
|
iov[iovpos].iov_base = (char *)"\n";
|
||||||
|
iov[iovpos].iov_len = 1;
|
||||||
|
|
||||||
|
iovpos++;
|
||||||
|
|
||||||
|
writev(fd, iov, iovpos);
|
||||||
|
|
||||||
|
iovpos = 0;
|
||||||
|
ts_pos = ts_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(iovpos == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zlog_fd_sigsafe(struct zlog_target *zt, const char *text,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
|
||||||
|
struct iovec iov[4];
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
iov[0].iov_base = (char *)prionames[LOG_CRIT];
|
||||||
|
iov[0].iov_len = zte->record_priority ? strlen(iov[0].iov_base) : 0;
|
||||||
|
|
||||||
|
iov[1].iov_base = zlog_prefix;
|
||||||
|
iov[1].iov_len = zlog_prefixsz;
|
||||||
|
|
||||||
|
iov[2].iov_base = (char *)text;
|
||||||
|
iov[2].iov_len = len;
|
||||||
|
|
||||||
|
iov[3].iov_base = (char *)"\n";
|
||||||
|
iov[3].iov_len = 1;
|
||||||
|
|
||||||
|
fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
|
||||||
|
|
||||||
|
writev(fd, iov, array_size(iov));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (re-)configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
void zlog_file_init(struct zlog_cfg_file *zcf)
|
||||||
|
{
|
||||||
|
memset(zcf, 0, sizeof(*zcf));
|
||||||
|
zcf->prio_min = ZLOG_DISABLED;
|
||||||
|
zcf->fd = -1;
|
||||||
|
pthread_mutex_init(&zcf->cfg_mtx, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zlog_file_target_free(struct zlt_fd *zlt)
|
||||||
|
{
|
||||||
|
if (!zlt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcu_close(&zlt->head_close, zlt->fd);
|
||||||
|
rcu_free(MTYPE_LOG_FD, zlt, zt.rcu_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_file_fini(struct zlog_cfg_file *zcf)
|
||||||
|
{
|
||||||
|
if (zcf->active) {
|
||||||
|
struct zlt_fd *ztf;
|
||||||
|
struct zlog_target *zt;
|
||||||
|
|
||||||
|
zt = zlog_target_replace(&zcf->active->zt, NULL);
|
||||||
|
ztf = container_of(zt, struct zlt_fd, zt);
|
||||||
|
zlog_file_target_free(ztf);
|
||||||
|
}
|
||||||
|
XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
|
||||||
|
pthread_mutex_destroy(&zcf->cfg_mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool zlog_file_cycle(struct zlog_cfg_file *zcf)
|
||||||
|
{
|
||||||
|
struct zlog_target *zt, *old;
|
||||||
|
struct zlt_fd *zlt = NULL;
|
||||||
|
int fd;
|
||||||
|
bool rv = true;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (zcf->prio_min == ZLOG_DISABLED)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (zcf->fd != -1)
|
||||||
|
fd = dup(zcf->fd);
|
||||||
|
else if (zcf->filename)
|
||||||
|
fd = open(zcf->filename,
|
||||||
|
O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC
|
||||||
|
| O_NOCTTY,
|
||||||
|
LOGFILE_MASK);
|
||||||
|
else
|
||||||
|
fd = -1;
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
rv = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
zt = zlog_target_clone(MTYPE_LOG_FD, &zcf->active->zt,
|
||||||
|
sizeof(*zlt));
|
||||||
|
zlt = container_of(zt, struct zlt_fd, zt);
|
||||||
|
|
||||||
|
zlt->fd = fd;
|
||||||
|
zlt->record_priority = zcf->record_priority;
|
||||||
|
zlt->ts_subsec = zcf->ts_subsec;
|
||||||
|
|
||||||
|
zlt->zt.prio_min = zcf->prio_min;
|
||||||
|
zlt->zt.logfn = zlog_fd;
|
||||||
|
zlt->zt.logfn_sigsafe = zlog_fd_sigsafe;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
old = zlog_target_replace(&zcf->active->zt, &zlt->zt);
|
||||||
|
zcf->active = zlt;
|
||||||
|
|
||||||
|
zlog_file_target_free(container_of(old, struct zlt_fd, zt));
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_file_set_other(struct zlog_cfg_file *zcf)
|
||||||
|
{
|
||||||
|
frr_with_mutex(&zcf->cfg_mtx) {
|
||||||
|
zlog_file_cycle(zcf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *filename)
|
||||||
|
{
|
||||||
|
frr_with_mutex(&zcf->cfg_mtx) {
|
||||||
|
XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
|
||||||
|
zcf->filename = XSTRDUP(MTYPE_LOG_FD_NAME, filename);
|
||||||
|
zcf->fd = -1;
|
||||||
|
|
||||||
|
return zlog_file_cycle(zcf);
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd)
|
||||||
|
{
|
||||||
|
frr_with_mutex(&zcf->cfg_mtx) {
|
||||||
|
if (zcf->fd == fd)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
|
||||||
|
zcf->fd = fd;
|
||||||
|
|
||||||
|
return zlog_file_cycle(zcf);
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rcu_close_rotate {
|
||||||
|
struct rcu_head_close head_close;
|
||||||
|
struct rcu_head head_self;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool zlog_file_rotate(struct zlog_cfg_file *zcf)
|
||||||
|
{
|
||||||
|
struct rcu_close_rotate *rcr;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
frr_with_mutex(&zcf->cfg_mtx) {
|
||||||
|
if (!zcf->active || !zcf->filename)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fd = open(zcf->filename,
|
||||||
|
O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC | O_NOCTTY,
|
||||||
|
LOGFILE_MASK);
|
||||||
|
if (fd < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fd = atomic_exchange_explicit(&zcf->active->fd,
|
||||||
|
(uint_fast32_t)fd,
|
||||||
|
memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcr = XCALLOC(MTYPE_LOG_FD_ROTATE, sizeof(*rcr));
|
||||||
|
rcu_close(&rcr->head_close, fd);
|
||||||
|
rcu_free(MTYPE_LOG_FD_ROTATE, rcr, head_self);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixed crash logging */
|
||||||
|
|
||||||
|
static struct zlt_fd zlog_crashlog;
|
||||||
|
|
||||||
|
static void zlog_crashlog_sigsafe(struct zlog_target *zt, const char *text,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
static int crashlog_fd = -1;
|
||||||
|
|
||||||
|
if (crashlog_fd == -1) {
|
||||||
|
#ifdef HAVE_OPENAT
|
||||||
|
crashlog_fd = openat(zlog_tmpdirfd, "crashlog",
|
||||||
|
O_WRONLY | O_APPEND | O_CREAT,
|
||||||
|
LOGFILE_MASK);
|
||||||
|
#endif
|
||||||
|
if (crashlog_fd < 0)
|
||||||
|
crashlog_fd = -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crashlog_fd == -2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
zlog_crashlog.fd = crashlog_fd;
|
||||||
|
zlog_fd_sigsafe(&zlog_crashlog.zt, text, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is used for assert failures (they don't need AS-Safe logging) */
|
||||||
|
static void zlog_crashlog_plain(struct zlog_target *zt, struct zlog_msg *msgs[],
|
||||||
|
size_t nmsgs)
|
||||||
|
{
|
||||||
|
size_t i, len;
|
||||||
|
const char *text;
|
||||||
|
|
||||||
|
for (i = 0; i < nmsgs; i++) {
|
||||||
|
if (zlog_msg_prio(msgs[i]) > zt->prio_min)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
text = zlog_msg_text(msgs[i], &len);
|
||||||
|
zlog_crashlog_sigsafe(zt, text, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zlog_crashlog_init(void)
|
||||||
|
{
|
||||||
|
zlog_crashlog.zt.prio_min = LOG_CRIT;
|
||||||
|
zlog_crashlog.zt.logfn = zlog_crashlog_plain;
|
||||||
|
zlog_crashlog.zt.logfn_sigsafe = zlog_crashlog_sigsafe;
|
||||||
|
zlog_crashlog.fd = -1;
|
||||||
|
|
||||||
|
zlog_target_replace(NULL, &zlog_crashlog.zt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixed logging for test/auxiliary programs */
|
||||||
|
|
||||||
|
static struct zlt_fd zlog_aux_stdout;
|
||||||
|
static bool zlog_is_aux;
|
||||||
|
|
||||||
|
static int zlt_aux_init(const char *prefix, int prio_min)
|
||||||
|
{
|
||||||
|
zlog_is_aux = true;
|
||||||
|
|
||||||
|
zlog_aux_stdout.zt.prio_min = prio_min;
|
||||||
|
zlog_aux_stdout.zt.logfn = zlog_fd;
|
||||||
|
zlog_aux_stdout.zt.logfn_sigsafe = zlog_fd_sigsafe;
|
||||||
|
zlog_aux_stdout.fd = STDOUT_FILENO;
|
||||||
|
|
||||||
|
zlog_target_replace(NULL, &zlog_aux_stdout.zt);
|
||||||
|
zlog_startup_end();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zlt_init(const char *progname, const char *protoname,
|
||||||
|
unsigned short instance, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
openlog(progname, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zlt_fini(void)
|
||||||
|
{
|
||||||
|
closelog();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixed startup logging to stderr */
|
||||||
|
|
||||||
|
static struct zlt_fd zlog_startup_stderr;
|
||||||
|
|
||||||
|
__attribute__((_CONSTRUCTOR(450))) static void zlog_startup_init(void)
|
||||||
|
{
|
||||||
|
zlog_startup_stderr.zt.prio_min = LOG_WARNING;
|
||||||
|
zlog_startup_stderr.zt.logfn = zlog_fd;
|
||||||
|
zlog_startup_stderr.zt.logfn_sigsafe = zlog_fd_sigsafe;
|
||||||
|
zlog_startup_stderr.fd = STDERR_FILENO;
|
||||||
|
|
||||||
|
zlog_target_replace(NULL, &zlog_startup_stderr.zt);
|
||||||
|
|
||||||
|
hook_register(zlog_aux_init, zlt_aux_init);
|
||||||
|
hook_register(zlog_init, zlt_init);
|
||||||
|
hook_register(zlog_fini, zlt_fini);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_startup_end(void)
|
||||||
|
{
|
||||||
|
static bool startup_ended = false;
|
||||||
|
|
||||||
|
if (startup_ended)
|
||||||
|
return;
|
||||||
|
startup_ended = true;
|
||||||
|
|
||||||
|
zlog_target_replace(&zlog_startup_stderr.zt, NULL);
|
||||||
|
|
||||||
|
if (zlog_is_aux)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* until here, crashlogs go to stderr */
|
||||||
|
zlog_crashlog_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* syslog */
|
||||||
|
|
||||||
|
struct zlt_syslog {
|
||||||
|
struct zlog_target zt;
|
||||||
|
|
||||||
|
int syslog_facility;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[],
|
||||||
|
size_t nmsgs)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt);
|
||||||
|
|
||||||
|
for (i = 0; i < nmsgs; i++) {
|
||||||
|
if (zlog_msg_prio(msgs[i]) > zt->prio_min)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s",
|
||||||
|
zlog_msg_text(msgs[i], NULL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _PATH_LOG
|
||||||
|
#define _PATH_LOG "/dev/log"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void zlog_syslog_sigsafe(struct zlog_target *zt, const char *text,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
static int syslog_fd = -1;
|
||||||
|
|
||||||
|
char hdr[192];
|
||||||
|
size_t hdrlen;
|
||||||
|
struct iovec iov[2];
|
||||||
|
|
||||||
|
if (syslog_fd == -1) {
|
||||||
|
syslog_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||||
|
if (syslog_fd >= 0) {
|
||||||
|
struct sockaddr_un sa;
|
||||||
|
socklen_t salen = sizeof(sa);
|
||||||
|
|
||||||
|
sa.sun_family = AF_UNIX;
|
||||||
|
strlcpy(sa.sun_path, _PATH_LOG, sizeof(sa.sun_path));
|
||||||
|
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
|
||||||
|
salen = sa.sun_len = SUN_LEN(&sa);
|
||||||
|
#endif
|
||||||
|
if (connect(syslog_fd, (struct sockaddr *)&sa, salen)) {
|
||||||
|
close(syslog_fd);
|
||||||
|
syslog_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /dev/log could be a fifo instead of a socket */
|
||||||
|
if (syslog_fd == -1) {
|
||||||
|
syslog_fd = open(_PATH_LOG, O_WRONLY | O_NOCTTY);
|
||||||
|
if (syslog_fd < 0)
|
||||||
|
/* give up ... */
|
||||||
|
syslog_fd = -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syslog_fd == -2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* note zlog_prefix includes trailing ": ", need to cut off 2 chars */
|
||||||
|
hdrlen = snprintfrr(hdr, sizeof(hdr), "<%d>%.*s[%ld]: ", LOG_CRIT,
|
||||||
|
zlog_prefixsz > 2 ? (int)(zlog_prefixsz - 2) : 0,
|
||||||
|
zlog_prefix, (long)getpid());
|
||||||
|
|
||||||
|
iov[0].iov_base = hdr;
|
||||||
|
iov[0].iov_len = hdrlen;
|
||||||
|
|
||||||
|
iov[1].iov_base = (char *)text;
|
||||||
|
iov[1].iov_len = len;
|
||||||
|
|
||||||
|
writev(syslog_fd, iov, array_size(iov));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static pthread_mutex_t syslog_cfg_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static struct zlt_syslog *zlt_syslog;
|
||||||
|
static int syslog_facility = LOG_DAEMON;
|
||||||
|
static int syslog_prio_min = ZLOG_DISABLED;
|
||||||
|
|
||||||
|
void zlog_syslog_set_facility(int facility)
|
||||||
|
{
|
||||||
|
struct zlog_target *newztc;
|
||||||
|
struct zlt_syslog *newzt;
|
||||||
|
|
||||||
|
frr_with_mutex(&syslog_cfg_mutex) {
|
||||||
|
if (facility == syslog_facility)
|
||||||
|
return;
|
||||||
|
syslog_facility = facility;
|
||||||
|
|
||||||
|
if (syslog_prio_min == ZLOG_DISABLED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
newztc = zlog_target_clone(MTYPE_LOG_SYSL, &zlt_syslog->zt,
|
||||||
|
sizeof(*newzt));
|
||||||
|
newzt = container_of(newztc, struct zlt_syslog, zt);
|
||||||
|
newzt->syslog_facility = syslog_facility;
|
||||||
|
|
||||||
|
zlog_target_free(MTYPE_LOG_SYSL,
|
||||||
|
zlog_target_replace(&zlt_syslog->zt,
|
||||||
|
&newzt->zt));
|
||||||
|
|
||||||
|
zlt_syslog = newzt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int zlog_syslog_get_facility(void)
|
||||||
|
{
|
||||||
|
frr_with_mutex(&syslog_cfg_mutex) {
|
||||||
|
return syslog_facility;
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zlog_syslog_set_prio_min(int prio_min)
|
||||||
|
{
|
||||||
|
struct zlog_target *newztc;
|
||||||
|
struct zlt_syslog *newzt = NULL;
|
||||||
|
|
||||||
|
frr_with_mutex(&syslog_cfg_mutex) {
|
||||||
|
if (prio_min == syslog_prio_min)
|
||||||
|
return;
|
||||||
|
syslog_prio_min = prio_min;
|
||||||
|
|
||||||
|
if (syslog_prio_min != ZLOG_DISABLED) {
|
||||||
|
newztc = zlog_target_clone(MTYPE_LOG_SYSL,
|
||||||
|
&zlt_syslog->zt,
|
||||||
|
sizeof(*newzt));
|
||||||
|
newzt = container_of(newztc, struct zlt_syslog, zt);
|
||||||
|
newzt->zt.prio_min = prio_min;
|
||||||
|
newzt->zt.logfn = zlog_syslog;
|
||||||
|
newzt->zt.logfn_sigsafe = zlog_syslog_sigsafe;
|
||||||
|
newzt->syslog_facility = syslog_facility;
|
||||||
|
}
|
||||||
|
|
||||||
|
zlog_target_free(MTYPE_LOG_SYSL,
|
||||||
|
zlog_target_replace(&zlt_syslog->zt,
|
||||||
|
&newzt->zt));
|
||||||
|
|
||||||
|
zlt_syslog = newzt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int zlog_syslog_get_prio_min(void)
|
||||||
|
{
|
||||||
|
frr_with_mutex(&syslog_cfg_mutex) {
|
||||||
|
return syslog_prio_min;
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
}
|
60
lib/zlog_targets.h
Normal file
60
lib/zlog_targets.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FRR_ZLOG_TARGETS_H
|
||||||
|
#define _FRR_ZLOG_TARGETS_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "zlog.h"
|
||||||
|
|
||||||
|
/* multiple file log targets can be active */
|
||||||
|
|
||||||
|
struct zlt_fd;
|
||||||
|
|
||||||
|
struct zlog_cfg_file {
|
||||||
|
struct zlt_fd *active;
|
||||||
|
|
||||||
|
pthread_mutex_t cfg_mtx;
|
||||||
|
|
||||||
|
/* call zlog_file_set_other() to apply these */
|
||||||
|
int prio_min;
|
||||||
|
char ts_subsec;
|
||||||
|
bool record_priority;
|
||||||
|
|
||||||
|
/* call zlog_file_set_filename/fd() to change this */
|
||||||
|
char *filename;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void zlog_file_init(struct zlog_cfg_file *zcf);
|
||||||
|
extern void zlog_file_fini(struct zlog_cfg_file *zcf);
|
||||||
|
|
||||||
|
extern void zlog_file_set_other(struct zlog_cfg_file *zcf);
|
||||||
|
extern bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *name);
|
||||||
|
extern bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd);
|
||||||
|
extern bool zlog_file_rotate(struct zlog_cfg_file *zcf);
|
||||||
|
|
||||||
|
/* syslog is always limited to one target */
|
||||||
|
|
||||||
|
extern void zlog_syslog_set_facility(int facility);
|
||||||
|
extern int zlog_syslog_get_facility(void);
|
||||||
|
|
||||||
|
/* use ZLOG_DISABLED to disable */
|
||||||
|
extern void zlog_syslog_set_prio_min(int prio_min);
|
||||||
|
extern int zlog_syslog_get_prio_min(void);
|
||||||
|
|
||||||
|
#endif /* _FRR_ZLOG_TARGETS_H */
|
|
@ -767,7 +767,7 @@ static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg)
|
||||||
int pim_zebra_mlag_handle_msg(struct stream *s, int len)
|
int pim_zebra_mlag_handle_msg(struct stream *s, int len)
|
||||||
{
|
{
|
||||||
struct mlag_msg mlag_msg;
|
struct mlag_msg mlag_msg;
|
||||||
char buf[ZLOG_FILTER_LENGTH_MAX];
|
char buf[80];
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
size_t length;
|
size_t length;
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,8 @@ static void *logpump_run(void *arg)
|
||||||
|
|
||||||
period = 1000000000L / lp_frequency;
|
period = 1000000000L / lp_frequency;
|
||||||
|
|
||||||
|
zlog_tls_buffer_init();
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
next = start;
|
next = start;
|
||||||
do {
|
do {
|
||||||
|
@ -109,6 +111,8 @@ static void *logpump_run(void *arg)
|
||||||
#endif
|
#endif
|
||||||
} while (delta < lp_duration);
|
} while (delta < lp_duration);
|
||||||
|
|
||||||
|
zlog_tls_buffer_fini();
|
||||||
|
|
||||||
#ifdef RUSAGE_THREAD
|
#ifdef RUSAGE_THREAD
|
||||||
getrusage(RUSAGE_THREAD, &lp_rusage);
|
getrusage(RUSAGE_THREAD, &lp_rusage);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -1381,8 +1381,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
|
||||||
static void bgp_startup(void)
|
static void bgp_startup(void)
|
||||||
{
|
{
|
||||||
cmd_init(1);
|
cmd_init(1);
|
||||||
openzlog("testbgpd", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||||
LOG_DAEMON);
|
|
||||||
zprivs_preinit(&bgpd_privs);
|
zprivs_preinit(&bgpd_privs);
|
||||||
zprivs_init(&bgpd_privs);
|
zprivs_init(&bgpd_privs);
|
||||||
|
|
||||||
|
@ -1438,7 +1437,6 @@ static void bgp_shutdown(void)
|
||||||
zprivs_terminate(&bgpd_privs);
|
zprivs_terminate(&bgpd_privs);
|
||||||
thread_master_free(master);
|
thread_master_free(master);
|
||||||
master = NULL;
|
master = NULL;
|
||||||
closezlog();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
|
|
|
@ -53,7 +53,6 @@ static void vty_do_exit(int isexit)
|
||||||
nb_terminate();
|
nb_terminate();
|
||||||
yang_terminate();
|
yang_terminate();
|
||||||
thread_master_free(master);
|
thread_master_free(master);
|
||||||
closezlog();
|
|
||||||
|
|
||||||
log_memstats(stderr, "testcli");
|
log_memstats(stderr, "testcli");
|
||||||
if (!isexit)
|
if (!isexit)
|
||||||
|
@ -71,11 +70,7 @@ int main(int argc, char **argv)
|
||||||
/* master init. */
|
/* master init. */
|
||||||
master = thread_master_create(NULL);
|
master = thread_master_create(NULL);
|
||||||
|
|
||||||
openzlog("common-cli", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
zlog_aux_init("NONE: ", ZLOG_DISABLED);
|
||||||
LOG_DAEMON);
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, LOG_DEBUG);
|
|
||||||
|
|
||||||
/* Library inits. */
|
/* Library inits. */
|
||||||
cmd_init(1);
|
cmd_init(1);
|
||||||
|
|
|
@ -374,7 +374,6 @@ static void vty_do_exit(int isexit)
|
||||||
nb_terminate();
|
nb_terminate();
|
||||||
yang_terminate();
|
yang_terminate();
|
||||||
thread_master_free(master);
|
thread_master_free(master);
|
||||||
closezlog();
|
|
||||||
|
|
||||||
log_memstats(stderr, "test-nb-oper-data");
|
log_memstats(stderr, "test-nb-oper-data");
|
||||||
if (!isexit)
|
if (!isexit)
|
||||||
|
@ -402,11 +401,7 @@ int main(int argc, char **argv)
|
||||||
/* master init. */
|
/* master init. */
|
||||||
master = thread_master_create(NULL);
|
master = thread_master_create(NULL);
|
||||||
|
|
||||||
openzlog("test-nb-oper-data", "NONE", 0,
|
zlog_aux_init("NONE: ", ZLOG_DISABLED);
|
||||||
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, LOG_DEBUG);
|
|
||||||
|
|
||||||
/* Library inits. */
|
/* Library inits. */
|
||||||
cmd_init(1);
|
cmd_init(1);
|
||||||
|
|
|
@ -73,11 +73,7 @@ int main(void)
|
||||||
master = thread_master_create(NULL);
|
master = thread_master_create(NULL);
|
||||||
signal_init(master, array_size(sigs), sigs);
|
signal_init(master, array_size(sigs), sigs);
|
||||||
|
|
||||||
openzlog("testsegv", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||||
LOG_DAEMON);
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG);
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
|
|
||||||
|
|
||||||
thread_execute(master, threadfunc, 0, 0);
|
thread_execute(master, threadfunc, 0, 0);
|
||||||
|
|
||||||
|
|
|
@ -57,11 +57,7 @@ int main(void)
|
||||||
master = thread_master_create(NULL);
|
master = thread_master_create(NULL);
|
||||||
signal_init(master, array_size(sigs), sigs);
|
signal_init(master, array_size(sigs), sigs);
|
||||||
|
|
||||||
openzlog("testsig", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||||
LOG_DAEMON);
|
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
|
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG);
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
|
|
||||||
|
|
||||||
while (thread_fetch(master, &t))
|
while (thread_fetch(master, &t))
|
||||||
thread_call(&t);
|
thread_call(&t);
|
||||||
|
|
|
@ -52,9 +52,7 @@ bool (*tests[])(void) = {
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
openzlog("testzlog", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
zlog_aux_init("NONE: ", ZLOG_DISABLED);
|
||||||
LOG_ERR);
|
|
||||||
zlog_set_file("test_zlog.log", LOG_DEBUG);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < array_size(tests); i++)
|
for (unsigned int i = 0; i < array_size(tests); i++)
|
||||||
if (!tests[i]())
|
if (!tests[i]())
|
||||||
|
|
100
vtysh/vtysh.c
100
vtysh/vtysh.c
|
@ -2710,103 +2710,6 @@ DEFUNSH(VTYSH_ALL, no_vtysh_config_enable_password,
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Log filter */
|
|
||||||
DEFUN (vtysh_log_filter,
|
|
||||||
vtysh_log_filter_cmd,
|
|
||||||
"[no] log-filter WORD ["DAEMONS_LIST"]",
|
|
||||||
NO_STR
|
|
||||||
FILTER_LOG_STR
|
|
||||||
"String to filter by\n"
|
|
||||||
DAEMONS_STR)
|
|
||||||
{
|
|
||||||
char *filter = NULL;
|
|
||||||
char *daemon = NULL;
|
|
||||||
int found = 0;
|
|
||||||
int idx = 0;
|
|
||||||
int daemon_idx = 2;
|
|
||||||
int total_len = 0;
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
char line[ZLOG_FILTER_LENGTH_MAX + 20];
|
|
||||||
|
|
||||||
found = argv_find(argv, argc, "no", &idx);
|
|
||||||
if (found == 1) {
|
|
||||||
len = snprintf(line, sizeof(line), "no log-filter");
|
|
||||||
daemon_idx += 1;
|
|
||||||
} else
|
|
||||||
len = snprintf(line, sizeof(line), "log-filter");
|
|
||||||
|
|
||||||
total_len += len;
|
|
||||||
|
|
||||||
idx = 1;
|
|
||||||
found = argv_find(argv, argc, "WORD", &idx);
|
|
||||||
if (found != 1) {
|
|
||||||
vty_out(vty, "%% No filter string given\n");
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
filter = argv[idx]->arg;
|
|
||||||
|
|
||||||
if (strnlen(filter, ZLOG_FILTER_LENGTH_MAX + 1)
|
|
||||||
> ZLOG_FILTER_LENGTH_MAX) {
|
|
||||||
vty_out(vty, "%% Filter is too long\n");
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = snprintf(line + total_len, sizeof(line) - total_len, " %s\n",
|
|
||||||
filter);
|
|
||||||
|
|
||||||
if ((len < 0) || (size_t)(total_len + len) > sizeof(line)) {
|
|
||||||
vty_out(vty, "%% Error buffering filter to daemons\n");
|
|
||||||
return CMD_ERR_INCOMPLETE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc >= (daemon_idx + 1))
|
|
||||||
daemon = argv[daemon_idx]->text;
|
|
||||||
|
|
||||||
if (daemon != NULL) {
|
|
||||||
vty_out(vty, "Applying log filter change to %s:\n", daemon);
|
|
||||||
return vtysh_client_execute_name(daemon, line);
|
|
||||||
} else
|
|
||||||
return show_per_daemon(line,
|
|
||||||
"Applying log filter change to %s:\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear log filters */
|
|
||||||
DEFUN (vtysh_log_filter_clear,
|
|
||||||
vtysh_log_filter_clear_cmd,
|
|
||||||
"log-filter clear ["DAEMONS_LIST"]",
|
|
||||||
FILTER_LOG_STR
|
|
||||||
CLEAR_STR
|
|
||||||
DAEMONS_STR)
|
|
||||||
{
|
|
||||||
char *daemon = NULL;
|
|
||||||
int daemon_idx = 2;
|
|
||||||
|
|
||||||
char line[] = "clear log-filter\n";
|
|
||||||
|
|
||||||
if (argc >= (daemon_idx + 1))
|
|
||||||
daemon = argv[daemon_idx]->text;
|
|
||||||
|
|
||||||
if (daemon != NULL) {
|
|
||||||
vty_out(vty, "Clearing all filters applied to %s:\n", daemon);
|
|
||||||
return vtysh_client_execute_name(daemon, line);
|
|
||||||
} else
|
|
||||||
return show_per_daemon(line,
|
|
||||||
"Clearing all filters applied to %s:\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show log filter */
|
|
||||||
DEFUN (vtysh_show_log_filter,
|
|
||||||
vtysh_show_log_filter_cmd,
|
|
||||||
"show log-filter",
|
|
||||||
SHOW_STR
|
|
||||||
FILTER_LOG_STR)
|
|
||||||
{
|
|
||||||
char line[] = "do show log-filter\n";
|
|
||||||
|
|
||||||
return show_per_daemon(line, "Log filters applied to %s:\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN (vtysh_write_terminal,
|
DEFUN (vtysh_write_terminal,
|
||||||
vtysh_write_terminal_cmd,
|
vtysh_write_terminal_cmd,
|
||||||
"write terminal ["DAEMONS_LIST"]",
|
"write terminal ["DAEMONS_LIST"]",
|
||||||
|
@ -4107,9 +4010,6 @@ void vtysh_init_vty(void)
|
||||||
|
|
||||||
/* Logging */
|
/* Logging */
|
||||||
install_element(VIEW_NODE, &vtysh_show_logging_cmd);
|
install_element(VIEW_NODE, &vtysh_show_logging_cmd);
|
||||||
install_element(VIEW_NODE, &vtysh_show_log_filter_cmd);
|
|
||||||
install_element(CONFIG_NODE, &vtysh_log_filter_cmd);
|
|
||||||
install_element(CONFIG_NODE, &vtysh_log_filter_clear_cmd);
|
|
||||||
install_element(CONFIG_NODE, &vtysh_log_stdout_cmd);
|
install_element(CONFIG_NODE, &vtysh_log_stdout_cmd);
|
||||||
install_element(CONFIG_NODE, &vtysh_log_stdout_level_cmd);
|
install_element(CONFIG_NODE, &vtysh_log_stdout_level_cmd);
|
||||||
install_element(CONFIG_NODE, &no_vtysh_log_stdout_cmd);
|
install_element(CONFIG_NODE, &no_vtysh_log_stdout_cmd);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "libfrr.h"
|
#include "libfrr.h"
|
||||||
#include "lib_errors.h"
|
#include "lib_errors.h"
|
||||||
|
#include "zlog_targets.h"
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
@ -1369,11 +1370,10 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
frr_config_fork();
|
frr_config_fork();
|
||||||
|
|
||||||
zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
|
|
||||||
if (watchfrr_di.daemon_mode)
|
if (watchfrr_di.daemon_mode)
|
||||||
zlog_set_level(ZLOG_DEST_SYSLOG, MIN(gs.loglevel, LOG_DEBUG));
|
zlog_syslog_set_prio_min(MIN(gs.loglevel, LOG_DEBUG));
|
||||||
else
|
else
|
||||||
zlog_set_level(ZLOG_DEST_STDOUT, MIN(gs.loglevel, LOG_DEBUG));
|
zlog_aux_init(NULL, MIN(gs.loglevel, LOG_DEBUG));
|
||||||
|
|
||||||
frr_run(master);
|
frr_run(master);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "log_vty.h"
|
||||||
#include "vty.h"
|
#include "vty.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
|
@ -134,6 +135,19 @@ DEFUN (show_watchfrr,
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* we don't have the other logging commands since watchfrr only accepts
|
||||||
|
* log config through command line options
|
||||||
|
*/
|
||||||
|
DEFUN_NOSH (show_logging,
|
||||||
|
show_logging_cmd,
|
||||||
|
"show logging",
|
||||||
|
SHOW_STR
|
||||||
|
"Show current logging configuration\n")
|
||||||
|
{
|
||||||
|
log_show_syslog(vty);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef VTYSH_EXTRACT_PL
|
#ifndef VTYSH_EXTRACT_PL
|
||||||
#include "watchfrr/watchfrr_vty_clippy.c"
|
#include "watchfrr/watchfrr_vty_clippy.c"
|
||||||
#endif
|
#endif
|
||||||
|
@ -190,4 +204,5 @@ void watchfrr_vty_init(void)
|
||||||
|
|
||||||
install_element(CONFIG_NODE, &show_debugging_watchfrr_cmd);
|
install_element(CONFIG_NODE, &show_debugging_watchfrr_cmd);
|
||||||
install_element(VIEW_NODE, &show_watchfrr_cmd);
|
install_element(VIEW_NODE, &show_watchfrr_cmd);
|
||||||
|
install_element(VIEW_NODE, &show_logging_cmd);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue