summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/agentx.c12
-rw-r--r--lib/atomlist.h34
-rw-r--r--lib/bitfield.h5
-rw-r--r--lib/clippy.c20
-rw-r--r--lib/command.c734
-rw-r--r--lib/command.h23
-rw-r--r--lib/csv.c18
-rw-r--r--lib/defaults.h8
-rw-r--r--lib/filter.c33
-rw-r--r--lib/frr_pthread.c5
-rw-r--r--lib/frratomic.h2
-rw-r--r--lib/frrcu.h9
-rw-r--r--lib/grammar_sandbox.c2
-rw-r--r--lib/grammar_sandbox_main.c6
-rw-r--r--lib/hook.c23
-rw-r--r--lib/hook.h28
-rw-r--r--lib/iana_afi.h8
-rw-r--r--lib/if.c211
-rw-r--r--lib/keychain.c24
-rw-r--r--lib/libfrr.c14
-rw-r--r--lib/log.c703
-rw-r--r--lib/log.h62
-rw-r--r--lib/log_filter.c156
-rw-r--r--lib/log_int.h59
-rw-r--r--lib/log_vty.c653
-rw-r--r--lib/log_vty.h22
-rw-r--r--lib/memory.c3
-rw-r--r--lib/memory.h14
-rw-r--r--lib/netns_linux.c2
-rw-r--r--lib/network.c18
-rw-r--r--lib/network.h2
-rw-r--r--lib/nexthop_group.c11
-rw-r--r--lib/northbound.c267
-rw-r--r--lib/northbound.h252
-rw-r--r--lib/northbound_cli.c9
-rw-r--r--lib/northbound_grpc.cpp3
-rw-r--r--lib/pid_output.c2
-rw-r--r--lib/plist.c41
-rw-r--r--lib/plist_int.h2
-rw-r--r--lib/printfrr.h8
-rw-r--r--lib/ptm_lib.c8
-rw-r--r--lib/pullwr.h8
-rw-r--r--lib/qobj.c5
-rw-r--r--lib/resolver.c10
-rw-r--r--lib/resolver.h8
-rw-r--r--lib/routemap.c10
-rw-r--r--lib/routemap.h6
-rw-r--r--lib/routemap_cli.c11
-rw-r--r--lib/routemap_northbound.c425
-rw-r--r--lib/seqlock.h8
-rw-r--r--lib/skiplist.c3
-rw-r--r--lib/srv6.c2
-rw-r--r--lib/subdir.am62
-rw-r--r--lib/thread.c1
-rw-r--r--lib/typesafe.c3
-rw-r--r--lib/version.h.in8
-rw-r--r--lib/vrf.c197
-rw-r--r--lib/vrf.h2
-rw-r--r--lib/vty.c42
-rw-r--r--lib/vty.h1
-rw-r--r--lib/yang.c4
-rw-r--r--lib/yang.h2
-rw-r--r--lib/yang_wrappers.c77
-rw-r--r--lib/yang_wrappers.h23
-rw-r--r--lib/zassert.h8
-rw-r--r--lib/zclient.c1
-rw-r--r--lib/zclient.h11
-rw-r--r--lib/zlog.c701
-rw-r--r--lib/zlog.h194
-rw-r--r--lib/zlog_targets.c574
-rw-r--r--lib/zlog_targets.h74
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 *);
diff --git a/lib/csv.c b/lib/csv.c
index 445742807c..b48b79792e 100644
--- a/lib/csv.c
+++ b/lib/csv.c
@@ -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
diff --git a/lib/if.c b/lib/if.c
index cc964106d0..ff95cd9043 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -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();
diff --git a/lib/log.c b/lib/log.c
index b3be5216aa..9b0f5b3d85 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -25,7 +25,6 @@
#include "zclient.h"
#include "log.h"
-#include "log_int.h"
#include "memory.h"
#include "command.h"
#include "lib_errors.h"
@@ -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)
{
diff --git a/lib/log.h b/lib/log.h
index d79ad9f805..b65ae5d5d5 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -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 */
diff --git a/lib/vrf.c b/lib/vrf.c
index f642aa5609..f8702973bc 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -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,
+ },
+ }
+};
+
diff --git a/lib/vrf.h b/lib/vrf.h
index 2dc2648837..83ed16b48e 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -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
diff --git a/lib/vty.c b/lib/vty.c
index 8056236de9..784f9cf2ac 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -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);
diff --git a/lib/vty.h b/lib/vty.h
index f90a35a260..694aac3944 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -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 */