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