lib: add cli preprocessor for | actions

This patch adds a CLI preprocessor function that activates when `|` is
found in the command. This is the start of adding support for some text
processing utilities intended for inline use. The first one implemented
here is `| include`, which provides grep-like filtering of command
output.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2018-05-12 20:14:22 -04:00
parent 01e24c4a69
commit fe6b47b9e9
4 changed files with 116 additions and 15 deletions

View file

@ -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. */

View file

@ -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 *);

View file

@ -21,10 +21,13 @@
#include <zebra.h>
#include <lib/version.h>
#include <sys/types.h>
#include <regex.h>
#include "linklist.h"
#include "thread.h"
#include "buffer.h"
#include <lib/version.h>
#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 <arpa/telnet.h>
#include <termios.h>
@ -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;
}

View file

@ -21,6 +21,9 @@
#ifndef _ZEBRA_VTY_H
#define _ZEBRA_VTY_H
#include <sys/types.h>
#include <regex.h>
#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);