diff options
Diffstat (limited to 'lib')
71 files changed, 3897 insertions, 2100 deletions
diff --git a/lib/agentx.c b/lib/agentx.c index d1b801fe8c..7c4bdcbe27 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -158,9 +158,13 @@ static void agentx_events_update(void) } /* AgentX node. */ -static struct cmd_node agentx_node = {SMUX_NODE, - "", /* AgentX has no interface. */ - 1}; +static int config_write_agentx(struct vty *vty); +static struct cmd_node agentx_node = { + .name = "smux", + .node = SMUX_NODE, + .prompt = "", + .config_write = config_write_agentx, +}; /* Logging NetSNMP messages */ static int agentx_log_callback(int major, int minor, void *serverarg, @@ -246,7 +250,7 @@ void smux_init(struct thread_master *tm) agentx_log_callback, NULL); init_agent(FRR_SMUX_NAME); - install_node(&agentx_node, config_write_agentx); + install_node(&agentx_node); install_element(CONFIG_NODE, &agentx_enable_cmd); install_element(CONFIG_NODE, &no_agentx_cmd); } diff --git a/lib/atomlist.h b/lib/atomlist.h index 621db6824f..5ca19cbcd4 100644 --- a/lib/atomlist.h +++ b/lib/atomlist.h @@ -20,6 +20,10 @@ #include "typesafe.h" #include "frratomic.h" +#ifdef __cplusplus +extern "C" { +#endif + /* pointer with lock/deleted/invalid bit in lowest bit * * for atomlist/atomsort, "locked" means "this pointer can't be updated, the @@ -34,7 +38,11 @@ * ATOMPTR_USER is currently unused (and available for atomic hash or skiplist * implementations.) */ -typedef uintptr_t atomptr_t; + +/* atomic_atomptr_t may look a bit odd, it's for the sake of C++ compat */ +typedef uintptr_t atomptr_t; +typedef atomic_uintptr_t atomic_atomptr_t; + #define ATOMPTR_MASK (UINTPTR_MAX - 3) #define ATOMPTR_LOCK (1) #define ATOMPTR_USER (2) @@ -104,13 +112,13 @@ static inline bool atomptr_u(atomptr_t val) /* don't use these structs directly */ struct atomlist_item { - _Atomic atomptr_t next; + atomic_uintptr_t next; }; #define atomlist_itemp(val) ((struct atomlist_item *)atomptr_p(val)) struct atomlist_head { - _Atomic atomptr_t first, last; - _Atomic size_t count; + atomic_uintptr_t first, last; + atomic_size_t count; }; /* use as: @@ -133,7 +141,7 @@ macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ { atomlist_add_tail(&h->ah, &item->field.ai); } \ macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ - _Atomic atomptr_t *hint) \ + atomic_atomptr_t *hint) \ { atomlist_del_hint(&h->ah, &item->field.ai, hint); } \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { atomlist_del_hint(&h->ah, &item->field.ai, NULL); \ @@ -189,7 +197,7 @@ void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item); * reads starting later. */ void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item, - _Atomic atomptr_t *hint); + atomic_atomptr_t *hint); /* pop: * @@ -202,13 +210,13 @@ struct atomlist_item *atomlist_pop(struct atomlist_head *h); struct atomsort_item { - _Atomic atomptr_t next; + atomic_atomptr_t next; }; #define atomsort_itemp(val) ((struct atomsort_item *)atomptr_p(val)) struct atomsort_head { - _Atomic atomptr_t first; - _Atomic size_t count; + atomic_atomptr_t first; + atomic_size_t count; }; #define _PREDECL_ATOMSORT(prefix) \ @@ -271,7 +279,7 @@ macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ return prev; \ } \ macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ - _Atomic atomptr_t *hint) \ + atomic_atomptr_t *hint) \ { \ atomsort_del_hint(&h->ah, &item->field.ai, hint); \ } \ @@ -353,8 +361,12 @@ struct atomsort_item *atomsort_add(struct atomsort_head *h, const struct atomsort_item *)); void atomsort_del_hint(struct atomsort_head *h, - struct atomsort_item *item, _Atomic atomptr_t *hint); + struct atomsort_item *item, atomic_atomptr_t *hint); struct atomsort_item *atomsort_pop(struct atomsort_head *h); +#ifdef __cplusplus +} +#endif + #endif /* _FRR_ATOMLIST_H */ diff --git a/lib/bitfield.h b/lib/bitfield.h index eebfc049d9..72980165f9 100644 --- a/lib/bitfield.h +++ b/lib/bitfield.h @@ -152,9 +152,8 @@ typedef unsigned int word_t; */ #define bf_free(v) \ do { \ - if ((v).data) { \ - free((v).data); \ - } \ + free((v).data); \ + (v).data = NULL; \ } while (0) #ifdef __cplusplus diff --git a/lib/clippy.c b/lib/clippy.c index cd8067f5eb..2e09c24c66 100644 --- a/lib/clippy.c +++ b/lib/clippy.c @@ -107,21 +107,11 @@ int main(int argc, char **argv) #include "log.h" #include "zassert.h" -#define ZLOG_FUNC(FUNCNAME) \ - void FUNCNAME(const char *format, ...) \ - { \ - va_list args; \ - va_start(args, format); \ - vfprintf(stderr, format, args); \ - fputs("\n", stderr); \ - va_end(args); \ - } - -ZLOG_FUNC(zlog_err) -ZLOG_FUNC(zlog_warn) -ZLOG_FUNC(zlog_info) -ZLOG_FUNC(zlog_notice) -ZLOG_FUNC(zlog_debug) +void vzlog(int prio, const char *format, va_list args) +{ + vfprintf(stderr, format, args); + fputs("\n", stderr); +} void _zlog_assert_failed(const char *assertion, const char *file, unsigned int line, const char *function) diff --git a/lib/command.c b/lib/command.c index 8811b3a791..38a7b3fe7c 100644 --- a/lib/command.c +++ b/lib/command.c @@ -31,7 +31,7 @@ #include "frrstr.h" #include "memory.h" #include "log.h" -#include "log_int.h" +#include "log_vty.h" #include "thread.h" #include "vector.h" #include "linklist.h" @@ -47,6 +47,7 @@ #include "hook.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "network.h" DEFINE_MTYPE_STATIC(LIB, HOST, "Host config") DEFINE_MTYPE(LIB, COMPLETION, "Completion item") @@ -73,86 +74,6 @@ const struct message tokennames[] = { item(END_TKN), {0}, }; - -const char *const node_names[] = { - "auth", // AUTH_NODE, - "view", // VIEW_NODE, - "auth enable", // AUTH_ENABLE_NODE, - "enable", // ENABLE_NODE, - "config", // CONFIG_NODE, - "debug", // DEBUG_NODE, - "vrf debug", // VRF_DEBUG_NODE, - "northbound debug", // NORTHBOUND_DEBUG_NODE, - "vnc debug", // DEBUG_VNC_NODE, - "route-map debug", /* RMAP_DEBUG_NODE */ - "resolver debug", /* RESOLVER_DEBUG_NODE */ - "aaa", // AAA_NODE, - "keychain", // KEYCHAIN_NODE, - "keychain key", // KEYCHAIN_KEY_NODE, - "static ip", // IP_NODE, - "vrf", // VRF_NODE, - "interface", // INTERFACE_NODE, - "nexthop-group", // NH_GROUP_NODE, - "zebra", // ZEBRA_NODE, - "table", // TABLE_NODE, - "rip", // RIP_NODE, - "ripng", // RIPNG_NODE, - "babel", // BABEL_NODE, - "eigrp", // EIGRP_NODE, - "bgp", // BGP_NODE, - "bgp vpnv4", // BGP_VPNV4_NODE, - "bgp vpnv6", // BGP_VPNV6_NODE, - "bgp ipv4 unicast", // BGP_IPV4_NODE, - "bgp ipv4 multicast", // BGP_IPV4M_NODE, - "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE, - "bgp ipv6", // BGP_IPV6_NODE, - "bgp ipv6 multicast", // BGP_IPV6M_NODE, - "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE, - "bgp vrf policy", // BGP_VRF_POLICY_NODE, - "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE, - "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE, - "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE, - "rfp defaults", // RFP_DEFAULTS_NODE, - "bgp evpn", // BGP_EVPN_NODE, - "ospf", // OSPF_NODE, - "ospf6", // OSPF6_NODE, - "ldp", // LDP_NODE, - "ldp ipv4", // LDP_IPV4_NODE, - "ldp ipv6", // LDP_IPV6_NODE, - "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE, - "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE, - "ldp l2vpn", // LDP_L2VPN_NODE, - "ldp", // LDP_PSEUDOWIRE_NODE, - "isis", // ISIS_NODE, - "ipv4 access list", // ACCESS_NODE, - "ipv4 prefix list", // PREFIX_NODE, - "ipv6 access list", // ACCESS_IPV6_NODE, - "MAC access list", // ACCESS_MAC_NODE, - "ipv6 prefix list", // PREFIX_IPV6_NODE, - "as list", // AS_LIST_NODE, - "community list", // COMMUNITY_LIST_NODE, - "routemap", // RMAP_NODE, - "pbr-map", // PBRMAP_NODE, - "smux", // SMUX_NODE, - "dump", // DUMP_NODE, - "forwarding", // FORWARDING_NODE, - "protocol", // PROTOCOL_NODE, - "mpls", // MPLS_NODE, - "pw", // PW_NODE, - "vty", // VTY_NODE, - "link-params", // LINK_PARAMS_NODE, - "bgp evpn vni", // BGP_EVPN_VNI_NODE, - "rpki", // RPKI_NODE - "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE - */ - "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE - */ - "bfd", /* BFD_NODE */ - "bfd peer", /* BFD_PEER_NODE */ - "openfabric", // OPENFABRIC_NODE - "vrrp", /* VRRP_NODE */ - "bmp", /* BMP_NODE */ -}; /* clang-format on */ /* Command vector which includes some level of command lists. Normally @@ -179,84 +100,45 @@ const char *cmd_domainname_get(void) return host.domainname; } +static int root_on_exit(struct vty *vty); + /* Standard command node structures. */ static struct cmd_node auth_node = { - AUTH_NODE, "Password: ", + .name = "auth", + .node = AUTH_NODE, + .prompt = "Password: ", }; static struct cmd_node view_node = { - VIEW_NODE, "%s> ", + .name = "view", + .node = VIEW_NODE, + .prompt = "%s> ", + .node_exit = root_on_exit, }; static struct cmd_node auth_enable_node = { - AUTH_ENABLE_NODE, "Password: ", + .name = "auth enable", + .node = AUTH_ENABLE_NODE, + .prompt = "Password: ", }; static struct cmd_node enable_node = { - ENABLE_NODE, "%s# ", + .name = "enable", + .node = ENABLE_NODE, + .prompt = "%s# ", + .node_exit = root_on_exit, }; -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 int config_write_host(struct vty *vty); +static struct cmd_node config_node = { + .name = "config", + .node = CONFIG_NODE, + .parent_node = ENABLE_NODE, + .prompt = "%s(config)# ", + .config_write = config_write_host, + .node_exit = vty_config_node_exit, }; -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) { @@ -345,10 +227,9 @@ static bool cmd_hash_cmp(const void *a, const void *b) } /* Install top node of command vector. */ -void install_node(struct cmd_node *node, int (*func)(struct vty *)) +void install_node(struct cmd_node *node) { vector_set_index(cmdvec, node->node, node); - node->func = func; node->cmdgraph = graph_new(); node->cmd_vector = vector_init(VECTOR_MIN_SIZE); // add start node @@ -386,9 +267,9 @@ void install_element(enum node_type ntype, const struct cmd_element *cmd) if (cnode == NULL) { fprintf(stderr, "%s[%s]:\n" - "\tnode %d (%s) does not exist.\n" + "\tnode %d does not exist.\n" "\tplease call install_node() before install_element()\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype); exit(EXIT_FAILURE); } @@ -397,7 +278,7 @@ void install_element(enum node_type ntype, const struct cmd_element *cmd) "%s[%s]:\n" "\tnode %d (%s) already has this command installed.\n" "\tduplicate install_element call?\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype, cnode->name); return; } @@ -435,9 +316,9 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd) if (cnode == NULL) { fprintf(stderr, "%s[%s]:\n" - "\tnode %d (%s) does not exist.\n" + "\tnode %d does not exist.\n" "\tplease call install_node() before uninstall_element()\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype); exit(EXIT_FAILURE); } @@ -446,7 +327,7 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd) "%s[%s]:\n" "\tnode %d (%s) does not have this command installed.\n" "\tduplicate uninstall_element call?\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype, cnode->name); return; } @@ -486,13 +367,15 @@ static char *zencrypt(const char *passwd) gettimeofday(&tv, 0); - to64(&salt[0], random(), 3); + to64(&salt[0], frr_weak_random(), 3); to64(&salt[3], tv.tv_usec, 3); salt[5] = '\0'; 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 +391,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 +406,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"); @@ -1448,90 +1279,25 @@ DEFUN (config_exit, return CMD_SUCCESS; } +static int root_on_exit(struct vty *vty) +{ + if (vty_shell(vty)) + exit(0); + else + vty->status = VTY_CLOSE; + return 0; +} + void cmd_exit(struct vty *vty) { - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: - if (vty_shell(vty)) - exit(0); - else - vty->status = VTY_CLOSE; - break; - case CONFIG_NODE: - vty->node = ENABLE_NODE; - vty_config_exit(vty); - break; - case INTERFACE_NODE: - case PW_NODE: - case VRF_NODE: - case NH_GROUP_NODE: - case ZEBRA_NODE: - case BGP_NODE: - case RIP_NODE: - case EIGRP_NODE: - case BABEL_NODE: - case RIPNG_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case LDP_NODE: - case LDP_L2VPN_NODE: - case ISIS_NODE: - case OPENFABRIC_NODE: - case KEYCHAIN_NODE: - case RMAP_NODE: - case PBRMAP_NODE: - case VTY_NODE: - case BFD_NODE: - vty->node = CONFIG_NODE; - break; - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV4L_NODE: - case BGP_VPNV4_NODE: - case BGP_VPNV6_NODE: - case BGP_FLOWSPECV4_NODE: - case BGP_FLOWSPECV6_NODE: - case BGP_VRF_POLICY_NODE: - case BGP_VNC_DEFAULTS_NODE: - case BGP_VNC_NVE_GROUP_NODE: - case BGP_VNC_L2_GROUP_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - case BGP_EVPN_NODE: - case BGP_IPV6L_NODE: - case BMP_NODE: - vty->node = BGP_NODE; - break; - case BGP_EVPN_VNI_NODE: - vty->node = BGP_EVPN_NODE; - break; - case LDP_IPV4_NODE: - case LDP_IPV6_NODE: - vty->node = LDP_NODE; - break; - case LDP_IPV4_IFACE_NODE: - vty->node = LDP_IPV4_NODE; - break; - case LDP_IPV6_IFACE_NODE: - vty->node = LDP_IPV6_NODE; - break; - case LDP_PSEUDOWIRE_NODE: - vty->node = LDP_L2VPN_NODE; - break; - case KEYCHAIN_KEY_NODE: - vty->node = KEYCHAIN_NODE; - break; - case LINK_PARAMS_NODE: - vty->node = INTERFACE_NODE; - break; - case BFD_PEER_NODE: - vty->node = BFD_NODE; - break; - default: - break; - } + struct cmd_node *cnode = vector_lookup(cmdvec, vty->node); + if (cnode->node_exit) { + if (!cnode->node_exit(vty)) + return; + } + if (cnode->parent_node) + vty->node = cnode->parent_node; if (vty->xpath_index > 0) vty->xpath_index--; } @@ -1556,7 +1322,6 @@ DEFUN (config_end, vty_config_exit(vty); vty->node = ENABLE_NODE; } - return CMD_SUCCESS; } @@ -1716,9 +1481,8 @@ static int vty_write_config(struct vty *vty) vty_out(vty, "!\n"); for (i = 0; i < vector_active(cmdvec); i++) - if ((node = vector_slot(cmdvec, i)) && node->func - && (node->vtysh || vty->type != VTY_SHELL)) { - if ((*node->func)(vty)) + if ((node = vector_slot(cmdvec, i)) && node->config_write) { + if ((*node->config_write)(vty)) vty_out(vty, "!\n"); } @@ -1772,7 +1536,8 @@ static int file_write_config(struct vty *vty) config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8); - sprintf(config_file_tmp, "%s.XXXXXX", config_file); + snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX", + config_file); /* Open file to configuration write. */ fd = mkstemp(config_file_tmp); @@ -2273,7 +2038,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 +2050,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", @@ -2800,7 +2224,7 @@ DEFUN(find, if (regexec(&exp, cli->string, 0, NULL, 0) == 0) vty_out(vty, " (%s) %s\n", - node_names[node->node], cli->string); + node->name, cli->string); } } @@ -2848,9 +2272,6 @@ void cmd_init(int terminal) { struct utsname names; - if (array_size(node_names) != NODE_TYPE_MAX) - assert(!"Update the CLI node description array!"); - uname(&names); qobj_init(); @@ -2875,7 +2296,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; @@ -2883,11 +2303,11 @@ void cmd_init(int terminal) host.motdfile = NULL; /* Install top nodes. */ - install_node(&view_node, NULL); - install_node(&enable_node, NULL); - install_node(&auth_node, NULL); - install_node(&auth_enable_node, NULL); - install_node(&config_node, config_write_host); + install_node(&view_node); + install_node(&enable_node); + install_node(&auth_node); + install_node(&auth_enable_node); + install_node(&config_node); /* Each node's basic commands. */ install_element(VIEW_NODE, &show_version_cmd); @@ -2903,7 +2323,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 +2349,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 +2358,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 +2367,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 +2405,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); diff --git a/lib/command.h b/lib/command.h index ea8a76a964..725a201446 100644 --- a/lib/command.h +++ b/lib/command.h @@ -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; @@ -149,6 +146,7 @@ enum node_type { MPLS_NODE, /* MPLS config node */ PW_NODE, /* Pseudowire config node */ VTY_NODE, /* Vty node. */ + FPM_NODE, /* Dataplane FPM node. */ LINK_PARAMS_NODE, /* Link-parameters node */ BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */ RPKI_NODE, /* RPKI node for configuration of RPKI cache server @@ -165,22 +163,29 @@ enum node_type { extern vector cmdvec; extern const struct message tokennames[]; -extern const char *const node_names[]; + +/* for external users depending on struct layout */ +#define FRR_CMD_NODE_20200416 /* Node which has some commands and prompt string and configuration function pointer . */ struct cmd_node { + const char *name; + /* Node index. */ enum node_type node; + enum node_type parent_node; /* Prompt character at vty interface. */ const char *prompt; - /* Is this node's configuration goes to vtysh ? */ - int vtysh; - /* Node's configuration write function */ - int (*func)(struct vty *); + int (*config_write)(struct vty *); + + /* called when leaving the node on a VTY session. + * return 1 if normal exit processing should happen, 0 to suppress + */ + int (*node_exit)(struct vty *); /* Node's command graph */ struct graph *cmdgraph; @@ -434,7 +439,7 @@ struct cmd_node { #define NO_GR_NEIGHBOR_HELPER_CMD "Undo Graceful Restart Helper command for a neighbor\n" /* Prototypes. */ -extern void install_node(struct cmd_node *node, int (*)(struct vty *)); +extern void install_node(struct cmd_node *node); extern void install_default(enum node_type); extern void install_element(enum node_type, const struct cmd_element *); @@ -1,5 +1,5 @@ /* CSV - * Copyright (C) 2013 Cumulus Networks, Inc. + * Copyright (C) 2013,2020 Cumulus Networks, Inc. * * This file is part of Quagga. * @@ -22,6 +22,8 @@ #include "config.h" #endif +#include <zebra.h> + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -419,7 +421,7 @@ void csv_clone_record(csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec) } rec->record = curr; rec->rec_len = in_rec->rec_len; - strcpy(rec->record, in_rec->record); + strlcpy(rec->record, in_rec->record, csv->buflen); /* decode record into fields */ csv_decode_record(rec); @@ -635,10 +637,10 @@ void csv_dump(csv_t *csv) static int get_memory_usage(pid_t pid) { int fd, data, stack; - char buf[4096], status_child[BUFSIZ]; + char buf[4096], status_child[PATH_MAX]; char *vm; - sprintf(status_child, "/proc/%d/status", pid); + snprintf(status_child, sizeof(status_child), "/proc/%d/status", pid); if ((fd = open(status_child, O_RDONLY)) < 0) return -1; @@ -670,8 +672,8 @@ int main() log_verbose("Mem: %d\n", get_memory_usage(getpid())); csv_init(&csv, buf, 256); - sprintf(hdr1, "%4d", 0); - sprintf(hdr2, "%4d", 1); + snprintf(hdr1, sizeof(hdr1), "%4d", 0); + snprintf(hdr2, sizeof(hdr2), "%4d", 1); log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1), strlen(hdr2), atoi(hdr1), atoi(hdr2)); rec = csv_encode(&csv, 2, hdr1, hdr2); @@ -683,8 +685,8 @@ int main() } csv_encode(&csv, 2, "pdfadfadfadsadsaddfdfdsfdsd", "35444554545454545"); log_verbose("%s\n", buf); - sprintf(hdr1, "%4d", csv.csv_len); - sprintf(hdr2, "%4d", 1); + snprintf(hdr1, sizeof(hdr1), "%4d", csv.csv_len); + snprintf(hdr2, sizeof(hdr2), "%4d", 1); log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1), strlen(hdr2), atoi(hdr1), atoi(hdr2)); rec = csv_encode_record(&csv, rec, 2, hdr1, hdr2); diff --git a/lib/defaults.h b/lib/defaults.h index 7cdd18120e..20ef28db31 100644 --- a/lib/defaults.h +++ b/lib/defaults.h @@ -22,6 +22,10 @@ #include "compiler.h" +#ifdef __cplusplus +extern "C" { +#endif + /* frr_default wraps information about a default that has different * values depending on FRR version or default-set * @@ -135,4 +139,8 @@ extern bool frr_defaults_profile_valid(const char *profile); /* like strcmp(), but with version ordering */ extern int frr_version_cmp(const char *aa, const char *bb); +#ifdef __cplusplus +} +#endif + #endif /* _FRR_DEFAULTS_H */ diff --git a/lib/filter.c b/lib/filter.c index 3226fb2f5e..da02a77763 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -2294,6 +2294,7 @@ DEFUN (ipv6_access_list_exact, if (argv_find(argv, argc, "exact-match", &idx)) exact = 1; + assert(prefix); return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, AFI_IP6, prefix, exact, 1); } @@ -2812,9 +2813,13 @@ static int config_write_access(struct vty *vty, afi_t afi) return write; } +static int config_write_access_mac(struct vty *vty); static struct cmd_node access_mac_node = { - ACCESS_MAC_NODE, "", /* Access list has no interface. */ - 1}; + .name = "MAC access list", + .node = ACCESS_MAC_NODE, + .prompt = "", + .config_write = config_write_access_mac, +}; static int config_write_access_mac(struct vty *vty) { @@ -2850,7 +2855,7 @@ static void access_list_reset_mac(void) /* Install vty related command. */ static void access_list_init_mac(void) { - install_node(&access_mac_node, config_write_access_mac); + install_node(&access_mac_node); install_element(ENABLE_NODE, &show_mac_access_list_cmd); install_element(ENABLE_NODE, &show_mac_access_list_name_cmd); @@ -2863,9 +2868,13 @@ static void access_list_init_mac(void) } /* Access-list node. */ -static struct cmd_node access_node = {ACCESS_NODE, - "", /* Access list has no interface. */ - 1}; +static int config_write_access_ipv4(struct vty *vty); +static struct cmd_node access_node = { + .name = "ipv4 access list", + .node = ACCESS_NODE, + .prompt = "", + .config_write = config_write_access_ipv4, +}; static int config_write_access_ipv4(struct vty *vty) { @@ -2901,7 +2910,7 @@ static void access_list_reset_ipv4(void) /* Install vty related command. */ static void access_list_init_ipv4(void) { - install_node(&access_node, config_write_access_ipv4); + install_node(&access_node); install_element(ENABLE_NODE, &show_ip_access_list_cmd); install_element(ENABLE_NODE, &show_ip_access_list_name_cmd); @@ -2948,7 +2957,13 @@ static void access_list_init_ipv4(void) install_element(CONFIG_NODE, &no_access_list_remark_comment_cmd); } -static struct cmd_node access_ipv6_node = {ACCESS_IPV6_NODE, "", 1}; +static int config_write_access_ipv6(struct vty *vty); +static struct cmd_node access_ipv6_node = { + .name = "ipv6 access list", + .node = ACCESS_IPV6_NODE, + .prompt = "", + .config_write = config_write_access_ipv6, +}; static int config_write_access_ipv6(struct vty *vty) { @@ -2983,7 +2998,7 @@ static void access_list_reset_ipv6(void) static void access_list_init_ipv6(void) { - install_node(&access_ipv6_node, config_write_access_ipv6); + install_node(&access_ipv6_node); install_element(ENABLE_NODE, &show_ipv6_access_list_cmd); install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd); diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 55f0b55ed6..e237934f81 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -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; } diff --git a/lib/frratomic.h b/lib/frratomic.h index 1e28253f2b..bafc6445e5 100644 --- a/lib/frratomic.h +++ b/lib/frratomic.h @@ -41,6 +41,7 @@ using std::memory_order_seq_cst; typedef std::atomic<bool> atomic_bool; typedef std::atomic<size_t> atomic_size_t; typedef std::atomic<uint_fast32_t> atomic_uint_fast32_t; +typedef std::atomic<uintptr_t> atomic_uintptr_t; #elif defined(HAVE_STDATOMIC_H) #include <stdatomic.h> @@ -230,6 +231,7 @@ typedef std::atomic<uint_fast32_t> atomic_uint_fast32_t; typedef _Atomic bool atomic_bool; typedef _Atomic size_t atomic_size_t; typedef _Atomic uint_fast32_t atomic_uint_fast32_t; +typedef _Atomic uintptr_t atomic_uintptr_t; #endif #endif /* _FRRATOMIC_H */ diff --git a/lib/frrcu.h b/lib/frrcu.h index 06d87c39f1..47751ae7df 100644 --- a/lib/frrcu.h +++ b/lib/frrcu.h @@ -19,7 +19,10 @@ #include "memory.h" #include "atomlist.h" -#include "seqlock.h" + +#ifdef __cplusplus +extern "C" { +#endif /* quick RCU primer: * There's a global sequence counter. Whenever a thread does a @@ -171,4 +174,8 @@ extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action); extern void rcu_close(struct rcu_head_close *head, int fd); +#ifdef __cplusplus +} +#endif + #endif /* _FRRCU_H */ diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 8ccdbfcbc1..a40b815caa 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -399,7 +399,7 @@ DEFUN (grammar_findambig, if (!nodegraph) continue; vty_out(vty, "scanning node %d (%s)\n", scannode - 1, - node_names[scannode - 1]); + cnode->name); } commands = cmd_graph_permutations(nodegraph); diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index aa54720dab..fbb97d2dd5 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -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); diff --git a/lib/hook.c b/lib/hook.c index 870d158aac..5a8ad00d66 100644 --- a/lib/hook.c +++ b/lib/hook.c @@ -18,16 +18,26 @@ #include "config.h" #endif +#include <string.h> + #include "memory.h" #include "hook.h" DEFINE_MTYPE_STATIC(LIB, HOOK_ENTRY, "Hook entry") -void _hook_register(struct hook *hook, void *funcptr, void *arg, bool has_arg, - struct frrmod_runtime *module, const char *funcname, - int priority) +void _hook_register(struct hook *hook, struct hookent *stackent, void *funcptr, + void *arg, bool has_arg, struct frrmod_runtime *module, + const char *funcname, int priority) { - struct hookent *he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he)), **pos; + struct hookent *he, **pos; + + if (!stackent->ent_used) + he = stackent; + else { + he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he)); + he->ent_on_heap = true; + } + he->ent_used = true; he->hookfn = funcptr; he->hookarg = arg; he->has_arg = has_arg; @@ -52,7 +62,10 @@ void _hook_unregister(struct hook *hook, void *funcptr, void *arg, bool has_arg) if (he->hookfn == funcptr && he->hookarg == arg && he->has_arg == has_arg) { *prev = he->next; - XFREE(MTYPE_HOOK_ENTRY, he); + if (he->ent_on_heap) + XFREE(MTYPE_HOOK_ENTRY, he); + else + memset(he, 0, sizeof(*he)); break; } } diff --git a/lib/hook.h b/lib/hook.h index f7fb7b8a5c..3823cebe6a 100644 --- a/lib/hook.h +++ b/lib/hook.h @@ -114,7 +114,9 @@ struct hookent { struct hookent *next; void *hookfn; /* actually a function pointer */ void *hookarg; - bool has_arg; + bool has_arg : 1; + bool ent_on_heap : 1; + bool ent_used : 1; int priority; struct frrmod_runtime *module; const char *fnname; @@ -133,21 +135,33 @@ struct hook { * always use hook_register(), which uses the static inline helper from * DECLARE_HOOK in order to get type safety */ -extern void _hook_register(struct hook *hook, void *funcptr, void *arg, - bool has_arg, struct frrmod_runtime *module, +extern void _hook_register(struct hook *hook, struct hookent *stackent, + void *funcptr, void *arg, bool has_arg, + struct frrmod_runtime *module, const char *funcname, int priority); + +/* most hook_register calls are not in a loop or similar and can use a + * statically allocated "struct hookent" from the data segment + */ +#define _hook_reg_svar(hook, funcptr, arg, has_arg, module, funcname, prio) \ + do { \ + static struct hookent stack_hookent = { .ent_on_heap = 0, }; \ + _hook_register(hook, &stack_hookent, funcptr, arg, has_arg, \ + module, funcname, prio); \ + } while (0) + #define hook_register(hookname, func) \ - _hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \ + _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \ NULL, false, THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY) #define hook_register_arg(hookname, func, arg) \ - _hook_register(&_hook_##hookname, \ + _hook_reg_svar(&_hook_##hookname, \ _hook_typecheck_arg_##hookname(func), arg, true, \ THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY) #define hook_register_prio(hookname, prio, func) \ - _hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \ + _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \ NULL, false, THIS_MODULE, #func, prio) #define hook_register_arg_prio(hookname, prio, func, arg) \ - _hook_register(&_hook_##hookname, \ + _hook_reg_svar(&_hook_##hookname, \ _hook_typecheck_arg_##hookname(func), arg, true, \ THIS_MODULE, #func, prio) diff --git a/lib/iana_afi.h b/lib/iana_afi.h index ac03f73193..56e8a24b86 100644 --- a/lib/iana_afi.h +++ b/lib/iana_afi.h @@ -21,6 +21,10 @@ #include <prefix.h> +#ifdef __cplusplus +extern "C" { +#endif + /* * The above AFI and SAFI definitions are for internal use. The protocol * definitions (IANA values) as for example used in BGP protocol packets @@ -130,4 +134,8 @@ static inline const char *iana_safi2str(iana_safi_t safi) return safi2str(safi_iana2int(safi)); } +#ifdef __cplusplus +} +#endif + #endif @@ -949,8 +949,9 @@ connected_log(struct connected *connected, char *str) p = connected->address; vrf = vrf_lookup_by_id(ifp->vrf_id); - snprintf(logbuf, BUFSIZ, "%s interface %s vrf %s(%u) %s %s/%d ", str, - ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, prefix_family_str(p), + snprintf(logbuf, sizeof(logbuf), "%s interface %s vrf %s(%u) %s %s/%d ", + str, ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, + prefix_family_str(p), inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); p = connected->destination; @@ -973,8 +974,8 @@ nbr_connected_log(struct nbr_connected *connected, char *str) ifp = connected->ifp; p = connected->address; - snprintf(logbuf, BUFSIZ, "%s interface %s %s %s/%d ", str, ifp->name, - prefix_family_str(p), + snprintf(logbuf, sizeof(logbuf), "%s interface %s %s %s/%d ", str, + ifp->name, prefix_family_str(p), inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); zlog_info("%s", logbuf); @@ -1489,19 +1490,17 @@ void if_zapi_callbacks(int (*create)(struct interface *ifp), /* * XPath: /frr-interface:lib/interface */ -static int lib_interface_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_interface_create(struct nb_cb_create_args *args) { const char *ifname; const char *vrfname; struct vrf *vrf; struct interface *ifp; - ifname = yang_dnode_get_string(dnode, "./name"); - vrfname = yang_dnode_get_string(dnode, "./vrf"); + ifname = yang_dnode_get_string(args->dnode, "./name"); + vrfname = yang_dnode_get_string(args->dnode, "./vrf"); - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: vrf = vrf_lookup_by_name(vrfname); if (!vrf) { @@ -1542,22 +1541,21 @@ static int lib_interface_create(enum nb_event event, #endif /* SUNOS_5 */ ifp->configured = true; - nb_running_set_entry(dnode, ifp); + nb_running_set_entry(args->dnode, ifp); break; } return NB_OK; } -static int lib_interface_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_interface_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: - ifp = nb_running_get_entry(dnode, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { zlog_warn("%s: only inactive interfaces can be deleted", __func__); @@ -1568,7 +1566,7 @@ static int lib_interface_destroy(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: - ifp = nb_running_unset_entry(dnode); + ifp = nb_running_unset_entry(args->dnode); ifp->configured = false; if_delete(&ifp); @@ -1581,13 +1579,12 @@ static int lib_interface_destroy(enum nb_event event, /* * XPath: /frr-interface:lib/interface */ -static const void *lib_interface_get_next(const void *parent_list_entry, - const void *list_entry) +static const void *lib_interface_get_next(struct nb_cb_get_next_args *args) { struct vrf *vrf; - struct interface *pif = (struct interface *)list_entry; + struct interface *pif = (struct interface *)args->list_entry; - if (list_entry == NULL) { + if (args->list_entry == NULL) { vrf = RB_MIN(vrf_name_head, &vrfs_by_name); assert(vrf); pif = RB_MIN(if_name_head, &vrf->ifaces_by_name); @@ -1606,27 +1603,26 @@ static const void *lib_interface_get_next(const void *parent_list_entry, return pif; } -static int lib_interface_get_keys(const void *list_entry, - struct yang_list_keys *keys) +static int lib_interface_get_keys(struct nb_cb_get_keys_args *args) { - const struct interface *ifp = list_entry; + const struct interface *ifp = args->list_entry; struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); assert(vrf); - keys->num = 2; - strlcpy(keys->key[0], ifp->name, sizeof(keys->key[0])); - strlcpy(keys->key[1], vrf->name, sizeof(keys->key[1])); + args->keys->num = 2; + strlcpy(args->keys->key[0], ifp->name, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], vrf->name, sizeof(args->keys->key[1])); return NB_OK; } -static const void *lib_interface_lookup_entry(const void *parent_list_entry, - const struct yang_list_keys *keys) +static const void * +lib_interface_lookup_entry(struct nb_cb_lookup_entry_args *args) { - const char *ifname = keys->key[0]; - const char *vrfname = keys->key[1]; + const char *ifname = args->keys->key[0]; + const char *vrfname = args->keys->key[1]; struct vrf *vrf = vrf_lookup_by_name(vrfname); return vrf ? if_lookup_by_name(ifname, vrf->vrf_id) : NULL; @@ -1635,40 +1631,125 @@ static const void *lib_interface_lookup_entry(const void *parent_list_entry, /* * XPath: /frr-interface:lib/interface/description */ -static int lib_interface_description_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_interface_description_modify(struct nb_cb_modify_args *args) { struct interface *ifp; const char *description; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - ifp = nb_running_get_entry(dnode, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); XFREE(MTYPE_TMP, ifp->desc); - description = yang_dnode_get_string(dnode, NULL); + description = yang_dnode_get_string(args->dnode, NULL); ifp->desc = XSTRDUP(MTYPE_TMP, description); return NB_OK; } -static int lib_interface_description_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_interface_description_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - ifp = nb_running_get_entry(dnode, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); XFREE(MTYPE_TMP, ifp->desc); return NB_OK; } -/* clang-format off */ +/* + * XPath: /frr-interface:lib/interface/state/if-index + */ +static struct yang_data * +lib_interface_state_if_index_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_int32(args->xpath, ifp->ifindex); +} + +/* + * XPath: /frr-interface:lib/interface/state/mtu + */ +static struct yang_data * +lib_interface_state_mtu_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint16(args->xpath, ifp->mtu); +} + +/* + * XPath: /frr-interface:lib/interface/state/mtu6 + */ +static struct yang_data * +lib_interface_state_mtu6_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint32(args->xpath, ifp->mtu6); +} + +/* + * XPath: /frr-interface:lib/interface/state/speed + */ +static struct yang_data * +lib_interface_state_speed_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint32(args->xpath, ifp->speed); +} + +/* + * XPath: /frr-interface:lib/interface/state/metric + */ +static struct yang_data * +lib_interface_state_metric_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint32(args->xpath, ifp->metric); +} + +/* + * XPath: /frr-interface:lib/interface/state/flags + */ +static struct yang_data * +lib_interface_state_flags_get_elem(struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: /frr-interface:lib/interface/state/type + */ +static struct yang_data * +lib_interface_state_type_get_elem(struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: /frr-interface:lib/interface/state/phy-address + */ +static struct yang_data * +lib_interface_state_phy_address_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct ethaddr macaddr; + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + return yang_data_new_mac(args->xpath, &macaddr); +} + +/* clang-format off */ const struct frr_yang_module_info frr_interface_info = { .name = "frr-interface", .nodes = { @@ -1692,6 +1773,54 @@ const struct frr_yang_module_info frr_interface_info = { }, }, { + .xpath = "/frr-interface:lib/interface/state/if-index", + .cbs = { + .get_elem = lib_interface_state_if_index_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/mtu", + .cbs = { + .get_elem = lib_interface_state_mtu_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/mtu6", + .cbs = { + .get_elem = lib_interface_state_mtu6_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/speed", + .cbs = { + .get_elem = lib_interface_state_speed_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/metric", + .cbs = { + .get_elem = lib_interface_state_metric_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/flags", + .cbs = { + .get_elem = lib_interface_state_flags_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/type", + .cbs = { + .get_elem = lib_interface_state_type_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/phy-address", + .cbs = { + .get_elem = lib_interface_state_phy_address_get_elem, + } + }, + { .xpath = NULL, }, } diff --git a/lib/keychain.c b/lib/keychain.c index ea512a2699..251211734b 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -959,11 +959,21 @@ DEFUN (no_send_lifetime, return CMD_SUCCESS; } -static struct cmd_node keychain_node = {KEYCHAIN_NODE, "%s(config-keychain)# ", - 1}; - -static struct cmd_node keychain_key_node = {KEYCHAIN_KEY_NODE, - "%s(config-keychain-key)# ", 1}; +static int keychain_config_write(struct vty *vty); +static struct cmd_node keychain_node = { + .name = "keychain", + .node = KEYCHAIN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-keychain)# ", + .config_write = keychain_config_write, +}; + +static struct cmd_node keychain_key_node = { + .name = "keychain key", + .node = KEYCHAIN_KEY_NODE, + .parent_node = KEYCHAIN_NODE, + .prompt = "%s(config-keychain-key)# ", +}; static int keychain_strftime(char *buf, int bufsiz, time_t *time) { @@ -1042,8 +1052,8 @@ void keychain_init(void) { keychain_list = list_new(); - install_node(&keychain_node, keychain_config_write); - install_node(&keychain_key_node, NULL); + install_node(&keychain_node); + install_node(&keychain_key_node); install_default(KEYCHAIN_NODE); install_default(KEYCHAIN_KEY_NODE); diff --git a/lib/libfrr.c b/lib/libfrr.c index 9a681103d4..ac165f254e 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -33,7 +33,6 @@ #include "lib_vty.h" #include "log_vty.h" #include "zclient.h" -#include "log_int.h" #include "module.h" #include "network.h" #include "lib_errors.h" @@ -630,6 +629,7 @@ struct thread_master *frr_init(void) { struct option_chain *oc; struct frrmod_runtime *module; + struct zprivs_ids_t ids; char moderr[256]; char p_instance[16] = "", p_pathspace[256] = ""; const char *dir; @@ -657,9 +657,11 @@ struct thread_master *frr_init(void) #endif zprivs_preinit(di->privs); + zprivs_get_ids(&ids); - openzlog(di->progname, di->logname, di->instance, - LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); + zlog_init(di->progname, di->logname, di->instance, + ids.uid_normal, ids.gid_normal); + zlog_tls_buffer_init(); command_setup_early_logging(di->early_logging, di->early_loglevel); @@ -709,7 +711,6 @@ struct thread_master *frr_init(void) vty_init(master, di->log_always); lib_cmd_init(); - log_filter_cmd_init(); frr_pthread_init(); @@ -1086,7 +1087,7 @@ void frr_run(struct thread_master *master) } /* end fixed stderr startup logging */ - zlog_startup_stderr = false; + zlog_startup_end(); struct thread thread; while (thread_fetch(master, &thread)) @@ -1119,7 +1120,8 @@ void frr_fini(void) /* signal_init -> nothing needed */ thread_master_free(master); master = NULL; - closezlog(); + zlog_tls_buffer_fini(); + zlog_fini(); /* frrmod_init -> nothing needed / hooks */ rcu_shutdown(); @@ -25,7 +25,6 @@ #include "zclient.h" #include "log.h" -#include "log_int.h" #include "memory.h" #include "command.h" #include "lib_errors.h" @@ -33,154 +32,12 @@ #include "printfrr.h" #include "frr_pthread.h" -#ifndef SUNOS_5 -#include <sys/un.h> -#endif -/* for printstack on solaris */ -#ifdef HAVE_UCONTEXT_H -#include <ucontext.h> -#endif - #ifdef HAVE_LIBUNWIND #define UNW_LOCAL_ONLY #include <libunwind.h> #include <dlfcn.h> #endif -DEFINE_MTYPE_STATIC(LIB, ZLOG, "Logging") - -/* hook for external logging */ -DEFINE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args), - (priority, format, args)); - -static int logfile_fd = -1; /* Used in signal handler. */ - -struct zlog *zlog_default = NULL; -bool zlog_startup_stderr = true; - -/* lock protecting zlog_default for mt-safe zlog */ -static pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER; - -const char *zlog_priority[] = { - "emergencies", "alerts", "critical", "errors", "warnings", - "notifications", "informational", "debugging", NULL, -}; - -static char zlog_filters[ZLOG_FILTERS_MAX][ZLOG_FILTER_LENGTH_MAX + 1]; -static uint8_t zlog_filter_count; - -/* - * look for a match on the filter in the current filters, loglock must be held - */ -static int zlog_filter_lookup(const char *lookup) -{ - for (int i = 0; i < zlog_filter_count; i++) { - if (strncmp(lookup, zlog_filters[i], sizeof(zlog_filters[0])) - == 0) - return i; - } - return -1; -} - -void zlog_filter_clear(void) -{ - frr_with_mutex(&loglock) { - zlog_filter_count = 0; - } -} - -int zlog_filter_add(const char *filter) -{ - frr_with_mutex(&loglock) { - if (zlog_filter_count >= ZLOG_FILTERS_MAX) - return 1; - - if (zlog_filter_lookup(filter) != -1) - /* Filter already present */ - return -1; - - strlcpy(zlog_filters[zlog_filter_count], filter, - sizeof(zlog_filters[0])); - - if (zlog_filters[zlog_filter_count][0] == '\0') - /* Filter was either empty or didn't get copied - * correctly - */ - return -1; - - zlog_filter_count++; - } - return 0; -} - -int zlog_filter_del(const char *filter) -{ - frr_with_mutex(&loglock) { - int found_idx = zlog_filter_lookup(filter); - int last_idx = zlog_filter_count - 1; - - if (found_idx == -1) - /* Didn't find the filter to delete */ - return -1; - - /* Adjust the filter array */ - memmove(zlog_filters[found_idx], zlog_filters[found_idx + 1], - (last_idx - found_idx) * sizeof(zlog_filters[0])); - - zlog_filter_count--; - } - return 0; -} - -/* Dump all filters to buffer, delimited by new line */ -int zlog_filter_dump(char *buf, size_t max_size) -{ - int len = 0; - - frr_with_mutex(&loglock) { - for (int i = 0; i < zlog_filter_count; i++) { - int ret; - ret = snprintf(buf + len, max_size - len, " %s\n", - zlog_filters[i]); - len += ret; - if ((ret < 0) || ((size_t)len >= max_size)) - return -1; - } - } - - return len; -} - -/* - * write_wrapper - * - * glibc has declared that the return value from write *must* not be - * ignored. - * gcc see's this problem and issues a warning for the line. - * - * Why is this a big deal you say? Because both of them are right - * and if you have -Werror enabled then all calls to write - * generate a build error and the build stops. - * - * clang has helpfully allowed this construct: - * (void)write(...) - * to tell the compiler yeah I know it has a return value - * I don't care about it at this time. - * gcc doesn't have this ability. - * - * This code was written such that it didn't care about the - * return value from write. At this time do I want - * to go through and fix and test this code for correctness. - * So just wrapper the bad behavior and move on. - */ -static void write_wrapper(int fd, const void *buf, size_t count) -{ - if (write(fd, buf, count) <= 0) - return; - - return; -} - /** * Looks up a message in a message list by key. * @@ -264,274 +121,12 @@ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) return 0; } -static inline void timestamp_control_render(struct timestamp_control *ctl) -{ - if (!ctl->already_rendered) { - ctl->len = quagga_timestamp(ctl->precision, ctl->buf, - sizeof(ctl->buf)); - ctl->already_rendered = 1; - } -} - -/* Utility routine for current time printing. */ -static void time_print(FILE *fp, struct timestamp_control *ctl) -{ - timestamp_control_render(ctl); - fprintf(fp, "%s ", ctl->buf); -} - -static int time_print_buf(char *buf, int len, int max_size, - struct timestamp_control *ctl) -{ - timestamp_control_render(ctl); - - if (ctl->len + 1 >= (unsigned long)max_size) - return -1; - - return snprintf(buf + len, max_size - len, "%s ", ctl->buf); -} - -static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl, - const char *proto_str, int record_priority, int priority, - FILE *fp, const char *msg) -{ - time_print(fp, tsctl); - if (record_priority) - fprintf(fp, "%s: ", zlog_priority[priority]); - - fprintf(fp, "%s%s\n", proto_str, msg); - fflush(fp); -} - -/* Search a buf for the filter strings, loglock must be held */ -static int search_buf(const char *buf) -{ - char *found = NULL; - - for (int i = 0; i < zlog_filter_count; i++) { - found = strstr(buf, zlog_filters[i]); - if (found != NULL) - return 0; - } - - return -1; -} - -/* Filter out a log */ -static int vzlog_filter(struct zlog *zl, struct timestamp_control *tsctl, - const char *proto_str, int priority, const char *msg) -{ - int len = 0; - int ret = 0; - char buf[1024] = ""; - - ret = time_print_buf(buf, len, sizeof(buf), tsctl); - - len += ret; - if ((ret < 0) || ((size_t)len >= sizeof(buf))) - goto search; - - if (zl && zl->record_priority) - snprintf(buf + len, sizeof(buf) - len, "%s: %s: %s", - zlog_priority[priority], proto_str, msg); - else - snprintf(buf + len, sizeof(buf) - len, "%s: %s", proto_str, - msg); - -search: - return search_buf(buf); -} - -/* va_list version of zlog. */ -void vzlog(int priority, const char *format, va_list args) -{ - frr_mutex_lock_autounlock(&loglock); - - char proto_str[32] = ""; - int original_errno = errno; - struct timestamp_control tsctl = {}; - tsctl.already_rendered = 0; - struct zlog *zl = zlog_default; - char buf[256], *msg; - - if (zl == NULL) { - tsctl.precision = 0; - } else { - tsctl.precision = zl->timestamp_precision; - if (zl->instance) - sprintf(proto_str, "%s[%d]: ", zl->protoname, - zl->instance); - else - sprintf(proto_str, "%s: ", zl->protoname); - } - - msg = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), format, args); - - /* If it doesn't match on a filter, do nothing with the debug log */ - if ((priority == LOG_DEBUG) && zlog_filter_count - && vzlog_filter(zl, &tsctl, proto_str, priority, msg)) - goto out; - - /* call external hook */ - hook_call(zebra_ext_log, priority, format, args); - - /* When zlog_default is also NULL, use stderr for logging. */ - if (zl == NULL) { - time_print(stderr, &tsctl); - fprintf(stderr, "%s: %s\n", "unknown", msg); - fflush(stderr); - goto out; - } - - /* Syslog output */ - if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) - syslog(priority | zlog_default->facility, "%s", msg); - - /* File output. */ - if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) - vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - zl->fp, msg); - - /* fixed-config logging to stderr while we're stating up & haven't - * daemonized / reached mainloop yet - * - * note the "else" on stdout output -- we don't want to print the same - * message to both stderr and stdout. */ - if (zlog_startup_stderr && priority <= LOG_WARNING) - vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, msg); - else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) - vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - stdout, msg); - - /* Terminal monitor. */ - if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) - vty_log((zl->record_priority ? zlog_priority[priority] : NULL), - proto_str, msg, &tsctl); - -out: - if (msg != buf) - XFREE(MTYPE_TMP, msg); - errno = original_errno; -} - -int vzlog_test(int priority) -{ - frr_mutex_lock_autounlock(&loglock); - - struct zlog *zl = zlog_default; - - /* When zlog_default is also NULL, use stderr for logging. */ - if (zl == NULL) - return 1; - /* Syslog output */ - else if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) - return 1; - /* File output. */ - else if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) - return 1; - /* stdout output. */ - else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) - return 1; - /* Terminal monitor. */ - else if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) - return 1; - - return 0; -} - /* * crash handling * * NB: only AS-Safe (async-signal) functions can be used here! */ -/* Needs to be enhanced to support Solaris. */ -static int syslog_connect(void) -{ -#ifdef SUNOS_5 - return -1; -#else - int fd; - struct sockaddr_un addr; - - if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) - return -1; - addr.sun_family = AF_UNIX; -#ifdef _PATH_LOG -#define SYSLOG_SOCKET_PATH _PATH_LOG -#else -#define SYSLOG_SOCKET_PATH "/dev/log" -#endif - strlcpy(addr.sun_path, SYSLOG_SOCKET_PATH, sizeof(addr.sun_path)); -#undef SYSLOG_SOCKET_PATH - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - close(fd); - return -1; - } - return fd; -#endif -} - -static void syslog_sigsafe(int priority, const char *msg, size_t msglen) -{ - static int syslog_fd = -1; - char buf[sizeof("<1234567890>ripngd[1234567890]: ") + msglen + 50]; - struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; - - if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0)) - return; - - /* forget about the timestamp, too difficult in a signal handler */ - bprintfrr(&fb, "<%d>%s", priority, zlog_default->ident); - if (zlog_default->syslog_options & LOG_PID) - bprintfrr(&fb, "[%ld]", (long)getpid()); - bprintfrr(&fb, ": %s", msg); - write_wrapper(syslog_fd, fb.buf, fb.pos - fb.buf); -} - -static int open_crashlog(void) -{ - char crashlog_buf[PATH_MAX]; - const char *crashlog_default = "/var/tmp/frr.crashlog", *crashlog; - - if (!zlog_default || !zlog_default->ident) - crashlog = crashlog_default; - else { - snprintfrr(crashlog_buf, sizeof(crashlog_buf), - "/var/tmp/frr.%s.crashlog", zlog_default->ident); - crashlog = crashlog_buf; - } - return open(crashlog, O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); -} - -/* N.B. implicit priority is most severe */ -#define PRI LOG_CRIT - -static void crash_write(struct fbuf *fb, char *msgstart) -{ - if (fb->pos == fb->buf) - return; - if (!msgstart) - msgstart = fb->buf; - - /* If no file logging configured, try to write to fallback log file. */ - if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) - write(logfile_fd, fb->buf, fb->pos - fb->buf); - if (!zlog_default) - write(STDERR_FILENO, fb->buf, fb->pos - fb->buf); - else { - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - write(STDOUT_FILENO, fb->buf, fb->pos - fb->buf); - /* Remove trailing '\n' for monitor and syslog */ - fb->pos--; - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(fb->buf, fb->pos - fb->buf); - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(PRI | zlog_default->facility, msgstart, - fb->pos - msgstart); - } -} - /* Note: the goal here is to use only async-signal-safe functions. */ void zlog_signal(int signo, const char *action, void *siginfo_v, void *program_counter) @@ -540,14 +135,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v, time_t now; char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") + 100]; - char *msgstart; struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; time(&now); - if (zlog_default) - bprintfrr(&fb, "%s: ", zlog_default->protoname); - - msgstart = fb.pos; bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now); if (program_counter) @@ -559,9 +149,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v, (ptrdiff_t)siginfo->si_addr); bprintfrr(&fb, "; %s\n", action); - crash_write(&fb, msgstart); + zlog_sigsafe(fb.buf, fb.pos - fb.buf); - zlog_backtrace_sigsafe(PRI, program_counter); + zlog_backtrace_sigsafe(LOG_CRIT, program_counter); fb.pos = buf; @@ -574,7 +164,7 @@ void zlog_signal(int signo, const char *action, void *siginfo_v, bprintfrr(&fb, "in thread %s scheduled from %s:%d\n", tc->funcname, tc->schedfrom, tc->schedfrom_line); - crash_write(&fb, NULL); + zlog_sigsafe(fb.buf, fb.pos - fb.buf); } /* Log a backtrace using only async-signal-safe functions. @@ -609,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) { @@ -22,21 +22,22 @@ #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" +#include "lib/zlog_targets.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 +54,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 +62,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,23 +74,16 @@ 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 */ +struct zlog_cfg_filterfile { + struct zlog_cfg_file parent; +}; + +extern void zlog_filterfile_init(struct zlog_cfg_filterfile *zcf); +extern void zlog_filterfile_fini(struct zlog_cfg_filterfile *zcf); + /* Add/Del/Dump log filters */ extern void zlog_filter_clear(void); extern int zlog_filter_add(const char *filter); @@ -176,8 +142,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_filter.c b/lib/log_filter.c new file mode 100644 index 0000000000..721e57a628 --- /dev/null +++ b/lib/log_filter.c @@ -0,0 +1,156 @@ +/* + * Logging - Filtered file log target + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 + */ + +#include <zebra.h> + +#include "frr_pthread.h" +#include "log.h" + +static pthread_mutex_t logfilterlock = PTHREAD_MUTEX_INITIALIZER; +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, + * logfilterlock 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(&logfilterlock) { + zlog_filter_count = 0; + } +} + +int zlog_filter_add(const char *filter) +{ + frr_with_mutex(&logfilterlock) { + 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(&logfilterlock) { + 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(&logfilterlock) { + 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; +} + +static int search_buf(const char *buf) +{ + char *found = NULL; + + frr_with_mutex(&logfilterlock) { + for (int i = 0; i < zlog_filter_count; i++) { + found = strstr(buf, zlog_filters[i]); + if (found != NULL) + return 0; + } + } + + return -1; +} + +static void zlog_filterfile_fd(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs) +{ + struct zlog_msg *msgfilt[nmsgs]; + size_t i, o = 0; + + for (i = 0; i < nmsgs; i++) { + if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG + && search_buf(zlog_msg_text(msgs[i], NULL)) < 0) + continue; + + msgfilt[o++] = msgs[i]; + } + + if (o) + zlog_fd(zt, msgfilt, o); +} + +void zlog_filterfile_init(struct zlog_cfg_filterfile *zcf) +{ + zlog_file_init(&zcf->parent); + zcf->parent.zlog_wrap = zlog_filterfile_fd; +} + +void zlog_filterfile_fini(struct zlog_cfg_filterfile *zcf) +{ + zlog_file_fini(&zcf->parent); +} diff --git a/lib/log_int.h b/lib/log_int.h deleted file mode 100644 index 287e626eab..0000000000 --- a/lib/log_int.h +++ /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 */ diff --git a/lib/log_vty.c b/lib/log_vty.c index 68d598f565..d1dcac2340 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -22,12 +22,548 @@ #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 +#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 struct zlog_cfg_filterfile zt_filterfile = { + .parent = { + .prio_min = ZLOG_DISABLED, + }, +}; + +static const char *zlog_progname; +static const char *zlog_protoname; + +static const struct facility_map { + int facility; + const char *name; + size_t match; +} syslog_facilities[] = { + {LOG_KERN, "kern", 1}, + {LOG_USER, "user", 2}, + {LOG_MAIL, "mail", 1}, + {LOG_DAEMON, "daemon", 1}, + {LOG_AUTH, "auth", 1}, + {LOG_SYSLOG, "syslog", 1}, + {LOG_LPR, "lpr", 2}, + {LOG_NEWS, "news", 1}, + {LOG_UUCP, "uucp", 2}, + {LOG_CRON, "cron", 1}, +#ifdef LOG_FTP + {LOG_FTP, "ftp", 1}, +#endif + {LOG_LOCAL0, "local0", 6}, + {LOG_LOCAL1, "local1", 6}, + {LOG_LOCAL2, "local2", 6}, + {LOG_LOCAL3, "local3", 6}, + {LOG_LOCAL4, "local4", 6}, + {LOG_LOCAL5, "local5", 6}, + {LOG_LOCAL6, "local6", 6}, + {LOG_LOCAL7, "local7", 6}, + {0, NULL, 0}, +}; + +static const char * const zlog_priority[] = { + "emergencies", "alerts", "critical", "errors", "warnings", + "notifications", "informational", "debugging", NULL, +}; + +static const char *facility_name(int facility) +{ + const struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (fm->facility == facility) + return fm->name; + return ""; +} + +static int facility_match(const char *str) +{ + const struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (!strncmp(str, fm->name, fm->match)) + return fm->facility; + return -1; +} + +int log_level_match(const char *s) +{ + int level; + + for (level = 0; zlog_priority[level] != NULL; level++) + if (!strncmp(s, zlog_priority[level], 2)) + return level; + return ZLOG_DISABLED; +} + +void zlog_rotate(void) +{ + zlog_file_rotate(&zt_file); + zlog_file_rotate(&zt_filterfile.parent); + 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 + vty_out(vty, "level %s, facility %s, ident %s\n", + zlog_priority[level], + facility_name(zlog_syslog_get_facility()), + zlog_progname); +} + +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 (zt_filterfile.parent.prio_min != ZLOG_DISABLED + && zt_filterfile.parent.filename) + vty_out(vty, "Filtered-file logging: level %s, filename %s\n", + zlog_priority[zt_filterfile.parent.prio_min], + zt_filterfile.parent.filename); + + if (log_cmdline_syslog_lvl != ZLOG_DISABLED) + vty_out(vty, + "From command line: \"--log syslog --log-level %s\"\n", + zlog_priority[log_cmdline_syslog_lvl]); + if (log_cmdline_stdout_lvl != ZLOG_DISABLED) + vty_out(vty, + "From command line: \"--log stdout --log-level %s\"\n", + zlog_priority[log_cmdline_stdout_lvl]); + if (zt_file_cmdline.prio_min != ZLOG_DISABLED) + vty_out(vty, + "From command line: \"--log file:%s --log-level %s\"\n", + zt_file_cmdline.filename, + zlog_priority[zt_file_cmdline.prio_min]); + + vty_out(vty, "Protocol name: %s\n", zlog_protoname); + vty_out(vty, "Record priority: %s\n", + (zt_file.record_priority ? "enabled" : "disabled")); + vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec); + return CMD_SUCCESS; +} + +DEFPY (config_log_stdout, + config_log_stdout_cmd, + "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]", + "Logging control\n" + "Set stdout logging level\n" + LOG_LEVEL_DESC) +{ + int level; + + if (levelarg) { + level = log_level_match(levelarg); + if (level == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + } else + level = log_default_lvl; + + log_config_stdout_lvl = level; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_stdout, + no_config_log_stdout_cmd, + "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", + NO_STR + "Logging control\n" + "Cancel logging to stdout\n" + LOG_LEVEL_DESC) +{ + log_config_stdout_lvl = ZLOG_DISABLED; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (config_log_monitor, + config_log_monitor_cmd, + "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", + "Logging control\n" + "Set terminal line (monitor) logging level\n" + LOG_LEVEL_DESC) +{ + vty_out(vty, "%% \"log monitor\" is deprecated and does nothing.\n"); + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_config_log_monitor, + no_config_log_monitor_cmd, + "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", + NO_STR + "Logging control\n" + "Disable terminal line (monitor) logging\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; +} + +static int set_log_file(struct zlog_cfg_file *target, struct vty *vty, + const char *fname, int loglevel) +{ + char path[MAXPATHLEN + 1]; + 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; + } + + int pr = snprintf(path, sizeof(path), "%s/%s", cwd, fname); + if (pr < 0 || (unsigned int)pr >= sizeof(path)) { + flog_err_sys( + EC_LIB_SYSTEM_CALL, + "%s: Path too long ('%s/%s'); system maximum is %u", + __func__, cwd, fname, MAXPATHLEN); + return CMD_WARNING_CONFIG_FAILED; + } + + fullpath = path; + } else + fullpath = fname; + + target->prio_min = loglevel; + ok = zlog_file_set_filename(target, fullpath); + + if (!ok) { + if (vty) + vty_out(vty, "can't open logfile %s\n", fname); + return CMD_WARNING_CONFIG_FAILED; + } + return CMD_SUCCESS; +} + +void command_setup_early_logging(const char *dest, const char *level) +{ + int nlevel; + char *sep; + int len; + char type[8]; + + if (level) { + nlevel = log_level_match(level); + + if (nlevel == ZLOG_DISABLED) { + fprintf(stderr, "invalid log level \"%s\"\n", level); + exit(1); + } + } else + nlevel = log_default_lvl; + + if (!dest) + return; + + sep = strchr(dest, ':'); + len = sep ? (int)(sep - dest) : (int)strlen(dest); + + snprintfrr(type, sizeof(type), "%.*s", len, dest); + + if (strcmp(type, "stdout") == 0) { + log_cmdline_stdout_lvl = nlevel; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + return; + } + if (strcmp(type, "syslog") == 0) { + log_cmdline_syslog_lvl = nlevel; + zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl, + log_cmdline_syslog_lvl)); + return; + } + if (strcmp(type, "file") == 0 && sep) { + sep++; + set_log_file(&zt_file_cmdline, NULL, sep, nlevel); + return; + } + + fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest); + exit(1); +} + +DEFUN (clear_log_cmdline, + clear_log_cmdline_cmd, + "clear log cmdline-targets", + CLEAR_STR + "Logging control\n" + "Disable log targets specified at startup by --log option\n") +{ + zt_file_cmdline.prio_min = ZLOG_DISABLED; + zlog_file_set_other(&zt_file_cmdline); + + log_cmdline_syslog_lvl = ZLOG_DISABLED; + zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl, + log_cmdline_syslog_lvl)); + + log_cmdline_stdout_lvl = ZLOG_DISABLED; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + + return CMD_SUCCESS; +} + +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); + zt_filterfile.parent.record_priority = true; + zlog_file_set_other(&zt_filterfile.parent); + 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); + zt_filterfile.parent.record_priority = false; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + +DEFPY (config_log_timestamp_precision, + config_log_timestamp_precision_cmd, + "log timestamp precision (0-6)", + "Logging control\n" + "Timestamp configuration\n" + "Set the timestamp precision\n" + "Number of subsecond digits\n") +{ + zt_file.ts_subsec = precision; + zlog_file_set_other(&zt_file); + zt_stdout.ts_subsec = precision; + zlog_file_set_other(&zt_stdout); + zt_filterfile.parent.ts_subsec = precision; + zlog_file_set_other(&zt_filterfile.parent); + 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); + zt_filterfile.parent.ts_subsec = 0; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + +DEFPY (config_log_filterfile, + config_log_filterfile_cmd, + "log filtered-file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]", + "Logging control\n" + "Logging to file with string filter\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_filterfile.parent, vty, filename, level); +} + +DEFUN (no_config_log_filterfile, + no_config_log_filterfile_cmd, + "no log filtered-file [FILENAME [LEVEL]]", + NO_STR + "Logging control\n" + "Cancel logging to file with string filter\n" + "Logging file name\n" + "Logging level\n") +{ + zt_filterfile.parent.prio_min = ZLOG_DISABLED; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + DEFPY (log_filter, log_filter_cmd, "[no] log-filter WORD$filter", @@ -89,9 +625,122 @@ DEFPY (show_log_filter, return CMD_SUCCESS; } -void log_filter_cmd_init(void) +void log_config_write(struct vty *vty) { + bool show_cmdline_hint = false; + + if (zt_file.prio_min != ZLOG_DISABLED && zt_file.filename) { + vty_out(vty, "log file %s", zt_file.filename); + + if (zt_file.prio_min != log_default_lvl) + vty_out(vty, " %s", zlog_priority[zt_file.prio_min]); + vty_out(vty, "\n"); + } + + if (zt_filterfile.parent.prio_min != ZLOG_DISABLED + && zt_filterfile.parent.filename) { + vty_out(vty, "log filtered-file %s", + zt_filterfile.parent.filename); + + if (zt_filterfile.parent.prio_min != log_default_lvl) + vty_out(vty, " %s", + zlog_priority[zt_filterfile.parent.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_filterfile_init(&zt_filterfile); + + 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); + install_element(VIEW_NODE, &show_log_filter_cmd); install_element(CONFIG_NODE, &log_filter_cmd); install_element(CONFIG_NODE, &log_filter_clear_cmd); + install_element(CONFIG_NODE, &config_log_filterfile_cmd); + install_element(CONFIG_NODE, &no_config_log_filterfile_cmd); } diff --git a/lib/log_vty.h b/lib/log_vty.h index fa5627e4bd..16c4475467 100644 --- a/lib/log_vty.h +++ b/lib/log_vty.h @@ -20,5 +20,25 @@ #ifndef __LOG_VTY_H__ #define __LOG_VTY_H__ -extern void log_filter_cmd_init(void); + +#include "lib/hook.h" + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +#ifdef __cplusplus +} +#endif + #endif /* __LOG_VTY_H__ */ diff --git a/lib/memory.c b/lib/memory.c index 149e294d50..3a29404827 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -163,7 +163,8 @@ static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt) } else if (mt->n_alloc) { char size[32]; - eda->error++; + if (!mg->active_at_exit) + eda->error++; snprintf(size, sizeof(size), "%10zu", mt->size); fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", eda->prefix, mt->name, mt->n_alloc, diff --git a/lib/memory.h b/lib/memory.h index e4e05faa4f..13f2f9b11a 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -17,6 +17,7 @@ #ifndef _QUAGGA_MEMORY_H #define _QUAGGA_MEMORY_H +#include <stdbool.h> #include <stdlib.h> #include <stdio.h> #include <frratomic.h> @@ -48,6 +49,8 @@ struct memgroup { struct memgroup *next, **ref; struct memtype *types, **insert; const char *name; + /* ignore group on dumping memleaks at exit */ + bool active_at_exit; }; /* macro usage: @@ -76,7 +79,7 @@ struct memgroup { */ #define DECLARE_MGROUP(name) extern struct memgroup _mg_##name; -#define DEFINE_MGROUP(mname, desc) \ +#define _DEFINE_MGROUP(mname, desc, ...) \ struct memgroup _mg_##mname \ __attribute__((section(".data.mgroups"))) = { \ .name = desc, \ @@ -84,6 +87,7 @@ struct memgroup { .next = NULL, \ .insert = NULL, \ .ref = NULL, \ + __VA_ARGS__ \ }; \ static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \ static void _mginit_##mname(void) \ @@ -99,7 +103,13 @@ struct memgroup { if (_mg_##mname.next) \ _mg_##mname.next->ref = _mg_##mname.ref; \ *_mg_##mname.ref = _mg_##mname.next; \ - } + } \ + /* end */ + +#define DEFINE_MGROUP(mname, desc) \ + _DEFINE_MGROUP(mname, desc, ) +#define DEFINE_MGROUP_ACTIVEATEXIT(mname, desc) \ + _DEFINE_MGROUP(mname, desc, .active_at_exit = true) #define DECLARE_MTYPE(name) \ extern struct memtype MTYPE_##name[1]; \ diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 4d4376250f..98f359401e 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -431,7 +431,7 @@ char *ns_netns_pathname(struct vty *vty, const char *name) /* relevant pathname */ char tmp_name[PATH_MAX]; - snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name); + snprintf(tmp_name, sizeof(tmp_name), "%s/%s", NS_RUN_DIR, name); result = realpath(tmp_name, pathname); } diff --git a/lib/network.c b/lib/network.c index 411661a5e1..d2482bd55e 100644 --- a/lib/network.c +++ b/lib/network.c @@ -121,3 +121,21 @@ float ntohf(float net) { return htonf(net); } + +/** + * Helper function that returns a random long value. The main purpose of + * this function is to hide a `random()` call that gets flagged by coverity + * scan and put it into one place. + * + * The main usage of this function should be for generating jitter or weak + * random values for simple purposes. + * + * See 'man 3 random' for more information. + * + * \returns random long integer. + */ +long frr_weak_random(void) +{ + /* coverity[dont_call] */ + return random(); +} diff --git a/lib/network.h b/lib/network.h index a00c5a0a65..83c9e59e76 100644 --- a/lib/network.h +++ b/lib/network.h @@ -45,6 +45,8 @@ extern int set_cloexec(int fd); extern float htonf(float); extern float ntohf(float); +extern long frr_weak_random(void); + #ifdef __cplusplus } #endif diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index a4c823e37a..c23c57d2e1 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -933,10 +933,13 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, return CMD_SUCCESS; } +static int nexthop_group_write(struct vty *vty); static struct cmd_node nexthop_group_node = { - NH_GROUP_NODE, - "%s(config-nh-group)# ", - 1 + .name = "nexthop-group", + .node = NH_GROUP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-nh-group)# ", + .config_write = nexthop_group_write, }; void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) @@ -1210,7 +1213,7 @@ void nexthop_group_init(void (*new)(const char *name), cmd_variable_handler_register(nhg_name_handlers); - install_node(&nexthop_group_node, nexthop_group_write); + install_node(&nexthop_group_node); install_element(CONFIG_NODE, &nexthop_group_cmd); install_element(CONFIG_NODE, &no_nexthop_group_cmd); diff --git a/lib/northbound.c b/lib/northbound.c index 85e723d7cf..18bd4f5fd8 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -62,11 +62,10 @@ static struct { */ static bool transaction_in_progress; +static int nb_callback_pre_validate(const struct nb_node *nb_node, + const struct lyd_node *dnode); static int nb_callback_configuration(const enum nb_event event, struct nb_config_change *change); -static void nb_log_callback(const enum nb_event event, - enum nb_operation operation, const char *xpath, - const char *value); static struct nb_transaction *nb_transaction_new(struct nb_config *config, struct nb_config_cbs *changes, enum nb_client client, @@ -609,18 +608,7 @@ static int nb_candidate_validate_code(struct nb_config *candidate, if (!nb_node->cbs.pre_validate) goto next; - if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, - DEBUG_MODE_ALL)) { - char xpath[XPATH_MAXLEN]; - - yang_dnode_get_path(child, xpath, - sizeof(xpath)); - nb_log_callback(NB_EV_VALIDATE, - NB_OP_PRE_VALIDATE, xpath, - NULL); - } - - ret = (*nb_node->cbs.pre_validate)(child); + ret = nb_callback_pre_validate(nb_node, child); if (ret != NB_OK) return NB_ERR_VALIDATION; @@ -791,14 +779,173 @@ int nb_running_lock_check(enum nb_client client, const void *user) return ret; } -static void nb_log_callback(const enum nb_event event, - enum nb_operation operation, const char *xpath, - const char *value) +static void nb_log_config_callback(const enum nb_event event, + enum nb_operation operation, + const struct lyd_node *dnode) { + const char *value; + char xpath[XPATH_MAXLEN]; + + if (!DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) + return; + + yang_dnode_get_path(dnode, xpath, sizeof(xpath)); + if (yang_snode_is_typeless_data(dnode->schema)) + value = "(none)"; + else + value = yang_dnode_get_string(dnode, NULL); + zlog_debug( "northbound callback: event [%s] op [%s] xpath [%s] value [%s]", nb_event_name(event), nb_operation_name(operation), xpath, - value ? value : "(NULL)"); + value); +} + +static int nb_callback_create(const struct nb_node *nb_node, + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct nb_cb_create_args args = {}; + + nb_log_config_callback(event, NB_OP_CREATE, dnode); + + args.event = event; + args.dnode = dnode; + args.resource = resource; + return nb_node->cbs.create(&args); +} + +static int nb_callback_modify(const struct nb_node *nb_node, + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct nb_cb_modify_args args = {}; + + nb_log_config_callback(event, NB_OP_MODIFY, dnode); + + args.event = event; + args.dnode = dnode; + args.resource = resource; + return nb_node->cbs.modify(&args); +} + +static int nb_callback_destroy(const struct nb_node *nb_node, + enum nb_event event, + const struct lyd_node *dnode) +{ + struct nb_cb_destroy_args args = {}; + + nb_log_config_callback(event, NB_OP_DESTROY, dnode); + + args.event = event; + args.dnode = dnode; + return nb_node->cbs.destroy(&args); +} + +static int nb_callback_move(const struct nb_node *nb_node, enum nb_event event, + const struct lyd_node *dnode) +{ + struct nb_cb_move_args args = {}; + + nb_log_config_callback(event, NB_OP_MOVE, dnode); + + args.event = event; + args.dnode = dnode; + return nb_node->cbs.move(&args); +} + +static int nb_callback_pre_validate(const struct nb_node *nb_node, + const struct lyd_node *dnode) +{ + struct nb_cb_pre_validate_args args = {}; + + nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode); + + args.dnode = dnode; + return nb_node->cbs.pre_validate(&args); +} + +static void nb_callback_apply_finish(const struct nb_node *nb_node, + const struct lyd_node *dnode) +{ + struct nb_cb_apply_finish_args args = {}; + + nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode); + + args.dnode = dnode; + nb_node->cbs.apply_finish(&args); +} + +struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node, + const char *xpath, + const void *list_entry) +{ + struct nb_cb_get_elem_args args = {}; + + DEBUGD(&nb_dbg_cbs_state, + "northbound callback (get_elem): xpath [%s] list_entry [%p]", + xpath, list_entry); + + args.xpath = xpath; + args.list_entry = list_entry; + return nb_node->cbs.get_elem(&args); +} + +const void *nb_callback_get_next(const struct nb_node *nb_node, + const void *parent_list_entry, + const void *list_entry) +{ + struct nb_cb_get_next_args args = {}; + + DEBUGD(&nb_dbg_cbs_state, + "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]", + nb_node->xpath, parent_list_entry, list_entry); + + args.parent_list_entry = parent_list_entry; + args.list_entry = list_entry; + return nb_node->cbs.get_next(&args); +} + +int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry, + struct yang_list_keys *keys) +{ + struct nb_cb_get_keys_args args = {}; + + DEBUGD(&nb_dbg_cbs_state, + "northbound callback (get_keys): node [%s] list_entry [%p]", + nb_node->xpath, list_entry); + + args.list_entry = list_entry; + args.keys = keys; + return nb_node->cbs.get_keys(&args); +} + +const void *nb_callback_lookup_entry(const struct nb_node *nb_node, + const void *parent_list_entry, + const struct yang_list_keys *keys) +{ + struct nb_cb_lookup_entry_args args = {}; + + DEBUGD(&nb_dbg_cbs_state, + "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]", + nb_node->xpath, parent_list_entry); + + args.parent_list_entry = parent_list_entry; + args.keys = keys; + return nb_node->cbs.lookup_entry(&args); +} + +int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, + const struct list *input, struct list *output) +{ + struct nb_cb_rpc_args args = {}; + + DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath); + + args.xpath = xpath; + args.input = input; + args.output = output; + return nb_node->cbs.rpc(&args); } /* @@ -815,15 +962,6 @@ static int nb_callback_configuration(const enum nb_event event, union nb_resource *resource; int ret = NB_ERR; - if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) { - const char *value = "(none)"; - - if (dnode && !yang_snode_is_typeless_data(dnode->schema)) - value = yang_dnode_get_string(dnode, NULL); - - yang_dnode_get_path(dnode, xpath, sizeof(xpath)); - nb_log_callback(event, operation, xpath, value); - } if (event == NB_EV_VALIDATE) resource = NULL; @@ -832,16 +970,16 @@ static int nb_callback_configuration(const enum nb_event event, switch (operation) { case NB_OP_CREATE: - ret = (*nb_node->cbs.create)(event, dnode, resource); + ret = nb_callback_create(nb_node, event, dnode, resource); break; case NB_OP_MODIFY: - ret = (*nb_node->cbs.modify)(event, dnode, resource); + ret = nb_callback_modify(nb_node, event, dnode, resource); break; case NB_OP_DESTROY: - ret = (*nb_node->cbs.destroy)(event, dnode); + ret = nb_callback_destroy(nb_node, event, dnode); break; case NB_OP_MOVE: - ret = (*nb_node->cbs.move)(event, dnode); + ret = nb_callback_move(nb_node, event, dnode); break; default: yang_dnode_get_path(dnode, xpath, sizeof(xpath)); @@ -890,57 +1028,6 @@ static int nb_callback_configuration(const enum nb_event event, return ret; } -struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node, - const char *xpath, - const void *list_entry) -{ - DEBUGD(&nb_dbg_cbs_state, - "northbound callback (get_elem): xpath [%s] list_entry [%p]", - xpath, list_entry); - - return nb_node->cbs.get_elem(xpath, list_entry); -} - -const void *nb_callback_get_next(const struct nb_node *nb_node, - const void *parent_list_entry, - const void *list_entry) -{ - DEBUGD(&nb_dbg_cbs_state, - "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]", - nb_node->xpath, parent_list_entry, list_entry); - - return nb_node->cbs.get_next(parent_list_entry, list_entry); -} - -int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry, - struct yang_list_keys *keys) -{ - DEBUGD(&nb_dbg_cbs_state, - "northbound callback (get_keys): node [%s] list_entry [%p]", - nb_node->xpath, list_entry); - - return nb_node->cbs.get_keys(list_entry, keys); -} - -const void *nb_callback_lookup_entry(const struct nb_node *nb_node, - const void *parent_list_entry, - const struct yang_list_keys *keys) -{ - DEBUGD(&nb_dbg_cbs_state, - "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]", - nb_node->xpath, parent_list_entry); - - return nb_node->cbs.lookup_entry(parent_list_entry, keys); -} - -int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, - const struct list *input, struct list *output) -{ - DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath); - - return nb_node->cbs.rpc(xpath, input, output); -} - static struct nb_transaction * nb_transaction_new(struct nb_config *config, struct nb_config_cbs *changes, enum nb_client client, const void *user, const char *comment) @@ -1058,7 +1145,6 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction) { struct nb_config_cbs cbs; struct nb_config_cb *cb; - char xpath[XPATH_MAXLEN]; /* Initialize tree of 'apply_finish' callbacks. */ RB_INIT(nb_config_cbs, &cbs); @@ -1075,6 +1161,8 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction) * be called though). */ if (change->cb.operation == NB_OP_DESTROY) { + char xpath[XPATH_MAXLEN]; + dnode = dnode->parent; if (!dnode) break; @@ -1111,15 +1199,8 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction) } /* Call the 'apply_finish' callbacks, sorted by their priorities. */ - RB_FOREACH (cb, nb_config_cbs, &cbs) { - if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) { - yang_dnode_get_path(cb->dnode, xpath, sizeof(xpath)); - nb_log_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, xpath, - NULL); - } - - (*cb->nb_node->cbs.apply_finish)(cb->dnode); - } + RB_FOREACH (cb, nb_config_cbs, &cbs) + nb_callback_apply_finish(cb->nb_node, cb->dnode); /* Release memory. */ while (!RB_EMPTY(nb_config_cbs, &cbs)) { diff --git a/lib/northbound.h b/lib/northbound.h index 19a2ba0865..84382eeb60 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -86,6 +86,129 @@ union nb_resource { void *ptr; }; +/* + * Northbound callbacks parameters. + */ + +struct nb_cb_create_args { + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being created. */ + const struct lyd_node *dnode; + + /* + * Pointer to store resource(s) allocated during the NB_EV_PREPARE + * phase. The same pointer can be used during the NB_EV_ABORT and + * NB_EV_APPLY phases to either release or make use of the allocated + * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + */ + union nb_resource *resource; +}; + +struct nb_cb_modify_args { + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being modified. */ + const struct lyd_node *dnode; + + /* + * Pointer to store resource(s) allocated during the NB_EV_PREPARE + * phase. The same pointer can be used during the NB_EV_ABORT and + * NB_EV_APPLY phases to either release or make use of the allocated + * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + */ + union nb_resource *resource; +}; + +struct nb_cb_destroy_args { + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being deleted. */ + const struct lyd_node *dnode; +}; + +struct nb_cb_move_args { + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being moved. */ + const struct lyd_node *dnode; +}; + +struct nb_cb_pre_validate_args { + /* libyang data node associated with the 'pre_validate' callback. */ + const struct lyd_node *dnode; +}; + +struct nb_cb_apply_finish_args { + /* libyang data node associated with the 'apply_finish' callback. */ + const struct lyd_node *dnode; +}; + +struct nb_cb_get_elem_args { + /* YANG data path of the data we want to get. */ + const char *xpath; + + /* Pointer to list entry (might be NULL). */ + const void *list_entry; +}; + +struct nb_cb_get_next_args { + /* Pointer to parent list entry. */ + const void *parent_list_entry; + + /* Pointer to (leaf-)list entry. */ + const void *list_entry; +}; + +struct nb_cb_get_keys_args { + /* Pointer to list entry. */ + const void *list_entry; + + /* + * Structure to be filled based on the attributes of the provided list + * entry. + */ + struct yang_list_keys *keys; +}; + +struct nb_cb_lookup_entry_args { + /* Pointer to parent list entry. */ + const void *parent_list_entry; + + /* Structure containing the keys of the list entry. */ + const struct yang_list_keys *keys; +}; + +struct nb_cb_rpc_args { + /* XPath of the YANG RPC or action. */ + const char *xpath; + + /* Read-only list of input parameters. */ + const struct list *input; + + /* List of output parameters to be populated by the callback. */ + struct list *output; +}; + +/* + * Set of configuration callbacks that can be associated to a northbound node. + */ struct nb_callbacks { /* * Configuration callback. @@ -97,18 +220,9 @@ struct nb_callbacks { * initialize the default values of its children (if any) from the YANG * models. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being created. - * - * resource - * Pointer to store resource(s) allocated during the NB_EV_PREPARE - * phase. The same pointer can be used during the NB_EV_ABORT and - * NB_EV_APPLY phases to either release or make use of the allocated - * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + * args + * Refer to the documentation comments of nb_cb_create_args for + * details. * * Returns: * - NB_OK on success. @@ -117,8 +231,7 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*create)(enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource); + int (*create)(struct nb_cb_create_args *args); /* * Configuration callback. @@ -129,18 +242,9 @@ struct nb_callbacks { * modified, the northbound treats this as if the list was deleted and a * new one created with the updated key value. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being modified - * - * resource - * Pointer to store resource(s) allocated during the NB_EV_PREPARE - * phase. The same pointer can be used during the NB_EV_ABORT and - * NB_EV_APPLY phases to either release or make use of the allocated - * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + * args + * Refer to the documentation comments of nb_cb_modify_args for + * details. * * Returns: * - NB_OK on success. @@ -149,8 +253,7 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*modify)(enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource); + int (*modify)(struct nb_cb_modify_args *args); /* * Configuration callback. @@ -161,12 +264,9 @@ struct nb_callbacks { * The callback is supposed to delete the entire configuration object, * including its children when they exist. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being deleted. + * args + * Refer to the documentation comments of nb_cb_destroy_args for + * details. * * Returns: * - NB_OK on success. @@ -174,7 +274,7 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*destroy)(enum nb_event event, const struct lyd_node *dnode); + int (*destroy)(struct nb_cb_destroy_args *args); /* * Configuration callback. @@ -182,12 +282,9 @@ struct nb_callbacks { * A list entry or leaf-list entry has been moved. Only applicable when * the "ordered-by user" statement is present. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being moved. + * args + * Refer to the documentation comments of nb_cb_move_args for + * details. * * Returns: * - NB_OK on success. @@ -195,7 +292,7 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*move)(enum nb_event event, const struct lyd_node *dnode); + int (*move)(struct nb_cb_move_args *args); /* * Optional configuration callback. @@ -205,10 +302,11 @@ struct nb_callbacks { * changes themselves. It's useful to perform more complex validations * that depend on the relationship between multiple nodes. * - * dnode - * libyang data node associated with the 'pre_validate' callback. + * args + * Refer to the documentation comments of nb_cb_pre_validate_args for + * details. */ - int (*pre_validate)(const struct lyd_node *dnode); + int (*pre_validate)(struct nb_cb_pre_validate_args *args); /* * Optional configuration callback. @@ -224,10 +322,11 @@ struct nb_callbacks { * once even if multiple changes occurred within the descendants of the * data node. * - * dnode - * libyang data node associated with the 'apply_finish' callback. + * args + * Refer to the documentation comments of nb_cb_apply_finish_args for + * details. */ - void (*apply_finish)(const struct lyd_node *dnode); + void (*apply_finish)(struct nb_cb_apply_finish_args *args); /* * Operational data callback. @@ -236,18 +335,15 @@ struct nb_callbacks { * leaf-list entry or inform if a typeless value (presence containers or * leafs of type empty) exists or not. * - * xpath - * YANG data path of the data we want to get. - * - * list_entry - * Pointer to list entry (might be NULL). + * args + * Refer to the documentation comments of nb_cb_get_elem_args for + * details. * * Returns: * Pointer to newly created yang_data structure, or NULL to indicate * the absence of data. */ - struct yang_data *(*get_elem)(const char *xpath, - const void *list_entry); + struct yang_data *(*get_elem)(struct nb_cb_get_elem_args *args); /* * Operational data callback for YANG lists and leaf-lists. @@ -256,18 +352,15 @@ struct nb_callbacks { * leaf-list. The 'list_entry' parameter will be NULL on the first * invocation. * - * parent_list_entry - * Pointer to parent list entry. - * - * list_entry - * Pointer to (leaf-)list entry. + * args + * Refer to the documentation comments of nb_cb_get_next_args for + * details. * * Returns: * Pointer to the next entry in the (leaf-)list, or NULL to signal * that the end of the (leaf-)list was reached. */ - const void *(*get_next)(const void *parent_list_entry, - const void *list_entry); + const void *(*get_next)(struct nb_cb_get_next_args *args); /* * Operational data callback for YANG lists. @@ -276,17 +369,14 @@ struct nb_callbacks { * given list_entry. Keyless lists don't need to implement this * callback. * - * list_entry - * Pointer to list entry. - * - * keys - * Structure to be filled based on the attributes of the provided - * list entry. + * args + * Refer to the documentation comments of nb_cb_get_keys_args for + * details. * * Returns: * NB_OK on success, NB_ERR otherwise. */ - int (*get_keys)(const void *list_entry, struct yang_list_keys *keys); + int (*get_keys)(struct nb_cb_get_keys_args *args); /* * Operational data callback for YANG lists. @@ -295,17 +385,14 @@ struct nb_callbacks { * keys given as a parameter. Keyless lists don't need to implement this * callback. * - * parent_list_entry - * Pointer to parent list entry. - * - * keys - * Structure containing the keys of the list entry. + * args + * Refer to the documentation comments of nb_cb_lookup_entry_args for + * details. * * Returns: * Pointer to the list entry if found, or NULL if not found. */ - const void *(*lookup_entry)(const void *parent_list_entry, - const struct yang_list_keys *keys); + const void *(*lookup_entry)(struct nb_cb_lookup_entry_args *args); /* * RPC and action callback. @@ -314,20 +401,13 @@ struct nb_callbacks { * callback should fetch all the input parameters from the 'input' list, * and add output parameters to the 'output' list if necessary. * - * xpath - * XPath of the YANG RPC or action. - * - * input - * Read-only list of input parameters. - * - * output - * List of output parameters to be populated by the callback. + * args + * Refer to the documentation comments of nb_cb_rpc_args for details. * * Returns: * NB_OK on success, NB_ERR otherwise. */ - int (*rpc)(const char *xpath, const struct list *input, - struct list *output); + int (*rpc)(struct nb_cb_rpc_args *args); /* * Optional callback to show the CLI command associated to the given diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 17dc256281..d4467facaf 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -1674,7 +1674,12 @@ static int nb_debug_config_write(struct vty *vty) } static struct debug_callbacks nb_dbg_cbs = {.debug_set_all = nb_debug_set_all}; -static struct cmd_node nb_debug_node = {NORTHBOUND_DEBUG_NODE, "", 1}; +static struct cmd_node nb_debug_node = { + .name = "northbound debug", + .node = NORTHBOUND_DEBUG_NODE, + .prompt = "", + .config_write = nb_debug_config_write, +}; void nb_cli_install_default(int node) { @@ -1738,7 +1743,7 @@ void nb_cli_init(struct thread_master *tm) debug_init(&nb_dbg_cbs); - install_node(&nb_debug_node, nb_debug_config_write); + install_node(&nb_debug_node); install_element(ENABLE_NODE, &debug_nb_cmd); install_element(CONFIG_NODE, &debug_nb_cmd); diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index b195f1aeca..66bf05c1ab 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -545,7 +545,8 @@ class NorthboundImpl final : public frr::Northbound::Service } // Execute callback registered for this XPath. - if (nb_node->cbs.rpc(xpath, input_list, output_list) != NB_OK) { + if (nb_callback_rpc(nb_node, xpath, input_list, output_list) + != NB_OK) { flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", __func__, xpath); diff --git a/lib/pid_output.c b/lib/pid_output.c index f5f7b1d171..bd1d89a94c 100644 --- a/lib/pid_output.c +++ b/lib/pid_output.c @@ -65,7 +65,7 @@ pid_t pid_output(const char *path) exit(1); } - sprintf(buf, "%d\n", (int)pid); + snprintf(buf, sizeof(buf), "%d\n", (int)pid); pidsize = strlen(buf); if ((tmp = write(fd, buf, pidsize)) != (int)pidsize) flog_err_sys( diff --git a/lib/plist.c b/lib/plist.c index b7a020c6f7..d18d51618a 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -348,14 +348,14 @@ static void prefix_list_delete(struct prefix_list *plist) static struct prefix_list_entry * prefix_list_entry_make(struct prefix *prefix, enum prefix_list_type type, - int64_t seq, int le, int ge, int any) + int64_t seq, int le, int ge, bool any) { struct prefix_list_entry *pentry; pentry = prefix_list_entry_new(); if (any) - pentry->any = 1; + pentry->any = true; prefix_copy(&pentry->prefix, prefix); pentry->type = type; @@ -851,7 +851,7 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name, struct prefix_list_entry *pentry; struct prefix_list_entry *dup; struct prefix p, p_tmp; - int any = 0; + bool any = false; int64_t seqnum = -1; int lenum = 0; int genum = 0; @@ -889,7 +889,7 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name, (struct prefix_ipv4 *)&p); genum = 0; lenum = IPV4_MAX_BITLEN; - any = 1; + any = true; } else ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p); @@ -908,7 +908,7 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name, ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p); genum = 0; lenum = IPV6_MAX_BITLEN; - any = 1; + any = true; } else ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p); @@ -1898,7 +1898,7 @@ int prefix_bgp_orf_set(char *name, afi_t afi, struct orf_prefix *orfp, if (set) { pentry = prefix_list_entry_make( &orfp->p, (permit ? PREFIX_PERMIT : PREFIX_DENY), - orfp->seq, orfp->le, orfp->ge, 0); + orfp->seq, orfp->le, orfp->ge, false); if (prefix_entry_dup_check(plist, pentry)) { prefix_list_entry_free(pentry); @@ -1961,10 +1961,9 @@ int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name, char buf_a[BUFSIZ]; char buf_b[BUFSIZ]; - sprintf(buf_a, "%s/%d", - inet_ntop(p->family, p->u.val, buf_b, - BUFSIZ), - p->prefixlen); + snprintf(buf_a, sizeof(buf_a), "%s/%d", + inet_ntop(p->family, p->u.val, buf_b, BUFSIZ), + p->prefixlen); json_object_int_add(json_list, "seq", pentry->seq); json_object_string_add(json_list, "seqPrefixListType", @@ -2044,10 +2043,14 @@ static void prefix_list_reset_afi(afi_t afi, int orf) } +static int config_write_prefix_ipv4(struct vty *vty); /* Prefix-list node. */ -static struct cmd_node prefix_node = {PREFIX_NODE, - "", /* Prefix list has no interface. */ - 1}; +static struct cmd_node prefix_node = { + .name = "ipv4 prefix list", + .node = PREFIX_NODE, + .prompt = "", + .config_write = config_write_prefix_ipv4, +}; static int config_write_prefix_ipv4(struct vty *vty) { @@ -2085,7 +2088,7 @@ static const struct cmd_variable_handler plist_var_handlers[] = { static void prefix_list_init_ipv4(void) { - install_node(&prefix_node, config_write_prefix_ipv4); + install_node(&prefix_node); install_element(CONFIG_NODE, &ip_prefix_list_cmd); install_element(CONFIG_NODE, &no_ip_prefix_list_cmd); @@ -2107,10 +2110,14 @@ static void prefix_list_init_ipv4(void) install_element(ENABLE_NODE, &clear_ip_prefix_list_cmd); } +static int config_write_prefix_ipv6(struct vty *vty); /* Prefix-list node. */ static struct cmd_node prefix_ipv6_node = { - PREFIX_IPV6_NODE, "", /* Prefix list has no interface. */ - 1}; + .name = "ipv6 prefix list", + .node = PREFIX_IPV6_NODE, + .prompt = "", + .config_write = config_write_prefix_ipv6, +}; static int config_write_prefix_ipv6(struct vty *vty) { @@ -2119,7 +2126,7 @@ static int config_write_prefix_ipv6(struct vty *vty) static void prefix_list_init_ipv6(void) { - install_node(&prefix_ipv6_node, config_write_prefix_ipv6); + install_node(&prefix_ipv6_node); install_element(CONFIG_NODE, &ipv6_prefix_list_cmd); install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd); diff --git a/lib/plist_int.h b/lib/plist_int.h index 443b0c614d..ec8bbe1315 100644 --- a/lib/plist_int.h +++ b/lib/plist_int.h @@ -59,7 +59,7 @@ struct prefix_list_entry { enum prefix_list_type type; - int any; + bool any; struct prefix prefix; unsigned long refcnt; diff --git a/lib/printfrr.h b/lib/printfrr.h index 7d9e288655..a775e1517b 100644 --- a/lib/printfrr.h +++ b/lib/printfrr.h @@ -24,6 +24,10 @@ #include "compiler.h" #include "memory.h" +#ifdef __cplusplus +extern "C" { +#endif + struct fbuf { char *buf; char *pos; @@ -156,4 +160,8 @@ void printfrr_ext_reg(const struct printfrr_ext *); } \ /* end */ +#ifdef __cplusplus +} +#endif + #endif diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c index b66ae221cf..e00dd54290 100644 --- a/lib/ptm_lib.c +++ b/lib/ptm_lib.c @@ -65,10 +65,10 @@ static csv_record_t *_ptm_lib_encode_header(csv_t *csv, csv_record_t *rec, char client_buf[32]; csv_record_t *rec1; - sprintf(msglen_buf, "%4d", msglen); - sprintf(vers_buf, "%4d", version); - sprintf(type_buf, "%4d", type); - sprintf(cmdid_buf, "%4d", cmd_id); + snprintf(msglen_buf, sizeof(msglen_buf), "%4d", msglen); + snprintf(vers_buf, sizeof(vers_buf), "%4d", version); + snprintf(type_buf, sizeof(type_buf), "%4d", type); + snprintf(cmdid_buf, sizeof(cmdid_buf), "%4d", cmd_id); snprintf(client_buf, 17, "%16.16s", client_name); if (rec) { rec1 = csv_encode_record(csv, rec, 5, msglen_buf, vers_buf, diff --git a/lib/pullwr.h b/lib/pullwr.h index 601eac1b79..a0e89e0c30 100644 --- a/lib/pullwr.h +++ b/lib/pullwr.h @@ -26,6 +26,10 @@ #include "thread.h" #include "stream.h" +#ifdef __cplusplus +extern "C" { +#endif + struct pullwr; /* This is a "pull-driven" write event handler. Instead of having some buffer @@ -107,4 +111,8 @@ static inline void pullwr_write_stream(struct pullwr *pullwr, extern void pullwr_stats(struct pullwr *pullwr, uint64_t *total_written, size_t *pending, size_t *kernel_pending); +#ifdef __cplusplus +} +#endif + #endif /* _WRITEPOLL_H */ diff --git a/lib/qobj.c b/lib/qobj.c index 1e48b541dc..cb3254cbe9 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -26,6 +26,7 @@ #include "log.h" #include "qobj.h" #include "jhash.h" +#include "network.h" static uint32_t qobj_hash(const struct qobj_node *node) { @@ -53,8 +54,8 @@ void qobj_reg(struct qobj_node *node, const struct qobj_nodetype *type) node->type = type; pthread_rwlock_wrlock(&nodes_lock); do { - node->nid = (uint64_t)random(); - node->nid ^= (uint64_t)random() << 32; + node->nid = (uint64_t)frr_weak_random(); + node->nid ^= (uint64_t)frr_weak_random() << 32; } while (!node->nid || qobj_nodes_find(&nodes, node)); qobj_nodes_add(&nodes, node); pthread_rwlock_unlock(&nodes_lock); diff --git a/lib/resolver.c b/lib/resolver.c index 1be47bd6e1..e5caadb2d0 100644 --- a/lib/resolver.c +++ b/lib/resolver.c @@ -245,7 +245,13 @@ DEFUN(debug_resolver, return CMD_SUCCESS; } -static struct cmd_node resolver_debug_node = {RESOLVER_DEBUG_NODE, "", 1}; +static int resolver_config_write_debug(struct vty *vty); +static struct cmd_node resolver_debug_node = { + .name = "resolver debug", + .node = RESOLVER_DEBUG_NODE, + .prompt = "", + .config_write = resolver_config_write_debug, +}; static int resolver_config_write_debug(struct vty *vty) { @@ -274,7 +280,7 @@ void resolver_init(struct thread_master *tm) ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT | ARES_OPT_TRIES); - install_node(&resolver_debug_node, resolver_config_write_debug); + install_node(&resolver_debug_node); install_element(CONFIG_NODE, &debug_resolver_cmd); install_element(ENABLE_NODE, &debug_resolver_cmd); } diff --git a/lib/resolver.h b/lib/resolver.h index 59bf0d0f55..5f922dcb57 100644 --- a/lib/resolver.h +++ b/lib/resolver.h @@ -13,6 +13,10 @@ #include "thread.h" #include "sockunion.h" +#ifdef __cplusplus +extern "C" { +#endif + struct resolver_query { void (*callback)(struct resolver_query *, const char *errstr, int n, union sockunion *); @@ -28,4 +32,8 @@ void resolver_resolve(struct resolver_query *query, int af, const char *, int, union sockunion *)); +#ifdef __cplusplus +} +#endif + #endif /* _FRR_RESOLVER_H */ diff --git a/lib/routemap.c b/lib/routemap.c index e2baa36f24..210512212d 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -3015,7 +3015,13 @@ DEFUN (no_debug_rmap, } /* Debug node. */ -static struct cmd_node rmap_debug_node = {RMAP_DEBUG_NODE, "", 1}; +static int rmap_config_write_debug(struct vty *vty); +static struct cmd_node rmap_debug_node = { + .name = "route-map debug", + .node = RMAP_DEBUG_NODE, + .prompt = "", + .config_write = rmap_config_write_debug, +}; /* Configuration write function. */ static int rmap_config_write_debug(struct vty *vty) @@ -3242,7 +3248,7 @@ void route_map_init(void) route_map_cli_init(); /* Install route map top node. */ - install_node(&rmap_debug_node, rmap_config_write_debug); + install_node(&rmap_debug_node); /* Install route map commands. */ install_element(CONFIG_NODE, &debug_rmap_cmd); diff --git a/lib/routemap.h b/lib/routemap.h index e8cab64b47..62195b8349 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -681,10 +681,8 @@ struct routemap_hook_context { TAILQ_ENTRY(routemap_hook_context) rhc_entry; }; -int lib_route_map_entry_match_destroy(enum nb_event event, - const struct lyd_node *dnode); -int lib_route_map_entry_set_destroy(enum nb_event event, - const struct lyd_node *dnode); +int lib_route_map_entry_match_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_destroy(struct nb_cb_destroy_args *args); struct routemap_hook_context * routemap_hook_context_insert(struct route_map_index *rmi); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 41e8cacd81..2c45f09751 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1064,7 +1064,14 @@ static int route_map_config_write(struct vty *vty) } /* Route map node structure. */ -static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1}; +static int route_map_config_write(struct vty *vty); +static struct cmd_node rmap_node = { + .name = "routemap", + .node = RMAP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-route-map)# ", + .config_write = route_map_config_write, +}; static void rmap_autocomplete(vector comps, struct cmd_token *token) { @@ -1087,7 +1094,7 @@ void route_map_cli_init(void) cmd_variable_handler_register(rmap_var_handlers); /* CLI commands. */ - install_node(&rmap_node, route_map_config_write); + install_node(&rmap_node); install_default(RMAP_NODE); install_element(CONFIG_NODE, &route_map_cmd); install_element(CONFIG_NODE, &no_route_map_cmd); diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c index dd4cbd7d99..f500a6c408 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -33,16 +33,15 @@ * lib_route_map_entry_set_destroy: unset `set` commands. * lib_route_map_entry_match_destroy: unset `match` commands. */ -int lib_route_map_entry_match_destroy(enum nb_event event, - const struct lyd_node *dnode) +int lib_route_map_entry_match_destroy(struct nb_cb_destroy_args *args) { struct routemap_hook_context *rhc; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - rhc = nb_running_get_entry(dnode, NULL, true); + rhc = nb_running_get_entry(args->dnode, NULL, true); if (rhc->rhc_mhook == NULL) return NB_OK; @@ -54,16 +53,15 @@ int lib_route_map_entry_match_destroy(enum nb_event event, return NB_OK; } -int lib_route_map_entry_set_destroy(enum nb_event event, - const struct lyd_node *dnode) +int lib_route_map_entry_set_destroy(struct nb_cb_destroy_args *args) { struct routemap_hook_context *rhc; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - rhc = nb_running_get_entry(dnode, NULL, true); + rhc = nb_running_get_entry(args->dnode, NULL, true); if (rhc->rhc_shook == NULL) return NB_OK; @@ -89,8 +87,7 @@ routemap_hook_context_insert(struct route_map_index *rmi) return rhc; } -void -routemap_hook_context_free(struct routemap_hook_context *rhc) +void routemap_hook_context_free(struct routemap_hook_context *rhc) { struct route_map_index *rmi = rhc->rhc_rmi; @@ -101,42 +98,39 @@ routemap_hook_context_free(struct routemap_hook_context *rhc) /* * XPath: /frr-route-map:lib/route-map */ -static int lib_route_map_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_create(struct nb_cb_create_args *args) { struct route_map *rm; const char *rm_name; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rm_name = yang_dnode_get_string(dnode, "./name"); + rm_name = yang_dnode_get_string(args->dnode, "./name"); rm = route_map_get(rm_name); - nb_running_set_entry(dnode, rm); + nb_running_set_entry(args->dnode, rm); break; } return NB_OK; } -static int lib_route_map_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_route_map_destroy(struct nb_cb_destroy_args *args) { struct route_map *rm; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rm = nb_running_unset_entry(dnode); + rm = nb_running_unset_entry(args->dnode); route_map_delete(rm); break; } @@ -147,48 +141,45 @@ static int lib_route_map_destroy(enum nb_event event, /* * XPath: /frr-route-map:lib/route-map/entry */ -static int lib_route_map_entry_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_create(struct nb_cb_create_args *args) { struct route_map_index *rmi; struct route_map *rm; uint16_t sequence; int action; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - sequence = yang_dnode_get_uint16(dnode, "./sequence"); - action = yang_dnode_get_enum(dnode, "./action") == 0 + sequence = yang_dnode_get_uint16(args->dnode, "./sequence"); + action = yang_dnode_get_enum(args->dnode, "./action") == 0 ? RMAP_PERMIT : RMAP_DENY; - rm = nb_running_get_entry(dnode, NULL, true); + rm = nb_running_get_entry(args->dnode, NULL, true); rmi = route_map_index_get(rm, action, sequence); - nb_running_set_entry(dnode, rmi); + nb_running_set_entry(args->dnode, rmi); break; } return NB_OK; } -static int lib_route_map_entry_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_route_map_entry_destroy(struct nb_cb_destroy_args *args) { struct route_map_index *rmi; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rmi = nb_running_unset_entry(dnode); + rmi = nb_running_unset_entry(args->dnode); route_map_index_delete(rmi, 1); break; } @@ -199,49 +190,48 @@ static int lib_route_map_entry_destroy(enum nb_event event, /* * XPath: /frr-route-map:lib/route-map/entry/description */ -static int lib_route_map_entry_description_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int +lib_route_map_entry_description_modify(struct nb_cb_modify_args *args) { struct route_map_index *rmi; const char *description; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: /* NOTHING */ break; case NB_EV_PREPARE: - description = yang_dnode_get_string(dnode, NULL); - resource->ptr = XSTRDUP(MTYPE_TMP, description); - if (resource->ptr == NULL) + description = yang_dnode_get_string(args->dnode, NULL); + args->resource->ptr = XSTRDUP(MTYPE_TMP, description); + if (args->resource->ptr == NULL) return NB_ERR_RESOURCE; break; case NB_EV_ABORT: - XFREE(MTYPE_TMP, resource->ptr); + XFREE(MTYPE_TMP, args->resource->ptr); break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); + rmi = nb_running_get_entry(args->dnode, NULL, true); XFREE(MTYPE_TMP, rmi->description); - rmi->description = resource->ptr; + rmi->description = args->resource->ptr; break; } return NB_OK; } -static int lib_route_map_entry_description_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int +lib_route_map_entry_description_destroy(struct nb_cb_destroy_args *args) { struct route_map_index *rmi; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); + rmi = nb_running_get_entry(args->dnode, NULL, true); XFREE(MTYPE_TMP, rmi->description); break; } @@ -252,21 +242,19 @@ static int lib_route_map_entry_description_destroy(enum nb_event event, /* * XPath: /frr-route-map:lib/route-map/entry/action */ -static int lib_route_map_entry_action_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_action_modify(struct nb_cb_modify_args *args) { struct route_map_index *rmi; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); - rmi->type = yang_dnode_get_enum(dnode, NULL); + rmi = nb_running_get_entry(args->dnode, NULL, true); + rmi->type = yang_dnode_get_enum(args->dnode, NULL); /* TODO: notify? */ break; } @@ -277,17 +265,15 @@ static int lib_route_map_entry_action_modify(enum nb_event event, /* * XPath: /frr-route-map:lib/route-map/entry/call */ -static int lib_route_map_entry_call_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_call_modify(struct nb_cb_modify_args *args) { struct route_map_index *rmi; const char *rm_name, *rmn_name; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: - rm_name = yang_dnode_get_string(dnode, "../../name"); - rmn_name = yang_dnode_get_string(dnode, NULL); + rm_name = yang_dnode_get_string(args->dnode, "../../name"); + rmn_name = yang_dnode_get_string(args->dnode, NULL); /* Don't allow to jump to the same route map instance. */ if (strcmp(rm_name, rmn_name) == 0) return NB_ERR_VALIDATION; @@ -295,20 +281,20 @@ static int lib_route_map_entry_call_modify(enum nb_event event, /* TODO: detect circular route map sequences. */ break; case NB_EV_PREPARE: - rmn_name = yang_dnode_get_string(dnode, NULL); - resource->ptr = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmn_name); + rmn_name = yang_dnode_get_string(args->dnode, NULL); + args->resource->ptr = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmn_name); break; case NB_EV_ABORT: - XFREE(MTYPE_ROUTE_MAP_NAME, resource->ptr); + XFREE(MTYPE_ROUTE_MAP_NAME, args->resource->ptr); break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); + rmi = nb_running_get_entry(args->dnode, NULL, true); if (rmi->nextrm) { route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, rmi->nextrm, rmi->map->name); XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm); } - rmi->nextrm = resource->ptr; + rmi->nextrm = args->resource->ptr; route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, rmi->nextrm, rmi->map->name); break; @@ -317,19 +303,18 @@ static int lib_route_map_entry_call_modify(enum nb_event event, return NB_OK; } -static int lib_route_map_entry_call_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_route_map_entry_call_destroy(struct nb_cb_destroy_args *args) { struct route_map_index *rmi; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); + rmi = nb_running_get_entry(args->dnode, NULL, true); route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, rmi->nextrm, rmi->map->name); XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm); @@ -343,24 +328,24 @@ static int lib_route_map_entry_call_destroy(enum nb_event event, /* * XPath: /frr-route-map:lib/route-map/entry/exit-policy */ -static int lib_route_map_entry_exit_policy_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int +lib_route_map_entry_exit_policy_modify(struct nb_cb_modify_args *args) { struct route_map_index *rmi; int rm_action; int policy; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: - policy = yang_dnode_get_enum(dnode, NULL); + policy = yang_dnode_get_enum(args->dnode, NULL); switch (policy) { case 0: /* permit-or-deny */ break; case 1: /* next */ /* FALLTHROUGH */ case 2: /* goto */ - rm_action = yang_dnode_get_enum(dnode, "../action"); + rm_action = + yang_dnode_get_enum(args->dnode, "../action"); if (rm_action == 1 /* deny */) { /* * On deny it is not possible to 'goto' @@ -375,8 +360,8 @@ static int lib_route_map_entry_exit_policy_modify(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); - policy = yang_dnode_get_enum(dnode, NULL); + rmi = nb_running_get_entry(args->dnode, NULL, true); + policy = yang_dnode_get_enum(args->dnode, NULL); switch (policy) { case 0: /* permit-or-deny */ @@ -398,18 +383,16 @@ static int lib_route_map_entry_exit_policy_modify(enum nb_event event, /* * XPath: /frr-route-map:lib/route-map/entry/goto-value */ -static int lib_route_map_entry_goto_value_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_goto_value_modify(struct nb_cb_modify_args *args) { struct route_map_index *rmi; uint16_t rmi_index; uint16_t rmi_next; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: - rmi_index = yang_dnode_get_uint16(dnode, "../sequence"); - rmi_next = yang_dnode_get_uint16(dnode, NULL); + rmi_index = yang_dnode_get_uint16(args->dnode, "../sequence"); + rmi_next = yang_dnode_get_uint16(args->dnode, NULL); if (rmi_next <= rmi_index) { /* Can't jump backwards on a route map. */ return NB_ERR_VALIDATION; @@ -420,27 +403,27 @@ static int lib_route_map_entry_goto_value_modify(enum nb_event event, /* NOTHING */ break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); - rmi->nextpref = yang_dnode_get_uint16(dnode, NULL); + rmi = nb_running_get_entry(args->dnode, NULL, true); + rmi->nextpref = yang_dnode_get_uint16(args->dnode, NULL); break; } return NB_OK; } -static int lib_route_map_entry_goto_value_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int +lib_route_map_entry_goto_value_destroy(struct nb_cb_destroy_args *args) { struct route_map_index *rmi; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); + rmi = nb_running_get_entry(args->dnode, NULL, true); rmi->nextpref = 0; break; } @@ -452,23 +435,21 @@ static int lib_route_map_entry_goto_value_destroy(enum nb_event event, * XPath: /frr-route-map:lib/route-map/entry/match-condition */ static int -lib_route_map_entry_match_condition_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +lib_route_map_entry_match_condition_create(struct nb_cb_create_args *args) { struct routemap_hook_context *rhc; struct route_map_index *rmi; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: /* NOTHING */ break; case NB_EV_APPLY: - rmi = nb_running_get_entry(dnode, NULL, true); + rmi = nb_running_get_entry(args->dnode, NULL, true); rhc = routemap_hook_context_insert(rmi); - nb_running_set_entry(dnode, rhc); + nb_running_set_entry(args->dnode, rhc); break; } @@ -476,17 +457,16 @@ lib_route_map_entry_match_condition_create(enum nb_event event, } static int -lib_route_map_entry_match_condition_destroy(enum nb_event event, - const struct lyd_node *dnode) +lib_route_map_entry_match_condition_destroy(struct nb_cb_destroy_args *args) { struct routemap_hook_context *rhc; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - rv = lib_route_map_entry_match_destroy(event, dnode); - rhc = nb_running_unset_entry(dnode); + rv = lib_route_map_entry_match_destroy(args); + rhc = nb_running_unset_entry(args->dnode); routemap_hook_context_free(rhc); return rv; @@ -496,14 +476,13 @@ lib_route_map_entry_match_condition_destroy(enum nb_event event, * XPath: /frr-route-map:lib/route-map/entry/match-condition/interface */ static int lib_route_map_entry_match_condition_interface_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *ifname; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook function. */ @@ -511,8 +490,8 @@ static int lib_route_map_entry_match_condition_interface_modify( return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_mhook = rmap_match_set_hook.no_match_interface; @@ -531,30 +510,29 @@ static int lib_route_map_entry_match_condition_interface_modify( } static int lib_route_map_entry_match_condition_interface_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_match_destroy(event, dnode); + return lib_route_map_entry_match_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/match-condition/access-list-num */ static int lib_route_map_entry_match_condition_access_list_num_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *acl; int condition, rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook function. */ rv = CMD_SUCCESS; - acl = yang_dnode_get_string(dnode, NULL); - rhc = nb_running_get_entry(dnode, NULL, true); - condition = yang_dnode_get_enum(dnode, "../condition"); + acl = yang_dnode_get_string(args->dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + condition = yang_dnode_get_enum(args->dnode, "../condition"); switch (condition) { case 1: /* ipv4-address-list */ if (rmap_match_set_hook.match_ip_address == NULL) @@ -586,9 +564,9 @@ static int lib_route_map_entry_match_condition_access_list_num_modify( } static int lib_route_map_entry_match_condition_access_list_num_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_match_destroy(event, dnode); + return lib_route_map_entry_match_destroy(args); } /* @@ -596,39 +574,36 @@ static int lib_route_map_entry_match_condition_access_list_num_destroy( * /frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended */ static int lib_route_map_entry_match_condition_access_list_num_extended_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { - return lib_route_map_entry_match_condition_access_list_num_modify( - event, dnode, resource); + return lib_route_map_entry_match_condition_access_list_num_modify(args); } static int lib_route_map_entry_match_condition_access_list_num_extended_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { return lib_route_map_entry_match_condition_access_list_num_destroy( - event, dnode); + args); } /* * XPath: /frr-route-map:lib/route-map/entry/match-condition/list-name */ static int lib_route_map_entry_match_condition_list_name_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *acl; int condition; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook installation, otherwise we can just stop. */ - acl = yang_dnode_get_string(dnode, NULL); - rhc = nb_running_get_entry(dnode, NULL, true); - condition = yang_dnode_get_enum(dnode, "../condition"); + acl = yang_dnode_get_string(args->dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + condition = yang_dnode_get_enum(args->dnode, "../condition"); switch (condition) { case 1: /* ipv4-address-list */ if (rmap_match_set_hook.match_ip_address == NULL) @@ -706,23 +681,22 @@ static int lib_route_map_entry_match_condition_list_name_modify( } static int lib_route_map_entry_match_condition_list_name_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_match_destroy(event, dnode); + return lib_route_map_entry_match_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type */ static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *type; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook function. */ @@ -730,8 +704,8 @@ static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify( return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop_type; @@ -750,23 +724,22 @@ static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify( } static int lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_match_destroy(event, dnode); + return lib_route_map_entry_match_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type */ static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *type; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook function. */ @@ -774,8 +747,8 @@ static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify( return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_next_hop_type; @@ -794,24 +767,22 @@ static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify( } static int lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_match_destroy(event, dnode); + return lib_route_map_entry_match_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/match-condition/metric */ -static int -lib_route_map_entry_match_condition_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_match_condition_metric_modify( + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *type; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook function. */ @@ -819,8 +790,8 @@ lib_route_map_entry_match_condition_metric_modify(enum nb_event event, return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_mhook = rmap_match_set_hook.no_match_metric; @@ -837,26 +808,23 @@ lib_route_map_entry_match_condition_metric_modify(enum nb_event event, return NB_OK; } -static int -lib_route_map_entry_match_condition_metric_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_route_map_entry_match_condition_metric_destroy( + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_match_destroy(event, dnode); + return lib_route_map_entry_match_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/match-condition/tag */ static int -lib_route_map_entry_match_condition_tag_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +lib_route_map_entry_match_condition_tag_modify(struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *tag; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook function. */ @@ -864,8 +832,8 @@ lib_route_map_entry_match_condition_tag_modify(enum nb_event event, return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - tag = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + tag = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_mhook = rmap_match_set_hook.no_match_tag; @@ -883,34 +851,30 @@ lib_route_map_entry_match_condition_tag_modify(enum nb_event event, } static int -lib_route_map_entry_match_condition_tag_destroy(enum nb_event event, - const struct lyd_node *dnode) +lib_route_map_entry_match_condition_tag_destroy(struct nb_cb_destroy_args *args) { - return lib_route_map_entry_match_destroy(event, dnode); + return lib_route_map_entry_match_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action */ -static int lib_route_map_entry_set_action_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_set_action_create(struct nb_cb_create_args *args) { - return lib_route_map_entry_match_condition_create(event, dnode, - resource); + return lib_route_map_entry_match_condition_create(args); } -static int lib_route_map_entry_set_action_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int +lib_route_map_entry_set_action_destroy(struct nb_cb_destroy_args *args) { struct routemap_hook_context *rhc; int rv; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - rv = lib_route_map_entry_set_destroy(event, dnode); - rhc = nb_running_unset_entry(dnode); + rv = lib_route_map_entry_set_destroy(args); + rhc = nb_running_unset_entry(args->dnode); routemap_hook_context_free(rhc); return rv; @@ -919,24 +883,22 @@ static int lib_route_map_entry_set_action_destroy(enum nb_event event, /* * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv4-address */ -static int -lib_route_map_entry_set_action_ipv4_address_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_set_action_ipv4_address_modify( + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *address; struct in_addr ia; int rv; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: /* * NOTE: validate if 'action' is 'ipv4-next-hop', * currently it is not necessary because this is the * only implemented action. */ - yang_dnode_get_ipv4(&ia, dnode, NULL); + yang_dnode_get_ipv4(&ia, args->dnode, NULL); if (ia.s_addr == INADDR_ANY || IPV4_CLASS_DE(ntohl(ia.s_addr))) return NB_ERR_VALIDATION; /* FALLTHROUGH */ @@ -952,8 +914,8 @@ lib_route_map_entry_set_action_ipv4_address_modify(enum nb_event event, return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - address = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + address = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_shook = rmap_match_set_hook.no_set_ip_nexthop; @@ -970,25 +932,23 @@ lib_route_map_entry_set_action_ipv4_address_modify(enum nb_event event, } static int lib_route_map_entry_set_action_ipv4_address_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_destroy(event, dnode); + return lib_route_map_entry_set_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv6-address */ -static int -lib_route_map_entry_set_action_ipv6_address_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_route_map_entry_set_action_ipv6_address_modify( + struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *address; struct in6_addr i6a; int rv; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: /* * NOTE: validate if 'action' is 'ipv6-next-hop', @@ -996,7 +956,7 @@ lib_route_map_entry_set_action_ipv6_address_modify(enum nb_event event, * only implemented action. Other actions might have * different validations. */ - yang_dnode_get_ipv6(&i6a, dnode, NULL); + yang_dnode_get_ipv6(&i6a, args->dnode, NULL); if (!IN6_IS_ADDR_LINKLOCAL(&i6a)) return NB_ERR_VALIDATION; /* FALLTHROUGH */ @@ -1012,8 +972,8 @@ lib_route_map_entry_set_action_ipv6_address_modify(enum nb_event event, return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - address = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + address = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_shook = rmap_match_set_hook.no_set_ipv6_nexthop_local; @@ -1030,9 +990,9 @@ lib_route_map_entry_set_action_ipv6_address_modify(enum nb_event event, } static int lib_route_map_entry_set_action_ipv6_address_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_destroy(event, dnode); + return lib_route_map_entry_set_destroy(args); } /* @@ -1074,111 +1034,105 @@ static int set_action_modify(enum nb_event event, const struct lyd_node *dnode, } static int -lib_route_map_entry_set_action_value_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +lib_route_map_entry_set_action_value_modify(struct nb_cb_modify_args *args) { - const char *metric = yang_dnode_get_string(dnode, NULL); + const char *metric = yang_dnode_get_string(args->dnode, NULL); - return set_action_modify(event, dnode, resource, metric); + return set_action_modify(args->event, args->dnode, args->resource, + metric); } static int -lib_route_map_entry_set_action_value_destroy(enum nb_event event, - const struct lyd_node *dnode) +lib_route_map_entry_set_action_value_destroy(struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_destroy(event, dnode); + return lib_route_map_entry_set_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action/add-metric */ static int -lib_route_map_entry_set_action_add_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +lib_route_map_entry_set_action_add_metric_modify(struct nb_cb_modify_args *args) { - return set_action_modify(event, dnode, resource, "+metric"); + return set_action_modify(args->event, args->dnode, args->resource, + "+metric"); } -static int -lib_route_map_entry_set_action_add_metric_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_route_map_entry_set_action_add_metric_destroy( + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_action_value_destroy(event, dnode); + return lib_route_map_entry_set_action_value_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-metric */ static int lib_route_map_entry_set_action_subtract_metric_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { - return set_action_modify(event, dnode, resource, "-metric"); + return set_action_modify(args->event, args->dnode, args->resource, + "-metric"); } static int lib_route_map_entry_set_action_subtract_metric_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_action_value_destroy(event, dnode); + return lib_route_map_entry_set_action_value_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action/use-round-trip-time */ static int lib_route_map_entry_set_action_use_round_trip_time_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { - return set_action_modify(event, dnode, resource, "rtt"); + return set_action_modify(args->event, args->dnode, args->resource, + "rtt"); } static int lib_route_map_entry_set_action_use_round_trip_time_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_action_value_destroy(event, dnode); + return lib_route_map_entry_set_action_value_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action/add-round-trip-time */ static int lib_route_map_entry_set_action_add_round_trip_time_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { - return set_action_modify(event, dnode, resource, "+rtt"); + return set_action_modify(args->event, args->dnode, args->resource, + "+rtt"); } static int lib_route_map_entry_set_action_add_round_trip_time_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_action_value_destroy(event, dnode); + return lib_route_map_entry_set_action_value_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time */ static int lib_route_map_entry_set_action_subtract_round_trip_time_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + struct nb_cb_modify_args *args) { - return set_action_modify(event, dnode, resource, "-rtt"); + return set_action_modify(args->event, args->dnode, args->resource, + "-rtt"); } static int lib_route_map_entry_set_action_subtract_round_trip_time_destroy( - enum nb_event event, const struct lyd_node *dnode) + struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_action_value_destroy(event, dnode); + return lib_route_map_entry_set_action_value_destroy(args); } /* * XPath: /frr-route-map:lib/route-map/entry/set-action/tag */ static int -lib_route_map_entry_set_action_tag_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +lib_route_map_entry_set_action_tag_modify(struct nb_cb_modify_args *args) { struct routemap_hook_context *rhc; const char *tag; @@ -1189,7 +1143,7 @@ lib_route_map_entry_set_action_tag_modify(enum nb_event event, * necessary because this is the only implemented action. Other * actions might have different validations. */ - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; /* Check for hook function. */ @@ -1197,8 +1151,8 @@ lib_route_map_entry_set_action_tag_modify(enum nb_event event, return NB_OK; /* Add configuration. */ - rhc = nb_running_get_entry(dnode, NULL, true); - tag = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + tag = yang_dnode_get_string(args->dnode, NULL); /* Set destroy information. */ rhc->rhc_shook = rmap_match_set_hook.no_set_tag; @@ -1214,10 +1168,9 @@ lib_route_map_entry_set_action_tag_modify(enum nb_event event, } static int -lib_route_map_entry_set_action_tag_destroy(enum nb_event event, - const struct lyd_node *dnode) +lib_route_map_entry_set_action_tag_destroy(struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_destroy(event, dnode); + return lib_route_map_entry_set_destroy(args); } /* clang-format off */ diff --git a/lib/seqlock.h b/lib/seqlock.h index b551e3ffc4..bfbf97890e 100644 --- a/lib/seqlock.h +++ b/lib/seqlock.h @@ -27,6 +27,10 @@ #include <pthread.h> #include "frratomic.h" +#ifdef __cplusplus +extern "C" { +#endif + /* * this locking primitive is intended to use in a 1:N setup. * @@ -135,4 +139,8 @@ extern void seqlock_release(struct seqlock *sqlo); * anything other than reading RCU items was done */ +#ifdef __cplusplus +} +#endif + #endif /* _SEQLOCK_H */ diff --git a/lib/skiplist.c b/lib/skiplist.c index 790bd71c38..2bef18f525 100644 --- a/lib/skiplist.c +++ b/lib/skiplist.c @@ -61,6 +61,7 @@ #include "vty.h" #include "skiplist.h" #include "lib_errors.h" +#include "network.h" DEFINE_MTYPE_STATIC(LIB, SKIP_LIST, "Skip List") DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node") @@ -95,7 +96,7 @@ static int randomLevel(void) do { if (randomsLeft <= 0) { - randomBits = random(); + randomBits = frr_weak_random(); randomsLeft = BitsInRandom / 2; } b = randomBits & 3; diff --git a/lib/srv6.c b/lib/srv6.c index be340f13f5..287bf56089 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "zebra.h" + #include "srv6.h" #include "log.h" diff --git a/lib/subdir.am b/lib/subdir.am index 4f62eb2264..2f8cbe5d52 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -45,6 +45,7 @@ lib_libfrr_la_SOURCES = \ lib/libfrr.c \ lib/linklist.c \ lib/log.c \ + lib/log_filter.c \ lib/log_vty.c \ lib/md5.c \ lib/memory.c \ @@ -100,6 +101,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 \ @@ -110,46 +113,48 @@ nodist_lib_libfrr_la_SOURCES = \ yang/frr-interface.yang.c \ yang/frr-route-map.yang.c \ yang/frr-route-types.yang.c \ + yang/frr-vrf.yang.c \ + yang/frr-routing.yang.c \ yang/ietf/ietf-routing-types.yang.c \ + yang/ietf/ietf-interfaces.yang.c \ yang/frr-module-translator.yang.c \ + yang/frr-nexthop.yang.c \ + yang/frr-igmp.yang.c \ + yang/frr-pim.yang.c \ + yang/frr-pim-rp.yang.c \ # end vtysh_scan += \ - $(top_srcdir)/lib/distribute.c \ - $(top_srcdir)/lib/filter.c \ - $(top_srcdir)/lib/if.c \ - $(top_srcdir)/lib/if_rmap.c \ - $(top_srcdir)/lib/keychain.c \ - $(top_srcdir)/lib/lib_vty.c \ - $(top_srcdir)/lib/nexthop_group.c \ - $(top_srcdir)/lib/plist.c \ - $(top_srcdir)/lib/routemap.c \ - $(top_srcdir)/lib/routemap_cli.c \ - $(top_srcdir)/lib/vrf.c \ - $(top_srcdir)/lib/vty.c \ + lib/distribute.c \ + lib/filter.c \ + lib/if.c \ + lib/if_rmap.c \ + lib/keychain.c \ + lib/lib_vty.c \ + lib/nexthop_group.c \ + lib/plist.c \ + lib/routemap.c \ + lib/routemap_cli.c \ + lib/vrf.c \ + lib/vty.c \ # end # can be loaded as DSO - always include for vtysh -vtysh_scan += $(top_srcdir)/lib/agentx.c +vtysh_scan += lib/agentx.c if SQLITE3 lib_libfrr_la_LIBADD += $(SQLITE3_LIBS) lib_libfrr_la_SOURCES += lib/db.c endif -lib/if_clippy.c: $(CLIPPY_DEPS) -lib/if.lo: lib/if_clippy.c -lib/plist_clippy.c: $(CLIPPY_DEPS) -lib/plist.lo: lib/plist_clippy.c -lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) -lib/nexthop_group.lo: lib/nexthop_group_clippy.c -lib/northbound_cli_clippy.c: $(CLIPPY_DEPS) -lib/northbound_cli.lo: lib/northbound_cli_clippy.c -lib/routemap_cli_clippy.c: $(CLIPPY_DEPS) -lib/routemap_cli.lo: lib/routemap_cli_clippy.c -lib/vty_clippy.c: $(CLIPPY_DEPS) -lib/vty.lo: lib/vty_clippy.c -lib/log_vty_clippy.c: $(CLIPPY_DEPS) -lib/log_vty.lo: lib/log_vty_clippy.c +clippy_scan += \ + lib/if.c \ + lib/log_vty.c \ + lib/nexthop_group.c \ + lib/northbound_cli.c \ + lib/plist.c \ + lib/routemap_cli.c \ + lib/vty.c \ + # end pkginclude_HEADERS += \ lib/agg_table.h \ @@ -254,6 +259,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 +272,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 \ diff --git a/lib/thread.c b/lib/thread.c index dbf668a699..4d689a9f88 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -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/typesafe.c b/lib/typesafe.c index 6635cf7506..a52b55b734 100644 --- a/lib/typesafe.c +++ b/lib/typesafe.c @@ -23,6 +23,7 @@ #include "typesafe.h" #include "memory.h" +#include "network.h" DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket") DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow") @@ -196,7 +197,7 @@ struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, int cmpval; /* level / newlevel are 1-counted here */ - newlevel = __builtin_ctz(random()) + 1; + newlevel = __builtin_ctz(frr_weak_random()) + 1; if (newlevel > SKIPLIST_MAXDEPTH) newlevel = SKIPLIST_MAXDEPTH; diff --git a/lib/version.h.in b/lib/version.h.in index 52c10f7d68..d535d131c8 100644 --- a/lib/version.h.in +++ b/lib/version.h.in @@ -28,6 +28,10 @@ #include "gitversion.h" #endif +#ifdef __cplusplus +extern "C" { +#endif + #ifndef GIT_SUFFIX #define GIT_SUFFIX "" #endif @@ -54,4 +58,8 @@ pid_t pid_output (const char *); +#ifdef __cplusplus +} +#endif + #endif /* _ZEBRA_VERSION_H */ @@ -36,6 +36,8 @@ #include "privs.h" #include "nexthop_group.h" #include "lib_errors.h" +#include "northbound.h" +#include "northbound_cli.h" /* default VRF ID value used when VRF backend is not NETNS */ #define VRF_DEFAULT_INTERNAL 0 @@ -612,6 +614,8 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) { struct vrf *vrfp; + char xpath_list[XPATH_MAXLEN]; + int ret; if (strlen(vrfname) > VRF_NAMSIZ) { if (vty) @@ -626,13 +630,24 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, return CMD_WARNING_CONFIG_FAILED; } - vrfp = vrf_get(VRF_UNKNOWN, vrfname); - - if (vty) - VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + if (vty) { + snprintf(xpath_list, sizeof(xpath_list), + "/frr-vrf:lib/vrf[name='%s']", vrfname); + + nb_cli_enqueue_change(vty, xpath_list, NB_OP_CREATE, NULL); + ret = nb_cli_apply_changes(vty, xpath_list); + if (ret == CMD_SUCCESS) { + VTY_PUSH_XPATH(VRF_NODE, xpath_list); + vrfp = vrf_lookup_by_name(vrfname); + if (vrfp) + VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + } + } else { + vrfp = vrf_get(VRF_UNKNOWN, vrfname); - if (vrf) - *vrf = vrfp; + if (vrf) + *vrf = vrfp; + } return CMD_SUCCESS; } @@ -735,6 +750,7 @@ DEFUN (no_vrf, "VRF's name\n") { const char *vrfname = argv[2]->arg; + char xpath_list[XPATH_MAXLEN]; struct vrf *vrfp; @@ -750,15 +766,20 @@ DEFUN (no_vrf, return CMD_WARNING_CONFIG_FAILED; } - /* Clear configured flag and invoke delete. */ - UNSET_FLAG(vrfp->status, VRF_CONFIGURED); - vrf_delete(vrfp); + snprintf(xpath_list, sizeof(xpath_list), "/frr-vrf:lib/vrf[name='%s']", + vrfname); - return CMD_SUCCESS; + nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, xpath_list); } -static struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1}; +static struct cmd_node vrf_node = { + .name = "vrf", + .node = VRF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-vrf)# ", +}; DEFUN_NOSH (vrf_netns, vrf_netns_cmd, @@ -848,11 +869,17 @@ static int vrf_write_host(struct vty *vty) return 1; } -static struct cmd_node vrf_debug_node = {VRF_DEBUG_NODE, "", 1}; +static int vrf_write_host(struct vty *vty); +static struct cmd_node vrf_debug_node = { + .name = "vrf debug", + .node = VRF_DEBUG_NODE, + .prompt = "", + .config_write = vrf_write_host, +}; void vrf_install_commands(void) { - install_node(&vrf_debug_node, vrf_write_host); + install_node(&vrf_debug_node); install_element(CONFIG_NODE, &vrf_debug_cmd); install_element(ENABLE_NODE, &vrf_debug_cmd); @@ -865,7 +892,8 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty), { install_element(CONFIG_NODE, &vrf_cmd); install_element(CONFIG_NODE, &no_vrf_cmd); - install_node(&vrf_node, writefunc); + vrf_node.config_write = writefunc; + install_node(&vrf_node); install_default(VRF_NODE); install_element(VRF_NODE, &vrf_exit_cmd); if (vrf_is_backend_netns() && ns_have_netns()) { @@ -1019,3 +1047,144 @@ vrf_id_t vrf_generate_id(void) return ++vrf_id_local; } + +/* ------- Northbound callbacks ------- */ + +/* + * XPath: /frr-vrf:lib/vrf + */ +static int lib_vrf_create(struct nb_cb_create_args *args) +{ + const char *vrfname; + struct vrf *vrfp; + + vrfname = yang_dnode_get_string(args->dnode, "./name"); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrfp = vrf_get(VRF_UNKNOWN, vrfname); + + nb_running_set_entry(args->dnode, vrfp); + + return NB_OK; +} + +static int lib_vrf_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrfp; + + switch (args->event) { + case NB_EV_VALIDATE: + vrfp = nb_running_get_entry(args->dnode, NULL, true); + if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) { + zlog_debug("%s Only inactive VRFs can be deleted", + __func__); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrfp = nb_running_unset_entry(args->dnode); + + /* Clear configured flag and invoke delete. */ + UNSET_FLAG(vrfp->status, VRF_CONFIGURED); + vrf_delete(vrfp); + break; + } + + return NB_OK; +} + +static const void *lib_vrf_get_next(struct nb_cb_get_next_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + if (args->list_entry == NULL) { + vrfp = RB_MIN(vrf_name_head, &vrfs_by_name); + } else { + vrfp = RB_NEXT(vrf_name_head, vrfp); + } + + return vrfp; +} + +static int lib_vrf_get_keys(struct nb_cb_get_keys_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + args->keys->num = 1; + strlcpy(args->keys->key[0], vrfp->name, sizeof(args->keys->key[0])); + + return NB_OK; +} + +static const void *lib_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *vrfname = args->keys->key[0]; + + struct vrf *vrf = vrf_lookup_by_name(vrfname); + + return vrf; +} + +/* + * XPath: /frr-vrf:lib/vrf/id + */ +static struct yang_data * +lib_vrf_state_id_get_elem(struct nb_cb_get_elem_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + return yang_data_new_uint32(args->xpath, vrfp->vrf_id); +} + +/* + * XPath: /frr-vrf:lib/vrf/active + */ +static struct yang_data * +lib_vrf_state_active_get_elem(struct nb_cb_get_elem_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + if (vrfp->status == VRF_ACTIVE) + return yang_data_new_bool( + args->xpath, vrfp->status == VRF_ACTIVE ? true : false); + + return NULL; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_vrf_info = { + .name = "frr-vrf", + .nodes = { + { + .xpath = "/frr-vrf:lib/vrf", + .cbs = { + .create = lib_vrf_create, + .destroy = lib_vrf_destroy, + .get_next = lib_vrf_get_next, + .get_keys = lib_vrf_get_keys, + .lookup_entry = lib_vrf_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/state/id", + .cbs = { + .get_elem = lib_vrf_state_id_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/state/active", + .cbs = { + .get_elem = lib_vrf_state_active_get_elem, + } + }, + { + .xpath = NULL, + }, + } +}; + @@ -325,6 +325,8 @@ extern int vrf_enable(struct vrf *vrf); extern void vrf_delete(struct vrf *vrf); extern vrf_id_t vrf_generate_id(void); +extern const struct frr_yang_module_info frr_vrf_info; + #ifdef __cplusplus } #endif @@ -1882,7 +1882,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) req.ai_flags = AI_PASSIVE; req.ai_family = AF_UNSPEC; req.ai_socktype = SOCK_STREAM; - sprintf(port_str, "%d", port); + snprintf(port_str, sizeof(port_str), "%d", port); port_str[sizeof(port_str) - 1] = '\0'; ret = getaddrinfo(hostname, port_str, &req, &ainfo); @@ -2199,6 +2199,9 @@ void vty_close(struct vty *vty) int i; bool was_stdio = false; + /* Drop out of configure / transaction if needed. */ + vty_config_exit(vty); + /* Cancel threads.*/ THREAD_OFF(vty->t_read); THREAD_OFF(vty->t_write); @@ -2242,9 +2245,6 @@ void vty_close(struct vty *vty) list_delete(&vty->error); } - /* Check configure. */ - vty_config_exit(vty); - /* OK free vty. */ XFREE(MTYPE_VTY, vty); @@ -2380,7 +2380,7 @@ static FILE *vty_use_backup_config(const char *fullpath) } fullpath_tmp = malloc(strlen(fullpath) + 8); - sprintf(fullpath_tmp, "%s.XXXXXX", fullpath); + snprintf(fullpath_tmp, strlen(fullpath) + 8, "%s.XXXXXX", fullpath); /* Open file to configuration write. */ tmp = mkstemp(fullpath_tmp); @@ -2605,6 +2605,28 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) void vty_config_exit(struct vty *vty) { + enum node_type node = vty->node; + struct cmd_node *cnode; + + /* unlock and jump up to ENABLE_NODE if -and only if- we're + * somewhere below CONFIG_NODE */ + while (node && node != CONFIG_NODE) { + cnode = vector_lookup(cmdvec, node); + node = cnode->parent_node; + } + if (node != CONFIG_NODE) + /* called outside config, e.g. vty_close() in ENABLE_NODE */ + return; + + while (vty->node != ENABLE_NODE) + /* will call vty_config_node_exit() below */ + cmd_exit(vty); +} + +int vty_config_node_exit(struct vty *vty) +{ + vty->xpath_index = 0; + /* Check if there's a pending confirmed commit. */ if (vty->t_confirmed_commit_timeout) { vty_out(vty, @@ -2626,6 +2648,7 @@ void vty_config_exit(struct vty *vty) } vty->config = false; + return 1; } /* Master of the threads. */ @@ -2989,8 +3012,13 @@ static int vty_config_write(struct vty *vty) return CMD_SUCCESS; } +static int vty_config_write(struct vty *vty); struct cmd_node vty_node = { - VTY_NODE, "%s(config-line)# ", 1, + .name = "vty", + .node = VTY_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-line)# ", + .config_write = vty_config_write, }; /* Reset all VTY status. */ @@ -3084,7 +3112,7 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE); /* Install bgp top node. */ - install_node(&vty_node, vty_config_write); + install_node(&vty_node); install_element(VIEW_NODE, &config_who_cmd); install_element(VIEW_NODE, &show_history_cmd); @@ -323,6 +323,7 @@ extern void vty_log(const char *level, const char *proto, const char *msg, extern int vty_config_enter(struct vty *vty, bool private_config, bool exclusive); extern void vty_config_exit(struct vty *); +extern int vty_config_node_exit(struct vty *); extern int vty_shell(struct vty *); extern int vty_shell_serv(struct vty *); extern void vty_hello(struct vty *); diff --git a/lib/yang.c b/lib/yang.c index 0502d4952d..c80bf20306 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -72,13 +72,17 @@ static const char *yang_module_imp_clb(const char *mod_name, return NULL; } +/* clang-format off */ static const char *const frr_native_modules[] = { "frr-interface", + "frr-vrf", "frr-ripd", "frr-ripngd", "frr-isisd", "frr-vrrpd", + "frr-zebra", }; +/* clang-format on */ /* Generate the yang_modules tree. */ static inline int yang_module_compare(const struct yang_module *a, diff --git a/lib/yang.h b/lib/yang.h index 8af440d3ed..126521707b 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -34,7 +34,7 @@ extern "C" { #endif /* Maximum XPath length. */ -#define XPATH_MAXLEN 256 +#define XPATH_MAXLEN 512 /* Maximum list key length. */ #define LIST_MAXKEYS 8 diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 2b502d635b..c31ba3fcc0 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -23,6 +23,8 @@ #include "lib_errors.h" #include "northbound.h" #include "printfrr.h" +#include "nexthop.h" +#include "printfrr.h" static const char *yang_get_default_value(const char *xpath) { @@ -783,6 +785,14 @@ void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt, } /* + * Primitive type: empty. + */ +struct yang_data *yang_data_new_empty(const char *xpath) +{ + return yang_data_new(xpath, NULL); +} + +/* * Derived type: IP prefix. */ void yang_str2prefix(const char *value, union prefixptr prefix) @@ -1114,3 +1124,70 @@ void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...) value = yang_get_default_value(xpath); yang_str2ip(value, var); } + +struct yang_data *yang_data_new_mac(const char *xpath, + const struct ethaddr *mac) +{ + char value_str[ETHER_ADDR_STRLEN]; + + prefix_mac2str(mac, value_str, sizeof(value_str)); + return yang_data_new(xpath, value_str); +} + +void yang_str2mac(const char *value, struct ethaddr *mac) +{ + (void)prefix_str2mac(value, mac); +} + +struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time) +{ + struct tm tm; + char timebuf[MONOTIME_STRLEN]; + struct timeval _time, time_real; + char *ts_dot; + uint16_t buflen; + + _time.tv_sec = time; + _time.tv_usec = 0; + monotime_to_realtime(&_time, &time_real); + + gmtime_r(&time_real.tv_sec, &tm); + + /* rfc-3339 format */ + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", &tm); + buflen = strlen(timebuf); + ts_dot = timebuf + buflen; + + /* microseconds and appends Z */ + snprintfrr(ts_dot, sizeof(timebuf) - buflen, ".%06luZ", + (unsigned long)time_real.tv_usec); + + return yang_data_new(xpath, timebuf); +} + +const char *yang_nexthop_type2str(uint32_t ntype) +{ + switch (ntype) { + case NEXTHOP_TYPE_IFINDEX: + return "ifindex"; + break; + case NEXTHOP_TYPE_IPV4: + return "ip4"; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + return "ip4-ifindex"; + break; + case NEXTHOP_TYPE_IPV6: + return "ip6"; + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + return "ip6-ifindex"; + break; + case NEXTHOP_TYPE_BLACKHOLE: + return "blackhole"; + break; + default: + return "unknown"; + break; + } +} diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 10d1ea314f..ba2cf5139c 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -22,6 +22,10 @@ #include "prefix.h" +#ifdef __cplusplus +extern "C" { +#endif + /* bool */ extern bool yang_str2bool(const char *value); extern struct yang_data *yang_data_new_bool(const char *xpath, bool value); @@ -114,6 +118,9 @@ extern const char *yang_get_default_string(const char *xpath_fmt, ...); extern void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt, ...); +/* empty */ +extern struct yang_data *yang_data_new_empty(const char *xpath); + /* ip prefix */ extern void yang_str2prefix(const char *value, union prefixptr prefix); extern struct yang_data *yang_data_new_prefix(const char *xpath, @@ -172,4 +179,20 @@ extern void yang_dnode_get_ip(struct ipaddr *addr, const struct lyd_node *dnode, const char *xpath_fmt, ...); extern void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...); +/* mac */ +extern struct yang_data *yang_data_new_mac(const char *xpath, + const struct ethaddr *mac); +extern void yang_str2mac(const char *value, struct ethaddr *mac); + +/*data-and-time */ +extern struct yang_data *yang_data_new_date_and_time(const char *xpath, + time_t time); + +/* nexthop enum2str */ +extern const char *yang_nexthop_type2str(uint32_t ntype); + +#ifdef __cplusplus +} +#endif + #endif /* _FRR_NORTHBOUND_WRAPPERS_H_ */ diff --git a/lib/zassert.h b/lib/zassert.h index d45e1be5f8..e50a88f407 100644 --- a/lib/zassert.h +++ b/lib/zassert.h @@ -19,6 +19,10 @@ #ifndef _QUAGGA_ASSERT_H #define _QUAGGA_ASSERT_H +#ifdef __cplusplus +extern "C" { +#endif + extern void _zlog_assert_failed(const char *assertion, const char *file, unsigned int line, const char *function) __attribute__((noreturn)); @@ -39,4 +43,8 @@ extern void _zlog_assert_failed(const char *assertion, const char *file, #undef assert #define assert(EX) zassert(EX) +#ifdef __cplusplus +} +#endif + #endif /* _QUAGGA_ASSERT_H */ diff --git a/lib/zclient.c b/lib/zclient.c index 5402e9c3c5..be2c4e54a0 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -388,6 +388,7 @@ int zclient_send_hello(struct zclient *zclient) zclient_create_header(s, ZEBRA_HELLO, VRF_DEFAULT); stream_putc(s, zclient->redist_default); stream_putw(s, zclient->instance); + stream_putl(s, zclient->session_id); if (zclient->receive_notify) stream_putc(s, 1); else diff --git a/lib/zclient.h b/lib/zclient.h index 214226cf5f..4ada064623 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -38,6 +38,10 @@ #include "mlag.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Zebra types. Used in Zserv message header. */ typedef uint16_t zebra_size_t; @@ -258,6 +262,9 @@ struct zclient { /* Is this a synchronous client? */ bool synchronous; + /* Session id (optional) to support clients with multiple sessions */ + uint32_t session_id; + /* Socket to zebra daemon. */ int sock; @@ -823,4 +830,8 @@ extern void zclient_send_mlag_data(struct zclient *client, */ extern int zclient_send_hello(struct zclient *client); +#ifdef __cplusplus +} +#endif + #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/lib/zlog.c b/lib/zlog.c new file mode 100644 index 0000000000..45726755f8 --- /dev/null +++ b/lib/zlog.c @@ -0,0 +1,701 @@ +/* + * 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); + if (mmfd < 0) { + zlog_err("failed to open thread log buffer \"%s\": %s", + mmpath, strerror(errno)); + goto out_anon; + } + fchown(mmfd, zlog_uid, zlog_gid); + +#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 index 0000000000..1c5013746b --- /dev/null +++ b/lib/zlog.h @@ -0,0 +1,194 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +PRINTFRR(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); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_ZLOG_H */ diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c new file mode 100644 index 0000000000..b23ab073b4 --- /dev/null +++ b/lib/zlog_targets.c @@ -0,0 +1,574 @@ +/* + * 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" + +/* these allocations are intentionally left active even when doing full exit + * cleanup, in order to keep the logging subsystem fully functional until the + * absolute end. + */ + +DECLARE_MGROUP(LOG) +DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem") + +DEFINE_MTYPE_STATIC(LOG, LOG_FD, "log file target") +DEFINE_MTYPE_STATIC(LOG, LOG_FD_NAME, "log file name") +DEFINE_MTYPE_STATIC(LOG, LOG_FD_ROTATE, "log file rotate helper") +DEFINE_MTYPE_STATIC(LOG, 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: ", +}; + +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 = zcf->zlog_wrap ? zcf->zlog_wrap : 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 index 0000000000..6faf722ffd --- /dev/null +++ b/lib/zlog_targets.h @@ -0,0 +1,74 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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; + + void (*zlog_wrap)(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs); +}; + +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); + +extern void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs); + +/* 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); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_ZLOG_TARGETS_H */ |
