summaryrefslogtreecommitdiff
path: root/lib/command.c
diff options
context:
space:
mode:
authorRuss White <russ@riw.us>2018-06-08 07:39:36 -0400
committerGitHub <noreply@github.com>2018-06-08 07:39:36 -0400
commit9eafc8abd74a4d8144c764e0361dbb2a51da91c9 (patch)
treed9f741613b32425b0204c32d08492a4267b5d4a9 /lib/command.c
parent52cd2dfebc058882091ed3bdc17e4cfd9c3689e1 (diff)
parent4c54343ffbe9c82fb940e450d2e45c6a2bd0a868 (diff)
Merge pull request #2298 from qlyoung/pipe-actions-vtysh
*: add support for `|` actions
Diffstat (limited to 'lib/command.c')
-rw-r--r--lib/command.c246
1 files changed, 167 insertions, 79 deletions
diff --git a/lib/command.c b/lib/command.c
index 0fa6bde334..7df81438f2 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -25,17 +25,17 @@
*/
#include <zebra.h>
+#include <lib/version.h>
-
+#include "command.h"
+#include "frrstr.h"
#include "memory.h"
#include "log.h"
#include "log_int.h"
-#include <lib/version.h>
#include "thread.h"
#include "vector.h"
#include "linklist.h"
#include "vty.h"
-#include "command.h"
#include "workqueue.h"
#include "vrf.h"
#include "command_match.h"
@@ -44,9 +44,9 @@
#include "defaults.h"
#include "libfrr.h"
#include "jhash.h"
+#include "hook.h"
DEFINE_MTYPE(LIB, HOST, "Host config")
-DEFINE_MTYPE(LIB, STRVEC, "String vector")
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
#define item(x) \
@@ -259,30 +259,49 @@ void print_version(const char *progname)
printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
}
-
-/* Utility function to concatenate argv argument into a single string
- with inserting ' ' character between each argument. */
char *argv_concat(struct cmd_token **argv, int argc, int shift)
{
- int i;
- size_t len;
- char *str;
- char *p;
-
- len = 0;
- for (i = shift; i < argc; i++)
- len += strlen(argv[i]->arg) + 1;
- if (!len)
+ int cnt = argc - shift;
+ const char *argstr[cnt];
+
+ for (int i = 0; i < cnt; i++)
+ argstr[i] = argv[i + shift]->arg;
+
+ return frrstr_join(argstr, cnt, " ");
+}
+
+vector cmd_make_strvec(const char *string)
+{
+ if (!string)
return NULL;
- p = str = XMALLOC(MTYPE_TMP, len);
- for (i = shift; i < argc; i++) {
- size_t arglen;
- memcpy(p, argv[i]->arg, (arglen = strlen(argv[i]->arg)));
- p += arglen;
- *p++ = ' ';
+
+ const char *copy = string;
+
+ /* skip leading whitespace */
+ while (isspace((int)*copy) && *copy != '\0')
+ copy++;
+
+ /* if the entire string was whitespace or a comment, return */
+ if (*copy == '\0' || *copy == '!' || *copy == '#')
+ return NULL;
+
+ vector result = frrstr_split_vec(copy, "\n\r\t ");
+
+ for (unsigned int i = 0; i < vector_active(result); i++) {
+ if (strlen(vector_slot(result, i)) == 0) {
+ XFREE(MTYPE_TMP, vector_slot(result, i));
+ vector_unset(result, i);
+ }
}
- *(p - 1) = '\0';
- return str;
+
+ vector_compact(result);
+
+ return result;
+}
+
+void cmd_free_strvec(vector v)
+{
+ frrstr_strvec_free(v);
}
/**
@@ -332,61 +351,6 @@ void install_node(struct cmd_node *node, int (*func)(struct vty *))
"Command Hash");
}
-/**
- * Tokenizes a string, storing tokens in a vector.
- * Whitespace is ignored.
- *
- * Delimiter string = " \n\r\t".
- *
- * @param string to tokenize
- * @return tokenized string
- */
-vector cmd_make_strvec(const char *string)
-{
- if (!string)
- return NULL;
-
- char *copy, *copystart;
- copystart = copy = XSTRDUP(MTYPE_TMP, string);
-
- // skip leading whitespace
- while (isspace((int)*copy) && *copy != '\0')
- copy++;
-
- // if the entire string was whitespace or a comment, return
- if (*copy == '\0' || *copy == '!' || *copy == '#') {
- XFREE(MTYPE_TMP, copystart);
- return NULL;
- }
-
- vector strvec = vector_init(VECTOR_MIN_SIZE);
- const char *delim = " \n\r\t", *tok = NULL;
- while (copy) {
- tok = strsep(&copy, delim);
- if (*tok != '\0')
- vector_set(strvec, XSTRDUP(MTYPE_STRVEC, tok));
- }
-
- XFREE(MTYPE_TMP, copystart);
- return strvec;
-}
-
-/* Free allocated string vector. */
-void cmd_free_strvec(vector v)
-{
- unsigned int i;
- char *cp;
-
- if (!v)
- return;
-
- for (i = 0; i < vector_active(v); i++)
- if ((cp = vector_slot(v, i)) != NULL)
- XFREE(MTYPE_STRVEC, cp);
-
- vector_free(v);
-}
-
/* Return prompt character of specified node. */
const char *cmd_prompt(enum node_type node)
{
@@ -1180,6 +1144,123 @@ int cmd_execute_command_strict(vector vline, struct vty *vty,
return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
}
+/*
+ * Hook for preprocessing command string before executing.
+ *
+ * All subscribers are called with the raw command string that is to be
+ * executed. If any changes are to be made, a new string should be allocated
+ * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
+ * is then responsible for freeing this string.
+ *
+ * All processing functions must be mutually exclusive in their action, i.e. if
+ * one subscriber decides to modify the command, all others must not modify it
+ * when called. Feeding the output of one processing command into a subsequent
+ * one is not supported.
+ *
+ * This hook is intentionally internal to the command processing system.
+ *
+ * cmd_in
+ * The raw command string.
+ *
+ * cmd_out
+ * The result of any processing.
+ */
+DECLARE_HOOK(cmd_execute,
+ (struct vty *vty, const char *cmd_in, char **cmd_out),
+ (vty, cmd_in, cmd_out));
+DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
+ (vty, cmd_in, cmd_out));
+
+/* Hook executed after a CLI command. */
+DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
+ (vty, 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;
+
+ if (!regexp) {
+ vty_out(vty, "%% Need a regexp to filter with\n");
+ goto fail;
+ }
+
+ bool succ = vty_set_include(vty, regexp);
+
+ if (!succ) {
+ vty_out(vty, "%% Bad regexp '%s'\n", regexp);
+ goto fail;
+ }
+ *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
+ *(strstr(*cmd_out, "|")) = '\0';
+ } else {
+ vty_out(vty, "%% Unknown action '%s'\n", 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);
+
+ return 0;
+}
+
+int cmd_execute(struct vty *vty, const char *cmd,
+ const struct cmd_element **matched, int vtysh)
+{
+ int ret;
+ char *cmd_out = NULL;
+ const char *cmd_exec;
+ vector vline;
+
+ hook_call(cmd_execute, vty, cmd, &cmd_out);
+ cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
+
+ vline = cmd_make_strvec(cmd_exec);
+
+ if (vline) {
+ ret = cmd_execute_command(vline, vty, matched, vtysh);
+ cmd_free_strvec(vline);
+ } else {
+ ret = CMD_SUCCESS;
+ }
+
+ hook_call(cmd_execute_done, vty, cmd_exec);
+
+ if (cmd_out)
+ XFREE(MTYPE_TMP, cmd_out);
+
+ return ret;
+}
+
+
/**
* Parse one line of config, walking up the parse tree attempting to find a
* match
@@ -2697,6 +2778,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. */
@@ -2814,6 +2899,9 @@ void cmd_terminate()
{
struct cmd_node *cmd_node;
+ hook_unregister(cmd_execute, handle_pipe_action);
+ hook_unregister(cmd_execute_done, handle_pipe_action_done);
+
if (cmdvec) {
for (unsigned int i = 0; i < vector_active(cmdvec); i++)
if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {