]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: rewrite zlog lock-free & TLS-buffered
authorDavid Lamparter <equinox@opensourcerouting.org>
Sat, 6 May 2017 04:40:17 +0000 (06:40 +0200)
committerDavid Lamparter <equinox@diac24.net>
Wed, 1 Apr 2020 04:53:26 +0000 (06:53 +0200)
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>
33 files changed:
bfdd/bfdd.c
bgpd/rfapi/rfapi_vty.c
configure.ac
ldpd/ldpd.c
ldpd/log.c
lib/clippy.c
lib/command.c
lib/command.h
lib/frr_pthread.c
lib/grammar_sandbox_main.c
lib/libfrr.c
lib/log.c
lib/log.h
lib/log_int.h [deleted file]
lib/log_vty.c
lib/log_vty.h
lib/subdir.am
lib/thread.c
lib/zlog.c [new file with mode: 0644]
lib/zlog.h [new file with mode: 0644]
lib/zlog_targets.c [new file with mode: 0644]
lib/zlog_targets.h [new file with mode: 0644]
pimd/pim_mlag.c
sharpd/sharp_logpump.c
tests/bgpd/test_peer_attr.c
tests/lib/cli/common_cli.c
tests/lib/northbound/test_oper_data.c
tests/lib/test_segv.c
tests/lib/test_sig.c
tests/lib/test_zlog.c
vtysh/vtysh.c
watchfrr/watchfrr.c
watchfrr/watchfrr_vty.c

index 69f268ab016e811207c82b322145ea1945db5220..6190be917279421c3a5d13de3bf4063ee3226322 100644 (file)
@@ -25,6 +25,7 @@
 #include "bfd.h"
 #include "bfdd_nb.h"
 #include "lib/version.h"
+#include "lib/command.h"
 
 
 /*
index 5a84d14bd9da5b16ba2ea938a39933d8f1365009..0d4868fa5411baef57ff20e8d1797d2b50e1b900 100644 (file)
@@ -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)
index 628e0c8afc1dbce65b0f70cb5cc97219155bd040..f2343cc1b12735bd8d28f028bf0877a4e252f954 100755 (executable)
@@ -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"!
@@ -2197,22 +2201,12 @@ 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])
+    AC_CHECK_HEADER([execinfo.h], [
+      AC_SEARCH_LIBS([backtrace], [execinfo], [
+        AC_DEFINE([HAVE_GLIBC_BACKTRACE], [1], [Glibc backtrace])
         backtrace_ok=yes
-      ])
-      ;;
-    esac
-    if test "$backtrace_ok" = "no"; then
-      AC_CHECK_HEADER([execinfo.h], [
-        AC_SEARCH_LIBS([backtrace], [execinfo], [
-          AC_DEFINE([HAVE_GLIBC_BACKTRACE], [1], [Glibc backtrace])
-          backtrace_ok=yes
-        ],, [-lm])
-      ])
-    fi
+      ],, [-lm])
+    ])
   fi
 
   if test "$enable_backtrace" = "yes" -a "$backtrace_ok" = "no"; then
index 0f9f055d02544cc7a4010f5c87c4ee11a2e70dfb..818d744207d048841ce361139a6881fb3e768754 100644 (file)
@@ -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);
index b138e5754aa7762890c3e6d124c722718368024c..19030175884e94479e26863a83e5463723274404 100644 (file)
@@ -24,7 +24,6 @@
 #include "log.h"
 
 #include <lib/log.h>
-#include <lib/log_int.h>
 
 const char     *log_procname;
 
index cd8067f5ebfd34b43b029fa3a864d59198604025..2e09c24c66f8531bc149bb7fbdd1fb9e4e9faef0 100644 (file)
@@ -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)
index 8811b3a7915484d5d6429a8276f8a68e8e4e35b0..be8f1e72a36f0feddb16e21fd49c9b21350259ce 100644 (file)
@@ -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);
index ea8a76a964f73881650b59a88a59f6976c5bc643..190e0e93af2d86db93c6c5cc2b84d59f108d1943 100644 (file)
@@ -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;
index 55f0b55ed6e7e5b7a7fda9d3dbf2297a610e5eb6..e237934f815891d7d023600103c5f737f8afd7a2 100644 (file)
@@ -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;
 }
index 5d3f6675a354ea13dfc5b5bc8df72f72c87606e5..6a28580925f3800c5af4eaa6c63adfce5dc278b6 100644 (file)
@@ -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);
index 3622890e46585bd3dec2640af4bba159144f103c..084ab93a4e84575a498b0671ad6183ae276358cf 100644 (file)
@@ -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();
 
index b3be5216aaa6fd4ed060891ed053609da8e4400a..9b0f5b3d85c7d9631ff15a67432f5330ba8547d6 100644 (file)
--- a/lib/log.c
+++ b/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"
 #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,85 +199,35 @@ 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;
-
-#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:", size);
+       zlog_sigsafe(fb.pos, fb.buf - fb.pos);
 
-       bprintfrr(&fb, "Backtrace for %d stack frames:\n", size);
+       bt = backtrace_symbols(array, 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)
-                                       bprintfrr(&fb, "%s", bt[i]);
-                               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);
-                       }
-                       if (bt)
-                               free(bt);
-               }
+       for (i = 0; i < size; i++) {
+               fb.pos = buf;
+               if (bt)
+                       bprintfrr(&fb, "%s", bt[i]);
+               else
+                       bprintfrr(&fb, "[bt %d] 0x%tx", i,
+                                 (ptrdiff_t)(array[i]));
+               zlog_sigsafe(fb.buf, fb.pos - fb.buf);
        }
-#undef DUMP
+       if (bt)
+               free(bt);
 #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",
-                    size, name);
-       zlog_backtrace(LOG_ERR);
+       zlog(LOG_CRIT,
+            "out of memory: failed to allocate %zu bytes for %s object",
+            size, name);
+       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)
 {
index d79ad9f8055fea8ad690f4dd14f25e1ff0df06e6..e7297337bedc17b1f22928fdff556068641573f5 100644 (file)
--- a/lib/log.h
+++ b/lib/log.h
 #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 */
