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