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