diff --git a/lib/log_int.h b/lib/log_int.h
deleted file mode 100644 (file)
index 287e626..0000000
+++ /dev/null
@@ -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 */
index 68d598f5656d740a135668e78c685c25234b6d5c..01d19e679d77a41dbfd67a9fc4690ef5b6f9cb36 100644 (file)
 
 #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")
+#define ZLOG_MAXLVL(a, b) MAX(a, b)
+
+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)
 {
-       int ret = 0;
+       const struct facility_map *fm;
 
-       if (no)
-               ret = zlog_filter_del(filter);
+       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, "level %s, facility %s, ident %s\n",
+                       zlog_priority[level],
+                       facility_name(zlog_syslog_get_facility()),
+                       zlog_progname);
+}
 
-       vty_out(vty, " %s\n", filter);
+DEFUN (show_logging,
+       show_logging_cmd,
+       "show logging",
+       SHOW_STR
+       "Show current logging configuration\n")
+{
+       log_show_syslog(vty);
+
+       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");
+
+       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;
 }
 
-/* Clear all log filters */
-DEFPY (log_filter_clear,
-       log_filter_clear_cmd,
-       "clear log-filter",
-       CLEAR_STR
-       FILTER_LOG_STR)
+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)
 {
-       zlog_filter_clear();
+       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;
 }
 
-/* Show log filter */
-DEFPY (show_log_filter,
-       show_log_filter_cmd,
-       "show log-filter",
-       SHOW_STR
-       FILTER_LOG_STR)
+DEFUN_HIDDEN (config_log_monitor,
+       config_log_monitor_cmd,
+       "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
+       "Logging control\n"
+       "Set terminal line (monitor) logging level\n"
+       LOG_LEVEL_DESC)
+{
+       vty_out(vty, "%% \"log monitor\" is deprecated and does nothing.\n");
+       return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (no_config_log_monitor,
+       no_config_log_monitor_cmd,
+       "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
+       NO_STR
+       "Logging control\n"
+       "Disable terminal line (monitor) logging\n"
+       LOG_LEVEL_DESC)
+{
+       return CMD_SUCCESS;
+}
+
+static int set_log_file(struct zlog_cfg_file *target, struct vty *vty,
+                       const char *fname, int loglevel)
+{
+       char *p = NULL;
+       const char *fullpath;
+       bool ok;
+
+       /* Path detection. */
+       if (!IS_DIRECTORY_SEP(*fname)) {
+               char cwd[MAXPATHLEN + 1];
+
+               cwd[MAXPATHLEN] = '\0';
+
+               if (getcwd(cwd, MAXPATHLEN) == NULL) {
+                       flog_err_sys(EC_LIB_SYSTEM_CALL,
+                                    "config_log_file: Unable to alloc mem!");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+
+               p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2);
+               sprintf(p, "%s/%s", cwd, fname);
+               fullpath = p;
+       } else
+               fullpath = fname;
+
+       target->prio_min = loglevel;
+       ok = zlog_file_set_filename(target, fullpath);
+
+       XFREE(MTYPE_TMP, p);
+
+       if (!ok) {
+               if (vty)
+                       vty_out(vty, "can't open logfile %s\n", fname);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       return CMD_SUCCESS;
+}
+
+void command_setup_early_logging(const char *dest, const char *level)
+{
+       int nlevel;
+       char *sep;
+       int len;
+       char type[8];
+
+       if (level) {
+               nlevel = log_level_match(level);
+
+               if (nlevel == ZLOG_DISABLED) {
+                       fprintf(stderr, "invalid log level \"%s\"\n", level);
+                       exit(1);
+               }
+       } else
+               nlevel = log_default_lvl;
+
+       if (!dest)
+               return;
+
+       sep = strchr(dest, ':');
+       len = sep ? (int)(sep - dest) : (int)strlen(dest);
+
+       snprintfrr(type, sizeof(type), "%.*s", len, dest);
+
+       if (strcmp(type, "stdout") == 0) {
+               log_cmdline_stdout_lvl = nlevel;
+               zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
+                                                log_cmdline_stdout_lvl);
+               zlog_file_set_other(&zt_stdout);
+               return;
+       }
+       if (strcmp(type, "syslog") == 0) {
+               log_cmdline_syslog_lvl = nlevel;
+               zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
+                                                    log_cmdline_syslog_lvl));
+               return;
+       }
+       if (strcmp(type, "file") == 0 && sep) {
+               sep++;
+               set_log_file(&zt_file_cmdline, NULL, sep, nlevel);
+               return;
+       }
+
+       fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest);
+       exit(1);
+}
+
+DEFUN (clear_log_cmdline,
+       clear_log_cmdline_cmd,
+       "clear log cmdline-targets",
+       CLEAR_STR
+       "Logging control\n"
+       "Disable log targets specified at startup by --log option\n")
 {
-       char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = "";
-       int len = 0;
+       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);
 
