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