#include "lib/hook.h"
#include "lib/zlog.h"
+#include "lib/zlog_targets.h"
#ifdef __cplusplus
extern "C" {
extern void zlog_thread_info(int log_level);
+#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);
+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. */
--- /dev/null
+/*
+ * 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);
+}
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;
void zlog_rotate(void)
{
zlog_file_rotate(&zt_file);
+ zlog_file_rotate(&zt_filterfile.parent);
hook_call(zlog_rotate);
}
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_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;
}
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;
}
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;
}
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",
+ 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, "%% filter table full\n");
+ return CMD_WARNING;
+ } else if (ret != 0) {
+ vty_out(vty, "%% failed to %s log filter\n",
+ (no ? "remove" : "apply"));
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, " %s\n", filter);
+ return CMD_SUCCESS;
+}
+
+/* Clear all log filters */
+DEFPY (log_filter_clear,
+ log_filter_clear_cmd,
+ "clear log-filter",
+ CLEAR_STR
+ FILTER_LOG_STR)
+{
+ zlog_filter_clear();
+ return CMD_SUCCESS;
+}
+
+/* Show log filter */
+DEFPY (show_log_filter,
+ show_log_filter_cmd,
+ "show log-filter",
+ 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, "%% failed to get filters\n");
+ return CMD_WARNING;
+ }
+
+ if (len != 0)
+ vty_out(vty, "%s", log_filters);
+
return CMD_SUCCESS;
}
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");
zlog_progname = progname;
zlog_protoname = protoname;
+ zlog_filterfile_init(&zt_filterfile);
+
zlog_file_set_fd(&zt_stdout, STDOUT_FILENO);
return 0;
}
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);
}
lib/libfrr.c \
lib/linklist.c \
lib/log.c \
+ lib/log_filter.c \
lib/log_vty.c \
lib/md5.c \
lib/memory.c \
[LOG_DEBUG] = "debugging: ",
};
-static void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
+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;
zlt->ts_subsec = zcf->ts_subsec;
zlt->zt.prio_min = zcf->prio_min;
- zlt->zt.logfn = zlog_fd;
+ zlt->zt.logfn = zcf->zlog_wrap ? zcf->zlog_wrap : zlog_fd;
zlt->zt.logfn_sigsafe = zlog_fd_sigsafe;
} while (0);
/* 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 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);