-       len = zlog_filter_dump(log_filters, sizeof(log_filters));
+       return CMD_SUCCESS;
+}
 
-       if (len == -1) {
-               vty_out(vty, "%% failed to get filters\n");
-               return CMD_WARNING;
+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)
+{
+       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;
+}
 
-       if (len != 0)
-               vty_out(vty, "%s", log_filters);
+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_filter_cmd_init(void)
+void log_config_write(struct vty *vty)
 {
-       install_element(VIEW_NODE, &show_log_filter_cmd);
-       install_element(CONFIG_NODE, &log_filter_cmd);
-       install_element(CONFIG_NODE, &log_filter_clear_cmd);
+       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);
 }
index fa5627e4bd1ab3535ca15470d02247f153e140a4..0fd60e9b078ade35d8e7db6fdfa7b958ee8b4d8d 100644 (file)
 
 #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__ */
index 4f62eb22641390fbc9a16c184ba41000d02ffbf9..40c21dcfedc2291af4a4fd454272dde199292885 100644 (file)
@@ -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 \
index dbf668a699c1c1aa0d250fc05fa2436f4ae4e8db..4d689a9f88a9e6a5aca5c2123aa5b863d11d54d8 100644 (file)
@@ -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();
 
diff --git a/lib/zlog.c b/lib/zlog.c
new file mode 100644 (file)
index 0000000..4cab236
--- /dev/null
@@ -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));
+       }
+}
diff --git a/lib/zlog.h b/lib/zlog.h
new file mode 100644 (file)
index 0000000..fd42ad5
--- /dev/null
@@ -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 */
diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c
new file mode 100644 (file)
index 0000000..ff9cd1e
--- /dev/null
@@ -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);
+}
diff --git a/lib/zlog_targets.h b/lib/zlog_targets.h
new file mode 100644 (file)
index 0000000..7d7b26e
--- /dev/null
@@ -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 */
index 304e6ac6bc9a47c7203f34c77ef2608047c782c9..47735475f3f58ab32788b9961215c84ece645d0c 100644 (file)
@@ -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;
 
index d07e2d273f262e25f9b3005c58ed8fcb4159a5a9..322d802b8aa9b6feffe54c371e9eed6f5c53d4d8 100644 (file)
@@ -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
index 422d397479f10dbb4bb90b2e5183de66aab5553a..631ce5a11d6bd6158e30b3a12627cd9d8d306fdf 100644 (file)
@@ -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)
index e091372ab8766bee6d3eaf0a4760f61a997c1f84..3302bb3d19e9b2e6c36dba3d4469529c40244246 100644 (file)
@@ -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);
index 18d3180889b406b6054fcd58f7563838b11c6a66..52216ea38228370c9636fef039ba6d711a719875 100644 (file)
@@ -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);
index 43ca0837d5f38e0a89b456b27c2f73d5b371c43b..8133637adcd6819f16a4705c1d4932128965bcaa 100644 (file)
@@ -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);
 
index cf63a3d047f1509c0b8f0cae52004f2c390219de..2aceafb8f0f940b6389f22fb0452c1f14ac6fad7 100644 (file)
@@ -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);
index 07885d984737d6da2f689e111986a7259ae2f096..3042cc6cc1c8bae078436363d537b92bd6ab8c1e 100644 (file)
@@ -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]())
index 310acdf37f741a7a0de7f3316b2ca6caa20864d6..2460c4b2ca4b63ebfd9847fa5264e31d061f2baf 100644 (file)
@@ -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);
index 05e6651229a709867d9e81c6eb8b3e86697aba1c..d6d8f77243ba54e670af0b74a1f64562f756d544 100644 (file)
@@ -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);
 
index c06cb89382e65528237be13425f2463b83472897..eda4f5d516bf3785ea2ca174ddad88bb64e62d0e 100644 (file)
@@ -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);
 }