summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@opensourcerouting.org>2021-10-18 11:51:09 +0200
committerDavid Lamparter <equinox@opensourcerouting.org>2021-10-18 19:48:11 +0200
commit0e06eb8b2ea1eff8c67752533d66e96bb9bebfdb (patch)
treee906e47b16d0b157368d8ad7b1af3cf65c2d9c7d
parent8005767b2e5c177d0185a1541c7393d9ed8d1712 (diff)
vtysh: defer CLI tree building
We don't need the CLI tree until we actually enter the node. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
-rw-r--r--lib/command.c75
-rw-r--r--lib/command.h9
-rw-r--r--lib/grammar_sandbox.c2
-rw-r--r--vtysh/vtysh.c2
4 files changed, 74 insertions, 14 deletions
diff --git a/lib/command.c b/lib/command.c
index 53aa064705..ea66a17bb0 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -86,6 +86,9 @@ vector cmdvec = NULL;
/* Host information structure. */
struct host host;
+/* for vtysh, put together CLI trees only when switching into node */
+static bool defer_cli_tree;
+
/*
* Returns host.name if any, otherwise
* it returns the system hostname.
@@ -285,6 +288,11 @@ const char *cmd_prompt(enum node_type node)
return cnode->prompt;
}
+void cmd_defer_tree(bool val)
+{
+ defer_cli_tree = val;
+}
+
/* Install a command into a node. */
void _install_element(enum node_type ntype, const struct cmd_element *cmd)
{
@@ -319,20 +327,50 @@ void _install_element(enum node_type ntype, const struct cmd_element *cmd)
assert(hash_get(cnode->cmd_hash, (void *)cmd, hash_alloc_intern));
+ if (cnode->graph_built || !defer_cli_tree) {
+ struct graph *graph = graph_new();
+ struct cmd_token *token =
+ cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ graph_new_node(graph, token,
+ (void (*)(void *)) & cmd_token_del);
+
+ cmd_graph_parse(graph, cmd);
+ cmd_graph_names(graph);
+ cmd_graph_merge(cnode->cmdgraph, graph, +1);
+ graph_delete_graph(graph);
+
+ cnode->graph_built = true;
+ }
+
+ vector_set(cnode->cmd_vector, (void *)cmd);
+
+ if (ntype == VIEW_NODE)
+ _install_element(ENABLE_NODE, cmd);
+}
+
+static void cmd_finalize_iter(struct hash_bucket *hb, void *arg)
+{
+ struct cmd_node *cnode = arg;
+ const struct cmd_element *cmd = hb->data;
struct graph *graph = graph_new();
struct cmd_token *token =
cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+
graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
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, (void *)cmd);
+void cmd_finalize_node(struct cmd_node *cnode)
+{
+ if (cnode->graph_built)
+ return;
- if (ntype == VIEW_NODE)
- _install_element(ENABLE_NODE, cmd);
+ hash_iterate(cnode->cmd_hash, cmd_finalize_iter, cnode);
+ cnode->graph_built = true;
}
void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
@@ -368,15 +406,18 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
vector_unset_value(cnode->cmd_vector, (void *)cmd);
- struct graph *graph = graph_new();
- struct cmd_token *token =
- cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
- graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
+ if (cnode->graph_built) {
+ struct graph *graph = graph_new();
+ struct cmd_token *token =
+ cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ graph_new_node(graph, token,
+ (void (*)(void *)) & cmd_token_del);
- cmd_graph_parse(graph, cmd);
- cmd_graph_names(graph);
- cmd_graph_merge(cnode->cmdgraph, graph, -1);
- graph_delete_graph(graph);
+ cmd_graph_parse(graph, cmd);
+ cmd_graph_names(graph);
+ cmd_graph_merge(cnode->cmdgraph, graph, -1);
+ graph_delete_graph(graph);
+ }
if (ntype == VIEW_NODE)
uninstall_element(ENABLE_NODE, cmd);
@@ -503,6 +544,8 @@ static int config_write_host(struct vty *vty)
static struct graph *cmd_node_graph(vector v, enum node_type ntype)
{
struct cmd_node *cnode = vector_slot(v, ntype);
+
+ cmd_finalize_node(cnode);
return cnode->cmdgraph;
}
@@ -1506,9 +1549,10 @@ int cmd_list_cmds(struct vty *vty, int do_permute)
{
struct cmd_node *node = vector_slot(cmdvec, vty->node);
- if (do_permute)
+ if (do_permute) {
+ cmd_finalize_node(node);
permute(vector_slot(node->cmdgraph->nodes, 0), vty);
- else {
+ } else {
/* loop over all commands at this node */
const struct cmd_element *element = NULL;
for (unsigned int i = 0; i < vector_active(node->cmd_vector);
@@ -1551,7 +1595,10 @@ DEFUN_HIDDEN(show_cli_graph,
"Dump current command space as DOT graph\n")
{
struct cmd_node *cn = vector_slot(cmdvec, vty->node);
- char *dot = cmd_graph_dump_dot(cn->cmdgraph);
+ char *dot;
+
+ cmd_finalize_node(cn);
+ dot = cmd_graph_dump_dot(cn->cmdgraph);
vty_out(vty, "%s\n", dot);
XFREE(MTYPE_TMP, dot);
diff --git a/lib/command.h b/lib/command.h
index e2eec1aac6..e2086701ad 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -210,6 +210,9 @@ struct cmd_node {
/* Hashed index of command node list, for de-dupping primarily */
struct hash *cmd_hash;
+
+ /* set as soon as any command is in cmdgraph */
+ bool graph_built;
};
/* Return value of the commands. */
@@ -526,6 +529,12 @@ extern void _install_element(enum node_type, const struct cmd_element *);
* deprecated/hidden) are not reversed. */
extern void uninstall_element(enum node_type, const struct cmd_element *);
+/* construct CLI tree only when entering nodes */
+extern void cmd_defer_tree(bool val);
+
+/* finish CLI tree for node when above is true (noop otherwise) */
+extern void cmd_finalize_node(struct cmd_node *node);
+
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
string with a space between each element (allocated using
XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index 209765bd6f..f9778c5d4c 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -395,6 +395,7 @@ DEFUN (grammar_findambig,
vector_slot(cmdvec, scannode++);
if (!cnode)
continue;
+ cmd_finalize_node(cnode);
nodegraph = cnode->cmdgraph;
if (!nodegraph)
continue;
@@ -466,6 +467,7 @@ DEFUN (grammar_access,
}
vty_out(vty, "node %d\n", (int)cnode->node);
+ cmd_finalize_node(cnode);
nodegraph = cnode->cmdgraph;
return CMD_SUCCESS;
}
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index beb7045a7d..53349452e6 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -3923,6 +3923,8 @@ void vtysh_uninit(void)
void vtysh_init_vty(void)
{
+ cmd_defer_tree(true);
+
/* Make vty structure. */
vty = vty_new();
vty->type = VTY_SHELL;