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