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