From db9a7cf7c05c9fe82af50cb28d56a16fda236e76 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:06:36 -0400 Subject: lib: Add some all daemon command strings Add some strings for defining vty commands that target all daemons. Signed-off-by: Stephen Worley --- lib/command.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/command.h b/lib/command.h index cc4c5d52f..e02f1134b 100644 --- a/lib/command.h +++ b/lib/command.h @@ -411,6 +411,12 @@ struct cmd_node { #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nInterface name or neighbor tag\n" #define NEIGHBOR_ADDR_STR3 "Neighbor address\nIPv6 address\nInterface name\n" +/* Dameons lists */ +#define DAEMONS_STR \ + "For the zebra daemon\nFor the rip daemon\nFor the ripng daemon\nFor the ospf daemon\nFor the ospfv6 daemon\nFor the bgp daemon\nFor the isis daemon\nFor the pbr daemon\nFor the fabricd daemon\nFor the pim daemon\nFor the static daemon\n" +#define DAEMONS_LIST \ + "" + /* Prototypes. */ extern void install_node(struct cmd_node *, int (*)(struct vty *)); extern void install_default(enum node_type); -- cgit v1.2.3 From 2e77de95162842c85cd00b281896082eb566553e Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:08:36 -0400 Subject: vtysh: Update vtysh commands with all daemon str Update the vtysh commands that target all daemons to use the DAEMONS_* macro. Signed-off-by: Stephen Worley --- vtysh/vtysh.c | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c3aeb27eb..1d5fa8cbc 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2269,20 +2269,10 @@ DEFUN (vtysh_show_work_queues, DEFUN (vtysh_show_work_queues_daemon, vtysh_show_work_queues_daemon_cmd, - "show work-queues ", + "show work-queues " DAEMONS_LIST, SHOW_STR "Work Queue information\n" - "For the zebra daemon\n" - "For the rip daemon\n" - "For the ripng daemon\n" - "For the ospf daemon\n" - "For the ospfv6 daemon\n" - "For the bgp daemon\n" - "For the isis daemon\n" - "For the pbr daemon\n" - "For the fabricd daemon\n" - "For the pim daemon\n" - "For the static daemon\n") + DAEMONS_STR) { int idx_protocol = 2; unsigned int i; @@ -2629,20 +2619,10 @@ DEFUNSH(VTYSH_ALL, no_vtysh_config_enable_password, DEFUN (vtysh_write_terminal, vtysh_write_terminal_cmd, - "write terminal []", + "write terminal ["DAEMONS_LIST"]", "Write running configuration to memory, network, or terminal\n" "Write to terminal\n" - "For the zebra daemon\n" - "For the rip daemon\n" - "For the ripng daemon\n" - "For the ospf daemon\n" - "For the ospfv6 daemon\n" - "For the ldpd daemon\n" - "For the bgp daemon\n" - "For the isis daemon\n" - "For the fabricd daemon\n" - "For the pim daemon\n" - "For the static daemon\n") + DAEMONS_STR) { unsigned int i; char line[] = "do write terminal\n"; @@ -2668,20 +2648,10 @@ DEFUN (vtysh_write_terminal, DEFUN (vtysh_show_running_config, vtysh_show_running_config_cmd, - "show running-config []", + "show running-config ["DAEMONS_LIST"]", SHOW_STR "Current operating configuration\n" - "For the zebra daemon\n" - "For the rip daemon\n" - "For the ripng daemon\n" - "For the ospf daemon\n" - "For the ospfv6 daemon\n" - "For the ldp daemon\n" - "For the bgp daemon\n" - "For the isis daemon\n" - "For the fabricd daemon\n" - "For the pim daemon\n" - "For the static daemon\n") + DAEMONS_STR) { return vtysh_write_terminal(self, vty, argc, argv); } -- cgit v1.2.3 From c0dc23460ba43192703a3bf76eca08c71257d42c Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:15:32 -0400 Subject: lib: Add log filter manipulation code Add code for manipulation/creation of log filters and their table. Specifically, add lookup,clear,add,del,dump functionality. Signed-off-by: Stephen Worley --- lib/log.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/log.h | 9 ++++++ 2 files changed, 106 insertions(+) diff --git a/lib/log.c b/lib/log.c index 5ce3bd702..80ca66ca8 100644 --- a/lib/log.c +++ b/lib/log.c @@ -65,6 +65,103 @@ const char *zlog_priority[] = { "notifications", "informational", "debugging", NULL, }; +static char zlog_filters[ZLOG_FILTERS_MAX][ZLOG_FILTER_LENGTH_MAX + 1]; +static uint8_t zlog_filter_count; + +static int zlog_filter_lookup(const char *lookup) +{ + /* look for a match on the filter in the current filters */ + 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) +{ + pthread_mutex_lock(&loglock); + zlog_filter_count = 0; + pthread_mutex_unlock(&loglock); +} + +int zlog_filter_add(const char *filter) +{ + pthread_mutex_lock(&loglock); + + if (zlog_filter_count >= ZLOG_FILTERS_MAX) { + pthread_mutex_unlock(&loglock); + return 1; + } + + if (zlog_filter_lookup(filter) != -1) { + /* Filter already present */ + pthread_mutex_unlock(&loglock); + return -1; + } + + strlcpy(zlog_filters[zlog_filter_count], filter, + sizeof(zlog_filters[0])); + + if (zlog_filters[zlog_filter_count] == NULL + || zlog_filters[zlog_filter_count][0] == '\0') { + pthread_mutex_unlock(&loglock); + return -1; + } + + zlog_filter_count++; + + pthread_mutex_unlock(&loglock); + return 0; +} + +int zlog_filter_del(const char *filter) +{ + pthread_mutex_lock(&loglock); + + int found_idx = zlog_filter_lookup(filter); + + if (found_idx == -1) { + /* Didn't find the filter to delete */ + pthread_mutex_unlock(&loglock); + return -1; + } + + /* Remove and adjust the filter array */ + for (int i = found_idx; i < zlog_filter_count - 1; i++) + strlcpy(zlog_filters[i], zlog_filters[i + 1], + sizeof(zlog_filters[0])); + + zlog_filter_count--; + + pthread_mutex_unlock(&loglock); + return 0; +} + +/* Dump all filters to buffer, delimited by new line */ +int zlog_filter_dump(char *buf, size_t max_size) +{ + pthread_mutex_lock(&loglock); + + int ret = 0; + int len = 0; + + for (int i = 0; i < zlog_filter_count; i++) { + ret = snprintf(buf + len, max_size - len, "\t%s\n", + zlog_filters[i]); + len += ret; + if ((ret < 0) || ((size_t)len >= max_size)) { + pthread_mutex_unlock(&loglock); + return -1; + } + } + + pthread_mutex_unlock(&loglock); + + return len; +} + /* * write_wrapper * diff --git a/lib/log.h b/lib/log.h index c5ae6fe32..501da88a5 100644 --- a/lib/log.h +++ b/lib/log.h @@ -115,6 +115,15 @@ extern int zlog_reset_file(void); /* Rotate log. */ extern int zlog_rotate(void); +#define ZLOG_FILTERS_MAX 100 /* Max # of filters at once */ +#define ZLOG_FILTER_LENGTH_MAX 80 /* 80 character filter limit */ + +/* Add/Del/Dump log filters */ +extern void zlog_filter_clear(void); +extern int zlog_filter_add(const char *filter); +extern int zlog_filter_del(const char *filter); +extern int zlog_filter_dump(char *buf, size_t max_size); + const char *lookup_msg(const struct message *mz, int kz, const char *nf); /* Safe version of strerror -- never returns NULL. */ -- cgit v1.2.3 From bd1058c0af41dc68aa6b2b3ac72e073eff0bb8f2 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Tue, 18 Jun 2019 19:22:03 -0400 Subject: lib: Use memmove to adjust filters after del Simplify the code in deleting a filter by using memmove rather than iterating. Memmove handles overlapping strings safely so this is fine here. Signed-off-by: Stephen Worley --- lib/log.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/log.c b/lib/log.c index 80ca66ca8..4b8c55d65 100644 --- a/lib/log.c +++ b/lib/log.c @@ -121,6 +121,7 @@ int zlog_filter_del(const char *filter) pthread_mutex_lock(&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 */ @@ -128,10 +129,9 @@ int zlog_filter_del(const char *filter) return -1; } - /* Remove and adjust the filter array */ - for (int i = found_idx; i < zlog_filter_count - 1; i++) - strlcpy(zlog_filters[i], zlog_filters[i + 1], - sizeof(zlog_filters[0])); + /* 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--; -- cgit v1.2.3 From 2d96ff08dd5151fc091db7c6fa9196ac8ae769f8 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:29:33 -0400 Subject: vtysh: Execute command on client by name only Add static function path for exectuting a command on a client daemon via a string of its name only. Signed-off-by: Stephen Worley --- vtysh/vtysh.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 1d5fa8cbc..2160e4054 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -140,6 +140,21 @@ struct vtysh_client vtysh_client[] = { {.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL}, }; +/* Searches for client by name, returns index */ +static int vtysh_client_lookup(const char *name) +{ + int idx = -1; + + for (unsigned int i = 0; i < array_size(vtysh_client); i++) { + if (strmatch(vtysh_client[i].name, name)) { + idx = i; + break; + } + } + + return idx; +} + enum vtysh_write_integrated vtysh_write_integrated = WRITE_INTEGRATED_UNSPECIFIED; @@ -394,6 +409,23 @@ static int vtysh_client_execute(struct vtysh_client *head_client, return vtysh_client_run_all(head_client, line, 0, NULL, NULL); } +/* Execute by name */ +static int vtysh_client_execute_name(const char *name, const char *line) +{ + int ret = CMD_SUCCESS; + int idx_client = -1; + + idx_client = vtysh_client_lookup(name); + if (idx_client != -1) + ret = vtysh_client_execute(&vtysh_client[idx_client], line); + else { + vty_out(vty, "Client not found\n"); + ret = CMD_WARNING; + } + + return ret; +} + /* * Retrieve all running config from daemons and parse it with the vtysh config * parser. Returned output is not displayed to the user. -- cgit v1.2.3 From f9ed934c7f1052ca5700c09e2aece71df3f1d2f1 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:31:38 -0400 Subject: vtysh: Make show work-queue execute by name Update show work-queue to use the execute_name() code path when dispatching to a specific daemon. Signed-off-by: Stephen Worley --- vtysh/vtysh.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 2160e4054..56b366f08 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2307,17 +2307,9 @@ DEFUN (vtysh_show_work_queues_daemon, DAEMONS_STR) { int idx_protocol = 2; - unsigned int i; - int ret = CMD_SUCCESS; - - for (i = 0; i < array_size(vtysh_client); i++) { - if (strmatch(vtysh_client[i].name, argv[idx_protocol]->text)) - break; - } - ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n"); - - return ret; + return vtysh_client_execute_name(argv[idx_protocol]->text, + "show work-queues\n"); } DEFUNSH(VTYSH_ZEBRA, vtysh_link_params, vtysh_link_params_cmd, "link-params", -- cgit v1.2.3 From f73126c31acdd496dcc88c69059a5e0103d6a2ff Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:46:29 -0400 Subject: lib,vtysh: Add vtysh commands for log-filter Add vtysh commands to add/del/clear/show filters across all daemons and independently on each one. Add automake and clippy boilerplate for those commands as well. Signed-off-by: Stephen Worley --- lib/command.h | 1 + lib/libfrr.c | 2 ++ lib/log_vty.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/log_vty.h | 24 ++++++++++++++ lib/subdir.am | 4 +++ vtysh/vtysh.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 230 insertions(+) create mode 100644 lib/log_vty.c create mode 100644 lib/log_vty.h diff --git a/lib/command.h b/lib/command.h index e02f1134b..95be7ccb6 100644 --- a/lib/command.h +++ b/lib/command.h @@ -397,6 +397,7 @@ struct cmd_node { #define SR_STR "Segment-Routing specific commands\n" #define WATCHFRR_STR "watchfrr information\n" #define ZEBRA_STR "Zebra information\n" +#define FILTER_LOG_STR "Filter Logs\n" #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" diff --git a/lib/libfrr.c b/lib/libfrr.c index ed784fc73..3294a6129 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -31,6 +31,7 @@ #include "command.h" #include "version.h" #include "memory_vty.h" +#include "log_vty.h" #include "zclient.h" #include "log_int.h" #include "module.h" @@ -677,6 +678,7 @@ struct thread_master *frr_init(void) vty_init(master, di->log_always); memory_init(); + log_filter_cmd_init(); log_ref_init(); lib_error_init(); diff --git a/lib/log_vty.c b/lib/log_vty.c new file mode 100644 index 000000000..a3fa08711 --- /dev/null +++ b/lib/log_vty.c @@ -0,0 +1,99 @@ +/* + * Logging - VTY code + * 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 + +#include "lib/log_vty.h" +#include "command.h" +#include "lib/vty.h" +#include "lib/log.h" +#ifndef VTYSH_EXTRACT_PL +#include "lib/log_vty_clippy.c" +#endif + +/* Log filter */ +DEFPY (log_filter, + log_filter_cmd, + "[no] log-filter WORD$filter", + NO_STR + FILTER_LOG_STR + "String to filter by\n") +{ + int ret = 0; + + if (no) + ret = zlog_filter_del(filter); + else + ret = zlog_filter_add(filter); + + if (ret == 1) { + vty_out(vty, "\tfilter table full\n"); + return CMD_WARNING; + } else if (ret != 0) { + vty_out(vty, "\tfailed to %s log filter\n", + (no ? "remove" : "apply")); + return CMD_WARNING; + } + + vty_out(vty, "\t%s\n", filter); + return CMD_SUCCESS; +} + +/* Clear all log filters */ +DEFPY (log_filter_clear, + log_filter_clear_cmd, + "clear log-filter", + CLEAR_STR + FILTER_LOG_STR) +{ + zlog_filter_clear(); + vty_out(vty, "\tcleared all filters\n"); + return CMD_SUCCESS; +} + +/* Show log filter */ +DEFPY (show_log_filter, + show_log_filter_cmd, + "show log-filter", + SHOW_STR + FILTER_LOG_STR) +{ + char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = ""; + int len = 0; + + len = zlog_filter_dump(log_filters, sizeof(log_filters)); + + if (len == -1) { + vty_out(vty, "\tfailed to get filters\n"); + return CMD_WARNING; + } + + if (len != 0) + vty_out(vty, "%s", log_filters); + + return CMD_SUCCESS; +} + +void log_filter_cmd_init(void) +{ + install_element(VIEW_NODE, &show_log_filter_cmd); + install_element(CONFIG_NODE, &log_filter_cmd); + install_element(CONFIG_NODE, &log_filter_clear_cmd); +} diff --git a/lib/log_vty.h b/lib/log_vty.h new file mode 100644 index 000000000..fa5627e4b --- /dev/null +++ b/lib/log_vty.h @@ -0,0 +1,24 @@ +/* + * Logging - VTY library + * 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 + */ + +#ifndef __LOG_VTY_H__ +#define __LOG_VTY_H__ +extern void log_filter_cmd_init(void); +#endif /* __LOG_VTY_H__ */ diff --git a/lib/subdir.am b/lib/subdir.am index 8b6cbe2ae..3750d3620 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,6 +42,7 @@ lib_libfrr_la_SOURCES = \ lib/libfrr.c \ lib/linklist.c \ lib/log.c \ + lib/log_vty.c \ lib/md5.c \ lib/memory.c \ lib/memory_vty.c \ @@ -137,6 +138,8 @@ lib/northbound_cli_clippy.c: $(CLIPPY_DEPS) lib/northbound_cli.lo: lib/northbound_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 pkginclude_HEADERS += \ lib/agg_table.h \ @@ -179,6 +182,7 @@ pkginclude_HEADERS += \ lib/libospf.h \ lib/linklist.h \ lib/log.h \ + lib/log_vty.h \ lib/md5.h \ lib/memory.h \ lib/memory_vty.h \ diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 56b366f08..804dc38b1 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2641,6 +2641,103 @@ DEFUNSH(VTYSH_ALL, no_vtysh_config_enable_password, return CMD_SUCCESS; } +/* Log filter */ +DEFUN (vtysh_log_filter, + vtysh_log_filter_cmd, + "[no] log-filter WORD ["DAEMONS_LIST"]", + NO_STR + FILTER_LOG_STR + "String to filter by\n" + DAEMONS_STR) +{ + char *filter = NULL; + char *daemon = NULL; + int found = 0; + int idx = 0; + int daemon_idx = 2; + int total_len = 0; + int len = 0; + + char line[ZLOG_FILTER_LENGTH_MAX + 20]; + + found = argv_find(argv, argc, "no", &idx); + if (found == 1) { + len = snprintf(line, sizeof(line), "no log-filter"); + daemon_idx += 1; + } else + len = snprintf(line, sizeof(line), "log-filter"); + + total_len += len; + + idx = 1; + found = argv_find(argv, argc, "WORD", &idx); + if (found != 1) { + vty_out(vty, "No filter string given\n"); + return CMD_WARNING; + } + filter = argv[idx]->arg; + + if (strnlen(filter, ZLOG_FILTER_LENGTH_MAX + 1) + > ZLOG_FILTER_LENGTH_MAX) { + vty_out(vty, "Filter is too long\n"); + return CMD_WARNING; + } + + len = snprintf(line + total_len, sizeof(line) - total_len, " %s\n", + filter); + + if ((len < 0) || (size_t)(total_len + len) > sizeof(line)) { + vty_out(vty, "Error buffering filter to daemons\n"); + return CMD_ERR_INCOMPLETE; + } + + if (argc >= (daemon_idx + 1)) + daemon = argv[daemon_idx]->text; + + if (daemon != NULL) { + vty_out(vty, "Applying log filter change to %s:\n", daemon); + return vtysh_client_execute_name(daemon, line); + } else + return show_per_daemon(line, + "Applying log filter change to %s:\n"); +} + +/* Clear log filters */ +DEFUN (vtysh_log_filter_clear, + vtysh_log_filter_clear_cmd, + "log-filter clear ["DAEMONS_LIST"]", + FILTER_LOG_STR + CLEAR_STR + DAEMONS_STR) +{ + char *daemon = NULL; + int daemon_idx = 2; + + char line[] = "clear log-filter\n"; + + if (argc >= (daemon_idx + 1)) + daemon = argv[daemon_idx]->text; + + if (daemon != NULL) { + vty_out(vty, "Clearing all filters applied to %s:\n", daemon); + return vtysh_client_execute_name(daemon, line); + } else + return show_per_daemon(line, + "Clearing all filters applied to %s:\n"); +} + +/* Show log filter */ +DEFUN (vtysh_show_log_filter, + vtysh_show_log_filter_cmd, + "show log-filter", + SHOW_STR + FILTER_LOG_STR) +{ + char line[] = "do show log-filter\n"; + + return show_per_daemon(line, "Log filters applied to %s:\n"); +} + DEFUN (vtysh_write_terminal, vtysh_write_terminal_cmd, "write terminal ["DAEMONS_LIST"]", @@ -3865,6 +3962,9 @@ void vtysh_init_vty(void) /* Logging */ install_element(VIEW_NODE, &vtysh_show_logging_cmd); + install_element(VIEW_NODE, &vtysh_show_log_filter_cmd); + install_element(CONFIG_NODE, &vtysh_log_filter_cmd); + install_element(CONFIG_NODE, &vtysh_log_filter_clear_cmd); install_element(CONFIG_NODE, &vtysh_log_stdout_cmd); install_element(CONFIG_NODE, &vtysh_log_stdout_level_cmd); install_element(CONFIG_NODE, &no_vtysh_log_stdout_cmd); -- cgit v1.2.3 From fc2a67dd25162322e347cd970b83858a77bb5d81 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:48:53 -0400 Subject: lib: Filter logs as they come in via macros As logging functions are called, if filters are stored, look for the filter substring in the logs. If it is not found, do not output the log to a file or stdout. If the filter is matched, handle the log call per usual. Signed-off-by: Stephen Worley --- lib/log.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/lib/log.c b/lib/log.c index 4b8c55d65..4a616c778 100644 --- a/lib/log.c +++ b/lib/log.c @@ -68,9 +68,11 @@ const char *zlog_priority[] = { 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) { - /* look for a match on the filter in the current filters */ for (int i = 0; i < zlog_filter_count; i++) { if (strncmp(lookup, zlog_filters[i], sizeof(zlog_filters[0])) == 0) @@ -275,17 +277,32 @@ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) return 0; } -/* Utility routine for current time printing. */ -static void time_print(FILE *fp, struct timestamp_control *ctl) +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, @@ -299,42 +316,91 @@ static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl, 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))) + return search_buf(buf); + + 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); + + return search_buf(buf); +} + /* va_list version of zlog. */ void vzlog(int priority, const char *format, va_list args) { pthread_mutex_lock(&loglock); - char proto_str[32]; + char proto_str[32] = ""; int original_errno = errno; - struct timestamp_control tsctl; + struct timestamp_control tsctl = {}; tsctl.already_rendered = 0; struct zlog *zl = zlog_default; char buf[256], *msg; - /* call external hook */ - hook_call(zebra_ext_log, priority, format, args); + 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)) { + pthread_mutex_unlock(&loglock); + 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) { - tsctl.precision = 0; time_print(stderr, &tsctl); fprintf(stderr, "%s: %s\n", "unknown", msg); fflush(stderr); goto out; } - tsctl.precision = zl->timestamp_precision; /* Syslog output */ if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) syslog(priority | zlog_default->facility, "%s", msg); - if (zl->instance) - sprintf(proto_str, "%s[%d]: ", zl->protoname, zl->instance); - else - sprintf(proto_str, "%s: ", zl->protoname); - /* File output. */ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, -- cgit v1.2.3 From c103b19f6d0aea06e5b994b8e462de70ddaee5a8 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 16:07:08 -0400 Subject: doc: Add docs for log-filter and show log-filter Add some user documentation for applying/deleting/showing log filters with the new commands. Signed-off-by: Stephen Worley --- doc/user/basic.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 3d3a75d4b..8395087e8 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -189,6 +189,22 @@ Basic Config Commands is used to start the daemon then this command is turned on by default and cannot be turned off and the [no] form of the command is dissallowed. +.. index:: + single: no log-filter WORD [DAEMON] + single: log-filter WORD [DAEMON] + +.. clicmd:: [no] log-filter WORD [DAEMON] + + This command forces logs to be filtered on a specific string. A log message + will only be printed if it matches on one of the filters in the log-filter + table. Can be daemon independent. + +.. index:: log-filter clear [DAEMON] +.. clicmd:: log-filter clear [DAEMON] + + This command clears all current filters in the log-filter table. Can be + daemon independent. + .. index:: service password-encryption .. clicmd:: service password-encryption @@ -321,6 +337,11 @@ Terminal Mode Commands Shows the current configuration of the logging system. This includes the status of all logging destinations. +.. index:: show log-filter +.. clicmd:: show log-filter + + Shows the current log filters applied to each daemon. + .. index:: show memory .. clicmd:: show memory -- cgit v1.2.3 From 3b96430b72989d398500c9bd49ce3c60be4f8575 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 13 Jun 2019 17:59:16 -0400 Subject: lib: Add vrrpd,sharpd to DAEMONS_* strings Add vrrpd and sharpd to the DAEMONS_* list so they can be dispatched daemons independent commands such as `show work-queues` and `log-filter`. Signed-off-by: Stephen Worley --- lib/command.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/command.h b/lib/command.h index 95be7ccb6..fbc0e7dd6 100644 --- a/lib/command.h +++ b/lib/command.h @@ -414,9 +414,9 @@ struct cmd_node { /* Dameons lists */ #define DAEMONS_STR \ - "For the zebra daemon\nFor the rip daemon\nFor the ripng daemon\nFor the ospf daemon\nFor the ospfv6 daemon\nFor the bgp daemon\nFor the isis daemon\nFor the pbr daemon\nFor the fabricd daemon\nFor the pim daemon\nFor the static daemon\n" + "For the zebra daemon\nFor the rip daemon\nFor the ripng daemon\nFor the ospf daemon\nFor the ospfv6 daemon\nFor the bgp daemon\nFor the isis daemon\nFor the pbr daemon\nFor the fabricd daemon\nFor the pim daemon\nFor the static daemon\nFor the sharpd daemon\nFor the vrrpd daemon\n" #define DAEMONS_LIST \ - "" + "" /* Prototypes. */ extern void install_node(struct cmd_node *, int (*)(struct vty *)); -- cgit v1.2.3 From 4aaea8fb4941582039a2c024c0d04e4a391558fd Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Fri, 14 Jun 2019 14:13:22 -0400 Subject: tests: Make checkRouterRunning() `show logging` Adding in the command `show log-filter` made `show log` ambiguous. Change the checkRouterRunning() test to do full `show logging` so it works again. Signed-off-by: Stephen Worley --- tests/topotests/bgp_multiview_topo1/README.md | 2 +- tests/topotests/lib/topotest.py | 2 +- tests/topotests/ospf6-topo1/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/topotests/bgp_multiview_topo1/README.md b/tests/topotests/bgp_multiview_topo1/README.md index b9982d490..60b328505 100644 --- a/tests/topotests/bgp_multiview_topo1/README.md +++ b/tests/topotests/bgp_multiview_topo1/README.md @@ -96,7 +96,7 @@ Simplified `R1` config: Test is executed by running - vtysh -c "show log" | grep "Logging configuration for" + vtysh -c "show logging" | grep "Logging configuration for" on router `R1`. This should return the logging information for all daemons registered to Zebra and the list of running daemons is compared to the daemons started for this diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 86993665c..867f9f2f0 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -959,7 +959,7 @@ class Router(Node): global fatal_error - daemonsRunning = self.cmd('vtysh -c "show log" | grep "Logging configuration for"') + daemonsRunning = self.cmd('vtysh -c "show logging" | grep "Logging configuration for"') # Look for AddressSanitizer Errors in vtysh output and append to /tmp/AddressSanitzer.txt if found if checkAddressSanitizerError(daemonsRunning, self.name, "vtysh"): return "%s: vtysh killed by AddressSanitizer" % (self.name) diff --git a/tests/topotests/ospf6-topo1/README.md b/tests/topotests/ospf6-topo1/README.md index 28f68e8fa..526c019c6 100644 --- a/tests/topotests/ospf6-topo1/README.md +++ b/tests/topotests/ospf6-topo1/README.md @@ -102,7 +102,7 @@ Simplified `R3` config Test is executed by running - vtysh -c "show log" | grep "Logging configuration for" + vtysh -c "show logging" | grep "Logging configuration for" on each FRR router. This should return the logging information for all daemons registered to Zebra and the list of running daemons is compared to the daemons started for this test (`zebra` and `ospf6d`) -- cgit v1.2.3 From ec1d97751ed505f1a938d509a5c0171f64efc60d Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Tue, 18 Jun 2019 17:57:57 -0400 Subject: lib: Use gotos in filter control functions Use gotos to reduce return paths in the log filter add/del/dump functions. Signed-off-by: Stephen Worley --- lib/log.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/log.c b/lib/log.c index 4a616c778..b46e6cb2c 100644 --- a/lib/log.c +++ b/lib/log.c @@ -92,30 +92,33 @@ int zlog_filter_add(const char *filter) { pthread_mutex_lock(&loglock); + int ret = 0; + if (zlog_filter_count >= ZLOG_FILTERS_MAX) { - pthread_mutex_unlock(&loglock); - return 1; + ret = 1; + goto done; } if (zlog_filter_lookup(filter) != -1) { /* Filter already present */ - pthread_mutex_unlock(&loglock); - return -1; + ret = -1; + goto done; } strlcpy(zlog_filters[zlog_filter_count], filter, sizeof(zlog_filters[0])); - if (zlog_filters[zlog_filter_count] == NULL - || zlog_filters[zlog_filter_count][0] == '\0') { - pthread_mutex_unlock(&loglock); - return -1; + if (zlog_filters[zlog_filter_count][0] == '\0') { + /* Filter was either empty or didn't get copied correctly */ + ret = -1; + goto done; } zlog_filter_count++; +done: pthread_mutex_unlock(&loglock); - return 0; + return ret; } int zlog_filter_del(const char *filter) @@ -124,11 +127,12 @@ int zlog_filter_del(const char *filter) int found_idx = zlog_filter_lookup(filter); int last_idx = zlog_filter_count - 1; + int ret = 0; if (found_idx == -1) { /* Didn't find the filter to delete */ - pthread_mutex_unlock(&loglock); - return -1; + ret = -1; + goto done; } /* Adjust the filter array */ @@ -137,8 +141,9 @@ int zlog_filter_del(const char *filter) zlog_filter_count--; +done: pthread_mutex_unlock(&loglock); - return 0; + return ret; } /* Dump all filters to buffer, delimited by new line */ @@ -154,13 +159,13 @@ int zlog_filter_dump(char *buf, size_t max_size) zlog_filters[i]); len += ret; if ((ret < 0) || ((size_t)len >= max_size)) { - pthread_mutex_unlock(&loglock); - return -1; + len = -1; + goto done; } } +done: pthread_mutex_unlock(&loglock); - return len; } @@ -342,7 +347,7 @@ static int vzlog_filter(struct zlog *zl, struct timestamp_control *tsctl, len += ret; if ((ret < 0) || ((size_t)len >= sizeof(buf))) - return search_buf(buf); + goto search; if (zl && zl->record_priority) snprintf(buf + len, sizeof(buf) - len, "%s: %s: %s", @@ -351,6 +356,7 @@ static int vzlog_filter(struct zlog *zl, struct timestamp_control *tsctl, snprintf(buf + len, sizeof(buf) - len, "%s: %s", proto_str, msg); +search: return search_buf(buf); } -- cgit v1.2.3 From 8ad7c5c2e714e8f987e6920f924534f289758684 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Wed, 19 Jun 2019 11:47:38 -0400 Subject: lib: Remove extraneous spacing/output filter cmds Use %% style for errors in log commands and switch tabs to a single space in output. Also, remove un-needed output for success. Signed-off-by: Stephen Worley --- lib/log.c | 2 +- lib/log_vty.c | 10 ++++------ vtysh/vtysh.c | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/log.c b/lib/log.c index b46e6cb2c..732b238b1 100644 --- a/lib/log.c +++ b/lib/log.c @@ -155,7 +155,7 @@ int zlog_filter_dump(char *buf, size_t max_size) int len = 0; for (int i = 0; i < zlog_filter_count; i++) { - ret = snprintf(buf + len, max_size - len, "\t%s\n", + ret = snprintf(buf + len, max_size - len, " %s\n", zlog_filters[i]); len += ret; if ((ret < 0) || ((size_t)len >= max_size)) { diff --git a/lib/log_vty.c b/lib/log_vty.c index a3fa08711..68d598f56 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -28,7 +28,6 @@ #include "lib/log_vty_clippy.c" #endif -/* Log filter */ DEFPY (log_filter, log_filter_cmd, "[no] log-filter WORD$filter", @@ -44,15 +43,15 @@ DEFPY (log_filter, ret = zlog_filter_add(filter); if (ret == 1) { - vty_out(vty, "\tfilter table full\n"); + vty_out(vty, "%% filter table full\n"); return CMD_WARNING; } else if (ret != 0) { - vty_out(vty, "\tfailed to %s log filter\n", + vty_out(vty, "%% failed to %s log filter\n", (no ? "remove" : "apply")); return CMD_WARNING; } - vty_out(vty, "\t%s\n", filter); + vty_out(vty, " %s\n", filter); return CMD_SUCCESS; } @@ -64,7 +63,6 @@ DEFPY (log_filter_clear, FILTER_LOG_STR) { zlog_filter_clear(); - vty_out(vty, "\tcleared all filters\n"); return CMD_SUCCESS; } @@ -81,7 +79,7 @@ DEFPY (show_log_filter, len = zlog_filter_dump(log_filters, sizeof(log_filters)); if (len == -1) { - vty_out(vty, "\tfailed to get filters\n"); + vty_out(vty, "%% failed to get filters\n"); return CMD_WARNING; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 804dc38b1..053848bfc 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2672,14 +2672,14 @@ DEFUN (vtysh_log_filter, idx = 1; found = argv_find(argv, argc, "WORD", &idx); if (found != 1) { - vty_out(vty, "No filter string given\n"); + vty_out(vty, "%% No filter string given\n"); return CMD_WARNING; } filter = argv[idx]->arg; if (strnlen(filter, ZLOG_FILTER_LENGTH_MAX + 1) > ZLOG_FILTER_LENGTH_MAX) { - vty_out(vty, "Filter is too long\n"); + vty_out(vty, "%% Filter is too long\n"); return CMD_WARNING; } @@ -2687,7 +2687,7 @@ DEFUN (vtysh_log_filter, filter); if ((len < 0) || (size_t)(total_len + len) > sizeof(line)) { - vty_out(vty, "Error buffering filter to daemons\n"); + vty_out(vty, "%% Error buffering filter to daemons\n"); return CMD_ERR_INCOMPLETE; } -- cgit v1.2.3 From 018fa5f85dad6324c08b3f1fddf7553bc63819dd Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Tue, 25 Jun 2019 10:49:43 -0400 Subject: doc: Add a performance note for log filtering Add a not under the log-filter command to indicate that, while filtering reduces load on the system by large margins, there may still be a small performance hit from filtering those. Signed-off-by: Stephen Worley --- doc/user/basic.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 8395087e8..5509fd5f0 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -199,6 +199,13 @@ Basic Config Commands will only be printed if it matches on one of the filters in the log-filter table. Can be daemon independent. + .. note:: + + Log filters help when you need to turn on debugs that cause significant + load on the system (enabling certain debugs can bring FRR to a halt). + Log filters prevent this but you should still expect a small performance + hit due to filtering each of all those logs. + .. index:: log-filter clear [DAEMON] .. clicmd:: log-filter clear [DAEMON] -- cgit v1.2.3