diff options
Diffstat (limited to 'lib/command.c')
| -rw-r--r-- | lib/command.c | 252 |
1 files changed, 186 insertions, 66 deletions
diff --git a/lib/command.c b/lib/command.c index 6a4d504b2f..422544b70b 100644 --- a/lib/command.c +++ b/lib/command.c @@ -51,8 +51,8 @@ #include "frrscript.h" -DEFINE_MTYPE_STATIC(LIB, HOST, "Host config") -DEFINE_MTYPE(LIB, COMPLETION, "Completion item") +DEFINE_MTYPE_STATIC(LIB, HOST, "Host config"); +DEFINE_MTYPE(LIB, COMPLETION, "Completion item"); #define item(x) \ { \ @@ -160,6 +160,9 @@ static bool vty_check_node_for_xpath_decrement(enum node_type target_node, || node == BGP_FLOWSPECV6_NODE)) return false; + if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE) + return false; + return true; } @@ -255,6 +258,9 @@ static bool cmd_hash_cmp(const void *a, const void *b) /* Install top node of command vector. */ void install_node(struct cmd_node *node) { +#define CMD_HASH_STR_SIZE 256 + char hash_name[CMD_HASH_STR_SIZE]; + vector_set_index(cmdvec, node->node, node); node->cmdgraph = graph_new(); node->cmd_vector = vector_init(VECTOR_MIN_SIZE); @@ -263,8 +269,10 @@ void install_node(struct cmd_node *node) cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); graph_new_node(node->cmdgraph, token, (void (*)(void *)) & cmd_token_del); - node->cmd_hash = hash_create_size(16, cmd_hash_key, cmd_hash_cmp, - "Command Hash"); + + snprintf(hash_name, sizeof(hash_name), "Command Hash: %s", node->name); + node->cmd_hash = + hash_create_size(16, cmd_hash_key, cmd_hash_cmp, hash_name); } /* Return prompt character of specified node. */ @@ -434,6 +442,36 @@ static int config_write_host(struct vty *vty) } log_config_write(vty); + /* print disable always, but enable only if default is flipped + * => prep for future removal of compile-time knob + */ + if (!cputime_enabled) + vty_out(vty, "no service cputime-stats\n"); +#ifdef EXCLUDE_CPU_TIME + else + vty_out(vty, "service cputime-stats\n"); +#endif + + if (!cputime_threshold) + vty_out(vty, "no service cputime-warning\n"); +#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000 + else /* again, always print non-default */ +#else + else if (cputime_threshold != 5000000) +#endif + vty_out(vty, "service cputime-warning %lu\n", + cputime_threshold); + + if (!walltime_threshold) + vty_out(vty, "no service walltime-warning\n"); +#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000 + else /* again, always print non-default */ +#else + else if (walltime_threshold != 5000000) +#endif + vty_out(vty, "service walltime-warning %lu\n", + walltime_threshold); + if (host.advanced) vty_out(vty, "service advanced-vty\n"); @@ -889,6 +927,15 @@ enum node_type node_parent(enum node_type node) case PCEP_PCC_NODE: ret = PCEP_NODE; break; + case SRV6_NODE: + ret = SEGMENT_ROUTING_NODE; + break; + case SRV6_LOCS_NODE: + ret = SRV6_NODE; + break; + case SRV6_LOC_NODE: + ret = SRV6_LOCS_NODE; + break; default: ret = CONFIG_NODE; break; @@ -900,13 +947,31 @@ enum node_type node_parent(enum node_type node) /* Execute command by argument vline vector. */ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, struct vty *vty, - const struct cmd_element **cmd) + const struct cmd_element **cmd, + unsigned int up_level) { struct list *argv_list; enum matcher_rv status; const struct cmd_element *matched_element = NULL; + unsigned int i; + int xpath_index = vty->xpath_index; + int node = vty->node; - struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node); + /* only happens for legacy split config file load; need to check for + * a match before calling node_exit handlers below + */ + for (i = 0; i < up_level; i++) { + if (node <= CONFIG_NODE) + return CMD_NO_LEVEL_UP; + + node = node_parent(node); + + if (xpath_index > 0 + && vty_check_node_for_xpath_decrement(node, vty->node)) + xpath_index--; + } + + struct graph *cmdgraph = cmd_node_graph(cmdvec, node); status = command_match(cmdgraph, vline, &argv_list, &matched_element); if (cmd) @@ -926,12 +991,16 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, } } + for (i = 0; i < up_level; i++) + cmd_exit(vty); + // build argv array from argv list struct cmd_token **argv = XMALLOC( MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *)); struct listnode *ln; struct cmd_token *token; - unsigned int i = 0; + + i = 0; for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token)) argv[i++] = token; @@ -958,7 +1027,7 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, * non-YANG command. */ if (matched_element->attr != CMD_ATTR_YANG) - nb_cli_pending_commit_check(vty); + (void)nb_cli_pending_commit_check(vty); } ret = matched_element->func(matched_element, vty, argc, argv); @@ -1012,7 +1081,7 @@ int cmd_execute_command(vector vline, struct vty *vty, vector_lookup(vline, index)); ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED, - vty, cmd); + vty, cmd, 0); vector_free(shifted_vline); vty->node = onode; @@ -1021,12 +1090,13 @@ int cmd_execute_command(vector vline, struct vty *vty, } saved_ret = ret = - cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd); + cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd, 0); if (vtysh) return saved_ret; if (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) { /* This assumes all nodes above CONFIG_NODE are childs of * CONFIG_NODE */ @@ -1038,8 +1108,9 @@ int cmd_execute_command(vector vline, struct vty *vty, onode)) vty->xpath_index--; ret = cmd_execute_command_real(vline, FILTER_RELAXED, - vty, cmd); + vty, cmd, 0); if (ret == CMD_SUCCESS || ret == CMD_WARNING + || ret == CMD_ERR_AMBIGUOUS || ret == CMD_ERR_INCOMPLETE || ret == CMD_NOT_MY_INSTANCE || ret == CMD_WARNING_CONFIG_FAILED) return ret; @@ -1069,7 +1140,7 @@ int cmd_execute_command(vector vline, struct vty *vty, int cmd_execute_command_strict(vector vline, struct vty *vty, const struct cmd_element **cmd) { - return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); + return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, 0); } /* @@ -1220,6 +1291,7 @@ int command_config_read_one_line(struct vty *vty, { vector vline; int ret; + unsigned up_level = 0; vline = cmd_make_strvec(vty->buf); @@ -1230,36 +1302,21 @@ int command_config_read_one_line(struct vty *vty, /* Execute configuration command : this is strict match */ ret = cmd_execute_command_strict(vline, vty, cmd); - // Climb the tree and try the command again at each node - if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) - && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) - && ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED - && vty->node != CONFIG_NODE) { - int saved_node = vty->node; - int saved_xpath_index = vty->xpath_index; - - while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) - && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) - && ret != CMD_SUCCESS && ret != CMD_WARNING - && vty->node > CONFIG_NODE) { - vty->node = node_parent(vty->node); - if (vty->xpath_index > 0 - && vty_check_node_for_xpath_decrement(vty->node, - saved_node)) - vty->xpath_index--; - ret = cmd_execute_command_strict(vline, vty, cmd); - } - - // If climbing the tree did not work then ignore the command and - // stay at the same node - if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) - && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) - && ret != CMD_SUCCESS && ret != CMD_WARNING) { - vty->node = saved_node; - vty->xpath_index = saved_xpath_index; - } - } + /* The logic for trying parent nodes is in cmd_execute_command_real() + * since calling ->node_exit() correctly is a bit involved. This is + * also the only reason CMD_NO_LEVEL_UP exists. + */ + while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) + && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) + && ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE + && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED + && ret != CMD_NO_LEVEL_UP) + ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, + ++up_level); + + if (ret == CMD_NO_LEVEL_UP) + ret = CMD_ERR_NO_MATCH; if (ret != CMD_SUCCESS && ret != CMD_WARNING && @@ -1477,6 +1534,56 @@ static void permute(struct graph_node *start, struct vty *vty) list_delete_node(position, listtail(position)); } +static void print_cmd(struct vty *vty, const char *cmd) +{ + int i, j, len = strlen(cmd); + char buf[len + 1]; + bool skip = false; + + j = 0; + for (i = 0; i < len; i++) { + /* skip varname */ + if (cmd[i] == '$') + skip = true; + else if (strchr(" ()<>[]{}|", cmd[i])) + skip = false; + + if (skip) + continue; + + if (isspace(cmd[i])) { + /* skip leading whitespace */ + if (i == 0) + continue; + /* skip trailing whitespace */ + if (i == len - 1) + continue; + /* skip all whitespace after opening brackets or pipe */ + if (strchr("(<[{|", cmd[i - 1])) { + while (isspace(cmd[i + 1])) + i++; + continue; + } + /* skip repeated whitespace */ + if (isspace(cmd[i + 1])) + continue; + /* skip whitespace before closing brackets or pipe */ + if (strchr(")>]}|", cmd[i + 1])) + continue; + /* convert tabs to spaces */ + if (cmd[i] == '\t') { + buf[j++] = ' '; + continue; + } + } + + buf[j++] = cmd[i]; + } + buf[j] = 0; + + vty_out(vty, "%s\n", buf); +} + int cmd_list_cmds(struct vty *vty, int do_permute) { struct cmd_node *node = vector_slot(cmdvec, vty->node); @@ -1490,8 +1597,10 @@ int cmd_list_cmds(struct vty *vty, int do_permute) i++) if ((element = vector_slot(node->cmd_vector, i)) && element->attr != CMD_ATTR_DEPRECATED - && element->attr != CMD_ATTR_HIDDEN) - vty_out(vty, " %s\n", element->string); + && element->attr != CMD_ATTR_HIDDEN) { + vty_out(vty, " "); + print_cmd(vty, element->string); + } } return CMD_SUCCESS; } @@ -2224,20 +2333,17 @@ DEFUN (no_banner_motd, return CMD_SUCCESS; } -DEFUN(find, - find_cmd, - "find REGEX", - "Find CLI command matching a regular expression\n" - "Search pattern (POSIX regex)\n") +int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc) { - char *pattern = argv[1]->arg; const struct cmd_node *node; const struct cmd_element *cli; vector clis; regex_t exp = {}; + char *pattern = argv_concat(argv, argc, 1); int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); + XFREE(MTYPE_TMP, pattern); if (cr != 0) { switch (cr) { @@ -2294,9 +2400,10 @@ DEFUN(find, for (unsigned int j = 0; j < vector_active(clis); j++) { cli = vector_slot(clis, j); - if (regexec(&exp, cli->string, 0, NULL, 0) == 0) - vty_out(vty, " (%s) %s\n", - node->name, cli->string); + if (regexec(&exp, cli->string, 0, NULL, 0) == 0) { + vty_out(vty, " (%s) ", node->name); + print_cmd(vty, cli->string); + } } } @@ -2305,27 +2412,40 @@ done: return CMD_SUCCESS; } +DEFUN(find, + find_cmd, + "find REGEX...", + "Find CLI command matching a regular expression\n" + "Search pattern (POSIX regex)\n") +{ + return cmd_find_cmds(vty, argv, argc); +} + #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING) -DEFUN(script, - script_cmd, - "script SCRIPT", - "Test command - execute a script\n" - "Script name (same as filename in /etc/frr/scripts/\n") +DEFUN(script, script_cmd, "script SCRIPT FUNCTION", + "Test command - execute a function in a script\n" + "Script name (same as filename in /etc/frr/scripts/)\n" + "Function name (in the script)\n") { struct prefix p; (void)str2prefix("1.2.3.4/24", &p); + struct frrscript *fs = frrscript_new(argv[1]->arg); - struct frrscript *fs = frrscript_load(argv[1]->arg, NULL); - - if (fs == NULL) { - vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n", - argv[1]->arg); - } else { - int ret = frrscript_call(fs, NULL); - vty_out(vty, "Script result: %d\n", ret); + if (frrscript_load(fs, argv[2]->arg, NULL)) { + vty_out(vty, + "/etc/frr/scripts/%s.lua or function '%s' not found\n", + argv[1]->arg, argv[2]->arg); } + int ret = frrscript_call(fs, argv[2]->arg, ("p", &p)); + char buf[40]; + prefix2str(&p, buf, sizeof(buf)); + vty_out(vty, "p: %s\n", buf); + vty_out(vty, "Script result: %d\n", ret); + + frrscript_delete(fs); + return CMD_SUCCESS; } #endif |
