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