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 "bfdd_nb.h"
|
||||
#include "lib/version.h"
|
||||
#include "lib/command.h"
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "lib/memory.h"
|
||||
#include "lib/routemap.h"
|
||||
#include "lib/log.h"
|
||||
#include "lib/log_int.h"
|
||||
#include "lib/linklist.h"
|
||||
#include "lib/command.h"
|
||||
|
||||
|
@ -371,7 +370,7 @@ int rfapiStream2Vty(void *stream, /* input */
|
|||
*fp = (int (*)(void *, const char *, ...))rfapiDebugPrintf;
|
||||
*outstream = NULL;
|
||||
*vty_newline = str_vty_newline(*vty);
|
||||
return (vzlog_test(LOG_DEBUG));
|
||||
return 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],,, [
|
||||
#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
|
||||
|
||||
|
@ -1202,7 +1202,11 @@ dnl other functions
|
|||
dnl ---------------
|
||||
AC_CHECK_FUNCS([ \
|
||||
strlcat strlcpy \
|
||||
getgrouplist])
|
||||
getgrouplist \
|
||||
openat \
|
||||
unlinkat \
|
||||
posix_fallocate \
|
||||
])
|
||||
|
||||
dnl ##########################################################################
|
||||
dnl LARGE if block spans a lot of "configure"!
|
||||
|
@ -2196,15 +2200,6 @@ if test "$enable_backtrace" != "no" ; then
|
|||
])
|
||||
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
|
||||
AC_CHECK_HEADER([execinfo.h], [
|
||||
AC_SEARCH_LIBS([backtrace], [execinfo], [
|
||||
|
@ -2213,7 +2208,6 @@ if test "$enable_backtrace" != "no" ; then
|
|||
],, [-lm])
|
||||
])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$enable_backtrace" = "yes" -a "$backtrace_ok" = "no"; then
|
||||
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);
|
||||
}
|
||||
|
||||
if (lflag || eflag)
|
||||
openzlog(ldpd_di.progname, "LDP", 0,
|
||||
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
||||
if (lflag || eflag) {
|
||||
struct zprivs_ids_t ids;
|
||||
|
||||
zprivs_preinit(&ldpd_privs);
|
||||
zprivs_get_ids(&ids);
|
||||
|
||||
zlog_init(ldpd_di.progname, "LDP", 0,
|
||||
ids.uid_normal, ids.gid_normal);
|
||||
}
|
||||
if (lflag)
|
||||
lde();
|
||||
else if (eflag)
|
||||
|
@ -486,7 +492,7 @@ ldpd_shutdown(void)
|
|||
static pid_t
|
||||
start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync)
|
||||
{
|
||||
char *argv[3];
|
||||
char *argv[7];
|
||||
int argc = 0, nullfd;
|
||||
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";
|
||||
break;
|
||||
}
|
||||
|
||||
argv[argc++] = (char *)"-u";
|
||||
argv[argc++] = (char *)ldpd_privs.user;
|
||||
argv[argc++] = (char *)"-g";
|
||||
argv[argc++] = (char *)ldpd_privs.group;
|
||||
argv[argc++] = NULL;
|
||||
|
||||
execvp(argv0, argv);
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "log.h"
|
||||
|
||||
#include <lib/log.h>
|
||||
#include <lib/log_int.h>
|
||||
|
||||
const char *log_procname;
|
||||
|
||||
|
|
20
lib/clippy.c
20
lib/clippy.c
|
@ -107,21 +107,11 @@ int main(int argc, char **argv)
|
|||
#include "log.h"
|
||||
#include "zassert.h"
|
||||
|
||||
#define ZLOG_FUNC(FUNCNAME) \
|
||||
void FUNCNAME(const char *format, ...) \
|
||||
{ \
|
||||
va_list args; \
|
||||
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 vzlog(int prio, const char *format, va_list args)
|
||||
{
|
||||
vfprintf(stderr, format, args);
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
|
||||
void _zlog_assert_failed(const char *assertion, const char *file,
|
||||
unsigned int line, const char *function)
|
||||
|
|
487
lib/command.c
487
lib/command.c
|
@ -31,7 +31,7 @@
|
|||
#include "frrstr.h"
|
||||
#include "memory.h"
|
||||
#include "log.h"
|
||||
#include "log_int.h"
|
||||
#include "log_vty.h"
|
||||
#include "thread.h"
|
||||
#include "vector.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 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. */
|
||||
void print_version(const char *progname)
|
||||
{
|
||||
|
@ -493,6 +434,8 @@ static char *zencrypt(const char *passwd)
|
|||
return crypt(passwd, salt);
|
||||
}
|
||||
|
||||
static bool full_cli;
|
||||
|
||||
/* This function write configuration of this host. */
|
||||
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
|
||||
* parse frr.conf.
|
||||
*/
|
||||
if (strcmp(zlog_default->protoname, "WATCHFRR")) {
|
||||
if (full_cli) {
|
||||
if (host.encrypt) {
|
||||
if (host.password_encrypt)
|
||||
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",
|
||||
host.enable);
|
||||
}
|
||||
|
||||
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);
|
||||
log_config_write(vty);
|
||||
|
||||
if (host.advanced)
|
||||
vty_out(vty, "service advanced-vty\n");
|
||||
|
@ -2273,7 +2164,8 @@ DEFUN (config_logmsg,
|
|||
int level;
|
||||
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;
|
||||
|
||||
zlog(level, "%s",
|
||||
|
@ -2284,348 +2176,6 @@ DEFUN (config_logmsg,
|
|||
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,
|
||||
debug_memstats_cmd,
|
||||
"[no] debug memstats-at-exit",
|
||||
|
@ -2875,7 +2425,6 @@ void cmd_init(int terminal)
|
|||
#endif
|
||||
host.password = NULL;
|
||||
host.enable = NULL;
|
||||
host.logfile = NULL;
|
||||
host.config = NULL;
|
||||
host.noconfig = (terminal < 0);
|
||||
host.lines = -1;
|
||||
|
@ -2903,7 +2452,6 @@ void cmd_init(int terminal)
|
|||
install_element(VIEW_NODE, &config_enable_cmd);
|
||||
install_element(VIEW_NODE, &config_terminal_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, &echo_cmd);
|
||||
install_element(VIEW_NODE, &autocomplete_cmd);
|
||||
|
@ -2930,6 +2478,8 @@ void cmd_init(int terminal)
|
|||
install_element(CONFIG_NODE, &no_domainname_cmd);
|
||||
|
||||
if (terminal > 0) {
|
||||
full_cli = true;
|
||||
|
||||
install_element(CONFIG_NODE, &debug_memstats_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, &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, &no_service_password_encrypt_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, &no_service_terminal_length_cmd);
|
||||
|
||||
log_cmd_init();
|
||||
vrf_install_commands();
|
||||
}
|
||||
|
||||
|
@ -3000,7 +2534,6 @@ void cmd_terminate(void)
|
|||
XFREE(MTYPE_HOST, host.password_encrypt);
|
||||
XFREE(MTYPE_HOST, host.enable);
|
||||
XFREE(MTYPE_HOST, host.enable_encrypt);
|
||||
XFREE(MTYPE_HOST, host.logfile);
|
||||
XFREE(MTYPE_HOST, host.motdfile);
|
||||
XFREE(MTYPE_HOST, host.config);
|
||||
XFREE(MTYPE_HOST, host.motd);
|
||||
|
|
|
@ -66,9 +66,6 @@ struct host {
|
|||
/* System wide terminal lines. */
|
||||
int lines;
|
||||
|
||||
/* Log filename. */
|
||||
char *logfile;
|
||||
|
||||
/* config file name of this host */
|
||||
char *config;
|
||||
int noconfig;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "frr_pthread.h"
|
||||
#include "memory.h"
|
||||
#include "linklist.h"
|
||||
#include "zlog.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread")
|
||||
DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives")
|
||||
|
@ -273,6 +274,8 @@ static void *fpt_run(void *arg)
|
|||
struct frr_pthread *fpt = arg;
|
||||
fpt->master->owner = pthread_self();
|
||||
|
||||
zlog_tls_buffer_init();
|
||||
|
||||
int sleeper[2];
|
||||
pipe(sleeper);
|
||||
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[0]);
|
||||
|
||||
zlog_tls_buffer_fini();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -45,11 +45,7 @@ int main(int argc, char **argv)
|
|||
|
||||
master = thread_master_create(NULL);
|
||||
|
||||
openzlog("grammar_sandbox", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
||||
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);
|
||||
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||
|
||||
/* Library inits. */
|
||||
cmd_init(1);
|
||||
|
|
14
lib/libfrr.c
14
lib/libfrr.c
|
@ -33,7 +33,6 @@
|
|||
#include "lib_vty.h"
|
||||
#include "log_vty.h"
|
||||
#include "zclient.h"
|
||||
#include "log_int.h"
|
||||
#include "module.h"
|
||||
#include "network.h"
|
||||
#include "lib_errors.h"
|
||||
|
@ -630,6 +629,7 @@ struct thread_master *frr_init(void)
|
|||
{
|
||||
struct option_chain *oc;
|
||||
struct frrmod_runtime *module;
|
||||
struct zprivs_ids_t ids;
|
||||
char moderr[256];
|
||||
char p_instance[16] = "", p_pathspace[256] = "";
|
||||
const char *dir;
|
||||
|
@ -657,9 +657,11 @@ struct thread_master *frr_init(void)
|
|||
#endif
|
||||
|
||||
zprivs_preinit(di->privs);
|
||||
zprivs_get_ids(&ids);
|
||||
|
||||
openzlog(di->progname, di->logname, di->instance,
|
||||
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
||||
zlog_init(di->progname, di->logname, di->instance,
|
||||
ids.uid_normal, ids.gid_normal);
|
||||
zlog_tls_buffer_init();
|
||||
|
||||
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);
|
||||
lib_cmd_init();
|
||||
log_filter_cmd_init();
|
||||
|
||||
frr_pthread_init();
|
||||
|
||||
|
@ -1086,7 +1087,7 @@ void frr_run(struct thread_master *master)
|
|||
}
|
||||
|
||||
/* end fixed stderr startup logging */
|
||||
zlog_startup_stderr = false;
|
||||
zlog_startup_end();
|
||||
|
||||
struct thread thread;
|
||||
while (thread_fetch(master, &thread))
|
||||
|
@ -1119,7 +1120,8 @@ void frr_fini(void)
|
|||
/* signal_init -> nothing needed */
|
||||
thread_master_free(master);
|
||||
master = NULL;
|
||||
closezlog();
|
||||
zlog_tls_buffer_fini();
|
||||
zlog_fini();
|
||||
/* frrmod_init -> nothing needed / hooks */
|
||||
rcu_shutdown();
|
||||
|
||||
|
|
683
lib/log.c
683
lib/log.c
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "zclient.h"
|
||||
#include "log.h"
|
||||
#include "log_int.h"
|
||||
#include "memory.h"
|
||||
#include "command.h"
|
||||
#include "lib_errors.h"
|
||||
|
@ -33,154 +32,12 @@
|
|||
#include "printfrr.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
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#include <dlfcn.h>
|
||||
#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.
|
||||
*
|
||||
|
@ -264,274 +121,12 @@ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
|
|||
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
|
||||
*
|
||||
* 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. */
|
||||
void zlog_signal(int signo, const char *action, void *siginfo_v,
|
||||
void *program_counter)
|
||||
|
@ -540,14 +135,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
|
|||
time_t now;
|
||||
char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")
|
||||
+ 100];
|
||||
char *msgstart;
|
||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
|
||||
|
||||
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);
|
||||
if (program_counter)
|
||||
|
@ -559,9 +149,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
|
|||
(ptrdiff_t)siginfo->si_addr);
|
||||
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;
|
||||
|
||||
|
@ -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",
|
||||
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.
|
||||
|
@ -609,64 +199,24 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter)
|
|||
bprintfrr(&fb, " %s (mapped at %p)",
|
||||
dlinfo.dli_fname, dlinfo.dli_fbase);
|
||||
bprintfrr(&fb, "\n");
|
||||
crash_write(&fb, NULL);
|
||||
zlog_sigsafe(fb.buf, fb.pos - fb.buf);
|
||||
}
|
||||
#elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK)
|
||||
static const char pclabel[] = "Program counter: ";
|
||||
#elif defined(HAVE_GLIBC_BACKTRACE)
|
||||
void *array[64];
|
||||
int size;
|
||||
int size, i;
|
||||
char buf[128];
|
||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
|
||||
char **bt = NULL;
|
||||
|
||||
#ifdef HAVE_GLIBC_BACKTRACE
|
||||
size = backtrace(array, array_size(array));
|
||||
if (size <= 0 || (size_t)size > array_size(array))
|
||||
return;
|
||||
|
||||
#define DUMP(FD) \
|
||||
{ \
|
||||
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;
|
||||
bprintfrr(&fb, "Backtrace for %d stack frames:", size);
|
||||
zlog_sigsafe(fb.pos, fb.buf - fb.pos);
|
||||
|
||||
#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);
|
||||
#endif
|
||||
/* Just print the function addresses. */
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
fb.pos = buf;
|
||||
if (bt)
|
||||
|
@ -674,20 +224,10 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter)
|
|||
else
|
||||
bprintfrr(&fb, "[bt %d] 0x%tx", i,
|
||||
(ptrdiff_t)(array[i]));
|
||||
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);
|
||||
zlog_sigsafe(fb.buf, fb.pos - fb.buf);
|
||||
}
|
||||
if (bt)
|
||||
free(bt);
|
||||
}
|
||||
}
|
||||
#undef DUMP
|
||||
#endif /* HAVE_STRACK_TRACE */
|
||||
}
|
||||
|
||||
|
@ -754,36 +294,6 @@ void zlog_backtrace(int priority)
|
|||
#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)
|
||||
{
|
||||
struct thread *tc;
|
||||
|
@ -801,11 +311,6 @@ void zlog_thread_info(int log_level)
|
|||
void _zlog_assert_failed(const char *assertion, const char *file,
|
||||
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",
|
||||
assertion, file, line, (function ? function : "?"));
|
||||
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)
|
||||
{
|
||||
flog_err_sys(EC_LIB_SYSTEM_CALL,
|
||||
"out of memory: failed to allocate %zu bytes for %s"
|
||||
"object",
|
||||
zlog(LOG_CRIT,
|
||||
"out of memory: failed to allocate %zu bytes for %s object",
|
||||
size, name);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
zlog_backtrace(LOG_CRIT);
|
||||
log_memstats(stderr, "log");
|
||||
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. */
|
||||
const char *safe_strerror(int errnum)
|
||||
{
|
||||
|
|
63
lib/log.h
63
lib/log.h
|
@ -22,21 +22,21 @@
|
|||
#ifndef _ZEBRA_LOG_H
|
||||
#define _ZEBRA_LOG_H
|
||||
|
||||
#include "zassert.h"
|
||||
|
||||
#include <syslog.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "lib/hook.h"
|
||||
#include "lib/zlog.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent
|
||||
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;
|
||||
extern void zlog_rotate(void);
|
||||
|
||||
/* Message structure. */
|
||||
struct message {
|
||||
|
@ -73,22 +61,6 @@ struct message {
|
|||
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 */
|
||||
#define flog_err(ferr_id, format, ...) \
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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 int vzlog_test(int priority);
|
||||
|
||||
/* structure useful for avoiding repeated rendering of the same timestamp */
|
||||
struct timestamp_control {
|
||||
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 "command.h"
|
||||
#include "lib/vty.h"
|
||||
#include "lib/log.h"
|
||||
#include "lib/zlog_targets.h"
|
||||
#include "lib/lib_errors.h"
|
||||
#include "lib/printfrr.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/log_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY (log_filter,
|
||||
log_filter_cmd,
|
||||
"[no] log-filter WORD$filter",
|
||||
NO_STR
|
||||
FILTER_LOG_STR
|
||||
"String to filter by\n")
|
||||
{
|
||||
int ret = 0;
|
||||
#define ZLOG_MAXLVL(a, b) MAX(a, b)
|
||||
|
||||
if (no)
|
||||
ret = zlog_filter_del(filter);
|
||||
DEFINE_HOOK(zlog_rotate, (), ())
|
||||
|
||||
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
|
||||
ret = zlog_filter_add(filter);
|
||||
|
||||
if (ret == 1) {
|
||||
vty_out(vty, "%% filter table full\n");
|
||||
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);
|
||||
return CMD_SUCCESS;
|
||||
vty_out(vty, "level %s, facility %s, ident %s\n",
|
||||
zlog_priority[level],
|
||||
facility_name(zlog_syslog_get_facility()),
|
||||
zlog_progname);
|
||||
}
|
||||
|
||||
/* 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",
|
||||
DEFUN (show_logging,
|
||||
show_logging_cmd,
|
||||
"show logging",
|
||||
SHOW_STR
|
||||
FILTER_LOG_STR)
|
||||
"Show current logging configuration\n")
|
||||
{
|
||||
char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = "";
|
||||
int len = 0;
|
||||
log_show_syslog(vty);
|
||||
|
||||
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, "%% failed to get filters\n");
|
||||
return CMD_WARNING;
|
||||
vty_out(vty, "File logging: ");
|
||||
if (zt_file.prio_min == ZLOG_DISABLED || !zt_file.filename)
|
||||
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;
|
||||
}
|
||||
|
||||
DEFPY (config_log_stdout,
|
||||
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;
|
||||
}
|
||||
|
||||
if (len != 0)
|
||||
vty_out(vty, "%s", log_filters);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
install_element(CONFIG_NODE, &log_filter_cmd);
|
||||
install_element(CONFIG_NODE, &log_filter_clear_cmd);
|
||||
int level = log_default_lvl;
|
||||
|
||||
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__
|
||||
#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__ */
|
||||
|
|
|
@ -100,6 +100,8 @@ lib_libfrr_la_SOURCES = \
|
|||
lib/yang_translator.c \
|
||||
lib/yang_wrappers.c \
|
||||
lib/zclient.c \
|
||||
lib/zlog.c \
|
||||
lib/zlog_targets.c \
|
||||
lib/printf/printf-pos.c \
|
||||
lib/printf/vfprintf.c \
|
||||
lib/printf/glue.c \
|
||||
|
@ -254,6 +256,8 @@ pkginclude_HEADERS += \
|
|||
lib/zassert.h \
|
||||
lib/zclient.h \
|
||||
lib/zebra.h \
|
||||
lib/zlog.h \
|
||||
lib/zlog_targets.h \
|
||||
lib/pbr.h \
|
||||
# end
|
||||
|
||||
|
@ -265,7 +269,6 @@ nodist_pkginclude_HEADERS += \
|
|||
|
||||
noinst_HEADERS += \
|
||||
lib/clippy.h \
|
||||
lib/log_int.h \
|
||||
lib/plist_int.h \
|
||||
lib/printf/printfcommon.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)
|
||||
timeout = 0;
|
||||
|
||||
zlog_tls_buffer_flush();
|
||||
rcu_read_unlock();
|
||||
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)
|
||||
{
|
||||
struct mlag_msg mlag_msg;
|
||||
char buf[ZLOG_FILTER_LENGTH_MAX];
|
||||
char buf[80];
|
||||
int rc = 0;
|
||||
size_t length;
|
||||
|
||||
|
|
|
@ -77,6 +77,8 @@ static void *logpump_run(void *arg)
|
|||
|
||||
period = 1000000000L / lp_frequency;
|
||||
|
||||
zlog_tls_buffer_init();
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
next = start;
|
||||
do {
|
||||
|
@ -109,6 +111,8 @@ static void *logpump_run(void *arg)
|
|||
#endif
|
||||
} while (delta < lp_duration);
|
||||
|
||||
zlog_tls_buffer_fini();
|
||||
|
||||
#ifdef RUSAGE_THREAD
|
||||
getrusage(RUSAGE_THREAD, &lp_rusage);
|
||||
#else
|
||||
|
|
|
@ -1381,8 +1381,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
|
|||
static void bgp_startup(void)
|
||||
{
|
||||
cmd_init(1);
|
||||
openzlog("testbgpd", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
||||
LOG_DAEMON);
|
||||
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||
zprivs_preinit(&bgpd_privs);
|
||||
zprivs_init(&bgpd_privs);
|
||||
|
||||
|
@ -1438,7 +1437,6 @@ static void bgp_shutdown(void)
|
|||
zprivs_terminate(&bgpd_privs);
|
||||
thread_master_free(master);
|
||||
master = NULL;
|
||||
closezlog();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
|
|
@ -53,7 +53,6 @@ static void vty_do_exit(int isexit)
|
|||
nb_terminate();
|
||||
yang_terminate();
|
||||
thread_master_free(master);
|
||||
closezlog();
|
||||
|
||||
log_memstats(stderr, "testcli");
|
||||
if (!isexit)
|
||||
|
@ -71,11 +70,7 @@ int main(int argc, char **argv)
|
|||
/* master init. */
|
||||
master = thread_master_create(NULL);
|
||||
|
||||
openzlog("common-cli", "NONE", 0, 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);
|
||||
zlog_aux_init("NONE: ", ZLOG_DISABLED);
|
||||
|
||||
/* Library inits. */
|
||||
cmd_init(1);
|
||||
|
|
|
@ -374,7 +374,6 @@ static void vty_do_exit(int isexit)
|
|||
nb_terminate();
|
||||
yang_terminate();
|
||||
thread_master_free(master);
|
||||
closezlog();
|
||||
|
||||
log_memstats(stderr, "test-nb-oper-data");
|
||||
if (!isexit)
|
||||
|
@ -402,11 +401,7 @@ int main(int argc, char **argv)
|
|||
/* master init. */
|
||||
master = thread_master_create(NULL);
|
||||
|
||||
openzlog("test-nb-oper-data", "NONE", 0,
|
||||
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);
|
||||
zlog_aux_init("NONE: ", ZLOG_DISABLED);
|
||||
|
||||
/* Library inits. */
|
||||
cmd_init(1);
|
||||
|
|
|
@ -73,11 +73,7 @@ int main(void)
|
|||
master = thread_master_create(NULL);
|
||||
signal_init(master, array_size(sigs), sigs);
|
||||
|
||||
openzlog("testsegv", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
||||
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);
|
||||
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||
|
||||
thread_execute(master, threadfunc, 0, 0);
|
||||
|
||||
|
|
|
@ -57,11 +57,7 @@ int main(void)
|
|||
master = thread_master_create(NULL);
|
||||
signal_init(master, array_size(sigs), sigs);
|
||||
|
||||
openzlog("testsig", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
||||
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);
|
||||
zlog_aux_init("NONE: ", LOG_DEBUG);
|
||||
|
||||
while (thread_fetch(master, &t))
|
||||
thread_call(&t);
|
||||
|
|
|
@ -52,9 +52,7 @@ bool (*tests[])(void) = {
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
openzlog("testzlog", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
|
||||
LOG_ERR);
|
||||
zlog_set_file("test_zlog.log", LOG_DEBUG);
|
||||
zlog_aux_init("NONE: ", ZLOG_DISABLED);
|
||||
|
||||
for (unsigned int i = 0; i < array_size(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;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
vtysh_write_terminal_cmd,
|
||||
"write terminal ["DAEMONS_LIST"]",
|
||||
|
@ -4107,9 +4010,6 @@ void vtysh_init_vty(void)
|
|||
|
||||
/* Logging */
|
||||
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_level_cmd);
|
||||
install_element(CONFIG_NODE, &no_vtysh_log_stdout_cmd);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "command.h"
|
||||
#include "libfrr.h"
|
||||
#include "lib_errors.h"
|
||||
#include "zlog_targets.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <sys/un.h>
|
||||
|
@ -1369,11 +1370,10 @@ int main(int argc, char **argv)
|
|||
|
||||
frr_config_fork();
|
||||
|
||||
zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
|
||||
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
|
||||
zlog_set_level(ZLOG_DEST_STDOUT, MIN(gs.loglevel, LOG_DEBUG));
|
||||
zlog_aux_init(NULL, MIN(gs.loglevel, LOG_DEBUG));
|
||||
|
||||
frr_run(master);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "memory.h"
|
||||
#include "log.h"
|
||||
#include "log_vty.h"
|
||||
#include "vty.h"
|
||||
#include "command.h"
|
||||
|
||||
|
@ -134,6 +135,19 @@ DEFUN (show_watchfrr,
|
|||
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
|
||||
#include "watchfrr/watchfrr_vty_clippy.c"
|
||||
#endif
|
||||
|
@ -190,4 +204,5 @@ void watchfrr_vty_init(void)
|
|||
|
||||
install_element(CONFIG_NODE, &show_debugging_watchfrr_cmd);
|
||||
install_element(VIEW_NODE, &show_watchfrr_cmd);
|
||||
install_element(VIEW_NODE, &show_logging_cmd);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue