]> git.proxmox.com Git - mirror_frr.git/blob - lib/command.c
Merge pull request #8471 from idryzhov/cleanup-num-named-lists
[mirror_frr.git] / lib / command.c
1 /*
2 * CLI backend interface.
3 *
4 * --
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
9 *
10 * This file is part of GNU Zebra.
11 *
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
15 * later version.
16 *
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27 #include <zebra.h>
28 #include <lib/version.h>
29
30 #include "command.h"
31 #include "frrstr.h"
32 #include "memory.h"
33 #include "log.h"
34 #include "log_vty.h"
35 #include "thread.h"
36 #include "vector.h"
37 #include "linklist.h"
38 #include "vty.h"
39 #include "workqueue.h"
40 #include "vrf.h"
41 #include "command_match.h"
42 #include "command_graph.h"
43 #include "qobj.h"
44 #include "defaults.h"
45 #include "libfrr.h"
46 #include "jhash.h"
47 #include "hook.h"
48 #include "lib_errors.h"
49 #include "northbound_cli.h"
50 #include "network.h"
51
52 #include "frrscript.h"
53
54 DEFINE_MTYPE_STATIC(LIB, HOST, "Host config");
55 DEFINE_MTYPE(LIB, COMPLETION, "Completion item");
56
57 #define item(x) \
58 { \
59 x, #x \
60 }
61
62 /* clang-format off */
63 const struct message tokennames[] = {
64 item(WORD_TKN),
65 item(VARIABLE_TKN),
66 item(RANGE_TKN),
67 item(IPV4_TKN),
68 item(IPV4_PREFIX_TKN),
69 item(IPV6_TKN),
70 item(IPV6_PREFIX_TKN),
71 item(MAC_TKN),
72 item(MAC_PREFIX_TKN),
73 item(FORK_TKN),
74 item(JOIN_TKN),
75 item(START_TKN),
76 item(END_TKN),
77 {0},
78 };
79 /* clang-format on */
80
81 /* Command vector which includes some level of command lists. Normally
82 each daemon maintains each own cmdvec. */
83 vector cmdvec = NULL;
84
85 /* Host information structure. */
86 struct host host;
87
88 /*
89 * Returns host.name if any, otherwise
90 * it returns the system hostname.
91 */
92 const char *cmd_hostname_get(void)
93 {
94 return host.name;
95 }
96
97 /*
98 * Returns unix domainname
99 */
100 const char *cmd_domainname_get(void)
101 {
102 return host.domainname;
103 }
104
105 static int root_on_exit(struct vty *vty);
106
107 /* Standard command node structures. */
108 static struct cmd_node auth_node = {
109 .name = "auth",
110 .node = AUTH_NODE,
111 .prompt = "Password: ",
112 };
113
114 static struct cmd_node view_node = {
115 .name = "view",
116 .node = VIEW_NODE,
117 .prompt = "%s> ",
118 .node_exit = root_on_exit,
119 };
120
121 static struct cmd_node auth_enable_node = {
122 .name = "auth enable",
123 .node = AUTH_ENABLE_NODE,
124 .prompt = "Password: ",
125 };
126
127 static struct cmd_node enable_node = {
128 .name = "enable",
129 .node = ENABLE_NODE,
130 .prompt = "%s# ",
131 .node_exit = root_on_exit,
132 };
133
134 static int config_write_host(struct vty *vty);
135 static struct cmd_node config_node = {
136 .name = "config",
137 .node = CONFIG_NODE,
138 .parent_node = ENABLE_NODE,
139 .prompt = "%s(config)# ",
140 .config_write = config_write_host,
141 .node_exit = vty_config_node_exit,
142 };
143
144 static bool vty_check_node_for_xpath_decrement(enum node_type target_node,
145 enum node_type node)
146 {
147 /* bgp afi-safi (`address-family <afi> <safi>`) node
148 * does not increment xpath_index.
149 * In order to use (`router bgp`) BGP_NODE's xpath as a base,
150 * retain xpath_index as 1 upon exiting from
151 * afi-safi node.
152 */
153
154 if (target_node == BGP_NODE
155 && (node == BGP_IPV4_NODE || node == BGP_IPV6_NODE
156 || node == BGP_IPV4M_NODE || node == BGP_IPV6M_NODE
157 || node == BGP_VPNV4_NODE || node == BGP_VPNV6_NODE
158 || node == BGP_EVPN_NODE || node == BGP_IPV4L_NODE
159 || node == BGP_IPV6L_NODE || node == BGP_FLOWSPECV4_NODE
160 || node == BGP_FLOWSPECV6_NODE))
161 return false;
162
163 return true;
164 }
165
166 /* This is called from main when a daemon is invoked with -v or --version. */
167 void print_version(const char *progname)
168 {
169 printf("%s version %s\n", progname, FRR_VERSION);
170 printf("%s\n", FRR_COPYRIGHT);
171 #ifdef ENABLE_VERSION_BUILD_CONFIG
172 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
173 #endif
174 }
175
176 char *argv_concat(struct cmd_token **argv, int argc, int shift)
177 {
178 int cnt = MAX(argc - shift, 0);
179 const char *argstr[cnt + 1];
180
181 if (!cnt)
182 return NULL;
183
184 for (int i = 0; i < cnt; i++)
185 argstr[i] = argv[i + shift]->arg;
186
187 return frrstr_join(argstr, cnt, " ");
188 }
189
190 vector cmd_make_strvec(const char *string)
191 {
192 if (!string)
193 return NULL;
194
195 const char *copy = string;
196
197 /* skip leading whitespace */
198 while (isspace((unsigned char)*copy) && *copy != '\0')
199 copy++;
200
201 /* if the entire string was whitespace or a comment, return */
202 if (*copy == '\0' || *copy == '!' || *copy == '#')
203 return NULL;
204
205 vector result = frrstr_split_vec(copy, "\n\r\t ");
206
207 for (unsigned int i = 0; i < vector_active(result); i++) {
208 if (strlen(vector_slot(result, i)) == 0) {
209 XFREE(MTYPE_TMP, vector_slot(result, i));
210 vector_unset(result, i);
211 }
212 }
213
214 vector_compact(result);
215
216 return result;
217 }
218
219 void cmd_free_strvec(vector v)
220 {
221 frrstr_strvec_free(v);
222 }
223
224 /**
225 * Convenience function for accessing argv data.
226 *
227 * @param argc
228 * @param argv
229 * @param text definition snippet of the desired token
230 * @param index the starting index, and where to store the
231 * index of the found token if it exists
232 * @return 1 if found, 0 otherwise
233 */
234 int argv_find(struct cmd_token **argv, int argc, const char *text, int *index)
235 {
236 int found = 0;
237 for (int i = *index; i < argc && found == 0; i++)
238 if ((found = strmatch(text, argv[i]->text)))
239 *index = i;
240 return found;
241 }
242
243 static unsigned int cmd_hash_key(const void *p)
244 {
245 int size = sizeof(p);
246
247 return jhash(p, size, 0);
248 }
249
250 static bool cmd_hash_cmp(const void *a, const void *b)
251 {
252 return a == b;
253 }
254
255 /* Install top node of command vector. */
256 void install_node(struct cmd_node *node)
257 {
258 vector_set_index(cmdvec, node->node, node);
259 node->cmdgraph = graph_new();
260 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
261 // add start node
262 struct cmd_token *token =
263 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
264 graph_new_node(node->cmdgraph, token,
265 (void (*)(void *)) & cmd_token_del);
266 node->cmd_hash = hash_create_size(16, cmd_hash_key, cmd_hash_cmp,
267 "Command Hash");
268 }
269
270 /* Return prompt character of specified node. */
271 const char *cmd_prompt(enum node_type node)
272 {
273 struct cmd_node *cnode;
274
275 cnode = vector_slot(cmdvec, node);
276 return cnode->prompt;
277 }
278
279 /* Install a command into a node. */
280 void _install_element(enum node_type ntype, const struct cmd_element *cmd)
281 {
282 struct cmd_node *cnode;
283
284 /* cmd_init hasn't been called */
285 if (!cmdvec) {
286 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
287 __func__);
288 return;
289 }
290
291 cnode = vector_lookup(cmdvec, ntype);
292
293 if (cnode == NULL) {
294 fprintf(stderr,
295 "%s[%s]:\n"
296 "\tnode %d does not exist.\n"
297 "\tplease call install_node() before install_element()\n",
298 cmd->name, cmd->string, ntype);
299 exit(EXIT_FAILURE);
300 }
301
302 if (hash_lookup(cnode->cmd_hash, (void *)cmd) != NULL) {
303 fprintf(stderr,
304 "%s[%s]:\n"
305 "\tnode %d (%s) already has this command installed.\n"
306 "\tduplicate install_element call?\n",
307 cmd->name, cmd->string, ntype, cnode->name);
308 return;
309 }
310
311 assert(hash_get(cnode->cmd_hash, (void *)cmd, hash_alloc_intern));
312
313 struct graph *graph = graph_new();
314 struct cmd_token *token =
315 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
316 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
317
318 cmd_graph_parse(graph, cmd);
319 cmd_graph_names(graph);
320 cmd_graph_merge(cnode->cmdgraph, graph, +1);
321 graph_delete_graph(graph);
322
323 vector_set(cnode->cmd_vector, (void *)cmd);
324
325 if (ntype == VIEW_NODE)
326 _install_element(ENABLE_NODE, cmd);
327 }
328
329 void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
330 {
331 struct cmd_node *cnode;
332
333 /* cmd_init hasn't been called */
334 if (!cmdvec) {
335 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
336 __func__);
337 return;
338 }
339
340 cnode = vector_lookup(cmdvec, ntype);
341
342 if (cnode == NULL) {
343 fprintf(stderr,
344 "%s[%s]:\n"
345 "\tnode %d does not exist.\n"
346 "\tplease call install_node() before uninstall_element()\n",
347 cmd->name, cmd->string, ntype);
348 exit(EXIT_FAILURE);
349 }
350
351 if (hash_release(cnode->cmd_hash, (void *)cmd) == NULL) {
352 fprintf(stderr,
353 "%s[%s]:\n"
354 "\tnode %d (%s) does not have this command installed.\n"
355 "\tduplicate uninstall_element call?\n",
356 cmd->name, cmd->string, ntype, cnode->name);
357 return;
358 }
359
360 vector_unset_value(cnode->cmd_vector, (void *)cmd);
361
362 struct graph *graph = graph_new();
363 struct cmd_token *token =
364 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
365 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
366
367 cmd_graph_parse(graph, cmd);
368 cmd_graph_names(graph);
369 cmd_graph_merge(cnode->cmdgraph, graph, -1);
370 graph_delete_graph(graph);
371
372 if (ntype == VIEW_NODE)
373 uninstall_element(ENABLE_NODE, cmd);
374 }
375
376
377 static const unsigned char itoa64[] =
378 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
379
380 static void to64(char *s, long v, int n)
381 {
382 while (--n >= 0) {
383 *s++ = itoa64[v & 0x3f];
384 v >>= 6;
385 }
386 }
387
388 static char *zencrypt(const char *passwd)
389 {
390 char salt[6];
391 struct timeval tv;
392 char *crypt(const char *, const char *);
393
394 gettimeofday(&tv, 0);
395
396 to64(&salt[0], frr_weak_random(), 3);
397 to64(&salt[3], tv.tv_usec, 3);
398 salt[5] = '\0';
399
400 return crypt(passwd, salt);
401 }
402
403 static bool full_cli;
404
405 /* This function write configuration of this host. */
406 static int config_write_host(struct vty *vty)
407 {
408 if (cmd_hostname_get())
409 vty_out(vty, "hostname %s\n", cmd_hostname_get());
410
411 if (cmd_domainname_get())
412 vty_out(vty, "domainname %s\n", cmd_domainname_get());
413
414 /* The following are all configuration commands that are not sent to
415 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
416 * we would always display 'log syslog informational' in the config
417 * which would cause other daemons to then switch to syslog when they
418 * parse frr.conf.
419 */
420 if (full_cli) {
421 if (host.encrypt) {
422 if (host.password_encrypt)
423 vty_out(vty, "password 8 %s\n",
424 host.password_encrypt);
425 if (host.enable_encrypt)
426 vty_out(vty, "enable password 8 %s\n",
427 host.enable_encrypt);
428 } else {
429 if (host.password)
430 vty_out(vty, "password %s\n", host.password);
431 if (host.enable)
432 vty_out(vty, "enable password %s\n",
433 host.enable);
434 }
435 log_config_write(vty);
436
437 if (host.advanced)
438 vty_out(vty, "service advanced-vty\n");
439
440 if (host.encrypt)
441 vty_out(vty, "service password-encryption\n");
442
443 if (host.lines >= 0)
444 vty_out(vty, "service terminal-length %d\n",
445 host.lines);
446
447 if (host.motdfile)
448 vty_out(vty, "banner motd file %s\n", host.motdfile);
449 else if (host.motd
450 && strncmp(host.motd, FRR_DEFAULT_MOTD,
451 strlen(host.motd)))
452 vty_out(vty, "banner motd line %s\n", host.motd);
453 else if (!host.motd)
454 vty_out(vty, "no banner motd\n");
455 }
456
457 if (debug_memstats_at_exit)
458 vty_out(vty, "!\ndebug memstats-at-exit\n");
459
460 return 1;
461 }
462
463 /* Utility function for getting command graph. */
464 static struct graph *cmd_node_graph(vector v, enum node_type ntype)
465 {
466 struct cmd_node *cnode = vector_slot(v, ntype);
467 return cnode->cmdgraph;
468 }
469
470 static int cmd_try_do_shortcut(enum node_type node, char *first_word)
471 {
472 if (first_word != NULL && node != AUTH_NODE && node != VIEW_NODE
473 && node != AUTH_ENABLE_NODE && 0 == strcmp("do", first_word))
474 return 1;
475 return 0;
476 }
477
478 /**
479 * Compare function for cmd_token.
480 * Used with qsort to sort command completions.
481 */
482 static int compare_completions(const void *fst, const void *snd)
483 {
484 const struct cmd_token *first = *(const struct cmd_token * const *)fst,
485 *secnd = *(const struct cmd_token * const *)snd;
486 return strcmp(first->text, secnd->text);
487 }
488
489 /**
490 * Takes a list of completions returned by command_complete,
491 * dedeuplicates them based on both text and description,
492 * sorts them, and returns them as a vector.
493 *
494 * @param completions linked list of cmd_token
495 * @return deduplicated and sorted vector with
496 */
497 vector completions_to_vec(struct list *completions)
498 {
499 vector comps = vector_init(VECTOR_MIN_SIZE);
500
501 struct listnode *ln;
502 struct cmd_token *token, *cr = NULL;
503 unsigned int i, exists;
504 for (ALL_LIST_ELEMENTS_RO(completions, ln, token)) {
505 if (token->type == END_TKN && (cr = token))
506 continue;
507
508 // linear search for token in completions vector
509 exists = 0;
510 for (i = 0; i < vector_active(comps) && !exists; i++) {
511 struct cmd_token *curr = vector_slot(comps, i);
512 #ifdef VTYSH_DEBUG
513 exists = !strcmp(curr->text, token->text)
514 && !strcmp(curr->desc, token->desc);
515 #else
516 exists = !strcmp(curr->text, token->text);
517 #endif /* VTYSH_DEBUG */
518 }
519
520 if (!exists)
521 vector_set(comps, token);
522 }
523
524 // sort completions
525 qsort(comps->index, vector_active(comps), sizeof(void *),
526 &compare_completions);
527
528 // make <cr> the first element, if it is present
529 if (cr) {
530 vector_set_index(comps, vector_active(comps), NULL);
531 memmove(comps->index + 1, comps->index,
532 (comps->alloced - 1) * sizeof(void *));
533 vector_set_index(comps, 0, cr);
534 }
535
536 return comps;
537 }
538 /**
539 * Generates a vector of cmd_token representing possible completions
540 * on the current input.
541 *
542 * @param vline the vectorized input line
543 * @param vty the vty with the node to match on
544 * @param status pointer to matcher status code
545 * @return vector of struct cmd_token * with possible completions
546 */
547 static vector cmd_complete_command_real(vector vline, struct vty *vty,
548 int *status)
549 {
550 struct list *completions;
551 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
552
553 enum matcher_rv rv = command_complete(cmdgraph, vline, &completions);
554
555 if (MATCHER_ERROR(rv)) {
556 *status = CMD_ERR_NO_MATCH;
557 return NULL;
558 }
559
560 vector comps = completions_to_vec(completions);
561 list_delete(&completions);
562
563 // set status code appropriately
564 switch (vector_active(comps)) {
565 case 0:
566 *status = CMD_ERR_NO_MATCH;
567 break;
568 case 1:
569 *status = CMD_COMPLETE_FULL_MATCH;
570 break;
571 default:
572 *status = CMD_COMPLETE_LIST_MATCH;
573 }
574
575 return comps;
576 }
577
578 vector cmd_describe_command(vector vline, struct vty *vty, int *status)
579 {
580 vector ret;
581
582 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
583 enum node_type onode;
584 int orig_xpath_index;
585 vector shifted_vline;
586 unsigned int index;
587
588 onode = vty->node;
589 orig_xpath_index = vty->xpath_index;
590 vty->node = ENABLE_NODE;
591 vty->xpath_index = 0;
592 /* We can try it on enable node, cos' the vty is authenticated
593 */
594
595 shifted_vline = vector_init(vector_count(vline));
596 /* use memcpy? */
597 for (index = 1; index < vector_active(vline); index++) {
598 vector_set_index(shifted_vline, index - 1,
599 vector_lookup(vline, index));
600 }
601
602 ret = cmd_complete_command_real(shifted_vline, vty, status);
603
604 vector_free(shifted_vline);
605 vty->node = onode;
606 vty->xpath_index = orig_xpath_index;
607 return ret;
608 }
609
610 return cmd_complete_command_real(vline, vty, status);
611 }
612
613 static struct list *varhandlers = NULL;
614
615 void cmd_variable_complete(struct cmd_token *token, const char *arg,
616 vector comps)
617 {
618 struct listnode *ln;
619 const struct cmd_variable_handler *cvh;
620 size_t i, argsz;
621 vector tmpcomps;
622
623 tmpcomps = arg ? vector_init(VECTOR_MIN_SIZE) : comps;
624
625 for (ALL_LIST_ELEMENTS_RO(varhandlers, ln, cvh)) {
626 if (cvh->tokenname && strcmp(cvh->tokenname, token->text))
627 continue;
628 if (cvh->varname && (!token->varname
629 || strcmp(cvh->varname, token->varname)))
630 continue;
631 cvh->completions(tmpcomps, token);
632 break;
633 }
634
635 if (!arg)
636 return;
637
638 argsz = strlen(arg);
639 for (i = vector_active(tmpcomps); i; i--) {
640 char *item = vector_slot(tmpcomps, i - 1);
641 if (strlen(item) >= argsz && !strncmp(item, arg, argsz))
642 vector_set(comps, item);
643 else
644 XFREE(MTYPE_COMPLETION, item);
645 }
646 vector_free(tmpcomps);
647 }
648
649 #define AUTOCOMP_INDENT 5
650
651 char *cmd_variable_comp2str(vector comps, unsigned short cols)
652 {
653 size_t bsz = 16;
654 char *buf = XCALLOC(MTYPE_TMP, bsz);
655 int lc = AUTOCOMP_INDENT;
656 size_t cs = AUTOCOMP_INDENT;
657 size_t itemlen;
658 snprintf(buf, bsz, "%*s", AUTOCOMP_INDENT, "");
659 for (size_t j = 0; j < vector_active(comps); j++) {
660 char *item = vector_slot(comps, j);
661 itemlen = strlen(item);
662
663 if (cs + itemlen + AUTOCOMP_INDENT + 3 >= bsz)
664 buf = XREALLOC(MTYPE_TMP, buf, (bsz *= 2));
665
666 if (lc + itemlen + 1 >= cols) {
667 cs += snprintf(&buf[cs], bsz - cs, "\n%*s",
668 AUTOCOMP_INDENT, "");
669 lc = AUTOCOMP_INDENT;
670 }
671
672 size_t written = snprintf(&buf[cs], bsz - cs, "%s ", item);
673 lc += written;
674 cs += written;
675 XFREE(MTYPE_COMPLETION, item);
676 vector_set_index(comps, j, NULL);
677 }
678 return buf;
679 }
680
681 void cmd_variable_handler_register(const struct cmd_variable_handler *cvh)
682 {
683 if (!varhandlers)
684 return;
685
686 for (; cvh->completions; cvh++)
687 listnode_add(varhandlers, (void *)cvh);
688 }
689
690 DEFUN_HIDDEN (autocomplete,
691 autocomplete_cmd,
692 "autocomplete TYPE TEXT VARNAME",
693 "Autocompletion handler (internal, for vtysh)\n"
694 "cmd_token->type\n"
695 "cmd_token->text\n"
696 "cmd_token->varname\n")
697 {
698 struct cmd_token tok;
699 vector comps = vector_init(32);
700 size_t i;
701
702 memset(&tok, 0, sizeof(tok));
703 tok.type = atoi(argv[1]->arg);
704 tok.text = argv[2]->arg;
705 tok.varname = argv[3]->arg;
706 if (!strcmp(tok.varname, "-"))
707 tok.varname = NULL;
708
709 cmd_variable_complete(&tok, NULL, comps);
710
711 for (i = 0; i < vector_active(comps); i++) {
712 char *text = vector_slot(comps, i);
713 vty_out(vty, "%s\n", text);
714 XFREE(MTYPE_COMPLETION, text);
715 }
716
717 vector_free(comps);
718 return CMD_SUCCESS;
719 }
720
721 /**
722 * Generate possible tab-completions for the given input. This function only
723 * returns results that would result in a valid command if used as Readline
724 * completions (as is the case in vtysh). For instance, if the passed vline ends
725 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
726 *
727 * @param vline vectorized input line
728 * @param vty the vty
729 * @param status location to store matcher status code in
730 * @return set of valid strings for use with Readline as tab-completions.
731 */
732
733 char **cmd_complete_command(vector vline, struct vty *vty, int *status)
734 {
735 char **ret = NULL;
736 int original_node = vty->node;
737 vector input_line = vector_init(vector_count(vline));
738
739 // if the first token is 'do' we'll want to execute the command in the
740 // enable node
741 int do_shortcut = cmd_try_do_shortcut(vty->node, vector_slot(vline, 0));
742 vty->node = do_shortcut ? ENABLE_NODE : original_node;
743
744 // construct the input line we'll be matching on
745 unsigned int offset = (do_shortcut) ? 1 : 0;
746 for (unsigned index = 0; index + offset < vector_active(vline); index++)
747 vector_set_index(input_line, index,
748 vector_lookup(vline, index + offset));
749
750 // get token completions -- this is a copying operation
751 vector comps = NULL, initial_comps;
752 initial_comps = cmd_complete_command_real(input_line, vty, status);
753
754 if (!MATCHER_ERROR(*status)) {
755 assert(initial_comps);
756 // filter out everything that is not suitable for a
757 // tab-completion
758 comps = vector_init(VECTOR_MIN_SIZE);
759 for (unsigned int i = 0; i < vector_active(initial_comps);
760 i++) {
761 struct cmd_token *token = vector_slot(initial_comps, i);
762 if (token->type == WORD_TKN)
763 vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
764 token->text));
765 else if (IS_VARYING_TOKEN(token->type)) {
766 const char *ref = vector_lookup(
767 vline, vector_active(vline) - 1);
768 cmd_variable_complete(token, ref, comps);
769 }
770 }
771 vector_free(initial_comps);
772
773 // since we filtered results, we need to re-set status code
774 switch (vector_active(comps)) {
775 case 0:
776 *status = CMD_ERR_NO_MATCH;
777 break;
778 case 1:
779 *status = CMD_COMPLETE_FULL_MATCH;
780 break;
781 default:
782 *status = CMD_COMPLETE_LIST_MATCH;
783 }
784
785 // copy completions text into an array of char*
786 ret = XMALLOC(MTYPE_TMP,
787 (vector_active(comps) + 1) * sizeof(char *));
788 unsigned int i;
789 for (i = 0; i < vector_active(comps); i++) {
790 ret[i] = vector_slot(comps, i);
791 }
792 // set the last element to NULL, because this array is used in
793 // a Readline completion_generator function which expects NULL
794 // as a sentinel value
795 ret[i] = NULL;
796 vector_free(comps);
797 comps = NULL;
798 } else if (initial_comps)
799 vector_free(initial_comps);
800
801 // comps should always be null here
802 assert(!comps);
803
804 // free the adjusted input line
805 vector_free(input_line);
806
807 // reset vty->node to its original value
808 vty->node = original_node;
809
810 return ret;
811 }
812
813 /* return parent node */
814 /* MUST eventually converge on CONFIG_NODE */
815 enum node_type node_parent(enum node_type node)
816 {
817 enum node_type ret;
818
819 assert(node > CONFIG_NODE);
820
821 switch (node) {
822 case BGP_VPNV4_NODE:
823 case BGP_VPNV6_NODE:
824 case BGP_FLOWSPECV4_NODE:
825 case BGP_FLOWSPECV6_NODE:
826 case BGP_VRF_POLICY_NODE:
827 case BGP_VNC_DEFAULTS_NODE:
828 case BGP_VNC_NVE_GROUP_NODE:
829 case BGP_VNC_L2_GROUP_NODE:
830 case BGP_IPV4_NODE:
831 case BGP_IPV4M_NODE:
832 case BGP_IPV4L_NODE:
833 case BGP_IPV6_NODE:
834 case BGP_IPV6M_NODE:
835 case BGP_EVPN_NODE:
836 case BGP_IPV6L_NODE:
837 case BMP_NODE:
838 ret = BGP_NODE;
839 break;
840 case BGP_EVPN_VNI_NODE:
841 ret = BGP_EVPN_NODE;
842 break;
843 case KEYCHAIN_KEY_NODE:
844 ret = KEYCHAIN_NODE;
845 break;
846 case LINK_PARAMS_NODE:
847 ret = INTERFACE_NODE;
848 break;
849 case LDP_IPV4_NODE:
850 case LDP_IPV6_NODE:
851 ret = LDP_NODE;
852 break;
853 case LDP_IPV4_IFACE_NODE:
854 ret = LDP_IPV4_NODE;
855 break;
856 case LDP_IPV6_IFACE_NODE:
857 ret = LDP_IPV6_NODE;
858 break;
859 case LDP_PSEUDOWIRE_NODE:
860 ret = LDP_L2VPN_NODE;
861 break;
862 case BFD_PEER_NODE:
863 ret = BFD_NODE;
864 break;
865 case BFD_PROFILE_NODE:
866 ret = BFD_NODE;
867 break;
868 case SR_TRAFFIC_ENG_NODE:
869 ret = SEGMENT_ROUTING_NODE;
870 break;
871 case SR_SEGMENT_LIST_NODE:
872 ret = SR_TRAFFIC_ENG_NODE;
873 break;
874 case SR_POLICY_NODE:
875 ret = SR_TRAFFIC_ENG_NODE;
876 break;
877 case SR_CANDIDATE_DYN_NODE:
878 ret = SR_POLICY_NODE;
879 break;
880 case PCEP_NODE:
881 ret = SR_TRAFFIC_ENG_NODE;
882 break;
883 case PCEP_PCE_CONFIG_NODE:
884 ret = PCEP_NODE;
885 break;
886 case PCEP_PCE_NODE:
887 ret = PCEP_NODE;
888 break;
889 case PCEP_PCC_NODE:
890 ret = PCEP_NODE;
891 break;
892 default:
893 ret = CONFIG_NODE;
894 break;
895 }
896
897 return ret;
898 }
899
900 /* Execute command by argument vline vector. */
901 static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter,
902 struct vty *vty,
903 const struct cmd_element **cmd,
904 unsigned int up_level)
905 {
906 struct list *argv_list;
907 enum matcher_rv status;
908 const struct cmd_element *matched_element = NULL;
909 unsigned int i;
910 int xpath_index = vty->xpath_index;
911 int node = vty->node;
912
913 /* only happens for legacy split config file load; need to check for
914 * a match before calling node_exit handlers below
915 */
916 for (i = 0; i < up_level; i++) {
917 if (node <= CONFIG_NODE)
918 return CMD_NO_LEVEL_UP;
919
920 node = node_parent(node);
921
922 if (xpath_index > 0
923 && vty_check_node_for_xpath_decrement(node, vty->node))
924 xpath_index--;
925 }
926
927 struct graph *cmdgraph = cmd_node_graph(cmdvec, node);
928 status = command_match(cmdgraph, vline, &argv_list, &matched_element);
929
930 if (cmd)
931 *cmd = matched_element;
932
933 // if matcher error, return corresponding CMD_ERR
934 if (MATCHER_ERROR(status)) {
935 if (argv_list)
936 list_delete(&argv_list);
937 switch (status) {
938 case MATCHER_INCOMPLETE:
939 return CMD_ERR_INCOMPLETE;
940 case MATCHER_AMBIGUOUS:
941 return CMD_ERR_AMBIGUOUS;
942 default:
943 return CMD_ERR_NO_MATCH;
944 }
945 }
946
947 for (i = 0; i < up_level; i++)
948 cmd_exit(vty);
949
950 // build argv array from argv list
951 struct cmd_token **argv = XMALLOC(
952 MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *));
953 struct listnode *ln;
954 struct cmd_token *token;
955
956 i = 0;
957 for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token))
958 argv[i++] = token;
959
960 int argc = argv_list->count;
961
962 int ret;
963 if (matched_element->daemon)
964 ret = CMD_SUCCESS_DAEMON;
965 else {
966 if (vty->config) {
967 /* Clear array of enqueued configuration changes. */
968 vty->num_cfg_changes = 0;
969 memset(&vty->cfg_changes, 0, sizeof(vty->cfg_changes));
970
971 /* Regenerate candidate configuration if necessary. */
972 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
973 && running_config->version
974 > vty->candidate_config->version)
975 nb_config_replace(vty->candidate_config,
976 running_config, true);
977
978 /*
979 * Perform pending commit (if any) before executing
980 * non-YANG command.
981 */
982 if (matched_element->attr != CMD_ATTR_YANG)
983 nb_cli_pending_commit_check(vty);
984 }
985
986 ret = matched_element->func(matched_element, vty, argc, argv);
987 }
988
989 // delete list and cmd_token's in it
990 list_delete(&argv_list);
991 XFREE(MTYPE_TMP, argv);
992
993 return ret;
994 }
995
996 /**
997 * Execute a given command, handling things like "do ..." and checking
998 * whether the given command might apply at a parent node if doesn't
999 * apply for the current node.
1000 *
1001 * @param vline Command line input, vector of char* where each element is
1002 * one input token.
1003 * @param vty The vty context in which the command should be executed.
1004 * @param cmd Pointer where the struct cmd_element of the matched command
1005 * will be stored, if any. May be set to NULL if this info is
1006 * not needed.
1007 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1008 * @return The status of the command that has been executed or an error code
1009 * as to why no command could be executed.
1010 */
1011 int cmd_execute_command(vector vline, struct vty *vty,
1012 const struct cmd_element **cmd, int vtysh)
1013 {
1014 int ret, saved_ret = 0;
1015 enum node_type onode, try_node;
1016 int orig_xpath_index;
1017
1018 onode = try_node = vty->node;
1019 orig_xpath_index = vty->xpath_index;
1020
1021 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1022 vector shifted_vline;
1023 unsigned int index;
1024
1025 vty->node = ENABLE_NODE;
1026 vty->xpath_index = 0;
1027 /* We can try it on enable node, cos' the vty is authenticated
1028 */
1029
1030 shifted_vline = vector_init(vector_count(vline));
1031 /* use memcpy? */
1032 for (index = 1; index < vector_active(vline); index++)
1033 vector_set_index(shifted_vline, index - 1,
1034 vector_lookup(vline, index));
1035
1036 ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
1037 vty, cmd, 0);
1038
1039 vector_free(shifted_vline);
1040 vty->node = onode;
1041 vty->xpath_index = orig_xpath_index;
1042 return ret;
1043 }
1044
1045 saved_ret = ret =
1046 cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd, 0);
1047
1048 if (vtysh)
1049 return saved_ret;
1050
1051 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1052 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
1053 /* This assumes all nodes above CONFIG_NODE are childs of
1054 * CONFIG_NODE */
1055 while (vty->node > CONFIG_NODE) {
1056 try_node = node_parent(try_node);
1057 vty->node = try_node;
1058 if (vty->xpath_index > 0
1059 && vty_check_node_for_xpath_decrement(try_node,
1060 onode))
1061 vty->xpath_index--;
1062 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
1063 vty, cmd, 0);
1064 if (ret == CMD_SUCCESS || ret == CMD_WARNING
1065 || ret == CMD_NOT_MY_INSTANCE
1066 || ret == CMD_WARNING_CONFIG_FAILED)
1067 return ret;
1068 }
1069 /* no command succeeded, reset the vty to the original node */
1070 vty->node = onode;
1071 vty->xpath_index = orig_xpath_index;
1072 }
1073
1074 /* return command status for original node */
1075 return saved_ret;
1076 }
1077
1078 /**
1079 * Execute a given command, matching it strictly against the current node.
1080 * This mode is used when reading config files.
1081 *
1082 * @param vline Command line input, vector of char* where each element is
1083 * one input token.
1084 * @param vty The vty context in which the command should be executed.
1085 * @param cmd Pointer where the struct cmd_element* of the matched command
1086 * will be stored, if any. May be set to NULL if this info is
1087 * not needed.
1088 * @return The status of the command that has been executed or an error code
1089 * as to why no command could be executed.
1090 */
1091 int cmd_execute_command_strict(vector vline, struct vty *vty,
1092 const struct cmd_element **cmd)
1093 {
1094 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, 0);
1095 }
1096
1097 /*
1098 * Hook for preprocessing command string before executing.
1099 *
1100 * All subscribers are called with the raw command string that is to be
1101 * executed. If any changes are to be made, a new string should be allocated
1102 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1103 * is then responsible for freeing this string.
1104 *
1105 * All processing functions must be mutually exclusive in their action, i.e. if
1106 * one subscriber decides to modify the command, all others must not modify it
1107 * when called. Feeding the output of one processing command into a subsequent
1108 * one is not supported.
1109 *
1110 * This hook is intentionally internal to the command processing system.
1111 *
1112 * cmd_in
1113 * The raw command string.
1114 *
1115 * cmd_out
1116 * The result of any processing.
1117 */
1118 DECLARE_HOOK(cmd_execute,
1119 (struct vty *vty, const char *cmd_in, char **cmd_out),
1120 (vty, cmd_in, cmd_out));
1121 DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
1122 (vty, cmd_in, cmd_out));
1123
1124 /* Hook executed after a CLI command. */
1125 DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1126 (vty, cmd_exec));
1127 DEFINE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1128 (vty, cmd_exec));
1129
1130 /*
1131 * cmd_execute hook subscriber to handle `|` actions.
1132 */
1133 static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1134 char **cmd_out)
1135 {
1136 /* look for `|` */
1137 char *orig, *working, *token, *u;
1138 char *pipe = strstr(cmd_in, "| ");
1139 int ret = 0;
1140
1141 if (!pipe)
1142 return 0;
1143
1144 /* duplicate string for processing purposes, not including pipe */
1145 orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1146
1147 /* retrieve action */
1148 token = strsep(&working, " ");
1149 assert(token);
1150
1151 /* match result to known actions */
1152 if (strmatch(token, "include")) {
1153 /* the remaining text should be a regexp */
1154 char *regexp = working;
1155
1156 if (!regexp) {
1157 vty_out(vty, "%% Need a regexp to filter with\n");
1158 ret = 1;
1159 goto fail;
1160 }
1161
1162 bool succ = vty_set_include(vty, regexp);
1163
1164 if (!succ) {
1165 vty_out(vty, "%% Bad regexp '%s'\n", regexp);
1166 ret = 1;
1167 goto fail;
1168 }
1169 *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
1170 u = *cmd_out;
1171 strsep(&u, "|");
1172 } else {
1173 vty_out(vty, "%% Unknown action '%s'\n", token);
1174 ret = 1;
1175 goto fail;
1176 }
1177
1178 fail:
1179 XFREE(MTYPE_TMP, orig);
1180 return ret;
1181 }
1182
1183 static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1184 {
1185 if (vty->filter)
1186 vty_set_include(vty, NULL);
1187
1188 return 0;
1189 }
1190
1191 int cmd_execute(struct vty *vty, const char *cmd,
1192 const struct cmd_element **matched, int vtysh)
1193 {
1194 int ret;
1195 char *cmd_out = NULL;
1196 const char *cmd_exec = NULL;
1197 vector vline;
1198
1199 ret = hook_call(cmd_execute, vty, cmd, &cmd_out);
1200 if (ret) {
1201 ret = CMD_WARNING;
1202 goto free;
1203 }
1204
1205 cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1206
1207 vline = cmd_make_strvec(cmd_exec);
1208
1209 if (vline) {
1210 ret = cmd_execute_command(vline, vty, matched, vtysh);
1211 cmd_free_strvec(vline);
1212 } else {
1213 ret = CMD_SUCCESS;
1214 }
1215
1216 free:
1217 hook_call(cmd_execute_done, vty, cmd_exec);
1218
1219 XFREE(MTYPE_TMP, cmd_out);
1220
1221 return ret;
1222 }
1223
1224
1225 /**
1226 * Parse one line of config, walking up the parse tree attempting to find a
1227 * match
1228 *
1229 * @param vty The vty context in which the command should be executed.
1230 * @param cmd Pointer where the struct cmd_element* of the match command
1231 * will be stored, if any. May be set to NULL if this info is
1232 * not needed.
1233 * @param use_daemon Boolean to control whether or not we match on
1234 * CMD_SUCCESS_DAEMON
1235 * or not.
1236 * @return The status of the command that has been executed or an error code
1237 * as to why no command could be executed.
1238 */
1239 int command_config_read_one_line(struct vty *vty,
1240 const struct cmd_element **cmd,
1241 uint32_t line_num, int use_daemon)
1242 {
1243 vector vline;
1244 int ret;
1245 unsigned up_level = 0;
1246
1247 vline = cmd_make_strvec(vty->buf);
1248
1249 /* In case of comment line */
1250 if (vline == NULL)
1251 return CMD_SUCCESS;
1252
1253 /* Execute configuration command : this is strict match */
1254 ret = cmd_execute_command_strict(vline, vty, cmd);
1255
1256 /* The logic for trying parent nodes is in cmd_execute_command_real()
1257 * since calling ->node_exit() correctly is a bit involved. This is
1258 * also the only reason CMD_NO_LEVEL_UP exists.
1259 */
1260 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1261 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1262 && ret != CMD_SUCCESS && ret != CMD_WARNING
1263 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
1264 && ret != CMD_NO_LEVEL_UP)
1265 ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd,
1266 ++up_level);
1267
1268 if (ret == CMD_NO_LEVEL_UP)
1269 ret = CMD_ERR_NO_MATCH;
1270
1271 if (ret != CMD_SUCCESS &&
1272 ret != CMD_WARNING &&
1273 ret != CMD_SUCCESS_DAEMON) {
1274 struct vty_error *ve = XCALLOC(MTYPE_TMP, sizeof(*ve));
1275
1276 memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
1277 ve->line_num = line_num;
1278 if (!vty->error)
1279 vty->error = list_new();
1280
1281 listnode_add(vty->error, ve);
1282 }
1283
1284 cmd_free_strvec(vline);
1285
1286 return ret;
1287 }
1288
1289 /* Configuration make from file. */
1290 int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
1291 {
1292 int ret, error_ret = 0;
1293 *line_num = 0;
1294
1295 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
1296 ++(*line_num);
1297
1298 ret = command_config_read_one_line(vty, NULL, *line_num, 0);
1299
1300 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1301 && ret != CMD_ERR_NOTHING_TODO)
1302 error_ret = ret;
1303 }
1304
1305 if (error_ret) {
1306 return error_ret;
1307 }
1308
1309 return CMD_SUCCESS;
1310 }
1311
1312 /* Configuration from terminal */
1313 DEFUN (config_terminal,
1314 config_terminal_cmd,
1315 "configure [terminal]",
1316 "Configuration from vty interface\n"
1317 "Configuration terminal\n")
1318 {
1319 return vty_config_enter(vty, false, false);
1320 }
1321
1322 /* Enable command */
1323 DEFUN (enable,
1324 config_enable_cmd,
1325 "enable",
1326 "Turn on privileged mode command\n")
1327 {
1328 /* If enable password is NULL, change to ENABLE_NODE */
1329 if ((host.enable == NULL && host.enable_encrypt == NULL)
1330 || vty->type == VTY_SHELL_SERV)
1331 vty->node = ENABLE_NODE;
1332 else
1333 vty->node = AUTH_ENABLE_NODE;
1334
1335 return CMD_SUCCESS;
1336 }
1337
1338 /* Disable command */
1339 DEFUN (disable,
1340 config_disable_cmd,
1341 "disable",
1342 "Turn off privileged mode command\n")
1343 {
1344 if (vty->node == ENABLE_NODE)
1345 vty->node = VIEW_NODE;
1346 return CMD_SUCCESS;
1347 }
1348
1349 /* Down vty node level. */
1350 DEFUN (config_exit,
1351 config_exit_cmd,
1352 "exit",
1353 "Exit current mode and down to previous mode\n")
1354 {
1355 cmd_exit(vty);
1356 return CMD_SUCCESS;
1357 }
1358
1359 static int root_on_exit(struct vty *vty)
1360 {
1361 if (vty_shell(vty))
1362 exit(0);
1363 else
1364 vty->status = VTY_CLOSE;
1365 return 0;
1366 }
1367
1368 void cmd_exit(struct vty *vty)
1369 {
1370 struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
1371
1372 if (cnode->node_exit) {
1373 if (!cnode->node_exit(vty))
1374 return;
1375 }
1376 if (cnode->parent_node)
1377 vty->node = cnode->parent_node;
1378 if (vty->xpath_index > 0
1379 && vty_check_node_for_xpath_decrement(vty->node, cnode->node))
1380 vty->xpath_index--;
1381 }
1382
1383 /* ALIAS_FIXME */
1384 DEFUN (config_quit,
1385 config_quit_cmd,
1386 "quit",
1387 "Exit current mode and down to previous mode\n")
1388 {
1389 return config_exit(self, vty, argc, argv);
1390 }
1391
1392
1393 /* End of configuration. */
1394 DEFUN (config_end,
1395 config_end_cmd,
1396 "end",
1397 "End current mode and change to enable mode.\n")
1398 {
1399 if (vty->config) {
1400 vty_config_exit(vty);
1401 vty->node = ENABLE_NODE;
1402 }
1403 return CMD_SUCCESS;
1404 }
1405
1406 /* Show version. */
1407 DEFUN (show_version,
1408 show_version_cmd,
1409 "show version",
1410 SHOW_STR
1411 "Displays zebra version\n")
1412 {
1413 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
1414 cmd_hostname_get() ? cmd_hostname_get() : "");
1415 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
1416 #ifdef ENABLE_VERSION_BUILD_CONFIG
1417 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
1418 #endif
1419 return CMD_SUCCESS;
1420 }
1421
1422 /* Help display function for all node. */
1423 DEFUN (config_help,
1424 config_help_cmd,
1425 "help",
1426 "Description of the interactive help system\n")
1427 {
1428 vty_out(vty,
1429 "Quagga VTY provides advanced help feature. When you need help,\n\
1430 anytime at the command line please press '?'.\n\
1431 \n\
1432 If nothing matches, the help list will be empty and you must backup\n\
1433 until entering a '?' shows the available options.\n\
1434 Two styles of help are provided:\n\
1435 1. Full help is available when you are ready to enter a\n\
1436 command argument (e.g. 'show ?') and describes each possible\n\
1437 argument.\n\
1438 2. Partial help is provided when an abbreviated argument is entered\n\
1439 and you want to know what arguments match the input\n\
1440 (e.g. 'show me?'.)\n\n");
1441 return CMD_SUCCESS;
1442 }
1443
1444 static void permute(struct graph_node *start, struct vty *vty)
1445 {
1446 static struct list *position = NULL;
1447 if (!position)
1448 position = list_new();
1449
1450 struct cmd_token *stok = start->data;
1451 struct graph_node *gnn;
1452 struct listnode *ln;
1453
1454 // recursive dfs
1455 listnode_add(position, start);
1456 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1457 struct graph_node *gn = vector_slot(start->to, i);
1458 struct cmd_token *tok = gn->data;
1459 if (tok->attr == CMD_ATTR_HIDDEN
1460 || tok->attr == CMD_ATTR_DEPRECATED)
1461 continue;
1462 else if (tok->type == END_TKN || gn == start) {
1463 vty_out(vty, " ");
1464 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1465 struct cmd_token *tt = gnn->data;
1466 if (tt->type < SPECIAL_TKN)
1467 vty_out(vty, " %s", tt->text);
1468 }
1469 if (gn == start)
1470 vty_out(vty, "...");
1471 vty_out(vty, "\n");
1472 } else {
1473 bool skip = false;
1474 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1475 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1476 if (gnn == gn) {
1477 skip = true;
1478 break;
1479 }
1480 if (!skip)
1481 permute(gn, vty);
1482 }
1483 }
1484 list_delete_node(position, listtail(position));
1485 }
1486
1487 int cmd_list_cmds(struct vty *vty, int do_permute)
1488 {
1489 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1490
1491 if (do_permute)
1492 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1493 else {
1494 /* loop over all commands at this node */
1495 const struct cmd_element *element = NULL;
1496 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1497 i++)
1498 if ((element = vector_slot(node->cmd_vector, i))
1499 && element->attr != CMD_ATTR_DEPRECATED
1500 && element->attr != CMD_ATTR_HIDDEN)
1501 vty_out(vty, " %s\n", element->string);
1502 }
1503 return CMD_SUCCESS;
1504 }
1505
1506 /* Help display function for all node. */
1507 DEFUN (config_list,
1508 config_list_cmd,
1509 "list [permutations]",
1510 "Print command list\n"
1511 "Print all possible command permutations\n")
1512 {
1513 return cmd_list_cmds(vty, argc == 2);
1514 }
1515
1516 DEFUN (show_commandtree,
1517 show_commandtree_cmd,
1518 "show commandtree [permutations]",
1519 SHOW_STR
1520 "Show command tree\n"
1521 "Permutations that we are interested in\n")
1522 {
1523 return cmd_list_cmds(vty, argc == 3);
1524 }
1525
1526 DEFUN_HIDDEN(show_cli_graph,
1527 show_cli_graph_cmd,
1528 "show cli graph",
1529 SHOW_STR
1530 "CLI reflection\n"
1531 "Dump current command space as DOT graph\n")
1532 {
1533 struct cmd_node *cn = vector_slot(cmdvec, vty->node);
1534 char *dot = cmd_graph_dump_dot(cn->cmdgraph);
1535
1536 vty_out(vty, "%s\n", dot);
1537 XFREE(MTYPE_TMP, dot);
1538 return CMD_SUCCESS;
1539 }
1540
1541 static int vty_write_config(struct vty *vty)
1542 {
1543 size_t i;
1544 struct cmd_node *node;
1545
1546 if (host.noconfig)
1547 return CMD_SUCCESS;
1548
1549 nb_cli_show_config_prepare(running_config, false);
1550
1551 if (vty->type == VTY_TERM) {
1552 vty_out(vty, "\nCurrent configuration:\n");
1553 vty_out(vty, "!\n");
1554 }
1555
1556 if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
1557 vty_out(vty, "! loaded from %s\n", frr_defaults_version());
1558 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
1559 vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
1560 vty_out(vty, "!\n");
1561
1562 for (i = 0; i < vector_active(cmdvec); i++)
1563 if ((node = vector_slot(cmdvec, i)) && node->config_write) {
1564 if ((*node->config_write)(vty))
1565 vty_out(vty, "!\n");
1566 }
1567
1568 if (vty->type == VTY_TERM) {
1569 vty_out(vty, "end\n");
1570 }
1571
1572 return CMD_SUCCESS;
1573 }
1574
1575 static int file_write_config(struct vty *vty)
1576 {
1577 int fd, dirfd;
1578 char *config_file, *slash;
1579 char *config_file_tmp = NULL;
1580 char *config_file_sav = NULL;
1581 int ret = CMD_WARNING;
1582 struct vty *file_vty;
1583 struct stat conf_stat;
1584
1585 if (host.noconfig)
1586 return CMD_SUCCESS;
1587
1588 /* Check and see if we are operating under vtysh configuration */
1589 if (host.config == NULL) {
1590 vty_out(vty,
1591 "Can't save to configuration file, using vtysh.\n");
1592 return CMD_WARNING;
1593 }
1594
1595 /* Get filename. */
1596 config_file = host.config;
1597
1598 #ifndef O_DIRECTORY
1599 #define O_DIRECTORY 0
1600 #endif
1601 slash = strrchr(config_file, '/');
1602 if (slash) {
1603 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1604 config_dir[slash - config_file] = '\0';
1605 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1606 XFREE(MTYPE_TMP, config_dir);
1607 } else
1608 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1609 /* if dirfd is invalid, directory sync fails, but we're still OK */
1610
1611 size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
1612 config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
1613 strlcpy(config_file_sav, config_file, config_file_sav_sz);
1614 strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
1615
1616
1617 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
1618 snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX",
1619 config_file);
1620
1621 /* Open file to configuration write. */
1622 fd = mkstemp(config_file_tmp);
1623 if (fd < 0) {
1624 vty_out(vty, "Can't open configuration file %s.\n",
1625 config_file_tmp);
1626 goto finished;
1627 }
1628 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1629 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1630 config_file_tmp, safe_strerror(errno), errno);
1631 goto finished;
1632 }
1633
1634 /* Make vty for configuration file. */
1635 file_vty = vty_new();
1636 file_vty->wfd = fd;
1637 file_vty->type = VTY_FILE;
1638
1639 /* Config file header print. */
1640 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1641 vty_time_print(file_vty, 1);
1642 vty_out(file_vty, "!\n");
1643 vty_write_config(file_vty);
1644 vty_close(file_vty);
1645
1646 if (stat(config_file, &conf_stat) >= 0) {
1647 if (unlink(config_file_sav) != 0)
1648 if (errno != ENOENT) {
1649 vty_out(vty,
1650 "Can't unlink backup configuration file %s.\n",
1651 config_file_sav);
1652 goto finished;
1653 }
1654 if (link(config_file, config_file_sav) != 0) {
1655 vty_out(vty,
1656 "Can't backup old configuration file %s.\n",
1657 config_file_sav);
1658 goto finished;
1659 }
1660 if (dirfd >= 0)
1661 fsync(dirfd);
1662 }
1663 if (rename(config_file_tmp, config_file) != 0) {
1664 vty_out(vty, "Can't save configuration file %s.\n",
1665 config_file);
1666 goto finished;
1667 }
1668 if (dirfd >= 0)
1669 fsync(dirfd);
1670
1671 vty_out(vty, "Configuration saved to %s\n", config_file);
1672 ret = CMD_SUCCESS;
1673
1674 finished:
1675 if (ret != CMD_SUCCESS)
1676 unlink(config_file_tmp);
1677 if (dirfd >= 0)
1678 close(dirfd);
1679 XFREE(MTYPE_TMP, config_file_tmp);
1680 XFREE(MTYPE_TMP, config_file_sav);
1681 return ret;
1682 }
1683
1684 /* Write current configuration into file. */
1685
1686 DEFUN (config_write,
1687 config_write_cmd,
1688 "write [<file|memory|terminal>]",
1689 "Write running configuration to memory, network, or terminal\n"
1690 "Write to configuration file\n"
1691 "Write configuration currently in memory\n"
1692 "Write configuration to terminal\n")
1693 {
1694 const int idx_type = 1;
1695
1696 // if command was 'write terminal' or 'write memory'
1697 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1698 return vty_write_config(vty);
1699 }
1700
1701 return file_write_config(vty);
1702 }
1703
1704 /* ALIAS_FIXME for 'write <terminal|memory>' */
1705 DEFUN (show_running_config,
1706 show_running_config_cmd,
1707 "show running-config",
1708 SHOW_STR
1709 "running configuration (same as write terminal)\n")
1710 {
1711 return vty_write_config(vty);
1712 }
1713
1714 /* ALIAS_FIXME for 'write file' */
1715 DEFUN (copy_runningconf_startupconf,
1716 copy_runningconf_startupconf_cmd,
1717 "copy running-config startup-config",
1718 "Copy configuration\n"
1719 "Copy running config to... \n"
1720 "Copy running config to startup config (same as write file/memory)\n")
1721 {
1722 return file_write_config(vty);
1723 }
1724 /** -- **/
1725
1726 /* Write startup configuration into the terminal. */
1727 DEFUN (show_startup_config,
1728 show_startup_config_cmd,
1729 "show startup-config",
1730 SHOW_STR
1731 "Contents of startup configuration\n")
1732 {
1733 char buf[BUFSIZ];
1734 FILE *confp;
1735
1736 if (host.noconfig)
1737 return CMD_SUCCESS;
1738 if (host.config == NULL)
1739 return CMD_WARNING;
1740
1741 confp = fopen(host.config, "r");
1742 if (confp == NULL) {
1743 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1744 host.config, safe_strerror(errno));
1745 return CMD_WARNING;
1746 }
1747
1748 while (fgets(buf, BUFSIZ, confp)) {
1749 char *cp = buf;
1750
1751 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1752 cp++;
1753 *cp = '\0';
1754
1755 vty_out(vty, "%s\n", buf);
1756 }
1757
1758 fclose(confp);
1759
1760 return CMD_SUCCESS;
1761 }
1762
1763 int cmd_domainname_set(const char *domainname)
1764 {
1765 XFREE(MTYPE_HOST, host.domainname);
1766 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1767 return CMD_SUCCESS;
1768 }
1769
1770 /* Hostname configuration */
1771 DEFUN(config_domainname,
1772 domainname_cmd,
1773 "domainname WORD",
1774 "Set system's domain name\n"
1775 "This system's domain name\n")
1776 {
1777 struct cmd_token *word = argv[1];
1778
1779 if (!isalpha((unsigned char)word->arg[0])) {
1780 vty_out(vty, "Please specify string starting with alphabet\n");
1781 return CMD_WARNING_CONFIG_FAILED;
1782 }
1783
1784 return cmd_domainname_set(word->arg);
1785 }
1786
1787 DEFUN(config_no_domainname,
1788 no_domainname_cmd,
1789 "no domainname [DOMAINNAME]",
1790 NO_STR
1791 "Reset system's domain name\n"
1792 "domain name of this router\n")
1793 {
1794 return cmd_domainname_set(NULL);
1795 }
1796
1797 int cmd_hostname_set(const char *hostname)
1798 {
1799 XFREE(MTYPE_HOST, host.name);
1800 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1801 return CMD_SUCCESS;
1802 }
1803
1804 /* Hostname configuration */
1805 DEFUN (config_hostname,
1806 hostname_cmd,
1807 "hostname WORD",
1808 "Set system's network name\n"
1809 "This system's network name\n")
1810 {
1811 struct cmd_token *word = argv[1];
1812
1813 if (!isalnum((unsigned char)word->arg[0])) {
1814 vty_out(vty,
1815 "Please specify string starting with alphabet or number\n");
1816 return CMD_WARNING_CONFIG_FAILED;
1817 }
1818
1819 /* With reference to RFC 1123 Section 2.1 */
1820 if (strlen(word->arg) > HOSTNAME_LEN) {
1821 vty_out(vty, "Hostname length should be less than %d chars\n",
1822 HOSTNAME_LEN);
1823 return CMD_WARNING_CONFIG_FAILED;
1824 }
1825
1826 return cmd_hostname_set(word->arg);
1827 }
1828
1829 DEFUN (config_no_hostname,
1830 no_hostname_cmd,
1831 "no hostname [HOSTNAME]",
1832 NO_STR
1833 "Reset system's network name\n"
1834 "Host name of this router\n")
1835 {
1836 return cmd_hostname_set(NULL);
1837 }
1838
1839 /* VTY interface password set. */
1840 DEFUN (config_password,
1841 password_cmd,
1842 "password [(8-8)] WORD",
1843 "Modify the terminal connection password\n"
1844 "Specifies a HIDDEN password will follow\n"
1845 "The password string\n")
1846 {
1847 int idx_8 = 1;
1848 int idx_word = 2;
1849 if (argc == 3) // '8' was specified
1850 {
1851 if (host.password)
1852 XFREE(MTYPE_HOST, host.password);
1853 host.password = NULL;
1854 if (host.password_encrypt)
1855 XFREE(MTYPE_HOST, host.password_encrypt);
1856 host.password_encrypt =
1857 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1858 return CMD_SUCCESS;
1859 }
1860
1861 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1862 vty_out(vty,
1863 "Please specify string starting with alphanumeric\n");
1864 return CMD_WARNING_CONFIG_FAILED;
1865 }
1866
1867 if (host.password)
1868 XFREE(MTYPE_HOST, host.password);
1869 host.password = NULL;
1870
1871 if (host.encrypt) {
1872 if (host.password_encrypt)
1873 XFREE(MTYPE_HOST, host.password_encrypt);
1874 host.password_encrypt =
1875 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1876 } else
1877 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1878
1879 return CMD_SUCCESS;
1880 }
1881
1882 /* VTY interface password delete. */
1883 DEFUN (no_config_password,
1884 no_password_cmd,
1885 "no password",
1886 NO_STR
1887 "Modify the terminal connection password\n")
1888 {
1889 bool warned = false;
1890
1891 if (host.password) {
1892 if (!vty_shell_serv(vty)) {
1893 vty_out(vty, NO_PASSWD_CMD_WARNING);
1894 warned = true;
1895 }
1896 XFREE(MTYPE_HOST, host.password);
1897 }
1898 host.password = NULL;
1899
1900 if (host.password_encrypt) {
1901 if (!warned && !vty_shell_serv(vty))
1902 vty_out(vty, NO_PASSWD_CMD_WARNING);
1903 XFREE(MTYPE_HOST, host.password_encrypt);
1904 }
1905 host.password_encrypt = NULL;
1906
1907 return CMD_SUCCESS;
1908 }
1909
1910 /* VTY enable password set. */
1911 DEFUN (config_enable_password,
1912 enable_password_cmd,
1913 "enable password [(8-8)] WORD",
1914 "Modify enable password parameters\n"
1915 "Assign the privileged level password\n"
1916 "Specifies a HIDDEN password will follow\n"
1917 "The HIDDEN 'enable' password string\n")
1918 {
1919 int idx_8 = 2;
1920 int idx_word = 3;
1921
1922 /* Crypt type is specified. */
1923 if (argc == 4) {
1924 if (argv[idx_8]->arg[0] == '8') {
1925 if (host.enable)
1926 XFREE(MTYPE_HOST, host.enable);
1927 host.enable = NULL;
1928
1929 if (host.enable_encrypt)
1930 XFREE(MTYPE_HOST, host.enable_encrypt);
1931 host.enable_encrypt =
1932 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1933
1934 return CMD_SUCCESS;
1935 } else {
1936 vty_out(vty, "Unknown encryption type.\n");
1937 return CMD_WARNING_CONFIG_FAILED;
1938 }
1939 }
1940
1941 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1942 vty_out(vty,
1943 "Please specify string starting with alphanumeric\n");
1944 return CMD_WARNING_CONFIG_FAILED;
1945 }
1946
1947 if (host.enable)
1948 XFREE(MTYPE_HOST, host.enable);
1949 host.enable = NULL;
1950
1951 /* Plain password input. */
1952 if (host.encrypt) {
1953 if (host.enable_encrypt)
1954 XFREE(MTYPE_HOST, host.enable_encrypt);
1955 host.enable_encrypt =
1956 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1957 } else
1958 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1959
1960 return CMD_SUCCESS;
1961 }
1962
1963 /* VTY enable password delete. */
1964 DEFUN (no_config_enable_password,
1965 no_enable_password_cmd,
1966 "no enable password",
1967 NO_STR
1968 "Modify enable password parameters\n"
1969 "Assign the privileged level password\n")
1970 {
1971 bool warned = false;
1972
1973 if (host.enable) {
1974 if (!vty_shell_serv(vty)) {
1975 vty_out(vty, NO_PASSWD_CMD_WARNING);
1976 warned = true;
1977 }
1978 XFREE(MTYPE_HOST, host.enable);
1979 }
1980 host.enable = NULL;
1981
1982 if (host.enable_encrypt) {
1983 if (!warned && !vty_shell_serv(vty))
1984 vty_out(vty, NO_PASSWD_CMD_WARNING);
1985 XFREE(MTYPE_HOST, host.enable_encrypt);
1986 }
1987 host.enable_encrypt = NULL;
1988
1989 return CMD_SUCCESS;
1990 }
1991
1992 DEFUN (service_password_encrypt,
1993 service_password_encrypt_cmd,
1994 "service password-encryption",
1995 "Set up miscellaneous service\n"
1996 "Enable encrypted passwords\n")
1997 {
1998 if (host.encrypt)
1999 return CMD_SUCCESS;
2000
2001 host.encrypt = 1;
2002
2003 if (host.password) {
2004 if (host.password_encrypt)
2005 XFREE(MTYPE_HOST, host.password_encrypt);
2006 host.password_encrypt =
2007 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
2008 }
2009 if (host.enable) {
2010 if (host.enable_encrypt)
2011 XFREE(MTYPE_HOST, host.enable_encrypt);
2012 host.enable_encrypt =
2013 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
2014 }
2015
2016 return CMD_SUCCESS;
2017 }
2018
2019 DEFUN (no_service_password_encrypt,
2020 no_service_password_encrypt_cmd,
2021 "no service password-encryption",
2022 NO_STR
2023 "Set up miscellaneous service\n"
2024 "Enable encrypted passwords\n")
2025 {
2026 if (!host.encrypt)
2027 return CMD_SUCCESS;
2028
2029 host.encrypt = 0;
2030
2031 if (host.password_encrypt)
2032 XFREE(MTYPE_HOST, host.password_encrypt);
2033 host.password_encrypt = NULL;
2034
2035 if (host.enable_encrypt)
2036 XFREE(MTYPE_HOST, host.enable_encrypt);
2037 host.enable_encrypt = NULL;
2038
2039 return CMD_SUCCESS;
2040 }
2041
2042 DEFUN (config_terminal_length,
2043 config_terminal_length_cmd,
2044 "terminal length (0-512)",
2045 "Set terminal line parameters\n"
2046 "Set number of lines on a screen\n"
2047 "Number of lines on screen (0 for no pausing)\n")
2048 {
2049 int idx_number = 2;
2050
2051 vty->lines = atoi(argv[idx_number]->arg);
2052
2053 return CMD_SUCCESS;
2054 }
2055
2056 DEFUN (config_terminal_no_length,
2057 config_terminal_no_length_cmd,
2058 "terminal no length",
2059 "Set terminal line parameters\n"
2060 NO_STR
2061 "Set number of lines on a screen\n")
2062 {
2063 vty->lines = -1;
2064 return CMD_SUCCESS;
2065 }
2066
2067 DEFUN (service_terminal_length,
2068 service_terminal_length_cmd,
2069 "service terminal-length (0-512)",
2070 "Set up miscellaneous service\n"
2071 "System wide terminal length configuration\n"
2072 "Number of lines of VTY (0 means no line control)\n")
2073 {
2074 int idx_number = 2;
2075
2076 host.lines = atoi(argv[idx_number]->arg);
2077
2078 return CMD_SUCCESS;
2079 }
2080
2081 DEFUN (no_service_terminal_length,
2082 no_service_terminal_length_cmd,
2083 "no service terminal-length [(0-512)]",
2084 NO_STR
2085 "Set up miscellaneous service\n"
2086 "System wide terminal length configuration\n"
2087 "Number of lines of VTY (0 means no line control)\n")
2088 {
2089 host.lines = -1;
2090 return CMD_SUCCESS;
2091 }
2092
2093 DEFUN_HIDDEN (do_echo,
2094 echo_cmd,
2095 "echo MESSAGE...",
2096 "Echo a message back to the vty\n"
2097 "The message to echo\n")
2098 {
2099 char *message;
2100
2101 vty_out(vty, "%s\n",
2102 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2103 if (message)
2104 XFREE(MTYPE_TMP, message);
2105 return CMD_SUCCESS;
2106 }
2107
2108 DEFUN (config_logmsg,
2109 config_logmsg_cmd,
2110 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2111 "Send a message to enabled logging destinations\n"
2112 LOG_LEVEL_DESC
2113 "The message to send\n")
2114 {
2115 int idx_log_level = 1;
2116 int idx_message = 2;
2117 int level;
2118 char *message;
2119
2120 level = log_level_match(argv[idx_log_level]->arg);
2121 if (level == ZLOG_DISABLED)
2122 return CMD_ERR_NO_MATCH;
2123
2124 zlog(level, "%s",
2125 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2126 if (message)
2127 XFREE(MTYPE_TMP, message);
2128
2129 return CMD_SUCCESS;
2130 }
2131
2132 DEFUN (debug_memstats,
2133 debug_memstats_cmd,
2134 "[no] debug memstats-at-exit",
2135 NO_STR
2136 DEBUG_STR
2137 "Print memory type statistics at exit\n")
2138 {
2139 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2140 return CMD_SUCCESS;
2141 }
2142
2143 int cmd_banner_motd_file(const char *file)
2144 {
2145 int success = CMD_SUCCESS;
2146 char p[PATH_MAX];
2147 char *rpath;
2148 char *in;
2149
2150 rpath = realpath(file, p);
2151 if (!rpath)
2152 return CMD_ERR_NO_FILE;
2153 in = strstr(rpath, SYSCONFDIR);
2154 if (in == rpath) {
2155 XFREE(MTYPE_HOST, host.motdfile);
2156 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2157 } else
2158 success = CMD_WARNING_CONFIG_FAILED;
2159
2160 return success;
2161 }
2162
2163 void cmd_banner_motd_line(const char *line)
2164 {
2165 XFREE(MTYPE_HOST, host.motd);
2166 host.motd = XSTRDUP(MTYPE_HOST, line);
2167 }
2168
2169 DEFUN (banner_motd_file,
2170 banner_motd_file_cmd,
2171 "banner motd file FILE",
2172 "Set banner\n"
2173 "Banner for motd\n"
2174 "Banner from a file\n"
2175 "Filename\n")
2176 {
2177 int idx_file = 3;
2178 const char *filename = argv[idx_file]->arg;
2179 int cmd = cmd_banner_motd_file(filename);
2180
2181 if (cmd == CMD_ERR_NO_FILE)
2182 vty_out(vty, "%s does not exist", filename);
2183 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2184 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
2185
2186 return cmd;
2187 }
2188
2189 DEFUN (banner_motd_line,
2190 banner_motd_line_cmd,
2191 "banner motd line LINE...",
2192 "Set banner\n"
2193 "Banner for motd\n"
2194 "Banner from an input\n"
2195 "Text\n")
2196 {
2197 int idx = 0;
2198 char *motd;
2199
2200 argv_find(argv, argc, "LINE", &idx);
2201 motd = argv_concat(argv, argc, idx);
2202
2203 cmd_banner_motd_line(motd);
2204 XFREE(MTYPE_TMP, motd);
2205
2206 return CMD_SUCCESS;
2207 }
2208
2209 DEFUN (banner_motd_default,
2210 banner_motd_default_cmd,
2211 "banner motd default",
2212 "Set banner string\n"
2213 "Strings for motd\n"
2214 "Default string\n")
2215 {
2216 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2217 return CMD_SUCCESS;
2218 }
2219
2220 DEFUN (no_banner_motd,
2221 no_banner_motd_cmd,
2222 "no banner motd",
2223 NO_STR
2224 "Set banner string\n"
2225 "Strings for motd\n")
2226 {
2227 host.motd = NULL;
2228 if (host.motdfile)
2229 XFREE(MTYPE_HOST, host.motdfile);
2230 host.motdfile = NULL;
2231 return CMD_SUCCESS;
2232 }
2233
2234 DEFUN(find,
2235 find_cmd,
2236 "find REGEX...",
2237 "Find CLI command matching a regular expression\n"
2238 "Search pattern (POSIX regex)\n")
2239 {
2240 const struct cmd_node *node;
2241 const struct cmd_element *cli;
2242 vector clis;
2243
2244 regex_t exp = {};
2245
2246 char *pattern = argv_concat(argv, argc, 1);
2247 int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
2248 XFREE(MTYPE_TMP, pattern);
2249
2250 if (cr != 0) {
2251 switch (cr) {
2252 case REG_BADBR:
2253 vty_out(vty, "%% Invalid {...} expression\n");
2254 break;
2255 case REG_BADRPT:
2256 vty_out(vty, "%% Bad repetition operator\n");
2257 break;
2258 case REG_BADPAT:
2259 vty_out(vty, "%% Regex syntax error\n");
2260 break;
2261 case REG_ECOLLATE:
2262 vty_out(vty, "%% Invalid collating element\n");
2263 break;
2264 case REG_ECTYPE:
2265 vty_out(vty, "%% Invalid character class name\n");
2266 break;
2267 case REG_EESCAPE:
2268 vty_out(vty,
2269 "%% Regex ended with escape character (\\)\n");
2270 break;
2271 case REG_ESUBREG:
2272 vty_out(vty,
2273 "%% Invalid number in \\digit construction\n");
2274 break;
2275 case REG_EBRACK:
2276 vty_out(vty, "%% Unbalanced square brackets\n");
2277 break;
2278 case REG_EPAREN:
2279 vty_out(vty, "%% Unbalanced parentheses\n");
2280 break;
2281 case REG_EBRACE:
2282 vty_out(vty, "%% Unbalanced braces\n");
2283 break;
2284 case REG_ERANGE:
2285 vty_out(vty,
2286 "%% Invalid endpoint in range expression\n");
2287 break;
2288 case REG_ESPACE:
2289 vty_out(vty, "%% Failed to compile (out of memory)\n");
2290 break;
2291 }
2292
2293 goto done;
2294 }
2295
2296
2297 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
2298 node = vector_slot(cmdvec, i);
2299 if (!node)
2300 continue;
2301 clis = node->cmd_vector;
2302 for (unsigned int j = 0; j < vector_active(clis); j++) {
2303 cli = vector_slot(clis, j);
2304
2305 if (regexec(&exp, cli->string, 0, NULL, 0) == 0)
2306 vty_out(vty, " (%s) %s\n",
2307 node->name, cli->string);
2308 }
2309 }
2310
2311 done:
2312 regfree(&exp);
2313 return CMD_SUCCESS;
2314 }
2315
2316 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2317 DEFUN(script,
2318 script_cmd,
2319 "script SCRIPT",
2320 "Test command - execute a script\n"
2321 "Script name (same as filename in /etc/frr/scripts/\n")
2322 {
2323 struct prefix p;
2324
2325 (void)str2prefix("1.2.3.4/24", &p);
2326
2327 struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
2328
2329 if (fs == NULL) {
2330 vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
2331 argv[1]->arg);
2332 } else {
2333 int ret = frrscript_call(fs, NULL);
2334 vty_out(vty, "Script result: %d\n", ret);
2335 }
2336
2337 return CMD_SUCCESS;
2338 }
2339 #endif
2340
2341 /* Set config filename. Called from vty.c */
2342 void host_config_set(const char *filename)
2343 {
2344 XFREE(MTYPE_HOST, host.config);
2345 host.config = XSTRDUP(MTYPE_HOST, filename);
2346 }
2347
2348 const char *host_config_get(void)
2349 {
2350 return host.config;
2351 }
2352
2353 void install_default(enum node_type node)
2354 {
2355 _install_element(node, &config_exit_cmd);
2356 _install_element(node, &config_quit_cmd);
2357 _install_element(node, &config_end_cmd);
2358 _install_element(node, &config_help_cmd);
2359 _install_element(node, &config_list_cmd);
2360 _install_element(node, &show_cli_graph_cmd);
2361 _install_element(node, &find_cmd);
2362
2363 _install_element(node, &config_write_cmd);
2364 _install_element(node, &show_running_config_cmd);
2365
2366 _install_element(node, &autocomplete_cmd);
2367
2368 nb_cli_install_default(node);
2369 }
2370
2371 /* Initialize command interface. Install basic nodes and commands.
2372 *
2373 * terminal = 0 -- vtysh / no logging, no config control
2374 * terminal = 1 -- normal daemon
2375 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2376 void cmd_init(int terminal)
2377 {
2378 struct utsname names;
2379
2380 uname(&names);
2381 qobj_init();
2382
2383 /* register command preprocessors */
2384 hook_register(cmd_execute, handle_pipe_action);
2385 hook_register(cmd_execute_done, handle_pipe_action_done);
2386
2387 varhandlers = list_new();
2388
2389 /* Allocate initial top vector of commands. */
2390 cmdvec = vector_init(VECTOR_MIN_SIZE);
2391
2392 /* Default host value settings. */
2393 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2394 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2395 if ((strcmp(names.domainname, "(none)") == 0))
2396 host.domainname = NULL;
2397 else
2398 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
2399 #else
2400 host.domainname = NULL;
2401 #endif
2402 host.password = NULL;
2403 host.enable = NULL;
2404 host.config = NULL;
2405 host.noconfig = (terminal < 0);
2406 host.lines = -1;
2407 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2408 host.motdfile = NULL;
2409
2410 /* Install top nodes. */
2411 install_node(&view_node);
2412 install_node(&enable_node);
2413 install_node(&auth_node);
2414 install_node(&auth_enable_node);
2415 install_node(&config_node);
2416
2417 /* Each node's basic commands. */
2418 install_element(VIEW_NODE, &show_version_cmd);
2419 install_element(ENABLE_NODE, &show_startup_config_cmd);
2420
2421 if (terminal) {
2422 install_element(ENABLE_NODE, &debug_memstats_cmd);
2423
2424 install_element(VIEW_NODE, &config_list_cmd);
2425 install_element(VIEW_NODE, &config_exit_cmd);
2426 install_element(VIEW_NODE, &config_quit_cmd);
2427 install_element(VIEW_NODE, &config_help_cmd);
2428 install_element(VIEW_NODE, &config_enable_cmd);
2429 install_element(VIEW_NODE, &config_terminal_length_cmd);
2430 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2431 install_element(VIEW_NODE, &show_commandtree_cmd);
2432 install_element(VIEW_NODE, &echo_cmd);
2433 install_element(VIEW_NODE, &autocomplete_cmd);
2434 install_element(VIEW_NODE, &find_cmd);
2435 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2436 install_element(VIEW_NODE, &script_cmd);
2437 #endif
2438
2439
2440 install_element(ENABLE_NODE, &config_end_cmd);
2441 install_element(ENABLE_NODE, &config_disable_cmd);
2442 install_element(ENABLE_NODE, &config_terminal_cmd);
2443 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2444 install_element(ENABLE_NODE, &config_write_cmd);
2445 install_element(ENABLE_NODE, &show_running_config_cmd);
2446 install_element(ENABLE_NODE, &config_logmsg_cmd);
2447
2448 install_default(CONFIG_NODE);
2449
2450 thread_cmd_init();
2451 workqueue_cmd_init();
2452 hash_cmd_init();
2453 }
2454
2455 install_element(CONFIG_NODE, &hostname_cmd);
2456 install_element(CONFIG_NODE, &no_hostname_cmd);
2457 install_element(CONFIG_NODE, &domainname_cmd);
2458 install_element(CONFIG_NODE, &no_domainname_cmd);
2459
2460 if (terminal > 0) {
2461 full_cli = true;
2462
2463 install_element(CONFIG_NODE, &debug_memstats_cmd);
2464
2465 install_element(CONFIG_NODE, &password_cmd);
2466 install_element(CONFIG_NODE, &no_password_cmd);
2467 install_element(CONFIG_NODE, &enable_password_cmd);
2468 install_element(CONFIG_NODE, &no_enable_password_cmd);
2469
2470 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2471 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2472 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2473 install_element(CONFIG_NODE, &banner_motd_file_cmd);
2474 install_element(CONFIG_NODE, &banner_motd_line_cmd);
2475 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2476 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2477 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2478
2479 log_cmd_init();
2480 vrf_install_commands();
2481 }
2482
2483 #ifdef DEV_BUILD
2484 grammar_sandbox_init();
2485 #endif
2486 }
2487
2488 void cmd_terminate(void)
2489 {
2490 struct cmd_node *cmd_node;
2491
2492 hook_unregister(cmd_execute, handle_pipe_action);
2493 hook_unregister(cmd_execute_done, handle_pipe_action_done);
2494
2495 if (cmdvec) {
2496 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2497 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2498 // deleting the graph delets the cmd_element as
2499 // well
2500 graph_delete_graph(cmd_node->cmdgraph);
2501 vector_free(cmd_node->cmd_vector);
2502 hash_clean(cmd_node->cmd_hash, NULL);
2503 hash_free(cmd_node->cmd_hash);
2504 cmd_node->cmd_hash = NULL;
2505 }
2506
2507 vector_free(cmdvec);
2508 cmdvec = NULL;
2509 }
2510
2511 XFREE(MTYPE_HOST, host.name);
2512 XFREE(MTYPE_HOST, host.domainname);
2513 XFREE(MTYPE_HOST, host.password);
2514 XFREE(MTYPE_HOST, host.password_encrypt);
2515 XFREE(MTYPE_HOST, host.enable);
2516 XFREE(MTYPE_HOST, host.enable_encrypt);
2517 XFREE(MTYPE_HOST, host.motdfile);
2518 XFREE(MTYPE_HOST, host.config);
2519 XFREE(MTYPE_HOST, host.motd);
2520
2521 list_delete(&varhandlers);
2522 qobj_finish();
2523 }