]> git.proxmox.com Git - mirror_frr.git/blob - lib/command.c
Merge pull request #8455 from achernavin22/ospf_nssa_after_redist2
[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 {
905 struct list *argv_list;
906 enum matcher_rv status;
907 const struct cmd_element *matched_element = NULL;
908
909 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
910 status = command_match(cmdgraph, vline, &argv_list, &matched_element);
911
912 if (cmd)
913 *cmd = matched_element;
914
915 // if matcher error, return corresponding CMD_ERR
916 if (MATCHER_ERROR(status)) {
917 if (argv_list)
918 list_delete(&argv_list);
919 switch (status) {
920 case MATCHER_INCOMPLETE:
921 return CMD_ERR_INCOMPLETE;
922 case MATCHER_AMBIGUOUS:
923 return CMD_ERR_AMBIGUOUS;
924 default:
925 return CMD_ERR_NO_MATCH;
926 }
927 }
928
929 // build argv array from argv list
930 struct cmd_token **argv = XMALLOC(
931 MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *));
932 struct listnode *ln;
933 struct cmd_token *token;
934 unsigned int i = 0;
935 for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token))
936 argv[i++] = token;
937
938 int argc = argv_list->count;
939
940 int ret;
941 if (matched_element->daemon)
942 ret = CMD_SUCCESS_DAEMON;
943 else {
944 if (vty->config) {
945 /* Clear array of enqueued configuration changes. */
946 vty->num_cfg_changes = 0;
947 memset(&vty->cfg_changes, 0, sizeof(vty->cfg_changes));
948
949 /* Regenerate candidate configuration if necessary. */
950 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
951 && running_config->version
952 > vty->candidate_config->version)
953 nb_config_replace(vty->candidate_config,
954 running_config, true);
955
956 /*
957 * Perform pending commit (if any) before executing
958 * non-YANG command.
959 */
960 if (matched_element->attr != CMD_ATTR_YANG)
961 nb_cli_pending_commit_check(vty);
962 }
963
964 ret = matched_element->func(matched_element, vty, argc, argv);
965 }
966
967 // delete list and cmd_token's in it
968 list_delete(&argv_list);
969 XFREE(MTYPE_TMP, argv);
970
971 return ret;
972 }
973
974 /**
975 * Execute a given command, handling things like "do ..." and checking
976 * whether the given command might apply at a parent node if doesn't
977 * apply for the current node.
978 *
979 * @param vline Command line input, vector of char* where each element is
980 * one input token.
981 * @param vty The vty context in which the command should be executed.
982 * @param cmd Pointer where the struct cmd_element of the matched command
983 * will be stored, if any. May be set to NULL if this info is
984 * not needed.
985 * @param vtysh If set != 0, don't lookup the command at parent nodes.
986 * @return The status of the command that has been executed or an error code
987 * as to why no command could be executed.
988 */
989 int cmd_execute_command(vector vline, struct vty *vty,
990 const struct cmd_element **cmd, int vtysh)
991 {
992 int ret, saved_ret = 0;
993 enum node_type onode, try_node;
994 int orig_xpath_index;
995
996 onode = try_node = vty->node;
997 orig_xpath_index = vty->xpath_index;
998
999 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1000 vector shifted_vline;
1001 unsigned int index;
1002
1003 vty->node = ENABLE_NODE;
1004 vty->xpath_index = 0;
1005 /* We can try it on enable node, cos' the vty is authenticated
1006 */
1007
1008 shifted_vline = vector_init(vector_count(vline));
1009 /* use memcpy? */
1010 for (index = 1; index < vector_active(vline); index++)
1011 vector_set_index(shifted_vline, index - 1,
1012 vector_lookup(vline, index));
1013
1014 ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
1015 vty, cmd);
1016
1017 vector_free(shifted_vline);
1018 vty->node = onode;
1019 vty->xpath_index = orig_xpath_index;
1020 return ret;
1021 }
1022
1023 saved_ret = ret =
1024 cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd);
1025
1026 if (vtysh)
1027 return saved_ret;
1028
1029 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1030 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
1031 /* This assumes all nodes above CONFIG_NODE are childs of
1032 * CONFIG_NODE */
1033 while (vty->node > CONFIG_NODE) {
1034 try_node = node_parent(try_node);
1035 vty->node = try_node;
1036 if (vty->xpath_index > 0
1037 && vty_check_node_for_xpath_decrement(try_node,
1038 onode))
1039 vty->xpath_index--;
1040 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
1041 vty, cmd);
1042 if (ret == CMD_SUCCESS || ret == CMD_WARNING
1043 || ret == CMD_NOT_MY_INSTANCE
1044 || ret == CMD_WARNING_CONFIG_FAILED)
1045 return ret;
1046 }
1047 /* no command succeeded, reset the vty to the original node */
1048 vty->node = onode;
1049 vty->xpath_index = orig_xpath_index;
1050 }
1051
1052 /* return command status for original node */
1053 return saved_ret;
1054 }
1055
1056 /**
1057 * Execute a given command, matching it strictly against the current node.
1058 * This mode is used when reading config files.
1059 *
1060 * @param vline Command line input, vector of char* where each element is
1061 * one input token.
1062 * @param vty The vty context in which the command should be executed.
1063 * @param cmd Pointer where the struct cmd_element* of the matched command
1064 * will be stored, if any. May be set to NULL if this info is
1065 * not needed.
1066 * @return The status of the command that has been executed or an error code
1067 * as to why no command could be executed.
1068 */
1069 int cmd_execute_command_strict(vector vline, struct vty *vty,
1070 const struct cmd_element **cmd)
1071 {
1072 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
1073 }
1074
1075 /*
1076 * Hook for preprocessing command string before executing.
1077 *
1078 * All subscribers are called with the raw command string that is to be
1079 * executed. If any changes are to be made, a new string should be allocated
1080 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1081 * is then responsible for freeing this string.
1082 *
1083 * All processing functions must be mutually exclusive in their action, i.e. if
1084 * one subscriber decides to modify the command, all others must not modify it
1085 * when called. Feeding the output of one processing command into a subsequent
1086 * one is not supported.
1087 *
1088 * This hook is intentionally internal to the command processing system.
1089 *
1090 * cmd_in
1091 * The raw command string.
1092 *
1093 * cmd_out
1094 * The result of any processing.
1095 */
1096 DECLARE_HOOK(cmd_execute,
1097 (struct vty *vty, const char *cmd_in, char **cmd_out),
1098 (vty, cmd_in, cmd_out));
1099 DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
1100 (vty, cmd_in, cmd_out));
1101
1102 /* Hook executed after a CLI command. */
1103 DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1104 (vty, cmd_exec));
1105 DEFINE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1106 (vty, cmd_exec));
1107
1108 /*
1109 * cmd_execute hook subscriber to handle `|` actions.
1110 */
1111 static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1112 char **cmd_out)
1113 {
1114 /* look for `|` */
1115 char *orig, *working, *token, *u;
1116 char *pipe = strstr(cmd_in, "| ");
1117 int ret = 0;
1118
1119 if (!pipe)
1120 return 0;
1121
1122 /* duplicate string for processing purposes, not including pipe */
1123 orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1124
1125 /* retrieve action */
1126 token = strsep(&working, " ");
1127 assert(token);
1128
1129 /* match result to known actions */
1130 if (strmatch(token, "include")) {
1131 /* the remaining text should be a regexp */
1132 char *regexp = working;
1133
1134 if (!regexp) {
1135 vty_out(vty, "%% Need a regexp to filter with\n");
1136 ret = 1;
1137 goto fail;
1138 }
1139
1140 bool succ = vty_set_include(vty, regexp);
1141
1142 if (!succ) {
1143 vty_out(vty, "%% Bad regexp '%s'\n", regexp);
1144 ret = 1;
1145 goto fail;
1146 }
1147 *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
1148 u = *cmd_out;
1149 strsep(&u, "|");
1150 } else {
1151 vty_out(vty, "%% Unknown action '%s'\n", token);
1152 ret = 1;
1153 goto fail;
1154 }
1155
1156 fail:
1157 XFREE(MTYPE_TMP, orig);
1158 return ret;
1159 }
1160
1161 static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1162 {
1163 if (vty->filter)
1164 vty_set_include(vty, NULL);
1165
1166 return 0;
1167 }
1168
1169 int cmd_execute(struct vty *vty, const char *cmd,
1170 const struct cmd_element **matched, int vtysh)
1171 {
1172 int ret;
1173 char *cmd_out = NULL;
1174 const char *cmd_exec = NULL;
1175 vector vline;
1176
1177 ret = hook_call(cmd_execute, vty, cmd, &cmd_out);
1178 if (ret) {
1179 ret = CMD_WARNING;
1180 goto free;
1181 }
1182
1183 cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1184
1185 vline = cmd_make_strvec(cmd_exec);
1186
1187 if (vline) {
1188 ret = cmd_execute_command(vline, vty, matched, vtysh);
1189 cmd_free_strvec(vline);
1190 } else {
1191 ret = CMD_SUCCESS;
1192 }
1193
1194 free:
1195 hook_call(cmd_execute_done, vty, cmd_exec);
1196
1197 XFREE(MTYPE_TMP, cmd_out);
1198
1199 return ret;
1200 }
1201
1202
1203 /**
1204 * Parse one line of config, walking up the parse tree attempting to find a
1205 * match
1206 *
1207 * @param vty The vty context in which the command should be executed.
1208 * @param cmd Pointer where the struct cmd_element* of the match command
1209 * will be stored, if any. May be set to NULL if this info is
1210 * not needed.
1211 * @param use_daemon Boolean to control whether or not we match on
1212 * CMD_SUCCESS_DAEMON
1213 * or not.
1214 * @return The status of the command that has been executed or an error code
1215 * as to why no command could be executed.
1216 */
1217 int command_config_read_one_line(struct vty *vty,
1218 const struct cmd_element **cmd,
1219 uint32_t line_num, int use_daemon)
1220 {
1221 vector vline;
1222 int ret;
1223
1224 vline = cmd_make_strvec(vty->buf);
1225
1226 /* In case of comment line */
1227 if (vline == NULL)
1228 return CMD_SUCCESS;
1229
1230 /* Execute configuration command : this is strict match */
1231 ret = cmd_execute_command_strict(vline, vty, cmd);
1232
1233 // Climb the tree and try the command again at each node
1234 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1235 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1236 && ret != CMD_SUCCESS && ret != CMD_WARNING
1237 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
1238 && vty->node != CONFIG_NODE) {
1239 int saved_node = vty->node;
1240 int saved_xpath_index = vty->xpath_index;
1241
1242 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1243 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1244 && ret != CMD_SUCCESS && ret != CMD_WARNING
1245 && vty->node > CONFIG_NODE) {
1246 vty->node = node_parent(vty->node);
1247 if (vty->xpath_index > 0
1248 && vty_check_node_for_xpath_decrement(vty->node,
1249 saved_node))
1250 vty->xpath_index--;
1251 ret = cmd_execute_command_strict(vline, vty, cmd);
1252 }
1253
1254 // If climbing the tree did not work then ignore the command and
1255 // stay at the same node
1256 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1257 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1258 && ret != CMD_SUCCESS && ret != CMD_WARNING) {
1259 vty->node = saved_node;
1260 vty->xpath_index = saved_xpath_index;
1261 }
1262 }
1263
1264 if (ret != CMD_SUCCESS &&
1265 ret != CMD_WARNING &&
1266 ret != CMD_SUCCESS_DAEMON) {
1267 struct vty_error *ve = XCALLOC(MTYPE_TMP, sizeof(*ve));
1268
1269 memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
1270 ve->line_num = line_num;
1271 if (!vty->error)
1272 vty->error = list_new();
1273
1274 listnode_add(vty->error, ve);
1275 }
1276
1277 cmd_free_strvec(vline);
1278
1279 return ret;
1280 }
1281
1282 /* Configuration make from file. */
1283 int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
1284 {
1285 int ret, error_ret = 0;
1286 *line_num = 0;
1287
1288 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
1289 ++(*line_num);
1290
1291 ret = command_config_read_one_line(vty, NULL, *line_num, 0);
1292
1293 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1294 && ret != CMD_ERR_NOTHING_TODO)
1295 error_ret = ret;
1296 }
1297
1298 if (error_ret) {
1299 return error_ret;
1300 }
1301
1302 return CMD_SUCCESS;
1303 }
1304
1305 /* Configuration from terminal */
1306 DEFUN (config_terminal,
1307 config_terminal_cmd,
1308 "configure [terminal]",
1309 "Configuration from vty interface\n"
1310 "Configuration terminal\n")
1311 {
1312 return vty_config_enter(vty, false, false);
1313 }
1314
1315 /* Enable command */
1316 DEFUN (enable,
1317 config_enable_cmd,
1318 "enable",
1319 "Turn on privileged mode command\n")
1320 {
1321 /* If enable password is NULL, change to ENABLE_NODE */
1322 if ((host.enable == NULL && host.enable_encrypt == NULL)
1323 || vty->type == VTY_SHELL_SERV)
1324 vty->node = ENABLE_NODE;
1325 else
1326 vty->node = AUTH_ENABLE_NODE;
1327
1328 return CMD_SUCCESS;
1329 }
1330
1331 /* Disable command */
1332 DEFUN (disable,
1333 config_disable_cmd,
1334 "disable",
1335 "Turn off privileged mode command\n")
1336 {
1337 if (vty->node == ENABLE_NODE)
1338 vty->node = VIEW_NODE;
1339 return CMD_SUCCESS;
1340 }
1341
1342 /* Down vty node level. */
1343 DEFUN (config_exit,
1344 config_exit_cmd,
1345 "exit",
1346 "Exit current mode and down to previous mode\n")
1347 {
1348 cmd_exit(vty);
1349 return CMD_SUCCESS;
1350 }
1351
1352 static int root_on_exit(struct vty *vty)
1353 {
1354 if (vty_shell(vty))
1355 exit(0);
1356 else
1357 vty->status = VTY_CLOSE;
1358 return 0;
1359 }
1360
1361 void cmd_exit(struct vty *vty)
1362 {
1363 struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
1364
1365 if (cnode->node_exit) {
1366 if (!cnode->node_exit(vty))
1367 return;
1368 }
1369 if (cnode->parent_node)
1370 vty->node = cnode->parent_node;
1371 if (vty->xpath_index > 0
1372 && vty_check_node_for_xpath_decrement(vty->node, cnode->node))
1373 vty->xpath_index--;
1374 }
1375
1376 /* ALIAS_FIXME */
1377 DEFUN (config_quit,
1378 config_quit_cmd,
1379 "quit",
1380 "Exit current mode and down to previous mode\n")
1381 {
1382 return config_exit(self, vty, argc, argv);
1383 }
1384
1385
1386 /* End of configuration. */
1387 DEFUN (config_end,
1388 config_end_cmd,
1389 "end",
1390 "End current mode and change to enable mode.\n")
1391 {
1392 if (vty->config) {
1393 vty_config_exit(vty);
1394 vty->node = ENABLE_NODE;
1395 }
1396 return CMD_SUCCESS;
1397 }
1398
1399 /* Show version. */
1400 DEFUN (show_version,
1401 show_version_cmd,
1402 "show version",
1403 SHOW_STR
1404 "Displays zebra version\n")
1405 {
1406 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
1407 cmd_hostname_get() ? cmd_hostname_get() : "");
1408 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
1409 #ifdef ENABLE_VERSION_BUILD_CONFIG
1410 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
1411 #endif
1412 return CMD_SUCCESS;
1413 }
1414
1415 /* Help display function for all node. */
1416 DEFUN (config_help,
1417 config_help_cmd,
1418 "help",
1419 "Description of the interactive help system\n")
1420 {
1421 vty_out(vty,
1422 "Quagga VTY provides advanced help feature. When you need help,\n\
1423 anytime at the command line please press '?'.\n\
1424 \n\
1425 If nothing matches, the help list will be empty and you must backup\n\
1426 until entering a '?' shows the available options.\n\
1427 Two styles of help are provided:\n\
1428 1. Full help is available when you are ready to enter a\n\
1429 command argument (e.g. 'show ?') and describes each possible\n\
1430 argument.\n\
1431 2. Partial help is provided when an abbreviated argument is entered\n\
1432 and you want to know what arguments match the input\n\
1433 (e.g. 'show me?'.)\n\n");
1434 return CMD_SUCCESS;
1435 }
1436
1437 static void permute(struct graph_node *start, struct vty *vty)
1438 {
1439 static struct list *position = NULL;
1440 if (!position)
1441 position = list_new();
1442
1443 struct cmd_token *stok = start->data;
1444 struct graph_node *gnn;
1445 struct listnode *ln;
1446
1447 // recursive dfs
1448 listnode_add(position, start);
1449 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1450 struct graph_node *gn = vector_slot(start->to, i);
1451 struct cmd_token *tok = gn->data;
1452 if (tok->attr == CMD_ATTR_HIDDEN
1453 || tok->attr == CMD_ATTR_DEPRECATED)
1454 continue;
1455 else if (tok->type == END_TKN || gn == start) {
1456 vty_out(vty, " ");
1457 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1458 struct cmd_token *tt = gnn->data;
1459 if (tt->type < SPECIAL_TKN)
1460 vty_out(vty, " %s", tt->text);
1461 }
1462 if (gn == start)
1463 vty_out(vty, "...");
1464 vty_out(vty, "\n");
1465 } else {
1466 bool skip = false;
1467 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1468 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1469 if (gnn == gn) {
1470 skip = true;
1471 break;
1472 }
1473 if (!skip)
1474 permute(gn, vty);
1475 }
1476 }
1477 list_delete_node(position, listtail(position));
1478 }
1479
1480 int cmd_list_cmds(struct vty *vty, int do_permute)
1481 {
1482 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1483
1484 if (do_permute)
1485 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1486 else {
1487 /* loop over all commands at this node */
1488 const struct cmd_element *element = NULL;
1489 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1490 i++)
1491 if ((element = vector_slot(node->cmd_vector, i))
1492 && element->attr != CMD_ATTR_DEPRECATED
1493 && element->attr != CMD_ATTR_HIDDEN)
1494 vty_out(vty, " %s\n", element->string);
1495 }
1496 return CMD_SUCCESS;
1497 }
1498
1499 /* Help display function for all node. */
1500 DEFUN (config_list,
1501 config_list_cmd,
1502 "list [permutations]",
1503 "Print command list\n"
1504 "Print all possible command permutations\n")
1505 {
1506 return cmd_list_cmds(vty, argc == 2);
1507 }
1508
1509 DEFUN (show_commandtree,
1510 show_commandtree_cmd,
1511 "show commandtree [permutations]",
1512 SHOW_STR
1513 "Show command tree\n"
1514 "Permutations that we are interested in\n")
1515 {
1516 return cmd_list_cmds(vty, argc == 3);
1517 }
1518
1519 DEFUN_HIDDEN(show_cli_graph,
1520 show_cli_graph_cmd,
1521 "show cli graph",
1522 SHOW_STR
1523 "CLI reflection\n"
1524 "Dump current command space as DOT graph\n")
1525 {
1526 struct cmd_node *cn = vector_slot(cmdvec, vty->node);
1527 char *dot = cmd_graph_dump_dot(cn->cmdgraph);
1528
1529 vty_out(vty, "%s\n", dot);
1530 XFREE(MTYPE_TMP, dot);
1531 return CMD_SUCCESS;
1532 }
1533
1534 static int vty_write_config(struct vty *vty)
1535 {
1536 size_t i;
1537 struct cmd_node *node;
1538
1539 if (host.noconfig)
1540 return CMD_SUCCESS;
1541
1542 nb_cli_show_config_prepare(running_config, false);
1543
1544 if (vty->type == VTY_TERM) {
1545 vty_out(vty, "\nCurrent configuration:\n");
1546 vty_out(vty, "!\n");
1547 }
1548
1549 if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
1550 vty_out(vty, "! loaded from %s\n", frr_defaults_version());
1551 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
1552 vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
1553 vty_out(vty, "!\n");
1554
1555 for (i = 0; i < vector_active(cmdvec); i++)
1556 if ((node = vector_slot(cmdvec, i)) && node->config_write) {
1557 if ((*node->config_write)(vty))
1558 vty_out(vty, "!\n");
1559 }
1560
1561 if (vty->type == VTY_TERM) {
1562 vty_out(vty, "end\n");
1563 }
1564
1565 return CMD_SUCCESS;
1566 }
1567
1568 static int file_write_config(struct vty *vty)
1569 {
1570 int fd, dirfd;
1571 char *config_file, *slash;
1572 char *config_file_tmp = NULL;
1573 char *config_file_sav = NULL;
1574 int ret = CMD_WARNING;
1575 struct vty *file_vty;
1576 struct stat conf_stat;
1577
1578 if (host.noconfig)
1579 return CMD_SUCCESS;
1580
1581 /* Check and see if we are operating under vtysh configuration */
1582 if (host.config == NULL) {
1583 vty_out(vty,
1584 "Can't save to configuration file, using vtysh.\n");
1585 return CMD_WARNING;
1586 }
1587
1588 /* Get filename. */
1589 config_file = host.config;
1590
1591 #ifndef O_DIRECTORY
1592 #define O_DIRECTORY 0
1593 #endif
1594 slash = strrchr(config_file, '/');
1595 if (slash) {
1596 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1597 config_dir[slash - config_file] = '\0';
1598 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1599 XFREE(MTYPE_TMP, config_dir);
1600 } else
1601 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1602 /* if dirfd is invalid, directory sync fails, but we're still OK */
1603
1604 size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
1605 config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
1606 strlcpy(config_file_sav, config_file, config_file_sav_sz);
1607 strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
1608
1609
1610 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
1611 snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX",
1612 config_file);
1613
1614 /* Open file to configuration write. */
1615 fd = mkstemp(config_file_tmp);
1616 if (fd < 0) {
1617 vty_out(vty, "Can't open configuration file %s.\n",
1618 config_file_tmp);
1619 goto finished;
1620 }
1621 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1622 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1623 config_file_tmp, safe_strerror(errno), errno);
1624 goto finished;
1625 }
1626
1627 /* Make vty for configuration file. */
1628 file_vty = vty_new();
1629 file_vty->wfd = fd;
1630 file_vty->type = VTY_FILE;
1631
1632 /* Config file header print. */
1633 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1634 vty_time_print(file_vty, 1);
1635 vty_out(file_vty, "!\n");
1636 vty_write_config(file_vty);
1637 vty_close(file_vty);
1638
1639 if (stat(config_file, &conf_stat) >= 0) {
1640 if (unlink(config_file_sav) != 0)
1641 if (errno != ENOENT) {
1642 vty_out(vty,
1643 "Can't unlink backup configuration file %s.\n",
1644 config_file_sav);
1645 goto finished;
1646 }
1647 if (link(config_file, config_file_sav) != 0) {
1648 vty_out(vty,
1649 "Can't backup old configuration file %s.\n",
1650 config_file_sav);
1651 goto finished;
1652 }
1653 if (dirfd >= 0)
1654 fsync(dirfd);
1655 }
1656 if (rename(config_file_tmp, config_file) != 0) {
1657 vty_out(vty, "Can't save configuration file %s.\n",
1658 config_file);
1659 goto finished;
1660 }
1661 if (dirfd >= 0)
1662 fsync(dirfd);
1663
1664 vty_out(vty, "Configuration saved to %s\n", config_file);
1665 ret = CMD_SUCCESS;
1666
1667 finished:
1668 if (ret != CMD_SUCCESS)
1669 unlink(config_file_tmp);
1670 if (dirfd >= 0)
1671 close(dirfd);
1672 XFREE(MTYPE_TMP, config_file_tmp);
1673 XFREE(MTYPE_TMP, config_file_sav);
1674 return ret;
1675 }
1676
1677 /* Write current configuration into file. */
1678
1679 DEFUN (config_write,
1680 config_write_cmd,
1681 "write [<file|memory|terminal>]",
1682 "Write running configuration to memory, network, or terminal\n"
1683 "Write to configuration file\n"
1684 "Write configuration currently in memory\n"
1685 "Write configuration to terminal\n")
1686 {
1687 const int idx_type = 1;
1688
1689 // if command was 'write terminal' or 'write memory'
1690 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1691 return vty_write_config(vty);
1692 }
1693
1694 return file_write_config(vty);
1695 }
1696
1697 /* ALIAS_FIXME for 'write <terminal|memory>' */
1698 DEFUN (show_running_config,
1699 show_running_config_cmd,
1700 "show running-config",
1701 SHOW_STR
1702 "running configuration (same as write terminal)\n")
1703 {
1704 return vty_write_config(vty);
1705 }
1706
1707 /* ALIAS_FIXME for 'write file' */
1708 DEFUN (copy_runningconf_startupconf,
1709 copy_runningconf_startupconf_cmd,
1710 "copy running-config startup-config",
1711 "Copy configuration\n"
1712 "Copy running config to... \n"
1713 "Copy running config to startup config (same as write file/memory)\n")
1714 {
1715 return file_write_config(vty);
1716 }
1717 /** -- **/
1718
1719 /* Write startup configuration into the terminal. */
1720 DEFUN (show_startup_config,
1721 show_startup_config_cmd,
1722 "show startup-config",
1723 SHOW_STR
1724 "Contents of startup configuration\n")
1725 {
1726 char buf[BUFSIZ];
1727 FILE *confp;
1728
1729 if (host.noconfig)
1730 return CMD_SUCCESS;
1731 if (host.config == NULL)
1732 return CMD_WARNING;
1733
1734 confp = fopen(host.config, "r");
1735 if (confp == NULL) {
1736 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1737 host.config, safe_strerror(errno));
1738 return CMD_WARNING;
1739 }
1740
1741 while (fgets(buf, BUFSIZ, confp)) {
1742 char *cp = buf;
1743
1744 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1745 cp++;
1746 *cp = '\0';
1747
1748 vty_out(vty, "%s\n", buf);
1749 }
1750
1751 fclose(confp);
1752
1753 return CMD_SUCCESS;
1754 }
1755
1756 int cmd_domainname_set(const char *domainname)
1757 {
1758 XFREE(MTYPE_HOST, host.domainname);
1759 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1760 return CMD_SUCCESS;
1761 }
1762
1763 /* Hostname configuration */
1764 DEFUN(config_domainname,
1765 domainname_cmd,
1766 "domainname WORD",
1767 "Set system's domain name\n"
1768 "This system's domain name\n")
1769 {
1770 struct cmd_token *word = argv[1];
1771
1772 if (!isalpha((unsigned char)word->arg[0])) {
1773 vty_out(vty, "Please specify string starting with alphabet\n");
1774 return CMD_WARNING_CONFIG_FAILED;
1775 }
1776
1777 return cmd_domainname_set(word->arg);
1778 }
1779
1780 DEFUN(config_no_domainname,
1781 no_domainname_cmd,
1782 "no domainname [DOMAINNAME]",
1783 NO_STR
1784 "Reset system's domain name\n"
1785 "domain name of this router\n")
1786 {
1787 return cmd_domainname_set(NULL);
1788 }
1789
1790 int cmd_hostname_set(const char *hostname)
1791 {
1792 XFREE(MTYPE_HOST, host.name);
1793 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1794 return CMD_SUCCESS;
1795 }
1796
1797 /* Hostname configuration */
1798 DEFUN (config_hostname,
1799 hostname_cmd,
1800 "hostname WORD",
1801 "Set system's network name\n"
1802 "This system's network name\n")
1803 {
1804 struct cmd_token *word = argv[1];
1805
1806 if (!isalnum((unsigned char)word->arg[0])) {
1807 vty_out(vty,
1808 "Please specify string starting with alphabet or number\n");
1809 return CMD_WARNING_CONFIG_FAILED;
1810 }
1811
1812 /* With reference to RFC 1123 Section 2.1 */
1813 if (strlen(word->arg) > HOSTNAME_LEN) {
1814 vty_out(vty, "Hostname length should be less than %d chars\n",
1815 HOSTNAME_LEN);
1816 return CMD_WARNING_CONFIG_FAILED;
1817 }
1818
1819 return cmd_hostname_set(word->arg);
1820 }
1821
1822 DEFUN (config_no_hostname,
1823 no_hostname_cmd,
1824 "no hostname [HOSTNAME]",
1825 NO_STR
1826 "Reset system's network name\n"
1827 "Host name of this router\n")
1828 {
1829 return cmd_hostname_set(NULL);
1830 }
1831
1832 /* VTY interface password set. */
1833 DEFUN (config_password,
1834 password_cmd,
1835 "password [(8-8)] WORD",
1836 "Modify the terminal connection password\n"
1837 "Specifies a HIDDEN password will follow\n"
1838 "The password string\n")
1839 {
1840 int idx_8 = 1;
1841 int idx_word = 2;
1842 if (argc == 3) // '8' was specified
1843 {
1844 if (host.password)
1845 XFREE(MTYPE_HOST, host.password);
1846 host.password = NULL;
1847 if (host.password_encrypt)
1848 XFREE(MTYPE_HOST, host.password_encrypt);
1849 host.password_encrypt =
1850 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1851 return CMD_SUCCESS;
1852 }
1853
1854 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1855 vty_out(vty,
1856 "Please specify string starting with alphanumeric\n");
1857 return CMD_WARNING_CONFIG_FAILED;
1858 }
1859
1860 if (host.password)
1861 XFREE(MTYPE_HOST, host.password);
1862 host.password = NULL;
1863
1864 if (host.encrypt) {
1865 if (host.password_encrypt)
1866 XFREE(MTYPE_HOST, host.password_encrypt);
1867 host.password_encrypt =
1868 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1869 } else
1870 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1871
1872 return CMD_SUCCESS;
1873 }
1874
1875 /* VTY interface password delete. */
1876 DEFUN (no_config_password,
1877 no_password_cmd,
1878 "no password",
1879 NO_STR
1880 "Modify the terminal connection password\n")
1881 {
1882 bool warned = false;
1883
1884 if (host.password) {
1885 if (!vty_shell_serv(vty)) {
1886 vty_out(vty, NO_PASSWD_CMD_WARNING);
1887 warned = true;
1888 }
1889 XFREE(MTYPE_HOST, host.password);
1890 }
1891 host.password = NULL;
1892
1893 if (host.password_encrypt) {
1894 if (!warned && !vty_shell_serv(vty))
1895 vty_out(vty, NO_PASSWD_CMD_WARNING);
1896 XFREE(MTYPE_HOST, host.password_encrypt);
1897 }
1898 host.password_encrypt = NULL;
1899
1900 return CMD_SUCCESS;
1901 }
1902
1903 /* VTY enable password set. */
1904 DEFUN (config_enable_password,
1905 enable_password_cmd,
1906 "enable password [(8-8)] WORD",
1907 "Modify enable password parameters\n"
1908 "Assign the privileged level password\n"
1909 "Specifies a HIDDEN password will follow\n"
1910 "The HIDDEN 'enable' password string\n")
1911 {
1912 int idx_8 = 2;
1913 int idx_word = 3;
1914
1915 /* Crypt type is specified. */
1916 if (argc == 4) {
1917 if (argv[idx_8]->arg[0] == '8') {
1918 if (host.enable)
1919 XFREE(MTYPE_HOST, host.enable);
1920 host.enable = NULL;
1921
1922 if (host.enable_encrypt)
1923 XFREE(MTYPE_HOST, host.enable_encrypt);
1924 host.enable_encrypt =
1925 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1926
1927 return CMD_SUCCESS;
1928 } else {
1929 vty_out(vty, "Unknown encryption type.\n");
1930 return CMD_WARNING_CONFIG_FAILED;
1931 }
1932 }
1933
1934 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1935 vty_out(vty,
1936 "Please specify string starting with alphanumeric\n");
1937 return CMD_WARNING_CONFIG_FAILED;
1938 }
1939
1940 if (host.enable)
1941 XFREE(MTYPE_HOST, host.enable);
1942 host.enable = NULL;
1943
1944 /* Plain password input. */
1945 if (host.encrypt) {
1946 if (host.enable_encrypt)
1947 XFREE(MTYPE_HOST, host.enable_encrypt);
1948 host.enable_encrypt =
1949 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1950 } else
1951 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1952
1953 return CMD_SUCCESS;
1954 }
1955
1956 /* VTY enable password delete. */
1957 DEFUN (no_config_enable_password,
1958 no_enable_password_cmd,
1959 "no enable password",
1960 NO_STR
1961 "Modify enable password parameters\n"
1962 "Assign the privileged level password\n")
1963 {
1964 bool warned = false;
1965
1966 if (host.enable) {
1967 if (!vty_shell_serv(vty)) {
1968 vty_out(vty, NO_PASSWD_CMD_WARNING);
1969 warned = true;
1970 }
1971 XFREE(MTYPE_HOST, host.enable);
1972 }
1973 host.enable = NULL;
1974
1975 if (host.enable_encrypt) {
1976 if (!warned && !vty_shell_serv(vty))
1977 vty_out(vty, NO_PASSWD_CMD_WARNING);
1978 XFREE(MTYPE_HOST, host.enable_encrypt);
1979 }
1980 host.enable_encrypt = NULL;
1981
1982 return CMD_SUCCESS;
1983 }
1984
1985 DEFUN (service_password_encrypt,
1986 service_password_encrypt_cmd,
1987 "service password-encryption",
1988 "Set up miscellaneous service\n"
1989 "Enable encrypted passwords\n")
1990 {
1991 if (host.encrypt)
1992 return CMD_SUCCESS;
1993
1994 host.encrypt = 1;
1995
1996 if (host.password) {
1997 if (host.password_encrypt)
1998 XFREE(MTYPE_HOST, host.password_encrypt);
1999 host.password_encrypt =
2000 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
2001 }
2002 if (host.enable) {
2003 if (host.enable_encrypt)
2004 XFREE(MTYPE_HOST, host.enable_encrypt);
2005 host.enable_encrypt =
2006 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
2007 }
2008
2009 return CMD_SUCCESS;
2010 }
2011
2012 DEFUN (no_service_password_encrypt,
2013 no_service_password_encrypt_cmd,
2014 "no service password-encryption",
2015 NO_STR
2016 "Set up miscellaneous service\n"
2017 "Enable encrypted passwords\n")
2018 {
2019 if (!host.encrypt)
2020 return CMD_SUCCESS;
2021
2022 host.encrypt = 0;
2023
2024 if (host.password_encrypt)
2025 XFREE(MTYPE_HOST, host.password_encrypt);
2026 host.password_encrypt = NULL;
2027
2028 if (host.enable_encrypt)
2029 XFREE(MTYPE_HOST, host.enable_encrypt);
2030 host.enable_encrypt = NULL;
2031
2032 return CMD_SUCCESS;
2033 }
2034
2035 DEFUN (config_terminal_length,
2036 config_terminal_length_cmd,
2037 "terminal length (0-512)",
2038 "Set terminal line parameters\n"
2039 "Set number of lines on a screen\n"
2040 "Number of lines on screen (0 for no pausing)\n")
2041 {
2042 int idx_number = 2;
2043
2044 vty->lines = atoi(argv[idx_number]->arg);
2045
2046 return CMD_SUCCESS;
2047 }
2048
2049 DEFUN (config_terminal_no_length,
2050 config_terminal_no_length_cmd,
2051 "terminal no length",
2052 "Set terminal line parameters\n"
2053 NO_STR
2054 "Set number of lines on a screen\n")
2055 {
2056 vty->lines = -1;
2057 return CMD_SUCCESS;
2058 }
2059
2060 DEFUN (service_terminal_length,
2061 service_terminal_length_cmd,
2062 "service terminal-length (0-512)",
2063 "Set up miscellaneous service\n"
2064 "System wide terminal length configuration\n"
2065 "Number of lines of VTY (0 means no line control)\n")
2066 {
2067 int idx_number = 2;
2068
2069 host.lines = atoi(argv[idx_number]->arg);
2070
2071 return CMD_SUCCESS;
2072 }
2073
2074 DEFUN (no_service_terminal_length,
2075 no_service_terminal_length_cmd,
2076 "no service terminal-length [(0-512)]",
2077 NO_STR
2078 "Set up miscellaneous service\n"
2079 "System wide terminal length configuration\n"
2080 "Number of lines of VTY (0 means no line control)\n")
2081 {
2082 host.lines = -1;
2083 return CMD_SUCCESS;
2084 }
2085
2086 DEFUN_HIDDEN (do_echo,
2087 echo_cmd,
2088 "echo MESSAGE...",
2089 "Echo a message back to the vty\n"
2090 "The message to echo\n")
2091 {
2092 char *message;
2093
2094 vty_out(vty, "%s\n",
2095 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2096 if (message)
2097 XFREE(MTYPE_TMP, message);
2098 return CMD_SUCCESS;
2099 }
2100
2101 DEFUN (config_logmsg,
2102 config_logmsg_cmd,
2103 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2104 "Send a message to enabled logging destinations\n"
2105 LOG_LEVEL_DESC
2106 "The message to send\n")
2107 {
2108 int idx_log_level = 1;
2109 int idx_message = 2;
2110 int level;
2111 char *message;
2112
2113 level = log_level_match(argv[idx_log_level]->arg);
2114 if (level == ZLOG_DISABLED)
2115 return CMD_ERR_NO_MATCH;
2116
2117 zlog(level, "%s",
2118 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2119 if (message)
2120 XFREE(MTYPE_TMP, message);
2121
2122 return CMD_SUCCESS;
2123 }
2124
2125 DEFUN (debug_memstats,
2126 debug_memstats_cmd,
2127 "[no] debug memstats-at-exit",
2128 NO_STR
2129 DEBUG_STR
2130 "Print memory type statistics at exit\n")
2131 {
2132 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2133 return CMD_SUCCESS;
2134 }
2135
2136 int cmd_banner_motd_file(const char *file)
2137 {
2138 int success = CMD_SUCCESS;
2139 char p[PATH_MAX];
2140 char *rpath;
2141 char *in;
2142
2143 rpath = realpath(file, p);
2144 if (!rpath)
2145 return CMD_ERR_NO_FILE;
2146 in = strstr(rpath, SYSCONFDIR);
2147 if (in == rpath) {
2148 XFREE(MTYPE_HOST, host.motdfile);
2149 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2150 } else
2151 success = CMD_WARNING_CONFIG_FAILED;
2152
2153 return success;
2154 }
2155
2156 void cmd_banner_motd_line(const char *line)
2157 {
2158 XFREE(MTYPE_HOST, host.motd);
2159 host.motd = XSTRDUP(MTYPE_HOST, line);
2160 }
2161
2162 DEFUN (banner_motd_file,
2163 banner_motd_file_cmd,
2164 "banner motd file FILE",
2165 "Set banner\n"
2166 "Banner for motd\n"
2167 "Banner from a file\n"
2168 "Filename\n")
2169 {
2170 int idx_file = 3;
2171 const char *filename = argv[idx_file]->arg;
2172 int cmd = cmd_banner_motd_file(filename);
2173
2174 if (cmd == CMD_ERR_NO_FILE)
2175 vty_out(vty, "%s does not exist", filename);
2176 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2177 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
2178
2179 return cmd;
2180 }
2181
2182 DEFUN (banner_motd_line,
2183 banner_motd_line_cmd,
2184 "banner motd line LINE...",
2185 "Set banner\n"
2186 "Banner for motd\n"
2187 "Banner from an input\n"
2188 "Text\n")
2189 {
2190 int idx = 0;
2191 char *motd;
2192
2193 argv_find(argv, argc, "LINE", &idx);
2194 motd = argv_concat(argv, argc, idx);
2195
2196 cmd_banner_motd_line(motd);
2197 XFREE(MTYPE_TMP, motd);
2198
2199 return CMD_SUCCESS;
2200 }
2201
2202 DEFUN (banner_motd_default,
2203 banner_motd_default_cmd,
2204 "banner motd default",
2205 "Set banner string\n"
2206 "Strings for motd\n"
2207 "Default string\n")
2208 {
2209 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2210 return CMD_SUCCESS;
2211 }
2212
2213 DEFUN (no_banner_motd,
2214 no_banner_motd_cmd,
2215 "no banner motd",
2216 NO_STR
2217 "Set banner string\n"
2218 "Strings for motd\n")
2219 {
2220 host.motd = NULL;
2221 if (host.motdfile)
2222 XFREE(MTYPE_HOST, host.motdfile);
2223 host.motdfile = NULL;
2224 return CMD_SUCCESS;
2225 }
2226
2227 DEFUN(find,
2228 find_cmd,
2229 "find REGEX...",
2230 "Find CLI command matching a regular expression\n"
2231 "Search pattern (POSIX regex)\n")
2232 {
2233 const struct cmd_node *node;
2234 const struct cmd_element *cli;
2235 vector clis;
2236
2237 regex_t exp = {};
2238
2239 char *pattern = argv_concat(argv, argc, 1);
2240 int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
2241 XFREE(MTYPE_TMP, pattern);
2242
2243 if (cr != 0) {
2244 switch (cr) {
2245 case REG_BADBR:
2246 vty_out(vty, "%% Invalid {...} expression\n");
2247 break;
2248 case REG_BADRPT:
2249 vty_out(vty, "%% Bad repetition operator\n");
2250 break;
2251 case REG_BADPAT:
2252 vty_out(vty, "%% Regex syntax error\n");
2253 break;
2254 case REG_ECOLLATE:
2255 vty_out(vty, "%% Invalid collating element\n");
2256 break;
2257 case REG_ECTYPE:
2258 vty_out(vty, "%% Invalid character class name\n");
2259 break;
2260 case REG_EESCAPE:
2261 vty_out(vty,
2262 "%% Regex ended with escape character (\\)\n");
2263 break;
2264 case REG_ESUBREG:
2265 vty_out(vty,
2266 "%% Invalid number in \\digit construction\n");
2267 break;
2268 case REG_EBRACK:
2269 vty_out(vty, "%% Unbalanced square brackets\n");
2270 break;
2271 case REG_EPAREN:
2272 vty_out(vty, "%% Unbalanced parentheses\n");
2273 break;
2274 case REG_EBRACE:
2275 vty_out(vty, "%% Unbalanced braces\n");
2276 break;
2277 case REG_ERANGE:
2278 vty_out(vty,
2279 "%% Invalid endpoint in range expression\n");
2280 break;
2281 case REG_ESPACE:
2282 vty_out(vty, "%% Failed to compile (out of memory)\n");
2283 break;
2284 }
2285
2286 goto done;
2287 }
2288
2289
2290 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
2291 node = vector_slot(cmdvec, i);
2292 if (!node)
2293 continue;
2294 clis = node->cmd_vector;
2295 for (unsigned int j = 0; j < vector_active(clis); j++) {
2296 cli = vector_slot(clis, j);
2297
2298 if (regexec(&exp, cli->string, 0, NULL, 0) == 0)
2299 vty_out(vty, " (%s) %s\n",
2300 node->name, cli->string);
2301 }
2302 }
2303
2304 done:
2305 regfree(&exp);
2306 return CMD_SUCCESS;
2307 }
2308
2309 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2310 DEFUN(script,
2311 script_cmd,
2312 "script SCRIPT",
2313 "Test command - execute a script\n"
2314 "Script name (same as filename in /etc/frr/scripts/\n")
2315 {
2316 struct prefix p;
2317
2318 (void)str2prefix("1.2.3.4/24", &p);
2319
2320 struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
2321
2322 if (fs == NULL) {
2323 vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
2324 argv[1]->arg);
2325 } else {
2326 int ret = frrscript_call(fs, NULL);
2327 vty_out(vty, "Script result: %d\n", ret);
2328 }
2329
2330 return CMD_SUCCESS;
2331 }
2332 #endif
2333
2334 /* Set config filename. Called from vty.c */
2335 void host_config_set(const char *filename)
2336 {
2337 XFREE(MTYPE_HOST, host.config);
2338 host.config = XSTRDUP(MTYPE_HOST, filename);
2339 }
2340
2341 const char *host_config_get(void)
2342 {
2343 return host.config;
2344 }
2345
2346 void install_default(enum node_type node)
2347 {
2348 _install_element(node, &config_exit_cmd);
2349 _install_element(node, &config_quit_cmd);
2350 _install_element(node, &config_end_cmd);
2351 _install_element(node, &config_help_cmd);
2352 _install_element(node, &config_list_cmd);
2353 _install_element(node, &show_cli_graph_cmd);
2354 _install_element(node, &find_cmd);
2355
2356 _install_element(node, &config_write_cmd);
2357 _install_element(node, &show_running_config_cmd);
2358
2359 _install_element(node, &autocomplete_cmd);
2360
2361 nb_cli_install_default(node);
2362 }
2363
2364 /* Initialize command interface. Install basic nodes and commands.
2365 *
2366 * terminal = 0 -- vtysh / no logging, no config control
2367 * terminal = 1 -- normal daemon
2368 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2369 void cmd_init(int terminal)
2370 {
2371 struct utsname names;
2372
2373 uname(&names);
2374 qobj_init();
2375
2376 /* register command preprocessors */
2377 hook_register(cmd_execute, handle_pipe_action);
2378 hook_register(cmd_execute_done, handle_pipe_action_done);
2379
2380 varhandlers = list_new();
2381
2382 /* Allocate initial top vector of commands. */
2383 cmdvec = vector_init(VECTOR_MIN_SIZE);
2384
2385 /* Default host value settings. */
2386 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2387 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2388 if ((strcmp(names.domainname, "(none)") == 0))
2389 host.domainname = NULL;
2390 else
2391 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
2392 #else
2393 host.domainname = NULL;
2394 #endif
2395 host.password = NULL;
2396 host.enable = NULL;
2397 host.config = NULL;
2398 host.noconfig = (terminal < 0);
2399 host.lines = -1;
2400 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2401 host.motdfile = NULL;
2402
2403 /* Install top nodes. */
2404 install_node(&view_node);
2405 install_node(&enable_node);
2406 install_node(&auth_node);
2407 install_node(&auth_enable_node);
2408 install_node(&config_node);
2409
2410 /* Each node's basic commands. */
2411 install_element(VIEW_NODE, &show_version_cmd);
2412 install_element(ENABLE_NODE, &show_startup_config_cmd);
2413
2414 if (terminal) {
2415 install_element(ENABLE_NODE, &debug_memstats_cmd);
2416
2417 install_element(VIEW_NODE, &config_list_cmd);
2418 install_element(VIEW_NODE, &config_exit_cmd);
2419 install_element(VIEW_NODE, &config_quit_cmd);
2420 install_element(VIEW_NODE, &config_help_cmd);
2421 install_element(VIEW_NODE, &config_enable_cmd);
2422 install_element(VIEW_NODE, &config_terminal_length_cmd);
2423 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2424 install_element(VIEW_NODE, &show_commandtree_cmd);
2425 install_element(VIEW_NODE, &echo_cmd);
2426 install_element(VIEW_NODE, &autocomplete_cmd);
2427 install_element(VIEW_NODE, &find_cmd);
2428 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2429 install_element(VIEW_NODE, &script_cmd);
2430 #endif
2431
2432
2433 install_element(ENABLE_NODE, &config_end_cmd);
2434 install_element(ENABLE_NODE, &config_disable_cmd);
2435 install_element(ENABLE_NODE, &config_terminal_cmd);
2436 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2437 install_element(ENABLE_NODE, &config_write_cmd);
2438 install_element(ENABLE_NODE, &show_running_config_cmd);
2439 install_element(ENABLE_NODE, &config_logmsg_cmd);
2440
2441 install_default(CONFIG_NODE);
2442
2443 thread_cmd_init();
2444 workqueue_cmd_init();
2445 hash_cmd_init();
2446 }
2447
2448 install_element(CONFIG_NODE, &hostname_cmd);
2449 install_element(CONFIG_NODE, &no_hostname_cmd);
2450 install_element(CONFIG_NODE, &domainname_cmd);
2451 install_element(CONFIG_NODE, &no_domainname_cmd);
2452
2453 if (terminal > 0) {
2454 full_cli = true;
2455
2456 install_element(CONFIG_NODE, &debug_memstats_cmd);
2457
2458 install_element(CONFIG_NODE, &password_cmd);
2459 install_element(CONFIG_NODE, &no_password_cmd);
2460 install_element(CONFIG_NODE, &enable_password_cmd);
2461 install_element(CONFIG_NODE, &no_enable_password_cmd);
2462
2463 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2464 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2465 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2466 install_element(CONFIG_NODE, &banner_motd_file_cmd);
2467 install_element(CONFIG_NODE, &banner_motd_line_cmd);
2468 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2469 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2470 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2471
2472 log_cmd_init();
2473 vrf_install_commands();
2474 }
2475
2476 #ifdef DEV_BUILD
2477 grammar_sandbox_init();
2478 #endif
2479 }
2480
2481 void cmd_terminate(void)
2482 {
2483 struct cmd_node *cmd_node;
2484
2485 hook_unregister(cmd_execute, handle_pipe_action);
2486 hook_unregister(cmd_execute_done, handle_pipe_action_done);
2487
2488 if (cmdvec) {
2489 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2490 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2491 // deleting the graph delets the cmd_element as
2492 // well
2493 graph_delete_graph(cmd_node->cmdgraph);
2494 vector_free(cmd_node->cmd_vector);
2495 hash_clean(cmd_node->cmd_hash, NULL);
2496 hash_free(cmd_node->cmd_hash);
2497 cmd_node->cmd_hash = NULL;
2498 }
2499
2500 vector_free(cmdvec);
2501 cmdvec = NULL;
2502 }
2503
2504 XFREE(MTYPE_HOST, host.name);
2505 XFREE(MTYPE_HOST, host.domainname);
2506 XFREE(MTYPE_HOST, host.password);
2507 XFREE(MTYPE_HOST, host.password_encrypt);
2508 XFREE(MTYPE_HOST, host.enable);
2509 XFREE(MTYPE_HOST, host.enable_encrypt);
2510 XFREE(MTYPE_HOST, host.motdfile);
2511 XFREE(MTYPE_HOST, host.config);
2512 XFREE(MTYPE_HOST, host.motd);
2513
2514 list_delete(&varhandlers);
2515 qobj_finish();
2516 }