diff --git a/lib/command.c b/lib/command.c index 97e60a5929..5b2783d326 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1174,6 +1174,55 @@ DECLARE_KOOH(cmd_execute_done, (struct vty * vty, const char *cmd_exec), DEFINE_KOOH(cmd_execute_done, (struct vty * vty, const char *cmd_exec), (vty, cmd_exec)); +/* + * cmd_execute hook subscriber to handle `|` actions. + */ +static int handle_pipe_action(struct vty *vty, const char *cmd_in, + char **cmd_out) +{ + /* look for `|` */ + char *orig, *working, *token; + char *pipe = strstr(cmd_in, "| "); + + if (!pipe) + return 0; + + /* duplicate string for processing purposes, not including pipe */ + orig = working = XSTRDUP(MTYPE_TMP, pipe + 2); + + /* retrieve action */ + token = strsep(&working, " "); + + /* match result to known actions */ + if (strmatch(token, "include")) { + /* the remaining text should be a regexp */ + char *regexp = working; + bool succ = vty_set_include(vty, regexp); + if (!succ) { + vty_out(vty, "%% Bad regexp '%s'", regexp); + goto fail; + } + cmd_out = XSTRDUP(MTYPE_TMP, cmd_in); + *(strstr(cmd_in, "|")) = '\0'; + } else { + vty_out(vty, "%% Unknown action '%s'", token); + goto fail; + } + +fail: + XFREE(MTYPE_TMP, orig); + return 0; +} + +static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec) +{ + if (vty->filter) { + vty_set_include(vty, NULL); + vty_out(vty, "\n"); + } + return 0; +} + int cmd_execute(struct vty *vty, const char *cmd, const struct cmd_element **matched, int vtysh) { @@ -2720,6 +2769,10 @@ void cmd_init(int terminal) uname(&names); qobj_init(); + /* register command preprocessors */ + hook_register(cmd_execute, handle_pipe_action); + hook_register(cmd_execute_done, handle_pipe_action_done); + varhandlers = list_new(); /* Allocate initial top vector of commands. */ diff --git a/lib/version.h.in b/lib/version.h.in index d9eabb9f81..52c10f7d68 100644 --- a/lib/version.h.in +++ b/lib/version.h.in @@ -47,10 +47,10 @@ #define FRR_CONFIG_ARGS "@CONFIG_ARGS@" #define FRR_DEFAULT_MOTD \ - "\r\n" \ - "Hello, this is " FRR_FULL_NAME " (version " FRR_VERSION ").\r\n" \ - FRR_COPYRIGHT "\r\n" \ - GIT_INFO "\r\n" + "\n" \ + "Hello, this is " FRR_FULL_NAME " (version " FRR_VERSION ").\n" \ + FRR_COPYRIGHT "\n" \ + GIT_INFO "\n" pid_t pid_output (const char *); diff --git a/lib/vty.c b/lib/vty.c index e8463da3d7..5a30bfa8ad 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -21,10 +21,13 @@ #include +#include +#include +#include + #include "linklist.h" #include "thread.h" #include "buffer.h" -#include #include "command.h" #include "sockunion.h" #include "memory.h" @@ -35,6 +38,7 @@ #include "privs.h" #include "network.h" #include "libfrr.h" +#include "frrstr.h" #include #include @@ -109,6 +113,31 @@ void vty_endframe(struct vty *vty, const char *endtext) vty->frame_pos = 0; } +bool vty_set_include(struct vty *vty, const char *regexp) +{ + int errcode; + bool ret = true; + char errbuf[256]; + + if (!regexp) { + memset(&vty->include, 0x00, sizeof(vty->include)); + vty->filter = false; + return true; + } + + errcode = regcomp(&vty->include, regexp, + REG_EXTENDED | REG_NEWLINE | REG_NOSUB); + if (errcode) { + ret = false; + regerror(ret, &vty->include, errbuf, sizeof(errbuf)); + vty_out(vty, "%% Regex compilation error: %s", errbuf); + } else { + vty->filter = true; + } + + return ret; +} + /* VTY standard output function. */ int vty_out(struct vty *vty, const char *format, ...) { @@ -117,6 +146,7 @@ int vty_out(struct vty *vty, const char *format, ...) int size = 1024; char buf[1024]; char *p = NULL; + char *filtered; if (vty->frame_pos) { vty->frame_pos = 0; @@ -158,11 +188,28 @@ int vty_out(struct vty *vty, const char *format, ...) if (!p) p = buf; + /* filter buffer */ + if (vty->filter) { + vector lines = frrstr_split_vec(buf, "\n"); + frrstr_filter_vec(lines, &vty->include); + if (buf[strlen(buf) - 1] == '\n' && vector_active(lines) > 0) + vector_set(lines, XSTRDUP(MTYPE_TMP, "")); + filtered = frrstr_join_vec(lines, "\n"); + frrstr_strvec_free(lines); + } else { + filtered = p; + } + /* Pointer p must point out buffer. */ if (vty->type != VTY_TERM) - buffer_put(vty->obuf, (uint8_t *)p, len); + buffer_put(vty->obuf, (uint8_t *)filtered, + strlen(filtered)); else - buffer_put_crlf(vty->obuf, (uint8_t *)p, len); + buffer_put_crlf(vty->obuf, (uint8_t *)filtered, + strlen(filtered)); + + if (vty->filter) + XFREE(MTYPE_TMP, filtered); /* If p is not different with buf, it is allocated buffer. */ if (p != buf) @@ -391,7 +438,6 @@ static void vty_auth(struct vty *vty, char *buf) static int vty_command(struct vty *vty, char *buf) { int ret; - vector vline; const char *protocolname; char *cp = NULL; @@ -427,11 +473,6 @@ static int vty_command(struct vty *vty, char *buf) /* now log the command */ zlog_err("%s%s", prompt_str, buf); } - /* Split readline string up into the vector */ - vline = cmd_make_strvec(buf); - - if (vline == NULL) - return CMD_SUCCESS; #ifdef CONSUMED_TIME_CHECK { @@ -442,7 +483,7 @@ static int vty_command(struct vty *vty, char *buf) GETRUSAGE(&before); #endif /* CONSUMED_TIME_CHECK */ - ret = cmd_execute_command(vline, vty, NULL, 0); + ret = cmd_execute(vty, buf, NULL, 0); /* Get the name of the protocol if any */ protocolname = frr_protoname; @@ -475,7 +516,6 @@ static int vty_command(struct vty *vty, char *buf) vty_out(vty, "%% Command incomplete.\n"); break; } - cmd_free_strvec(vline); return ret; } diff --git a/lib/vty.h b/lib/vty.h index 6e5ff13ffd..32fd471f18 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -21,6 +21,9 @@ #ifndef _ZEBRA_VTY_H #define _ZEBRA_VTY_H +#include +#include + #include "thread.h" #include "log.h" #include "sockunion.h" @@ -47,6 +50,10 @@ struct vty { /* Failure count */ int fail; + /* Output filer regex */ + bool filter; + regex_t include; + /* Output buffer. */ struct buffer *obuf; @@ -223,6 +230,7 @@ extern struct vty *vty_stdio(void (*atclose)(int isexit)); extern int vty_out(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); extern void vty_frame(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); extern void vty_endframe(struct vty *, const char *); +bool vty_set_include(struct vty *vty, const char *regexp); extern void vty_read_config(const char *, char *); extern void vty_time_print(struct vty *, int);