summaryrefslogtreecommitdiff
path: root/lib/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/command.c')
-rw-r--r--lib/command.c835
1 files changed, 366 insertions, 469 deletions
diff --git a/lib/command.c b/lib/command.c
index cc52224523..09ffa6ce56 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -19,10 +19,9 @@
* 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 GNU Zebra; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 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>
@@ -40,15 +39,82 @@
#include "workqueue.h"
#include "vrf.h"
#include "command_match.h"
+#include "command_graph.h"
#include "qobj.h"
#include "defaults.h"
DEFINE_MTYPE(LIB, HOST, "Host config")
DEFINE_MTYPE(LIB, STRVEC, "String vector")
-DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
-DEFINE_MTYPE_STATIC(LIB, CMD_DESC, "Command Token Text")
-DEFINE_MTYPE_STATIC(LIB, CMD_TEXT, "Command Token Help")
-DEFINE_MTYPE(LIB, CMD_ARG, "Command Argument")
+DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
+
+const char *node_names[] = {
+ "auth", // AUTH_NODE,
+ "view", // VIEW_NODE,
+ "auth enable", // AUTH_ENABLE_NODE,
+ "enable", // ENABLE_NODE,
+ "config", // CONFIG_NODE,
+ "service", // SERVICE_NODE,
+ "debug", // DEBUG_NODE,
+ "vrf debug", // VRF_DEBUG_NODE,
+ "vnc debug", // DEBUG_VNC_NODE,
+ "aaa", // AAA_NODE,
+ "keychain", // KEYCHAIN_NODE,
+ "keychain key", // KEYCHAIN_KEY_NODE,
+ "logical-router", // NS_NODE,
+ "vrf", // VRF_NODE,
+ "interface", // INTERFACE_NODE,
+ "zebra", // ZEBRA_NODE,
+ "table", // TABLE_NODE,
+ "rip", // RIP_NODE,
+ "ripng", // RIPNG_NODE,
+ "babel", // BABEL_NODE,
+ "eigrp", // EIGRP_NODE,
+ "bgp", // BGP_NODE,
+ "bgp vpnv4", // BGP_VPNV4_NODE,
+ "bgp vpnv6", // BGP_VPNV6_NODE,
+ "bgp ipv4 unicast", // BGP_IPV4_NODE,
+ "bgp ipv4 multicast", // BGP_IPV4M_NODE,
+ "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE,
+ "bgp ipv6", // BGP_IPV6_NODE,
+ "bgp ipv6 multicast", // BGP_IPV6M_NODE,
+ "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE,
+ "bgp vrf policy", // BGP_VRF_POLICY_NODE,
+ "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE,
+ "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE,
+ "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE,
+ "rfp defaults", // RFP_DEFAULTS_NODE,
+ "bgp evpn", // BGP_EVPN_NODE,
+ "ospf", // OSPF_NODE,
+ "ospf6", // OSPF6_NODE,
+ "ldp", // LDP_NODE,
+ "ldp ipv4", // LDP_IPV4_NODE,
+ "ldp ipv6", // LDP_IPV6_NODE,
+ "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE,
+ "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE,
+ "ldp l2vpn", // LDP_L2VPN_NODE,
+ "ldp", // LDP_PSEUDOWIRE_NODE,
+ "isis", // ISIS_NODE,
+ "pim", // PIM_NODE,
+ "masc", // MASC_NODE,
+ "irdp", // IRDP_NODE,
+ "static ip", // IP_NODE,
+ "ipv4 access list", // ACCESS_NODE,
+ "ipv4 prefix list", // PREFIX_NODE,
+ "ipv6 access list", // ACCESS_IPV6_NODE,
+ "ipv6 prefix list", // PREFIX_IPV6_NODE,
+ "as list", // AS_LIST_NODE,
+ "community list", // COMMUNITY_LIST_NODE,
+ "routemap", // RMAP_NODE,
+ "smux", // SMUX_NODE,
+ "dump", // DUMP_NODE,
+ "forwarding", // FORWARDING_NODE,
+ "protocol", // PROTOCOL_NODE,
+ "mpls", // MPLS_NODE,
+ "pw", // PW_NODE,
+ "vty", // VTY_NODE,
+ "link-params", // LINK_PARAMS_NODE,
+ "bgp evpn vni", // BGP_EVPN_VNI_NODE,
+};
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
@@ -210,10 +276,10 @@ void install_node(struct cmd_node *node, int (*func)(struct vty *))
node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
// add start node
struct cmd_token *token =
- new_cmd_token(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
graph_new_node(node->cmdgraph, token,
- (void (*)(void *)) & del_cmd_token);
- node->cmd_hash = hash_create(cmd_hash_key, cmd_hash_cmp);
+ (void (*)(void *)) & cmd_token_del);
+ node->cmd_hash = hash_create(cmd_hash_key, cmd_hash_cmp, NULL);
}
/**
@@ -280,261 +346,6 @@ const char *cmd_prompt(enum node_type node)
return cnode->prompt;
}
-static bool cmd_nodes_link(struct graph_node *from, struct graph_node *to)
-{
- for (size_t i = 0; i < vector_active(from->to); i++)
- if (vector_slot(from->to, i) == to)
- return true;
- return false;
-}
-
-static bool cmd_nodes_equal(struct graph_node *ga, struct graph_node *gb);
-
-/* returns a single node to be excluded as "next" from iteration
- * - for JOIN_TKN, never continue back to the FORK_TKN
- * - in all other cases, don't try the node itself (in case of "...")
- */
-static inline struct graph_node *cmd_loopstop(struct graph_node *gn)
-{
- struct cmd_token *tok = gn->data;
- if (tok->type == JOIN_TKN)
- return tok->forkjoin;
- else
- return gn;
-}
-
-static bool cmd_subgraph_equal(struct graph_node *ga, struct graph_node *gb,
- struct graph_node *a_join)
-{
- size_t i, j;
- struct graph_node *a_fork, *b_fork;
- a_fork = cmd_loopstop(ga);
- b_fork = cmd_loopstop(gb);
-
- if (vector_active(ga->to) != vector_active(gb->to))
- return false;
- for (i = 0; i < vector_active(ga->to); i++) {
- struct graph_node *cga = vector_slot(ga->to, i);
-
- for (j = 0; j < vector_active(gb->to); j++) {
- struct graph_node *cgb = vector_slot(gb->to, i);
-
- if (cga == a_fork && cgb != b_fork)
- continue;
- if (cga == a_fork && cgb == b_fork)
- break;
-
- if (cmd_nodes_equal(cga, cgb)) {
- if (cga == a_join)
- break;
- if (cmd_subgraph_equal(cga, cgb, a_join))
- break;
- }
- }
- if (j == vector_active(gb->to))
- return false;
- }
- return true;
-}
-
-/* deep compare -- for FORK_TKN, the entire subgraph is compared.
- * this is what's needed since we're not currently trying to partially
- * merge subgraphs */
-static bool cmd_nodes_equal(struct graph_node *ga, struct graph_node *gb)
-{
- struct cmd_token *a = ga->data, *b = gb->data;
-
- if (a->type != b->type || a->allowrepeat != b->allowrepeat)
- return false;
- if (a->type < SPECIAL_TKN && strcmp(a->text, b->text))
- return false;
- /* one a ..., the other not. */
- if (cmd_nodes_link(ga, ga) != cmd_nodes_link(gb, gb))
- return false;
-
- switch (a->type) {
- case RANGE_TKN:
- return a->min == b->min && a->max == b->max;
-
- case FORK_TKN:
- /* one is keywords, the other just option or selector ... */
- if (cmd_nodes_link(a->forkjoin, ga)
- != cmd_nodes_link(b->forkjoin, gb))
- return false;
- if (cmd_nodes_link(ga, a->forkjoin)
- != cmd_nodes_link(gb, b->forkjoin))
- return false;
- return cmd_subgraph_equal(ga, gb, a->forkjoin);
-
- default:
- return true;
- }
-}
-
-static void cmd_fork_bump_attr(struct graph_node *gn, struct graph_node *join,
- u_char attr)
-{
- size_t i;
- struct cmd_token *tok = gn->data;
- struct graph_node *stop = cmd_loopstop(gn);
-
- tok->attr = attr;
- for (i = 0; i < vector_active(gn->to); i++) {
- struct graph_node *next = vector_slot(gn->to, i);
- if (next == stop || next == join)
- continue;
- cmd_fork_bump_attr(next, join, attr);
- }
-}
-
-/* move an entire subtree from the temporary graph resulting from
- * parse() into the permanent graph for the command node.
- *
- * this touches rather deeply into the graph code unfortunately.
- */
-static void cmd_reparent_tree(struct graph *fromgraph, struct graph *tograph,
- struct graph_node *node)
-{
- struct graph_node *stop = cmd_loopstop(node);
- size_t i;
-
- for (i = 0; i < vector_active(fromgraph->nodes); i++)
- if (vector_slot(fromgraph->nodes, i) == node) {
- /* agressive iteration punching through subgraphs - may
- * hit some
- * nodes twice. reparent only if found on old graph */
- vector_unset(fromgraph->nodes, i);
- vector_set(tograph->nodes, node);
- break;
- }
-
- for (i = 0; i < vector_active(node->to); i++) {
- struct graph_node *next = vector_slot(node->to, i);
- if (next != stop)
- cmd_reparent_tree(fromgraph, tograph, next);
- }
-}
-
-static void cmd_free_recur(struct graph *graph, struct graph_node *node,
- struct graph_node *stop)
-{
- struct graph_node *next, *nstop;
-
- for (size_t i = vector_active(node->to); i; i--) {
- next = vector_slot(node->to, i - 1);
- if (next == stop)
- continue;
- nstop = cmd_loopstop(next);
- if (nstop != next)
- cmd_free_recur(graph, next, nstop);
- cmd_free_recur(graph, nstop, stop);
- }
- graph_delete_node(graph, node);
-}
-
-static void cmd_free_node(struct graph *graph, struct graph_node *node)
-{
- struct cmd_token *tok = node->data;
- if (tok->type == JOIN_TKN)
- cmd_free_recur(graph, tok->forkjoin, node);
- graph_delete_node(graph, node);
-}
-
-/* recursive graph merge. call with
- * old ~= new
- * (which holds true for old == START_TKN, new == START_TKN)
- */
-static void cmd_merge_nodes(struct graph *oldgraph, struct graph *newgraph,
- struct graph_node *old, struct graph_node *new,
- int direction)
-{
- struct cmd_token *tok;
- struct graph_node *old_skip, *new_skip;
- old_skip = cmd_loopstop(old);
- new_skip = cmd_loopstop(new);
-
- assert(direction == 1 || direction == -1);
-
- tok = old->data;
- tok->refcnt += direction;
-
- size_t j, i;
- for (j = 0; j < vector_active(new->to); j++) {
- struct graph_node *cnew = vector_slot(new->to, j);
- if (cnew == new_skip)
- continue;
-
- for (i = 0; i < vector_active(old->to); i++) {
- struct graph_node *cold = vector_slot(old->to, i);
- if (cold == old_skip)
- continue;
-
- if (cmd_nodes_equal(cold, cnew)) {
- struct cmd_token *told = cold->data,
- *tnew = cnew->data;
-
- if (told->type == END_TKN) {
- if (direction < 0) {
- graph_delete_node(
- oldgraph,
- vector_slot(cold->to,
- 0));
- graph_delete_node(oldgraph,
- cold);
- } else
- /* force no-match handling to
- * install END_TKN */
- i = vector_active(old->to);
- break;
- }
-
- /* the entire fork compared as equal, we
- * continue after it. */
- if (told->type == FORK_TKN) {
- if (tnew->attr < told->attr
- && direction > 0)
- cmd_fork_bump_attr(
- cold, told->forkjoin,
- tnew->attr);
- /* XXX: no reverse bump on uninstall */
- told = (cold = told->forkjoin)->data;
- tnew = (cnew = tnew->forkjoin)->data;
- }
- if (tnew->attr < told->attr)
- told->attr = tnew->attr;
-
- cmd_merge_nodes(oldgraph, newgraph, cold, cnew,
- direction);
- break;
- }
- }
- /* nothing found => add new to old */
- if (i == vector_active(old->to) && direction > 0) {
- assert(vector_count(cnew->from)
- == cmd_nodes_link(cnew, cnew)
- ? 2
- : 1);
- graph_remove_edge(new, cnew);
-
- cmd_reparent_tree(newgraph, oldgraph, cnew);
-
- graph_add_edge(old, cnew);
- }
- }
-
- if (!tok->refcnt)
- cmd_free_node(oldgraph, old);
-}
-
-void cmd_merge_graphs(struct graph *old, struct graph *new, int direction)
-{
- assert(vector_active(old->nodes) >= 1);
- assert(vector_active(new->nodes) >= 1);
-
- cmd_merge_nodes(old, new, vector_slot(old->nodes, 0),
- vector_slot(new->nodes, 0), direction);
-}
-
/* Install a command into a node. */
void install_element(enum node_type ntype, struct cmd_element *cmd)
{
@@ -553,6 +364,8 @@ void install_element(enum node_type ntype, struct cmd_element *cmd)
fprintf(stderr,
"Command node %d doesn't exist, please check it\n",
ntype);
+ fprintf(stderr,
+ "Have you called install_node before this install_element?\n");
exit(EXIT_FAILURE);
}
@@ -567,11 +380,12 @@ void install_element(enum node_type ntype, struct cmd_element *cmd)
struct graph *graph = graph_new();
struct cmd_token *token =
- new_cmd_token(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
- graph_new_node(graph, token, (void (*)(void *)) & del_cmd_token);
+ cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
- command_parse_format(graph, cmd);
- cmd_merge_graphs(cnode->cmdgraph, graph, +1);
+ cmd_graph_parse(graph, cmd);
+ cmd_graph_names(graph);
+ cmd_graph_merge(cnode->cmdgraph, graph, +1);
graph_delete_graph(graph);
vector_set(cnode->cmd_vector, cmd);
@@ -597,6 +411,8 @@ void uninstall_element(enum node_type ntype, struct cmd_element *cmd)
fprintf(stderr,
"Command node %d doesn't exist, please check it\n",
ntype);
+ fprintf(stderr,
+ "Have you called install_node before this install_element?\n");
exit(EXIT_FAILURE);
}
@@ -611,11 +427,12 @@ void uninstall_element(enum node_type ntype, struct cmd_element *cmd)
struct graph *graph = graph_new();
struct cmd_token *token =
- new_cmd_token(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
- graph_new_node(graph, token, (void (*)(void *)) & del_cmd_token);
+ cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
- command_parse_format(graph, cmd);
- cmd_merge_graphs(cnode->cmdgraph, graph, -1);
+ cmd_graph_parse(graph, cmd);
+ cmd_graph_names(graph);
+ cmd_graph_merge(cnode->cmdgraph, graph, -1);
graph_delete_graph(graph);
if (ntype == VIEW_NODE)
@@ -653,29 +470,25 @@ static char *zencrypt(const char *passwd)
static int config_write_host(struct vty *vty)
{
if (host.name)
- vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
+ vty_out(vty, "hostname %s\n", host.name);
if (host.encrypt) {
if (host.password_encrypt)
- vty_out(vty, "password 8 %s%s", host.password_encrypt,
- VTY_NEWLINE);
+ vty_out(vty, "password 8 %s\n", host.password_encrypt);
if (host.enable_encrypt)
- vty_out(vty, "enable password 8 %s%s",
- host.enable_encrypt, VTY_NEWLINE);
+ vty_out(vty, "enable password 8 %s\n",
+ host.enable_encrypt);
} else {
if (host.password)
- vty_out(vty, "password %s%s", host.password,
- VTY_NEWLINE);
+ vty_out(vty, "password %s\n", host.password);
if (host.enable)
- vty_out(vty, "enable password %s%s", host.enable,
- VTY_NEWLINE);
+ vty_out(vty, "enable password %s\n", host.enable);
}
if (zlog_default->default_lvl != LOG_DEBUG) {
- vty_out(vty, "! N.B. The 'log trap' command is deprecated.%s",
- VTY_NEWLINE);
- vty_out(vty, "log trap %s%s",
- zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
+ vty_out(vty, "! N.B. The 'log trap' command is deprecated.\n");
+ vty_out(vty, "log trap %s\n",
+ zlog_priority[zlog_default->default_lvl]);
}
if (host.logfile
@@ -686,7 +499,7 @@ static int config_write_host(struct vty *vty)
vty_out(vty, " %s",
zlog_priority
[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
- vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "\n");
}
if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
@@ -696,16 +509,15 @@ static int config_write_host(struct vty *vty)
vty_out(vty, " %s",
zlog_priority[zlog_default->maxlvl
[ZLOG_DEST_STDOUT]]);
- vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "\n");
}
if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
- vty_out(vty, "no log monitor%s", VTY_NEWLINE);
+ vty_out(vty, "no log monitor\n");
else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
!= zlog_default->default_lvl)
- vty_out(vty, "log monitor %s%s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],
- VTY_NEWLINE);
+ vty_out(vty, "log monitor %s\n",
+ zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]]);
if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
vty_out(vty, "log syslog");
@@ -714,35 +526,33 @@ static int config_write_host(struct vty *vty)
vty_out(vty, " %s",
zlog_priority[zlog_default->maxlvl
[ZLOG_DEST_SYSLOG]]);
- vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "\n");
}
if (zlog_default->facility != LOG_DAEMON)
- vty_out(vty, "log facility %s%s",
- facility_name(zlog_default->facility), VTY_NEWLINE);
+ vty_out(vty, "log facility %s\n",
+ facility_name(zlog_default->facility));
if (zlog_default->record_priority == 1)
- vty_out(vty, "log record-priority%s", VTY_NEWLINE);
+ vty_out(vty, "log record-priority\n");
if (zlog_default->timestamp_precision > 0)
- vty_out(vty, "log timestamp precision %d%s",
- zlog_default->timestamp_precision, VTY_NEWLINE);
+ vty_out(vty, "log timestamp precision %d\n",
+ zlog_default->timestamp_precision);
if (host.advanced)
- vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
+ vty_out(vty, "service advanced-vty\n");
if (host.encrypt)
- vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
+ vty_out(vty, "service password-encryption\n");
if (host.lines >= 0)
- vty_out(vty, "service terminal-length %d%s", host.lines,
- VTY_NEWLINE);
+ vty_out(vty, "service terminal-length %d\n", host.lines);
if (host.motdfile)
- vty_out(vty, "banner motd file %s%s", host.motdfile,
- VTY_NEWLINE);
+ vty_out(vty, "banner motd file %s\n", host.motdfile);
else if (!host.motd)
- vty_out(vty, "no banner motd%s", VTY_NEWLINE);
+ vty_out(vty, "no banner motd\n");
return 1;
}
@@ -757,8 +567,7 @@ static struct graph *cmd_node_graph(vector v, enum node_type ntype)
static int cmd_try_do_shortcut(enum node_type node, char *first_word)
{
if (first_word != NULL && node != AUTH_NODE && node != VIEW_NODE
- && node != AUTH_ENABLE_NODE && node != ENABLE_NODE
- && 0 == strcmp("do", first_word))
+ && node != AUTH_ENABLE_NODE && 0 == strcmp("do", first_word))
return 1;
return 0;
}
@@ -894,6 +703,114 @@ vector cmd_describe_command(vector vline, struct vty *vty, int *status)
return cmd_complete_command_real(vline, vty, status);
}
+static struct list *varhandlers = NULL;
+
+void cmd_variable_complete(struct cmd_token *token, const char *arg,
+ vector comps)
+{
+ struct listnode *ln;
+ const struct cmd_variable_handler *cvh;
+ size_t i, argsz;
+ vector tmpcomps;
+
+ tmpcomps = arg ? vector_init(VECTOR_MIN_SIZE) : comps;
+
+ for (ALL_LIST_ELEMENTS_RO(varhandlers, ln, cvh)) {
+ if (cvh->tokenname && strcmp(cvh->tokenname, token->text))
+ continue;
+ if (cvh->varname && (!token->varname
+ || strcmp(cvh->varname, token->varname)))
+ continue;
+ cvh->completions(tmpcomps, token);
+ break;
+ }
+
+ if (!arg)
+ return;
+
+ argsz = strlen(arg);
+ for (i = vector_active(tmpcomps); i; i--) {
+ char *item = vector_slot(tmpcomps, i - 1);
+ if (strlen(item) >= argsz && !strncmp(item, arg, argsz))
+ vector_set(comps, item);
+ else
+ XFREE(MTYPE_COMPLETION, item);
+ }
+ vector_free(tmpcomps);
+}
+
+#define AUTOCOMP_INDENT 5
+
+char *cmd_variable_comp2str(vector comps, unsigned short cols)
+{
+ size_t bsz = 16;
+ char *buf = XCALLOC(MTYPE_TMP, bsz);
+ int lc = AUTOCOMP_INDENT;
+ size_t cs = AUTOCOMP_INDENT;
+ size_t itemlen;
+ snprintf(buf, bsz, "%*s", AUTOCOMP_INDENT, "");
+ for (size_t j = 0; j < vector_active(comps); j++) {
+ char *item = vector_slot(comps, j);
+ itemlen = strlen(item);
+
+ if (cs + itemlen + AUTOCOMP_INDENT + 3 >= bsz)
+ buf = XREALLOC(MTYPE_TMP, buf, (bsz *= 2));
+
+ if (lc + itemlen + 1 >= cols) {
+ cs += snprintf(&buf[cs], bsz - cs, "\n%*s",
+ AUTOCOMP_INDENT, "");
+ lc = AUTOCOMP_INDENT;
+ }
+
+ size_t written = snprintf(&buf[cs], bsz - cs, "%s ", item);
+ lc += written;
+ cs += written;
+ XFREE(MTYPE_COMPLETION, item);
+ vector_set_index(comps, j, NULL);
+ }
+ return buf;
+}
+
+void cmd_variable_handler_register(const struct cmd_variable_handler *cvh)
+{
+ if (!varhandlers)
+ return;
+
+ for (; cvh->completions; cvh++)
+ listnode_add(varhandlers, (void *)cvh);
+}
+
+DEFUN_HIDDEN (autocomplete,
+ autocomplete_cmd,
+ "autocomplete TYPE TEXT VARNAME",
+ "Autocompletion handler (internal, for vtysh)\n"
+ "cmd_token->type\n"
+ "cmd_token->text\n"
+ "cmd_token->varname\n")
+{
+ struct cmd_token tok;
+ vector comps = vector_init(32);
+ size_t i;
+
+ memset(&tok, 0, sizeof(tok));
+ tok.type = atoi(argv[1]->arg);
+ tok.text = argv[2]->arg;
+ tok.varname = argv[3]->arg;
+ if (!strcmp(tok.varname, "-"))
+ tok.varname = NULL;
+
+ cmd_variable_complete(&tok, NULL, comps);
+
+ for (i = 0; i < vector_active(comps); i++) {
+ char *text = vector_slot(comps, i);
+ vty_out(vty, "%s\n", text);
+ XFREE(MTYPE_COMPLETION, text);
+ }
+
+ vector_free(comps);
+ return CMD_SUCCESS;
+}
+
/**
* Generate possible tab-completions for the given input. This function only
* returns results that would result in a valid command if used as Readline
@@ -936,7 +853,13 @@ char **cmd_complete_command(vector vline, struct vty *vty, int *status)
i++) {
struct cmd_token *token = vector_slot(initial_comps, i);
if (token->type == WORD_TKN)
- vector_set(comps, token);
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
+ token->text));
+ else if (IS_VARYING_TOKEN(token->type)) {
+ const char *ref = vector_lookup(
+ vline, vector_active(vline) - 1);
+ cmd_variable_complete(token, ref, comps);
+ }
}
vector_free(initial_comps);
@@ -957,9 +880,7 @@ char **cmd_complete_command(vector vline, struct vty *vty, int *status)
(vector_active(comps) + 1) * sizeof(char *));
unsigned int i;
for (i = 0; i < vector_active(comps); i++) {
- struct cmd_token *token = vector_slot(comps, i);
- ret[i] = XSTRDUP(MTYPE_TMP, token->text);
- vector_unset(comps, i);
+ ret[i] = vector_slot(comps, i);
}
// set the last element to NULL, because this array is used in
// a Readline completion_generator function which expects NULL
@@ -999,11 +920,16 @@ enum node_type node_parent(enum node_type node)
case BGP_VNC_L2_GROUP_NODE:
case BGP_IPV4_NODE:
case BGP_IPV4M_NODE:
+ case BGP_IPV4L_NODE:
case BGP_IPV6_NODE:
case BGP_IPV6M_NODE:
case BGP_EVPN_NODE:
+ case BGP_IPV6L_NODE:
ret = BGP_NODE;
break;
+ case BGP_EVPN_VNI_NODE:
+ ret = BGP_EVPN_NODE;
+ break;
case KEYCHAIN_KEY_NODE:
ret = KEYCHAIN_NODE;
break;
@@ -1270,9 +1196,8 @@ DEFUN (config_terminal,
if (vty_config_lock(vty))
vty->node = CONFIG_NODE;
else {
- vty_out(vty, "VTY configuration is locked by other VTY%s",
- VTY_NEWLINE);
- return CMD_WARNING;
+ vty_out(vty, "VTY configuration is locked by other VTY\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
@@ -1335,6 +1260,8 @@ void cmd_exit(struct vty *vty)
case ZEBRA_NODE:
case BGP_NODE:
case RIP_NODE:
+ case EIGRP_NODE:
+ case BABEL_NODE:
case RIPNG_NODE:
case OSPF_NODE:
case OSPF6_NODE:
@@ -1350,6 +1277,7 @@ void cmd_exit(struct vty *vty)
break;
case BGP_IPV4_NODE:
case BGP_IPV4M_NODE:
+ case BGP_IPV4L_NODE:
case BGP_VPNV4_NODE:
case BGP_VPNV6_NODE:
case BGP_VRF_POLICY_NODE:
@@ -1359,8 +1287,12 @@ void cmd_exit(struct vty *vty)
case BGP_IPV6_NODE:
case BGP_IPV6M_NODE:
case BGP_EVPN_NODE:
+ case BGP_IPV6L_NODE:
vty->node = BGP_NODE;
break;
+ case BGP_EVPN_VNI_NODE:
+ vty->node = BGP_EVPN_NODE;
+ break;
case LDP_IPV4_NODE:
case LDP_IPV6_NODE:
vty->node = LDP_NODE;
@@ -1414,6 +1346,8 @@ DEFUN (config_end,
case ZEBRA_NODE:
case RIP_NODE:
case RIPNG_NODE:
+ case EIGRP_NODE:
+ case BABEL_NODE:
case BGP_NODE:
case BGP_VRF_POLICY_NODE:
case BGP_VNC_DEFAULTS_NODE:
@@ -1423,9 +1357,12 @@ DEFUN (config_end,
case BGP_VPNV6_NODE:
case BGP_IPV4_NODE:
case BGP_IPV4M_NODE:
+ case BGP_IPV4L_NODE:
case BGP_IPV6_NODE:
case BGP_IPV6M_NODE:
case BGP_EVPN_NODE:
+ case BGP_EVPN_VNI_NODE:
+ case BGP_IPV6L_NODE:
case RMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
@@ -1459,11 +1396,10 @@ DEFUN (show_version,
SHOW_STR
"Displays zebra version\n")
{
- vty_out(vty, "%s %s (%s).%s", FRR_FULL_NAME, FRR_VERSION,
- host.name ? host.name : "", VTY_NEWLINE);
- vty_out(vty, "%s%s%s", FRR_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
- vty_out(vty, "configured with:%s %s%s", VTY_NEWLINE, FRR_CONFIG_ARGS,
- VTY_NEWLINE);
+ vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
+ host.name ? host.name : "");
+ vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
+ vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
return CMD_SUCCESS;
}
@@ -1477,12 +1413,6 @@ DEFUN (frr_version_defaults,
"set of configuration defaults used\n"
"version string\n")
{
- if (vty->type == VTY_TERM || vty->type == VTY_SHELL)
- /* only print this when the user tries to do run it */
- vty_out(vty,
- "%% NOTE: This command currently does nothing.%s"
- "%% It is written to the configuration for future reference.%s",
- VTY_NEWLINE, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1493,21 +1423,18 @@ DEFUN (config_help,
"Description of the interactive help system\n")
{
vty_out(vty,
- "Quagga VTY provides advanced help feature. When you need help,%s\
-anytime at the command line please press '?'.%s\
-%s\
-If nothing matches, the help list will be empty and you must backup%s\
- until entering a '?' shows the available options.%s\
-Two styles of help are provided:%s\
-1. Full help is available when you are ready to enter a%s\
-command argument (e.g. 'show ?') and describes each possible%s\
-argument.%s\
-2. Partial help is provided when an abbreviated argument is entered%s\
- and you want to know what arguments match the input%s\
- (e.g. 'show me?'.)%s%s",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ "Quagga VTY provides advanced help feature. When you need help,\n\
+anytime at the command line please press '?'.\n\
+\n\
+If nothing matches, the help list will be empty and you must backup\n\
+ until entering a '?' shows the available options.\n\
+Two styles of help are provided:\n\
+1. Full help is available when you are ready to enter a\n\
+command argument (e.g. 'show ?') and describes each possible\n\
+argument.\n\
+2. Partial help is provided when an abbreviated argument is entered\n\
+ and you want to know what arguments match the input\n\
+ (e.g. 'show me?'.)\n\n");
return CMD_SUCCESS;
}
@@ -1538,7 +1465,7 @@ static void permute(struct graph_node *start, struct vty *vty)
}
if (gn == start)
vty_out(vty, "...");
- vty_out(vty, VTY_NEWLINE);
+ vty_out(vty, "\n");
} else {
bool skip = false;
if (stok->type == FORK_TKN && tok->type != FORK_TKN)
@@ -1568,8 +1495,7 @@ int cmd_list_cmds(struct vty *vty, int do_permute)
if ((element = vector_slot(node->cmd_vector, i))
&& element->attr != CMD_ATTR_DEPRECATED
&& element->attr != CMD_ATTR_HIDDEN)
- vty_out(vty, " %s%s", element->string,
- VTY_NEWLINE);
+ vty_out(vty, " %s\n", element->string);
}
return CMD_SUCCESS;
}
@@ -1600,24 +1526,23 @@ static void vty_write_config(struct vty *vty)
struct cmd_node *node;
if (vty->type == VTY_TERM) {
- vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
- VTY_NEWLINE);
- vty_out(vty, "!%s", VTY_NEWLINE);
+ vty_out(vty, "\nCurrent configuration:\n");
+ vty_out(vty, "!\n");
}
- vty_out(vty, "frr version %s%s", FRR_VER_SHORT, VTY_NEWLINE);
- vty_out(vty, "frr defaults %s%s", DFLT_NAME, VTY_NEWLINE);
- vty_out(vty, "!%s", VTY_NEWLINE);
+ vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
+ vty_out(vty, "frr defaults %s\n", DFLT_NAME);
+ vty_out(vty, "!\n");
for (i = 0; i < vector_active(cmdvec); i++)
if ((node = vector_slot(cmdvec, i)) && node->func
&& (node->vtysh || vty->type != VTY_SHELL)) {
if ((*node->func)(vty))
- vty_out(vty, "!%s", VTY_NEWLINE);
+ vty_out(vty, "!\n");
}
if (vty->type == VTY_TERM) {
- vty_out(vty, "end%s", VTY_NEWLINE);
+ vty_out(vty, "end\n");
}
}
@@ -1641,8 +1566,8 @@ DEFUN (config_write,
struct stat conf_stat;
// if command was 'write terminal' or 'show running-config'
- if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal")
- || !strcmp(argv[0]->text, "show"))) {
+ if (argc == 2 && (strmatch(argv[idx_type]->text, "terminal")
+ || strmatch(argv[0]->text, "show"))) {
vty_write_config(vty);
return CMD_SUCCESS;
}
@@ -1652,8 +1577,8 @@ DEFUN (config_write,
/* Check and see if we are operating under vtysh configuration */
if (host.config == NULL) {
- vty_out(vty, "Can't save to configuration file, using vtysh.%s",
- VTY_NEWLINE);
+ vty_out(vty,
+ "Can't save to configuration file, using vtysh.\n");
return CMD_WARNING;
}
@@ -1685,14 +1610,13 @@ DEFUN (config_write,
/* Open file to configuration write. */
fd = mkstemp(config_file_tmp);
if (fd < 0) {
- vty_out(vty, "Can't open configuration file %s.%s",
- config_file_tmp, VTY_NEWLINE);
+ vty_out(vty, "Can't open configuration file %s.\n",
+ config_file_tmp);
goto finished;
}
if (fchmod(fd, CONFIGFILE_MASK) != 0) {
- vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
- config_file_tmp, safe_strerror(errno), errno,
- VTY_NEWLINE);
+ vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
+ config_file_tmp, safe_strerror(errno), errno);
goto finished;
}
@@ -1712,28 +1636,28 @@ DEFUN (config_write,
if (unlink(config_file_sav) != 0)
if (errno != ENOENT) {
vty_out(vty,
- "Can't unlink backup configuration file %s.%s",
- config_file_sav, VTY_NEWLINE);
+ "Can't unlink backup configuration file %s.\n",
+ config_file_sav);
goto finished;
}
if (link(config_file, config_file_sav) != 0) {
vty_out(vty,
- "Can't backup old configuration file %s.%s",
- config_file_sav, VTY_NEWLINE);
+ "Can't backup old configuration file %s.\n",
+ config_file_sav);
goto finished;
}
if (dirfd >= 0)
fsync(dirfd);
}
if (rename(config_file_tmp, config_file) != 0) {
- vty_out(vty, "Can't save configuration file %s.%s", config_file,
- VTY_NEWLINE);
+ vty_out(vty, "Can't save configuration file %s.\n",
+ config_file);
goto finished;
}
if (dirfd >= 0)
fsync(dirfd);
- vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE);
+ vty_out(vty, "Configuration saved to %s\n", config_file);
ret = CMD_SUCCESS;
finished:
@@ -1787,8 +1711,8 @@ DEFUN (show_startup_config,
confp = fopen(host.config, "r");
if (confp == NULL) {
- vty_out(vty, "Can't open configuration file [%s] due to '%s'%s",
- host.config, safe_strerror(errno), VTY_NEWLINE);
+ vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
+ host.config, safe_strerror(errno));
return CMD_WARNING;
}
@@ -1799,7 +1723,7 @@ DEFUN (show_startup_config,
cp++;
*cp = '\0';
- vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+ vty_out(vty, "%s\n", buf);
}
fclose(confp);
@@ -1824,9 +1748,8 @@ DEFUN (config_hostname,
struct cmd_token *word = argv[1];
if (!isalpha((int)word->arg[0])) {
- vty_out(vty, "Please specify string starting with alphabet%s",
- VTY_NEWLINE);
- return CMD_WARNING;
+ vty_out(vty, "Please specify string starting with alphabet\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
return cmd_hostname_set(word->arg);
@@ -1866,9 +1789,8 @@ DEFUN (config_password,
if (!isalnum(argv[idx_8]->arg[0])) {
vty_out(vty,
- "Please specify string starting with alphanumeric%s",
- VTY_NEWLINE);
- return CMD_WARNING;
+ "Please specify string starting with alphanumeric\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
if (host.password)
@@ -1912,16 +1834,15 @@ DEFUN (config_enable_password,
return CMD_SUCCESS;
} else {
- vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
- return CMD_WARNING;
+ vty_out(vty, "Unknown encryption type.\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
}
if (!isalnum(argv[idx_8]->arg[0])) {
vty_out(vty,
- "Please specify string starting with alphanumeric%s",
- VTY_NEWLINE);
- return CMD_WARNING;
+ "Please specify string starting with alphanumeric\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
if (host.enable)
@@ -2017,15 +1938,8 @@ DEFUN (config_terminal_length,
"Number of lines on screen (0 for no pausing)\n")
{
int idx_number = 2;
- int lines;
- char *endptr = NULL;
- lines = strtol(argv[idx_number]->arg, &endptr, 10);
- if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- vty->lines = lines;
+ vty->lines = atoi(argv[idx_number]->arg);
return CMD_SUCCESS;
}
@@ -2049,15 +1963,8 @@ DEFUN (service_terminal_length,
"Number of lines of VTY (0 means no line control)\n")
{
int idx_number = 2;
- int lines;
- char *endptr = NULL;
- lines = strtol(argv[idx_number]->arg, &endptr, 10);
- if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- host.lines = lines;
+ host.lines = atoi(argv[idx_number]->arg);
return CMD_SUCCESS;
}
@@ -2082,9 +1989,8 @@ DEFUN_HIDDEN (do_echo,
{
char *message;
- vty_out(vty, "%s%s",
- ((message = argv_concat(argv, argc, 1)) ? message : ""),
- VTY_NEWLINE);
+ vty_out(vty, "%s\n",
+ ((message = argv_concat(argv, argc, 1)) ? message : ""));
if (message)
XFREE(MTYPE_TMP, message);
return CMD_SUCCESS;
@@ -2128,7 +2034,7 @@ DEFUN (show_logging,
vty_out(vty, "level %s, facility %s, ident %s",
zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
facility_name(zl->facility), zl->ident);
- vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "\n");
vty_out(vty, "Stdout logging: ");
if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
@@ -2136,7 +2042,7 @@ DEFUN (show_logging,
else
vty_out(vty, "level %s",
zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
- vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "\n");
vty_out(vty, "Monitor logging: ");
if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
@@ -2144,7 +2050,7 @@ DEFUN (show_logging,
else
vty_out(vty, "level %s",
zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
- vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "\n");
vty_out(vty, "File logging: ");
if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
@@ -2153,13 +2059,12 @@ DEFUN (show_logging,
vty_out(vty, "level %s, filename %s",
zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
zl->filename);
- vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "\n");
- vty_out(vty, "Protocol name: %s%s", zl->protoname, VTY_NEWLINE);
- vty_out(vty, "Record priority: %s%s",
- (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
- vty_out(vty, "Timestamp precision: %d%s", zl->timestamp_precision,
- VTY_NEWLINE);
+ vty_out(vty, "Protocol name: %s\n", zl->protoname);
+ vty_out(vty, "Record priority: %s\n",
+ (zl->record_priority ? "enabled" : "disabled"));
+ vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision);
return CMD_SUCCESS;
}
@@ -2243,13 +2148,13 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel)
if (getcwd(cwd, MAXPATHLEN) == NULL) {
zlog_err("config_log_file: Unable to alloc mem!");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
if ((p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2))
== NULL) {
zlog_err("config_log_file: Unable to alloc mem!");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
sprintf(p, "%s/%s", cwd, fname);
fullpath = p;
@@ -2263,7 +2168,7 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel)
if (!ret) {
vty_out(vty, "can't open logfile %s\n", fname);
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (host.logfile)
@@ -2306,7 +2211,6 @@ DEFUN (no_config_log_file,
"Logging control\n"
"Cancel logging to file\n"
"Logging file name\n"
- "Logging file name\n"
"Logging level\n")
{
zlog_reset_file();
@@ -2439,9 +2343,8 @@ DEFUN (config_log_timestamp_precision,
"Number of subsecond digits\n")
{
int idx_number = 3;
- VTY_GET_INTEGER_RANGE("Timestamp Precision",
- zlog_default->timestamp_precision,
- argv[idx_number]->arg, 0, 6);
+ zlog_default->timestamp_precision =
+ strtoul(argv[idx_number]->arg, NULL, 10);
return CMD_SUCCESS;
}
@@ -2473,7 +2376,7 @@ int cmd_banner_motd_file(const char *file)
XFREE(MTYPE_HOST, host.motdfile);
host.motdfile = XSTRDUP(MTYPE_HOST, file);
} else
- success = CMD_WARNING;
+ success = CMD_WARNING_CONFIG_FAILED;
return success;
}
@@ -2492,7 +2395,7 @@ DEFUN (banner_motd_file,
if (cmd == CMD_ERR_NO_FILE)
vty_out(vty, "%s does not exist", filename);
- else if (cmd == CMD_WARNING)
+ else if (cmd == CMD_WARNING_CONFIG_FAILED)
vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
return cmd;
@@ -2523,6 +2426,35 @@ DEFUN (no_banner_motd,
return CMD_SUCCESS;
}
+DEFUN(find,
+ find_cmd,
+ "find COMMAND...",
+ "Find CLI command containing text\n"
+ "Text to search for\n")
+{
+ char *text = argv_concat(argv, argc, 1);
+ const struct cmd_node *node;
+ const struct cmd_element *cli;
+ vector clis;
+
+ for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
+ node = vector_slot(cmdvec, i);
+ if (!node)
+ continue;
+ clis = node->cmd_vector;
+ for (unsigned int j = 0; j < vector_active(clis); j++) {
+ cli = vector_slot(clis, j);
+ if (strcasestr(cli->string, text))
+ vty_out(vty, " (%s) %s\n",
+ node_names[node->node], cli->string);
+ }
+ }
+
+ XFREE(MTYPE_TMP, text);
+
+ return CMD_SUCCESS;
+}
+
/* Set config filename. Called from vty.c */
void host_config_set(const char *filename)
{
@@ -2543,9 +2475,12 @@ void install_default(enum node_type node)
install_element(node, &config_end_cmd);
install_element(node, &config_help_cmd);
install_element(node, &config_list_cmd);
+ install_element(node, &find_cmd);
install_element(node, &config_write_cmd);
install_element(node, &show_running_config_cmd);
+
+ install_element(node, &autocomplete_cmd);
}
/* Initialize command interface. Install basic nodes and commands.
@@ -2555,8 +2490,13 @@ void install_default(enum node_type node)
* terminal = -1 -- watchfrr / no logging, but minimal config control */
void cmd_init(int terminal)
{
+ if (array_size(node_names) != NODE_TYPE_MAX)
+ assert(!"Update the CLI node description array!");
+
qobj_init();
+ varhandlers = list_new();
+
/* Allocate initial top vector of commands. */
cmdvec = vector_init(VECTOR_MIN_SIZE);
@@ -2580,6 +2520,8 @@ void cmd_init(int terminal)
/* Each node's basic commands. */
install_element(VIEW_NODE, &show_version_cmd);
+ install_element(ENABLE_NODE, &show_startup_config_cmd);
+
if (terminal) {
install_element(VIEW_NODE, &config_list_cmd);
install_element(VIEW_NODE, &config_exit_cmd);
@@ -2591,24 +2533,22 @@ void cmd_init(int terminal)
install_element(VIEW_NODE, &show_logging_cmd);
install_element(VIEW_NODE, &show_commandtree_cmd);
install_element(VIEW_NODE, &echo_cmd);
- }
+ install_element(VIEW_NODE, &autocomplete_cmd);
+ install_element(VIEW_NODE, &find_cmd);
- if (terminal) {
install_element(ENABLE_NODE, &config_end_cmd);
install_element(ENABLE_NODE, &config_disable_cmd);
install_element(ENABLE_NODE, &config_terminal_cmd);
install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
install_element(ENABLE_NODE, &config_write_cmd);
install_element(ENABLE_NODE, &show_running_config_cmd);
- }
- install_element(ENABLE_NODE, &show_startup_config_cmd);
-
- if (terminal) {
install_element(ENABLE_NODE, &config_logmsg_cmd);
+
install_default(CONFIG_NODE);
thread_cmd_init();
workqueue_cmd_init();
+ hash_cmd_init();
}
install_element(CONFIG_NODE, &hostname_cmd);
@@ -2655,50 +2595,6 @@ void cmd_init(int terminal)
#endif
}
-struct cmd_token *new_cmd_token(enum cmd_token_type type, u_char attr,
- const char *text, const char *desc)
-{
- struct cmd_token *token =
- XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_token));
- token->type = type;
- token->attr = attr;
- token->text = text ? XSTRDUP(MTYPE_CMD_TEXT, text) : NULL;
- token->desc = desc ? XSTRDUP(MTYPE_CMD_DESC, desc) : NULL;
- token->refcnt = 1;
- token->arg = NULL;
- token->allowrepeat = false;
-
- return token;
-}
-
-void del_cmd_token(struct cmd_token *token)
-{
- if (!token)
- return;
-
- if (token->text)
- XFREE(MTYPE_CMD_TEXT, token->text);
- if (token->desc)
- XFREE(MTYPE_CMD_DESC, token->desc);
- if (token->arg)
- XFREE(MTYPE_CMD_ARG, token->arg);
-
- XFREE(MTYPE_CMD_TOKENS, token);
-}
-
-struct cmd_token *copy_cmd_token(struct cmd_token *token)
-{
- struct cmd_token *copy =
- new_cmd_token(token->type, token->attr, NULL, NULL);
- copy->max = token->max;
- copy->min = token->min;
- copy->text = token->text ? XSTRDUP(MTYPE_CMD_TEXT, token->text) : NULL;
- copy->desc = token->desc ? XSTRDUP(MTYPE_CMD_DESC, token->desc) : NULL;
- copy->arg = token->arg ? XSTRDUP(MTYPE_CMD_ARG, token->arg) : NULL;
-
- return copy;
-}
-
void cmd_terminate()
{
struct cmd_node *cmd_node;
@@ -2736,5 +2632,6 @@ void cmd_terminate()
if (host.config)
XFREE(MTYPE_HOST, host.config);
+ list_delete(varhandlers);
qobj_finish();
}