]> git.proxmox.com Git - mirror_frr.git/blame - lib/grammar_sandbox.c
lib: disable vrf before terminating interfaces
[mirror_frr.git] / lib / grammar_sandbox.c
CommitLineData
1ab84bf3
QY
1/*
2 * Testing shim and API examples for the new CLI backend.
3 *
4 * This unit defines a number of commands in the old engine that can
5 * be used to test and interact with the new engine.
1ab84bf3
QY
6 * --
7 * Copyright (C) 2016 Cumulus Networks, Inc.
8 *
9 * This file is part of GNU Zebra.
10 *
11 * GNU Zebra is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2, or (at your option) any
14 * later version.
15 *
16 * GNU Zebra is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
896014f4
DL
21 * You should have received a copy of the GNU General Public License along
22 * with this program; see the file COPYING; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1ab84bf3
QY
24 */
25
b45ac5f5
DL
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
51e156b3 30#include "command.h"
1eb5e8dc 31#include "graph.h"
0a22b979 32#include "linklist.h"
9d0662e0 33#include "command_match.h"
51e156b3
QY
34
35#define GRAMMAR_STR "CLI grammar sandbox\n"
36
bf8d3d6a 37DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc");
fa133e00 38
97c45dae 39/** headers **/
d62a17ae 40void grammar_sandbox_init(void);
eb51bb9b
DL
41static void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
42 struct graph_node **, size_t);
43static void init_cmdgraph(struct vty *, struct graph **);
1ab84bf3 44
97c45dae 45/** shim interface commands **/
c17faa4b 46static struct graph *nodegraph = NULL, *nodegraph_free = NULL;
340a2b4a 47
d62a17ae 48#define check_nodegraph() \
49 do { \
50 if (!nodegraph) { \
51 vty_out(vty, "nodegraph not initialized\n"); \
52 return CMD_WARNING; \
53 } \
54 } while (0)
45082064 55
9d0662e0
QY
56DEFUN (grammar_test,
57 grammar_test_cmd,
fa133e00 58 "grammar parse LINE...",
9d0662e0 59 GRAMMAR_STR
fa133e00 60 "parse a command\n"
9d0662e0
QY
61 "command to pass to new parser\n")
62{
d62a17ae 63 check_nodegraph();
64
65 int idx_command = 2;
66 // make a string from tokenized command line
67 char *command = argv_concat(argv, argc, idx_command);
68
69 // create cmd_element for parser
70 struct cmd_element *cmd =
71 XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
72 cmd->string = command;
73 cmd->doc =
74 "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n";
75 cmd->func = NULL;
76
77 // parse the command and install it into the command graph
78 struct graph *graph = graph_new();
9eebf97e 79 struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
d62a17ae 80 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
81
82 cmd_graph_parse(graph, cmd);
83 cmd_graph_merge(nodegraph, graph, +1);
84
85 return CMD_SUCCESS;
9d0662e0
QY
86}
87
eceb1066
QY
88DEFUN (grammar_test_complete,
89 grammar_test_complete_cmd,
fa133e00 90 "grammar complete COMMAND...",
9d0662e0 91 GRAMMAR_STR
eceb1066 92 "attempt to complete input on DFA\n"
fa133e00 93 "command to complete\n")
9d0662e0 94{
d62a17ae 95 check_nodegraph();
96
97 int idx_command = 2;
98 char *cmdstr = argv_concat(argv, argc, idx_command);
99 if (!cmdstr)
100 return CMD_SUCCESS;
101
102 vector command = cmd_make_strvec(cmdstr);
103 if (!command) {
104 XFREE(MTYPE_TMP, cmdstr);
105 return CMD_SUCCESS;
106 }
107
108 // generate completions of user input
109 struct list *completions;
110 enum matcher_rv result =
111 command_complete(nodegraph, command, &completions);
112
113 // print completions or relevant error message
114 if (!MATCHER_ERROR(result)) {
115 vector comps = completions_to_vec(completions);
116 struct cmd_token *tkn;
117
118 // calculate length of longest tkn->text in completions
119 unsigned int width = 0, i = 0;
120 for (i = 0; i < vector_active(comps); i++) {
121 tkn = vector_slot(comps, i);
122 unsigned int len = strlen(tkn->text);
123 width = len > width ? len : width;
124 }
125
126 // print completions
127 for (i = 0; i < vector_active(comps); i++) {
128 tkn = vector_slot(comps, i);
129 vty_out(vty, " %-*s %s\n", width, tkn->text,
130 tkn->desc);
131 }
132
133 for (i = 0; i < vector_active(comps); i++)
134 cmd_token_del(
135 (struct cmd_token *)vector_slot(comps, i));
136 vector_free(comps);
137 } else
138 vty_out(vty, "%% No match\n");
139
140 // free resources
6a154c88 141 list_delete(&completions);
d62a17ae 142 cmd_free_strvec(command);
143 XFREE(MTYPE_TMP, cmdstr);
144
145 return CMD_SUCCESS;
340a2b4a
QY
146}
147
eceb1066
QY
148DEFUN (grammar_test_match,
149 grammar_test_match_cmd,
fa133e00 150 "grammar match COMMAND...",
eceb1066
QY
151 GRAMMAR_STR
152 "attempt to match input on DFA\n"
fa133e00 153 "command to match\n")
eceb1066 154{
d62a17ae 155 check_nodegraph();
156
157 int idx_command = 2;
158 if (argv[2]->arg[0] == '#')
159 return CMD_SUCCESS;
160
161 char *cmdstr = argv_concat(argv, argc, idx_command);
162 if (!cmdstr)
163 return CMD_SUCCESS;
164 vector command = cmd_make_strvec(cmdstr);
165 if (!command) {
166 XFREE(MTYPE_TMP, cmdstr);
167 return CMD_SUCCESS;
168 }
169
170 struct list *argvv = NULL;
171 const struct cmd_element *element = NULL;
172 enum matcher_rv result =
173 command_match(nodegraph, command, &argvv, &element);
174
175 // print completions or relevant error message
176 if (element) {
177 vty_out(vty, "Matched: %s\n", element->string);
178 struct listnode *ln;
179 struct cmd_token *token;
180 for (ALL_LIST_ELEMENTS_RO(argvv, ln, token))
181 vty_out(vty, "%s -- %s\n", token->text, token->arg);
182
183 vty_out(vty, "func: %p\n", element->func);
184
6a154c88 185 list_delete(&argvv);
d62a17ae 186 } else {
187 assert(MATCHER_ERROR(result));
188 switch (result) {
189 case MATCHER_NO_MATCH:
190 vty_out(vty, "%% Unknown command\n");
191 break;
192 case MATCHER_INCOMPLETE:
193 vty_out(vty, "%% Incomplete command\n");
194 break;
195 case MATCHER_AMBIGUOUS:
196 vty_out(vty, "%% Ambiguous command\n");
197 break;
198 default:
199 vty_out(vty, "%% Unknown error\n");
200 break;
201 }
202 }
203
204 // free resources
205 cmd_free_strvec(command);
206 XFREE(MTYPE_TMP, cmdstr);
207
208 return CMD_SUCCESS;
eceb1066
QY
209}
210
1ab84bf3
QY
211/**
212 * Testing shim to test docstrings
213 */
214DEFUN (grammar_test_doc,
215 grammar_test_doc_cmd,
216 "grammar test docstring",
217 GRAMMAR_STR
218 "Test function for docstring\n"
219 "Command end\n")
220{
d62a17ae 221 check_nodegraph();
222
223 // create cmd_element with docstring
224 struct cmd_element *cmd =
225 XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
226 cmd->string = XSTRDUP(
227 MTYPE_CMD_TOKENS,
228 "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG");
229 cmd->doc = XSTRDUP(MTYPE_CMD_TOKENS,
230 "Test stuff\n"
231 "docstring thing\n"
232 "first example\n"
233 "second example\n"
234 "follow\n"
235 "random range\n"
236 "end thingy\n"
237 "variable\n"
238 "optional variable\n"
239 "optional set\n"
240 "optional lol\n"
241 "vararg!\n");
242 cmd->func = NULL;
243
244 // parse element
245 cmd_graph_parse(nodegraph, cmd);
246
247 return CMD_SUCCESS;
1ab84bf3
QY
248}
249
250/**
251 * Debugging command to print command graph
252 */
253DEFUN (grammar_test_show,
254 grammar_test_show_cmd,
fa133e00 255 "grammar show [doc]",
1ab84bf3 256 GRAMMAR_STR
fa133e00
DL
257 "print current accumulated DFA\n"
258 "include docstrings\n")
1ab84bf3 259{
d62a17ae 260 check_nodegraph();
fa133e00 261
09f6d019 262 struct graph_node *stack[CMD_ARGC_MAX];
d62a17ae 263 pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3,
264 stack, 0);
265 return CMD_SUCCESS;
1ab84bf3 266}
51e156b3 267
818a5168
DL
268DEFUN (grammar_test_dot,
269 grammar_test_dot_cmd,
270 "grammar dotfile OUTNAME",
271 GRAMMAR_STR
272 "print current graph for dot\n"
273 ".dot filename\n")
274{
d62a17ae 275 check_nodegraph();
d62a17ae 276 FILE *ofd = fopen(argv[2]->arg, "w");
26fbe472 277
d62a17ae 278 if (!ofd) {
279 vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
280 return CMD_SUCCESS;
281 }
282
26fbe472
QY
283 char *dot = cmd_graph_dump_dot(nodegraph);
284
285 fprintf(ofd, "%s", dot);
d62a17ae 286 fclose(ofd);
26fbe472
QY
287 XFREE(MTYPE_TMP, dot);
288
d62a17ae 289 return CMD_SUCCESS;
818a5168
DL
290}
291
d62a17ae 292struct cmd_permute_item {
293 char *cmd;
294 struct cmd_element *el;
0a22b979
DL
295};
296
d62a17ae 297static void cmd_permute_free(void *arg)
0a22b979 298{
d62a17ae 299 struct cmd_permute_item *i = arg;
300 XFREE(MTYPE_TMP, i->cmd);
301 XFREE(MTYPE_TMP, i);
0a22b979
DL
302}
303
d62a17ae 304static int cmd_permute_cmp(void *a, void *b)
0a22b979 305{
d62a17ae 306 struct cmd_permute_item *aa = a, *bb = b;
307 return strcmp(aa->cmd, bb->cmd);
0a22b979
DL
308}
309
d62a17ae 310static void cmd_graph_permute(struct list *out, struct graph_node **stack,
311 size_t stackpos, char *cmd)
0a22b979 312{
d62a17ae 313 struct graph_node *gn = stack[stackpos];
314 struct cmd_token *tok = gn->data;
315 char *appendp = cmd + strlen(cmd);
c683bd44 316 size_t j;
d62a17ae 317
318 if (tok->type < SPECIAL_TKN) {
319 sprintf(appendp, "%s ", tok->text);
320 appendp += strlen(appendp);
321 } else if (tok->type == END_TKN) {
322 struct cmd_permute_item *i = XMALLOC(MTYPE_TMP, sizeof(*i));
323 i->el = ((struct graph_node *)vector_slot(gn->to, 0))->data;
324 i->cmd = XSTRDUP(MTYPE_TMP, cmd);
325 i->cmd[strlen(cmd) - 1] = '\0';
326 listnode_add_sort(out, i);
327 return;
328 }
329
09f6d019 330 if (++stackpos == CMD_ARGC_MAX)
d62a17ae 331 return;
332
c683bd44 333 for (size_t i = 0; i < vector_active(gn->to); i++) {
d62a17ae 334 struct graph_node *gnext = vector_slot(gn->to, i);
335 for (j = 0; j < stackpos; j++)
336 if (stack[j] == gnext)
337 break;
338 if (j != stackpos)
339 continue;
340
341 stack[stackpos] = gnext;
342 *appendp = '\0';
343 cmd_graph_permute(out, stack, stackpos, cmd);
344 }
0a22b979
DL
345}
346
d62a17ae 347static struct list *cmd_graph_permutations(struct graph *graph)
0a22b979 348{
d62a17ae 349 char accumulate[2048] = "";
09f6d019 350 struct graph_node *stack[CMD_ARGC_MAX];
d62a17ae 351
352 struct list *rv = list_new();
353 rv->cmp = cmd_permute_cmp;
354 rv->del = cmd_permute_free;
355 stack[0] = vector_slot(graph->nodes, 0);
356 cmd_graph_permute(rv, stack, 0, accumulate);
357 return rv;
0a22b979
DL
358}
359
360extern vector cmdvec;
361
362DEFUN (grammar_findambig,
363 grammar_findambig_cmd,
364 "grammar find-ambiguous [{printall|nodescan}]",
365 GRAMMAR_STR
366 "Find ambiguous commands\n"
367 "Print all permutations\n"
368 "Scan all nodes\n")
369{
d62a17ae 370 struct list *commands;
371 struct cmd_permute_item *prev = NULL, *cur = NULL;
372 struct listnode *ln;
373 int i, printall, scan, scannode = 0;
374 int ambig = 0;
375
376 i = 0;
377 printall = argv_find(argv, argc, "printall", &i);
378 i = 0;
379 scan = argv_find(argv, argc, "nodescan", &i);
380
381 if (scan && nodegraph_free) {
382 graph_delete_graph(nodegraph_free);
383 nodegraph_free = NULL;
384 }
385
386 if (!scan && !nodegraph) {
387 vty_out(vty, "nodegraph uninitialized\r\n");
388 return CMD_WARNING_CONFIG_FAILED;
389 }
390
391 do {
392 if (scan) {
393 struct cmd_node *cnode =
394 vector_slot(cmdvec, scannode++);
395 if (!cnode)
396 continue;
0e06eb8b 397 cmd_finalize_node(cnode);
d62a17ae 398 nodegraph = cnode->cmdgraph;
399 if (!nodegraph)
400 continue;
996c9314 401 vty_out(vty, "scanning node %d (%s)\n", scannode - 1,
f4b8291f 402 cnode->name);
d62a17ae 403 }
404
405 commands = cmd_graph_permutations(nodegraph);
406 prev = NULL;
407 for (ALL_LIST_ELEMENTS_RO(commands, ln, cur)) {
408 int same = prev && !strcmp(prev->cmd, cur->cmd);
409 if (printall && !same)
410 vty_out(vty, "'%s' [%x]\n", cur->cmd,
411 cur->el->daemon);
412 if (same) {
413 vty_out(vty, "'%s' AMBIGUOUS:\n", cur->cmd);
414 vty_out(vty, " %s\n '%s'\n", prev->el->name,
415 prev->el->string);
416 vty_out(vty, " %s\n '%s'\n", cur->el->name,
417 cur->el->string);
418 vty_out(vty, "\n");
419 ambig++;
420 }
421 prev = cur;
422 }
6a154c88 423 list_delete(&commands);
d62a17ae 424
425 vty_out(vty, "\n");
426 } while (scan && scannode < LINK_PARAMS_NODE);
427
428 vty_out(vty, "%d ambiguous commands found.\n", ambig);
429
430 if (scan)
431 nodegraph = NULL;
432 return ambig == 0 ? CMD_SUCCESS : CMD_WARNING_CONFIG_FAILED;
0a22b979
DL
433}
434
500b1a5b
QY
435DEFUN (grammar_init_graph,
436 grammar_init_graph_cmd,
fa133e00 437 "grammar init",
500b1a5b
QY
438 GRAMMAR_STR
439 "(re)initialize graph\n")
440{
d62a17ae 441 if (nodegraph_free)
442 graph_delete_graph(nodegraph_free);
443 nodegraph_free = NULL;
af2567b6 444
d62a17ae 445 init_cmdgraph(vty, &nodegraph);
446 return CMD_SUCCESS;
500b1a5b
QY
447}
448
af2567b6
DL
449DEFUN (grammar_access,
450 grammar_access_cmd,
451 "grammar access (0-65535)",
452 GRAMMAR_STR
453 "access node graph\n"
454 "node number\n")
455{
d62a17ae 456 if (nodegraph_free)
457 graph_delete_graph(nodegraph_free);
458 nodegraph_free = NULL;
459
460 struct cmd_node *cnode;
461
462 cnode = vector_slot(cmdvec, atoi(argv[2]->arg));
463 if (!cnode) {
464 vty_out(vty, "%% no such node\n");
465 return CMD_WARNING_CONFIG_FAILED;
466 }
467
468 vty_out(vty, "node %d\n", (int)cnode->node);
0e06eb8b 469 cmd_finalize_node(cnode);
d62a17ae 470 nodegraph = cnode->cmdgraph;
471 return CMD_SUCCESS;
af2567b6
DL
472}
473
1ab84bf3 474/* this is called in vtysh.c to set up the testing shim */
d62a17ae 475void grammar_sandbox_init(void)
476{
477 // install all enable elements
478 install_element(ENABLE_NODE, &grammar_test_cmd);
479 install_element(ENABLE_NODE, &grammar_test_show_cmd);
480 install_element(ENABLE_NODE, &grammar_test_dot_cmd);
481 install_element(ENABLE_NODE, &grammar_test_match_cmd);
482 install_element(ENABLE_NODE, &grammar_test_complete_cmd);
483 install_element(ENABLE_NODE, &grammar_test_doc_cmd);
484 install_element(ENABLE_NODE, &grammar_findambig_cmd);
485 install_element(ENABLE_NODE, &grammar_init_graph_cmd);
486 install_element(ENABLE_NODE, &grammar_access_cmd);
51e156b3 487}
1ab84bf3 488
97f13f08
QY
489/**
490 * Pretty-prints a graph, assuming it is a tree.
491 *
492 * @param start the node to take as the root
493 * @param level indent level for recursive calls, always pass 0
494 */
eb51bb9b
DL
495static void pretty_print_graph(struct vty *vty, struct graph_node *start,
496 int level, int desc, struct graph_node **stack,
497 size_t stackpos)
1ab84bf3 498{
d62a17ae 499 // print this node
500 char tokennum[32];
501 struct cmd_token *tok = start->data;
502
503 snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
504 vty_out(vty, "%s", lookup_msg(tokennames, tok->type, NULL));
505 if (tok->text)
506 vty_out(vty, ":\"%s\"", tok->text);
507 if (tok->varname)
508 vty_out(vty, " => %s", tok->varname);
509 if (desc)
510 vty_out(vty, " ?'%s'", tok->desc);
511 vty_out(vty, " ");
512
09f6d019 513 if (stackpos == CMD_ARGC_MAX) {
d62a17ae 514 vty_out(vty, " -aborting! (depth limit)\n");
515 return;
516 }
517 stack[stackpos++] = start;
518
519 int numto = desc ? 2 : vector_active(start->to);
520 if (numto) {
521 if (numto > 1)
522 vty_out(vty, "\n");
523 for (unsigned int i = 0; i < vector_active(start->to); i++) {
524 struct graph_node *adj = vector_slot(start->to, i);
525 // if we're listing multiple children, indent!
526 if (numto > 1)
527 for (int j = 0; j < level + 1; j++)
528 vty_out(vty, " ");
529 // if this node is a vararg, just print *
530 if (adj == start)
531 vty_out(vty, "*");
532 else if (((struct cmd_token *)adj->data)->type
533 == END_TKN)
534 vty_out(vty, "--END\n");
535 else {
536 size_t k;
537 for (k = 0; k < stackpos; k++)
538 if (stack[k] == adj) {
539 vty_out(vty, "<<loop@%zu \n",
540 k);
541 break;
542 }
543 if (k == stackpos)
544 pretty_print_graph(
545 vty, adj,
546 numto > 1 ? level + 1 : level,
547 desc, stack, stackpos);
548 }
549 }
550 } else
551 vty_out(vty, "\n");
1eb5e8dc
QY
552}
553
554/** stuff that should go in command.c + command.h */
eb51bb9b 555static void init_cmdgraph(struct vty *vty, struct graph **graph)
97c45dae 556{
d62a17ae 557 // initialize graph, add start noe
558 *graph = graph_new();
559 nodegraph_free = *graph;
560 struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
561 graph_new_node(*graph, token, (void (*)(void *)) & cmd_token_del);
562 if (vty)
563 vty_out(vty, "initialized graph\n");
97c45dae 564}