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