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