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 */
cmd_hostname_get(void)90 const char *cmd_hostname_get(void)
91 {
92 return host.name;
93 }
94
95 /*
96 * Returns unix domainname
97 */
cmd_domainname_get(void)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. */
print_version(const char * progname)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
argv_concat(struct cmd_token ** argv,int argc,int shift)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
cmd_make_strvec(const char * string)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
cmd_free_strvec(vector v)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 */
argv_find(struct cmd_token ** argv,int argc,const char * text,int * index)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
cmd_hash_key(const void * p)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
cmd_hash_cmp(const void * a,const void * b)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. */
install_node(struct cmd_node * node)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. */
cmd_prompt(enum node_type 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. */
install_element(enum node_type ntype,const struct cmd_element * cmd)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
uninstall_element(enum node_type ntype,const struct cmd_element * cmd)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
to64(char * s,long v,int n)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
zencrypt(const char * passwd)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. */
config_write_host(struct vty * vty)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. */
cmd_node_graph(vector v,enum node_type ntype)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
cmd_try_do_shortcut(enum node_type node,char * first_word)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 */
compare_completions(const void * fst,const void * snd)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 */
completions_to_vec(struct list * completions)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 */
cmd_complete_command_real(vector vline,struct vty * vty,int * status)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
cmd_describe_command(vector vline,struct vty * vty,int * status)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
cmd_variable_complete(struct cmd_token * token,const char * arg,vector comps)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
cmd_variable_comp2str(vector comps,unsigned short cols)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
cmd_variable_handler_register(const struct cmd_variable_handler * cvh)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
cmd_complete_command(vector vline,struct vty * vty,int * status)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 */
node_parent(enum node_type 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. */
cmd_execute_command_real(vector vline,enum cmd_filter_type filter,struct vty * vty,const struct cmd_element ** cmd)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 * Perform pending commit (if any) before executing
910 * non-YANG command.
911 */
912 if (matched_element->attr != CMD_ATTR_YANG)
913 nb_cli_pending_commit_check(vty);
914 }
915
916 ret = matched_element->func(matched_element, vty, argc, argv);
917 }
918
919 // delete list and cmd_token's in it
920 list_delete(&argv_list);
921 XFREE(MTYPE_TMP, argv);
922
923 return ret;
924 }
925
926 /**
927 * Execute a given command, handling things like "do ..." and checking
928 * whether the given command might apply at a parent node if doesn't
929 * apply for the current node.
930 *
931 * @param vline Command line input, vector of char* where each element is
932 * one input token.
933 * @param vty The vty context in which the command should be executed.
934 * @param cmd Pointer where the struct cmd_element of the matched command
935 * will be stored, if any. May be set to NULL if this info is
936 * not needed.
937 * @param vtysh If set != 0, don't lookup the command at parent nodes.
938 * @return The status of the command that has been executed or an error code
939 * as to why no command could be executed.
940 */
cmd_execute_command(vector vline,struct vty * vty,const struct cmd_element ** cmd,int vtysh)941 int cmd_execute_command(vector vline, struct vty *vty,
942 const struct cmd_element **cmd, int vtysh)
943 {
944 int ret, saved_ret = 0;
945 enum node_type onode, try_node;
946 int orig_xpath_index;
947
948 onode = try_node = vty->node;
949 orig_xpath_index = vty->xpath_index;
950
951 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
952 vector shifted_vline;
953 unsigned int index;
954
955 vty->node = ENABLE_NODE;
956 vty->xpath_index = 0;
957 /* We can try it on enable node, cos' the vty is authenticated
958 */
959
960 shifted_vline = vector_init(vector_count(vline));
961 /* use memcpy? */
962 for (index = 1; index < vector_active(vline); index++)
963 vector_set_index(shifted_vline, index - 1,
964 vector_lookup(vline, index));
965
966 ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
967 vty, cmd);
968
969 vector_free(shifted_vline);
970 vty->node = onode;
971 vty->xpath_index = orig_xpath_index;
972 return ret;
973 }
974
975 saved_ret = ret =
976 cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd);
977
978 if (vtysh)
979 return saved_ret;
980
981 if (ret != CMD_SUCCESS && ret != CMD_WARNING
982 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
983 /* This assumes all nodes above CONFIG_NODE are childs of
984 * CONFIG_NODE */
985 while (vty->node > CONFIG_NODE) {
986 try_node = node_parent(try_node);
987 vty->node = try_node;
988 if (vty->xpath_index > 0)
989 vty->xpath_index--;
990 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
991 vty, cmd);
992 if (ret == CMD_SUCCESS || ret == CMD_WARNING
993 || ret == CMD_NOT_MY_INSTANCE
994 || ret == CMD_WARNING_CONFIG_FAILED)
995 return ret;
996 }
997 /* no command succeeded, reset the vty to the original node */
998 vty->node = onode;
999 vty->xpath_index = orig_xpath_index;
1000 }
1001
1002 /* return command status for original node */
1003 return saved_ret;
1004 }
1005
1006 /**
1007 * Execute a given command, matching it strictly against the current node.
1008 * This mode is used when reading config files.
1009 *
1010 * @param vline Command line input, vector of char* where each element is
1011 * one input token.
1012 * @param vty The vty context in which the command should be executed.
1013 * @param cmd Pointer where the struct cmd_element* of the matched command
1014 * will be stored, if any. May be set to NULL if this info is
1015 * not needed.
1016 * @return The status of the command that has been executed or an error code
1017 * as to why no command could be executed.
1018 */
cmd_execute_command_strict(vector vline,struct vty * vty,const struct cmd_element ** cmd)1019 int cmd_execute_command_strict(vector vline, struct vty *vty,
1020 const struct cmd_element **cmd)
1021 {
1022 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
1023 }
1024
1025 /*
1026 * Hook for preprocessing command string before executing.
1027 *
1028 * All subscribers are called with the raw command string that is to be
1029 * executed. If any changes are to be made, a new string should be allocated
1030 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1031 * is then responsible for freeing this string.
1032 *
1033 * All processing functions must be mutually exclusive in their action, i.e. if
1034 * one subscriber decides to modify the command, all others must not modify it
1035 * when called. Feeding the output of one processing command into a subsequent
1036 * one is not supported.
1037 *
1038 * This hook is intentionally internal to the command processing system.
1039 *
1040 * cmd_in
1041 * The raw command string.
1042 *
1043 * cmd_out
1044 * The result of any processing.
1045 */
1046 DECLARE_HOOK(cmd_execute,
1047 (struct vty *vty, const char *cmd_in, char **cmd_out),
1048 (vty, cmd_in, cmd_out));
1049 DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
1050 (vty, cmd_in, cmd_out));
1051
1052 /* Hook executed after a CLI command. */
1053 DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1054 (vty, cmd_exec));
1055 DEFINE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1056 (vty, cmd_exec));
1057
1058 /*
1059 * cmd_execute hook subscriber to handle `|` actions.
1060 */
handle_pipe_action(struct vty * vty,const char * cmd_in,char ** cmd_out)1061 static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1062 char **cmd_out)
1063 {
1064 /* look for `|` */
1065 char *orig, *working, *token, *u;
1066 char *pipe = strstr(cmd_in, "| ");
1067 int ret = 0;
1068
1069 if (!pipe)
1070 return 0;
1071
1072 /* duplicate string for processing purposes, not including pipe */
1073 orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1074
1075 /* retrieve action */
1076 token = strsep(&working, " ");
1077 assert(token);
1078
1079 /* match result to known actions */
1080 if (strmatch(token, "include")) {
1081 /* the remaining text should be a regexp */
1082 char *regexp = working;
1083
1084 if (!regexp) {
1085 vty_out(vty, "%% Need a regexp to filter with\n");
1086 ret = 1;
1087 goto fail;
1088 }
1089
1090 bool succ = vty_set_include(vty, regexp);
1091
1092 if (!succ) {
1093 vty_out(vty, "%% Bad regexp '%s'\n", regexp);
1094 ret = 1;
1095 goto fail;
1096 }
1097 *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
1098 u = *cmd_out;
1099 strsep(&u, "|");
1100 } else {
1101 vty_out(vty, "%% Unknown action '%s'\n", token);
1102 ret = 1;
1103 goto fail;
1104 }
1105
1106 fail:
1107 XFREE(MTYPE_TMP, orig);
1108 return ret;
1109 }
1110
handle_pipe_action_done(struct vty * vty,const char * cmd_exec)1111 static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1112 {
1113 if (vty->filter)
1114 vty_set_include(vty, NULL);
1115
1116 return 0;
1117 }
1118
cmd_execute(struct vty * vty,const char * cmd,const struct cmd_element ** matched,int vtysh)1119 int cmd_execute(struct vty *vty, const char *cmd,
1120 const struct cmd_element **matched, int vtysh)
1121 {
1122 int ret;
1123 char *cmd_out = NULL;
1124 const char *cmd_exec = NULL;
1125 vector vline;
1126
1127 ret = hook_call(cmd_execute, vty, cmd, &cmd_out);
1128 if (ret) {
1129 ret = CMD_WARNING;
1130 goto free;
1131 }
1132
1133 cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1134
1135 vline = cmd_make_strvec(cmd_exec);
1136
1137 if (vline) {
1138 ret = cmd_execute_command(vline, vty, matched, vtysh);
1139 cmd_free_strvec(vline);
1140 } else {
1141 ret = CMD_SUCCESS;
1142 }
1143
1144 free:
1145 hook_call(cmd_execute_done, vty, cmd_exec);
1146
1147 XFREE(MTYPE_TMP, cmd_out);
1148
1149 return ret;
1150 }
1151
1152
1153 /**
1154 * Parse one line of config, walking up the parse tree attempting to find a
1155 * match
1156 *
1157 * @param vty The vty context in which the command should be executed.
1158 * @param cmd Pointer where the struct cmd_element* of the match command
1159 * will be stored, if any. May be set to NULL if this info is
1160 * not needed.
1161 * @param use_daemon Boolean to control whether or not we match on
1162 * CMD_SUCCESS_DAEMON
1163 * or not.
1164 * @return The status of the command that has been executed or an error code
1165 * as to why no command could be executed.
1166 */
command_config_read_one_line(struct vty * vty,const struct cmd_element ** cmd,uint32_t line_num,int use_daemon)1167 int command_config_read_one_line(struct vty *vty,
1168 const struct cmd_element **cmd,
1169 uint32_t line_num, int use_daemon)
1170 {
1171 vector vline;
1172 int ret;
1173
1174 vline = cmd_make_strvec(vty->buf);
1175
1176 /* In case of comment line */
1177 if (vline == NULL)
1178 return CMD_SUCCESS;
1179
1180 /* Execute configuration command : this is strict match */
1181 ret = cmd_execute_command_strict(vline, vty, cmd);
1182
1183 // Climb the tree and try the command again at each node
1184 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1185 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1186 && ret != CMD_SUCCESS && ret != CMD_WARNING
1187 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
1188 && vty->node != CONFIG_NODE) {
1189 int saved_node = vty->node;
1190 int saved_xpath_index = vty->xpath_index;
1191
1192 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1193 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1194 && ret != CMD_SUCCESS && ret != CMD_WARNING
1195 && vty->node > CONFIG_NODE) {
1196 vty->node = node_parent(vty->node);
1197 if (vty->xpath_index > 0)
1198 vty->xpath_index--;
1199 ret = cmd_execute_command_strict(vline, vty, cmd);
1200 }
1201
1202 // If climbing the tree did not work then ignore the command and
1203 // stay at the same node
1204 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1205 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1206 && ret != CMD_SUCCESS && ret != CMD_WARNING) {
1207 vty->node = saved_node;
1208 vty->xpath_index = saved_xpath_index;
1209 }
1210 }
1211
1212 if (ret != CMD_SUCCESS &&
1213 ret != CMD_WARNING &&
1214 ret != CMD_SUCCESS_DAEMON) {
1215 struct vty_error *ve = XCALLOC(MTYPE_TMP, sizeof(*ve));
1216
1217 memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
1218 ve->line_num = line_num;
1219 if (!vty->error)
1220 vty->error = list_new();
1221
1222 listnode_add(vty->error, ve);
1223 }
1224
1225 cmd_free_strvec(vline);
1226
1227 return ret;
1228 }
1229
1230 /* Configuration make from file. */
config_from_file(struct vty * vty,FILE * fp,unsigned int * line_num)1231 int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
1232 {
1233 int ret, error_ret = 0;
1234 *line_num = 0;
1235
1236 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
1237 ++(*line_num);
1238
1239 ret = command_config_read_one_line(vty, NULL, *line_num, 0);
1240
1241 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1242 && ret != CMD_ERR_NOTHING_TODO)
1243 error_ret = ret;
1244 }
1245
1246 if (error_ret) {
1247 return error_ret;
1248 }
1249
1250 return CMD_SUCCESS;
1251 }
1252
1253 /* Configuration from terminal */
1254 DEFUN (config_terminal,
1255 config_terminal_cmd,
1256 "configure [terminal]",
1257 "Configuration from vty interface\n"
1258 "Configuration terminal\n")
1259 {
1260 return vty_config_enter(vty, false, false);
1261 }
1262
1263 /* Enable command */
1264 DEFUN (enable,
1265 config_enable_cmd,
1266 "enable",
1267 "Turn on privileged mode command\n")
1268 {
1269 /* If enable password is NULL, change to ENABLE_NODE */
1270 if ((host.enable == NULL && host.enable_encrypt == NULL)
1271 || vty->type == VTY_SHELL_SERV)
1272 vty->node = ENABLE_NODE;
1273 else
1274 vty->node = AUTH_ENABLE_NODE;
1275
1276 return CMD_SUCCESS;
1277 }
1278
1279 /* Disable command */
1280 DEFUN (disable,
1281 config_disable_cmd,
1282 "disable",
1283 "Turn off privileged mode command\n")
1284 {
1285 if (vty->node == ENABLE_NODE)
1286 vty->node = VIEW_NODE;
1287 return CMD_SUCCESS;
1288 }
1289
1290 /* Down vty node level. */
1291 DEFUN (config_exit,
1292 config_exit_cmd,
1293 "exit",
1294 "Exit current mode and down to previous mode\n")
1295 {
1296 cmd_exit(vty);
1297 return CMD_SUCCESS;
1298 }
1299
root_on_exit(struct vty * vty)1300 static int root_on_exit(struct vty *vty)
1301 {
1302 if (vty_shell(vty))
1303 exit(0);
1304 else
1305 vty->status = VTY_CLOSE;
1306 return 0;
1307 }
1308
cmd_exit(struct vty * vty)1309 void cmd_exit(struct vty *vty)
1310 {
1311 struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
1312
1313 if (cnode->node_exit) {
1314 if (!cnode->node_exit(vty))
1315 return;
1316 }
1317 if (cnode->parent_node)
1318 vty->node = cnode->parent_node;
1319 if (vty->xpath_index > 0)
1320 vty->xpath_index--;
1321 }
1322
1323 /* ALIAS_FIXME */
1324 DEFUN (config_quit,
1325 config_quit_cmd,
1326 "quit",
1327 "Exit current mode and down to previous mode\n")
1328 {
1329 return config_exit(self, vty, argc, argv);
1330 }
1331
1332
1333 /* End of configuration. */
1334 DEFUN (config_end,
1335 config_end_cmd,
1336 "end",
1337 "End current mode and change to enable mode.\n")
1338 {
1339 if (vty->config) {
1340 vty_config_exit(vty);
1341 vty->node = ENABLE_NODE;
1342 }
1343 return CMD_SUCCESS;
1344 }
1345
1346 /* Show version. */
1347 DEFUN (show_version,
1348 show_version_cmd,
1349 "show version",
1350 SHOW_STR
1351 "Displays zebra version\n")
1352 {
1353 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
1354 cmd_hostname_get() ? cmd_hostname_get() : "");
1355 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
1356 #ifdef ENABLE_VERSION_BUILD_CONFIG
1357 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
1358 #endif
1359 return CMD_SUCCESS;
1360 }
1361
1362 /* Help display function for all node. */
1363 DEFUN (config_help,
1364 config_help_cmd,
1365 "help",
1366 "Description of the interactive help system\n")
1367 {
1368 vty_out(vty,
1369 "Quagga VTY provides advanced help feature. When you need help,\n\
1370 anytime at the command line please press '?'.\n\
1371 \n\
1372 If nothing matches, the help list will be empty and you must backup\n\
1373 until entering a '?' shows the available options.\n\
1374 Two styles of help are provided:\n\
1375 1. Full help is available when you are ready to enter a\n\
1376 command argument (e.g. 'show ?') and describes each possible\n\
1377 argument.\n\
1378 2. Partial help is provided when an abbreviated argument is entered\n\
1379 and you want to know what arguments match the input\n\
1380 (e.g. 'show me?'.)\n\n");
1381 return CMD_SUCCESS;
1382 }
1383
permute(struct graph_node * start,struct vty * vty)1384 static void permute(struct graph_node *start, struct vty *vty)
1385 {
1386 static struct list *position = NULL;
1387 if (!position)
1388 position = list_new();
1389
1390 struct cmd_token *stok = start->data;
1391 struct graph_node *gnn;
1392 struct listnode *ln;
1393
1394 // recursive dfs
1395 listnode_add(position, start);
1396 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1397 struct graph_node *gn = vector_slot(start->to, i);
1398 struct cmd_token *tok = gn->data;
1399 if (tok->attr == CMD_ATTR_HIDDEN
1400 || tok->attr == CMD_ATTR_DEPRECATED)
1401 continue;
1402 else if (tok->type == END_TKN || gn == start) {
1403 vty_out(vty, " ");
1404 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1405 struct cmd_token *tt = gnn->data;
1406 if (tt->type < SPECIAL_TKN)
1407 vty_out(vty, " %s", tt->text);
1408 }
1409 if (gn == start)
1410 vty_out(vty, "...");
1411 vty_out(vty, "\n");
1412 } else {
1413 bool skip = false;
1414 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1415 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1416 if (gnn == gn) {
1417 skip = true;
1418 break;
1419 }
1420 if (!skip)
1421 permute(gn, vty);
1422 }
1423 }
1424 list_delete_node(position, listtail(position));
1425 }
1426
cmd_list_cmds(struct vty * vty,int do_permute)1427 int cmd_list_cmds(struct vty *vty, int do_permute)
1428 {
1429 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1430
1431 if (do_permute)
1432 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1433 else {
1434 /* loop over all commands at this node */
1435 const struct cmd_element *element = NULL;
1436 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1437 i++)
1438 if ((element = vector_slot(node->cmd_vector, i))
1439 && element->attr != CMD_ATTR_DEPRECATED
1440 && element->attr != CMD_ATTR_HIDDEN)
1441 vty_out(vty, " %s\n", element->string);
1442 }
1443 return CMD_SUCCESS;
1444 }
1445
1446 /* Help display function for all node. */
1447 DEFUN (config_list,
1448 config_list_cmd,
1449 "list [permutations]",
1450 "Print command list\n"
1451 "Print all possible command permutations\n")
1452 {
1453 return cmd_list_cmds(vty, argc == 2);
1454 }
1455
1456 DEFUN (show_commandtree,
1457 show_commandtree_cmd,
1458 "show commandtree [permutations]",
1459 SHOW_STR
1460 "Show command tree\n"
1461 "Permutations that we are interested in\n")
1462 {
1463 return cmd_list_cmds(vty, argc == 3);
1464 }
1465
1466 DEFUN_HIDDEN(show_cli_graph,
1467 show_cli_graph_cmd,
1468 "show cli graph",
1469 SHOW_STR
1470 "CLI reflection\n"
1471 "Dump current command space as DOT graph\n")
1472 {
1473 struct cmd_node *cn = vector_slot(cmdvec, vty->node);
1474 char *dot = cmd_graph_dump_dot(cn->cmdgraph);
1475
1476 vty_out(vty, "%s\n", dot);
1477 XFREE(MTYPE_TMP, dot);
1478 return CMD_SUCCESS;
1479 }
1480
vty_write_config(struct vty * vty)1481 static int vty_write_config(struct vty *vty)
1482 {
1483 size_t i;
1484 struct cmd_node *node;
1485
1486 if (host.noconfig)
1487 return CMD_SUCCESS;
1488
1489 nb_cli_show_config_prepare(running_config, false);
1490
1491 if (vty->type == VTY_TERM) {
1492 vty_out(vty, "\nCurrent configuration:\n");
1493 vty_out(vty, "!\n");
1494 }
1495
1496 if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
1497 vty_out(vty, "! loaded from %s\n", frr_defaults_version());
1498 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
1499 vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
1500 vty_out(vty, "!\n");
1501
1502 for (i = 0; i < vector_active(cmdvec); i++)
1503 if ((node = vector_slot(cmdvec, i)) && node->config_write) {
1504 if ((*node->config_write)(vty))
1505 vty_out(vty, "!\n");
1506 }
1507
1508 if (vty->type == VTY_TERM) {
1509 vty_out(vty, "end\n");
1510 }
1511
1512 return CMD_SUCCESS;
1513 }
1514
file_write_config(struct vty * vty)1515 static int file_write_config(struct vty *vty)
1516 {
1517 int fd, dirfd;
1518 char *config_file, *slash;
1519 char *config_file_tmp = NULL;
1520 char *config_file_sav = NULL;
1521 int ret = CMD_WARNING;
1522 struct vty *file_vty;
1523 struct stat conf_stat;
1524
1525 if (host.noconfig)
1526 return CMD_SUCCESS;
1527
1528 /* Check and see if we are operating under vtysh configuration */
1529 if (host.config == NULL) {
1530 vty_out(vty,
1531 "Can't save to configuration file, using vtysh.\n");
1532 return CMD_WARNING;
1533 }
1534
1535 /* Get filename. */
1536 config_file = host.config;
1537
1538 #ifndef O_DIRECTORY
1539 #define O_DIRECTORY 0
1540 #endif
1541 slash = strrchr(config_file, '/');
1542 if (slash) {
1543 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1544 config_dir[slash - config_file] = '\0';
1545 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1546 XFREE(MTYPE_TMP, config_dir);
1547 } else
1548 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1549 /* if dirfd is invalid, directory sync fails, but we're still OK */
1550
1551 size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
1552 config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
1553 strlcpy(config_file_sav, config_file, config_file_sav_sz);
1554 strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
1555
1556
1557 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
1558 snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX",
1559 config_file);
1560
1561 /* Open file to configuration write. */
1562 fd = mkstemp(config_file_tmp);
1563 if (fd < 0) {
1564 vty_out(vty, "Can't open configuration file %s.\n",
1565 config_file_tmp);
1566 goto finished;
1567 }
1568 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1569 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1570 config_file_tmp, safe_strerror(errno), errno);
1571 goto finished;
1572 }
1573
1574 /* Make vty for configuration file. */
1575 file_vty = vty_new();
1576 file_vty->wfd = fd;
1577 file_vty->type = VTY_FILE;
1578
1579 /* Config file header print. */
1580 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1581 vty_time_print(file_vty, 1);
1582 vty_out(file_vty, "!\n");
1583 vty_write_config(file_vty);
1584 vty_close(file_vty);
1585
1586 if (stat(config_file, &conf_stat) >= 0) {
1587 if (unlink(config_file_sav) != 0)
1588 if (errno != ENOENT) {
1589 vty_out(vty,
1590 "Can't unlink backup configuration file %s.\n",
1591 config_file_sav);
1592 goto finished;
1593 }
1594 if (link(config_file, config_file_sav) != 0) {
1595 vty_out(vty,
1596 "Can't backup old configuration file %s.\n",
1597 config_file_sav);
1598 goto finished;
1599 }
1600 if (dirfd >= 0)
1601 fsync(dirfd);
1602 }
1603 if (rename(config_file_tmp, config_file) != 0) {
1604 vty_out(vty, "Can't save configuration file %s.\n",
1605 config_file);
1606 goto finished;
1607 }
1608 if (dirfd >= 0)
1609 fsync(dirfd);
1610
1611 vty_out(vty, "Configuration saved to %s\n", config_file);
1612 ret = CMD_SUCCESS;
1613
1614 finished:
1615 if (ret != CMD_SUCCESS)
1616 unlink(config_file_tmp);
1617 if (dirfd >= 0)
1618 close(dirfd);
1619 XFREE(MTYPE_TMP, config_file_tmp);
1620 XFREE(MTYPE_TMP, config_file_sav);
1621 return ret;
1622 }
1623
1624 /* Write current configuration into file. */
1625
1626 DEFUN (config_write,
1627 config_write_cmd,
1628 "write [<file|memory|terminal>]",
1629 "Write running configuration to memory, network, or terminal\n"
1630 "Write to configuration file\n"
1631 "Write configuration currently in memory\n"
1632 "Write configuration to terminal\n")
1633 {
1634 const int idx_type = 1;
1635
1636 // if command was 'write terminal' or 'write memory'
1637 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1638 return vty_write_config(vty);
1639 }
1640
1641 return file_write_config(vty);
1642 }
1643
1644 /* ALIAS_FIXME for 'write <terminal|memory>' */
1645 DEFUN (show_running_config,
1646 show_running_config_cmd,
1647 "show running-config",
1648 SHOW_STR
1649 "running configuration (same as write terminal)\n")
1650 {
1651 return vty_write_config(vty);
1652 }
1653
1654 /* ALIAS_FIXME for 'write file' */
1655 DEFUN (copy_runningconf_startupconf,
1656 copy_runningconf_startupconf_cmd,
1657 "copy running-config startup-config",
1658 "Copy configuration\n"
1659 "Copy running config to... \n"
1660 "Copy running config to startup config (same as write file/memory)\n")
1661 {
1662 return file_write_config(vty);
1663 }
1664 /** -- **/
1665
1666 /* Write startup configuration into the terminal. */
1667 DEFUN (show_startup_config,
1668 show_startup_config_cmd,
1669 "show startup-config",
1670 SHOW_STR
1671 "Contents of startup configuration\n")
1672 {
1673 char buf[BUFSIZ];
1674 FILE *confp;
1675
1676 if (host.noconfig)
1677 return CMD_SUCCESS;
1678 if (host.config == NULL)
1679 return CMD_WARNING;
1680
1681 confp = fopen(host.config, "r");
1682 if (confp == NULL) {
1683 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1684 host.config, safe_strerror(errno));
1685 return CMD_WARNING;
1686 }
1687
1688 while (fgets(buf, BUFSIZ, confp)) {
1689 char *cp = buf;
1690
1691 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1692 cp++;
1693 *cp = '\0';
1694
1695 vty_out(vty, "%s\n", buf);
1696 }
1697
1698 fclose(confp);
1699
1700 return CMD_SUCCESS;
1701 }
1702
cmd_domainname_set(const char * domainname)1703 int cmd_domainname_set(const char *domainname)
1704 {
1705 XFREE(MTYPE_HOST, host.domainname);
1706 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1707 return CMD_SUCCESS;
1708 }
1709
1710 /* Hostname configuration */
1711 DEFUN(config_domainname,
1712 domainname_cmd,
1713 "domainname WORD",
1714 "Set system's domain name\n"
1715 "This system's domain name\n")
1716 {
1717 struct cmd_token *word = argv[1];
1718
1719 if (!isalpha((unsigned char)word->arg[0])) {
1720 vty_out(vty, "Please specify string starting with alphabet\n");
1721 return CMD_WARNING_CONFIG_FAILED;
1722 }
1723
1724 return cmd_domainname_set(word->arg);
1725 }
1726
1727 DEFUN(config_no_domainname,
1728 no_domainname_cmd,
1729 "no domainname [DOMAINNAME]",
1730 NO_STR
1731 "Reset system's domain name\n"
1732 "domain name of this router\n")
1733 {
1734 return cmd_domainname_set(NULL);
1735 }
1736
cmd_hostname_set(const char * hostname)1737 int cmd_hostname_set(const char *hostname)
1738 {
1739 XFREE(MTYPE_HOST, host.name);
1740 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1741 return CMD_SUCCESS;
1742 }
1743
1744 /* Hostname configuration */
1745 DEFUN (config_hostname,
1746 hostname_cmd,
1747 "hostname WORD",
1748 "Set system's network name\n"
1749 "This system's network name\n")
1750 {
1751 struct cmd_token *word = argv[1];
1752
1753 if (!isalnum((unsigned char)word->arg[0])) {
1754 vty_out(vty,
1755 "Please specify string starting with alphabet or number\n");
1756 return CMD_WARNING_CONFIG_FAILED;
1757 }
1758
1759 /* With reference to RFC 1123 Section 2.1 */
1760 if (strlen(word->arg) > HOSTNAME_LEN) {
1761 vty_out(vty, "Hostname length should be less than %d chars\n",
1762 HOSTNAME_LEN);
1763 return CMD_WARNING_CONFIG_FAILED;
1764 }
1765
1766 return cmd_hostname_set(word->arg);
1767 }
1768
1769 DEFUN (config_no_hostname,
1770 no_hostname_cmd,
1771 "no hostname [HOSTNAME]",
1772 NO_STR
1773 "Reset system's network name\n"
1774 "Host name of this router\n")
1775 {
1776 return cmd_hostname_set(NULL);
1777 }
1778
1779 /* VTY interface password set. */
1780 DEFUN (config_password,
1781 password_cmd,
1782 "password [(8-8)] WORD",
1783 "Modify the terminal connection password\n"
1784 "Specifies a HIDDEN password will follow\n"
1785 "The password string\n")
1786 {
1787 int idx_8 = 1;
1788 int idx_word = 2;
1789 if (argc == 3) // '8' was specified
1790 {
1791 if (host.password)
1792 XFREE(MTYPE_HOST, host.password);
1793 host.password = NULL;
1794 if (host.password_encrypt)
1795 XFREE(MTYPE_HOST, host.password_encrypt);
1796 host.password_encrypt =
1797 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1798 return CMD_SUCCESS;
1799 }
1800
1801 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1802 vty_out(vty,
1803 "Please specify string starting with alphanumeric\n");
1804 return CMD_WARNING_CONFIG_FAILED;
1805 }
1806
1807 if (host.password)
1808 XFREE(MTYPE_HOST, host.password);
1809 host.password = NULL;
1810
1811 if (host.encrypt) {
1812 if (host.password_encrypt)
1813 XFREE(MTYPE_HOST, host.password_encrypt);
1814 host.password_encrypt =
1815 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1816 } else
1817 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1818
1819 return CMD_SUCCESS;
1820 }
1821
1822 /* VTY interface password delete. */
1823 DEFUN (no_config_password,
1824 no_password_cmd,
1825 "no password",
1826 NO_STR
1827 "Modify the terminal connection password\n")
1828 {
1829 bool warned = false;
1830
1831 if (host.password) {
1832 if (!vty_shell_serv(vty)) {
1833 vty_out(vty, NO_PASSWD_CMD_WARNING);
1834 warned = true;
1835 }
1836 XFREE(MTYPE_HOST, host.password);
1837 }
1838 host.password = NULL;
1839
1840 if (host.password_encrypt) {
1841 if (!warned && !vty_shell_serv(vty))
1842 vty_out(vty, NO_PASSWD_CMD_WARNING);
1843 XFREE(MTYPE_HOST, host.password_encrypt);
1844 }
1845 host.password_encrypt = NULL;
1846
1847 return CMD_SUCCESS;
1848 }
1849
1850 /* VTY enable password set. */
1851 DEFUN (config_enable_password,
1852 enable_password_cmd,
1853 "enable password [(8-8)] WORD",
1854 "Modify enable password parameters\n"
1855 "Assign the privileged level password\n"
1856 "Specifies a HIDDEN password will follow\n"
1857 "The HIDDEN 'enable' password string\n")
1858 {
1859 int idx_8 = 2;
1860 int idx_word = 3;
1861
1862 /* Crypt type is specified. */
1863 if (argc == 4) {
1864 if (argv[idx_8]->arg[0] == '8') {
1865 if (host.enable)
1866 XFREE(MTYPE_HOST, host.enable);
1867 host.enable = NULL;
1868
1869 if (host.enable_encrypt)
1870 XFREE(MTYPE_HOST, host.enable_encrypt);
1871 host.enable_encrypt =
1872 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1873
1874 return CMD_SUCCESS;
1875 } else {
1876 vty_out(vty, "Unknown encryption type.\n");
1877 return CMD_WARNING_CONFIG_FAILED;
1878 }
1879 }
1880
1881 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1882 vty_out(vty,
1883 "Please specify string starting with alphanumeric\n");
1884 return CMD_WARNING_CONFIG_FAILED;
1885 }
1886
1887 if (host.enable)
1888 XFREE(MTYPE_HOST, host.enable);
1889 host.enable = NULL;
1890
1891 /* Plain password input. */
1892 if (host.encrypt) {
1893 if (host.enable_encrypt)
1894 XFREE(MTYPE_HOST, host.enable_encrypt);
1895 host.enable_encrypt =
1896 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1897 } else
1898 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1899
1900 return CMD_SUCCESS;
1901 }
1902
1903 /* VTY enable password delete. */
1904 DEFUN (no_config_enable_password,
1905 no_enable_password_cmd,
1906 "no enable password",
1907 NO_STR
1908 "Modify enable password parameters\n"
1909 "Assign the privileged level password\n")
1910 {
1911 bool warned = false;
1912
1913 if (host.enable) {
1914 if (!vty_shell_serv(vty)) {
1915 vty_out(vty, NO_PASSWD_CMD_WARNING);
1916 warned = true;
1917 }
1918 XFREE(MTYPE_HOST, host.enable);
1919 }
1920 host.enable = NULL;
1921
1922 if (host.enable_encrypt) {
1923 if (!warned && !vty_shell_serv(vty))
1924 vty_out(vty, NO_PASSWD_CMD_WARNING);
1925 XFREE(MTYPE_HOST, host.enable_encrypt);
1926 }
1927 host.enable_encrypt = NULL;
1928
1929 return CMD_SUCCESS;
1930 }
1931
1932 DEFUN (service_password_encrypt,
1933 service_password_encrypt_cmd,
1934 "service password-encryption",
1935 "Set up miscellaneous service\n"
1936 "Enable encrypted passwords\n")
1937 {
1938 if (host.encrypt)
1939 return CMD_SUCCESS;
1940
1941 host.encrypt = 1;
1942
1943 if (host.password) {
1944 if (host.password_encrypt)
1945 XFREE(MTYPE_HOST, host.password_encrypt);
1946 host.password_encrypt =
1947 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
1948 }
1949 if (host.enable) {
1950 if (host.enable_encrypt)
1951 XFREE(MTYPE_HOST, host.enable_encrypt);
1952 host.enable_encrypt =
1953 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
1954 }
1955
1956 return CMD_SUCCESS;
1957 }
1958
1959 DEFUN (no_service_password_encrypt,
1960 no_service_password_encrypt_cmd,
1961 "no service password-encryption",
1962 NO_STR
1963 "Set up miscellaneous service\n"
1964 "Enable encrypted passwords\n")
1965 {
1966 if (!host.encrypt)
1967 return CMD_SUCCESS;
1968
1969 host.encrypt = 0;
1970
1971 if (host.password_encrypt)
1972 XFREE(MTYPE_HOST, host.password_encrypt);
1973 host.password_encrypt = NULL;
1974
1975 if (host.enable_encrypt)
1976 XFREE(MTYPE_HOST, host.enable_encrypt);
1977 host.enable_encrypt = NULL;
1978
1979 return CMD_SUCCESS;
1980 }
1981
1982 DEFUN (config_terminal_length,
1983 config_terminal_length_cmd,
1984 "terminal length (0-512)",
1985 "Set terminal line parameters\n"
1986 "Set number of lines on a screen\n"
1987 "Number of lines on screen (0 for no pausing)\n")
1988 {
1989 int idx_number = 2;
1990
1991 vty->lines = atoi(argv[idx_number]->arg);
1992
1993 return CMD_SUCCESS;
1994 }
1995
1996 DEFUN (config_terminal_no_length,
1997 config_terminal_no_length_cmd,
1998 "terminal no length",
1999 "Set terminal line parameters\n"
2000 NO_STR
2001 "Set number of lines on a screen\n")
2002 {
2003 vty->lines = -1;
2004 return CMD_SUCCESS;
2005 }
2006
2007 DEFUN (service_terminal_length,
2008 service_terminal_length_cmd,
2009 "service terminal-length (0-512)",
2010 "Set up miscellaneous service\n"
2011 "System wide terminal length configuration\n"
2012 "Number of lines of VTY (0 means no line control)\n")
2013 {
2014 int idx_number = 2;
2015
2016 host.lines = atoi(argv[idx_number]->arg);
2017
2018 return CMD_SUCCESS;
2019 }
2020
2021 DEFUN (no_service_terminal_length,
2022 no_service_terminal_length_cmd,
2023 "no service terminal-length [(0-512)]",
2024 NO_STR
2025 "Set up miscellaneous service\n"
2026 "System wide terminal length configuration\n"
2027 "Number of lines of VTY (0 means no line control)\n")
2028 {
2029 host.lines = -1;
2030 return CMD_SUCCESS;
2031 }
2032
2033 DEFUN_HIDDEN (do_echo,
2034 echo_cmd,
2035 "echo MESSAGE...",
2036 "Echo a message back to the vty\n"
2037 "The message to echo\n")
2038 {
2039 char *message;
2040
2041 vty_out(vty, "%s\n",
2042 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2043 if (message)
2044 XFREE(MTYPE_TMP, message);
2045 return CMD_SUCCESS;
2046 }
2047
2048 DEFUN (config_logmsg,
2049 config_logmsg_cmd,
2050 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2051 "Send a message to enabled logging destinations\n"
2052 LOG_LEVEL_DESC
2053 "The message to send\n")
2054 {
2055 int idx_log_level = 1;
2056 int idx_message = 2;
2057 int level;
2058 char *message;
2059
2060 level = log_level_match(argv[idx_log_level]->arg);
2061 if (level == ZLOG_DISABLED)
2062 return CMD_ERR_NO_MATCH;
2063
2064 zlog(level, "%s",
2065 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2066 if (message)
2067 XFREE(MTYPE_TMP, message);
2068
2069 return CMD_SUCCESS;
2070 }
2071
2072 DEFUN (debug_memstats,
2073 debug_memstats_cmd,
2074 "[no] debug memstats-at-exit",
2075 NO_STR
2076 DEBUG_STR
2077 "Print memory type statistics at exit\n")
2078 {
2079 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2080 return CMD_SUCCESS;
2081 }
2082
cmd_banner_motd_file(const char * file)2083 int cmd_banner_motd_file(const char *file)
2084 {
2085 int success = CMD_SUCCESS;
2086 char p[PATH_MAX];
2087 char *rpath;
2088 char *in;
2089
2090 rpath = realpath(file, p);
2091 if (!rpath)
2092 return CMD_ERR_NO_FILE;
2093 in = strstr(rpath, SYSCONFDIR);
2094 if (in == rpath) {
2095 XFREE(MTYPE_HOST, host.motdfile);
2096 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2097 } else
2098 success = CMD_WARNING_CONFIG_FAILED;
2099
2100 return success;
2101 }
2102
cmd_banner_motd_line(const char * line)2103 void cmd_banner_motd_line(const char *line)
2104 {
2105 XFREE(MTYPE_HOST, host.motd);
2106 host.motd = XSTRDUP(MTYPE_HOST, line);
2107 }
2108
2109 DEFUN (banner_motd_file,
2110 banner_motd_file_cmd,
2111 "banner motd file FILE",
2112 "Set banner\n"
2113 "Banner for motd\n"
2114 "Banner from a file\n"
2115 "Filename\n")
2116 {
2117 int idx_file = 3;
2118 const char *filename = argv[idx_file]->arg;
2119 int cmd = cmd_banner_motd_file(filename);
2120
2121 if (cmd == CMD_ERR_NO_FILE)
2122 vty_out(vty, "%s does not exist", filename);
2123 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2124 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
2125
2126 return cmd;
2127 }
2128
2129 DEFUN (banner_motd_line,
2130 banner_motd_line_cmd,
2131 "banner motd line LINE...",
2132 "Set banner\n"
2133 "Banner for motd\n"
2134 "Banner from an input\n"
2135 "Text\n")
2136 {
2137 int idx = 0;
2138 char *motd;
2139
2140 argv_find(argv, argc, "LINE", &idx);
2141 motd = argv_concat(argv, argc, idx);
2142
2143 cmd_banner_motd_line(motd);
2144 XFREE(MTYPE_TMP, motd);
2145
2146 return CMD_SUCCESS;
2147 }
2148
2149 DEFUN (banner_motd_default,
2150 banner_motd_default_cmd,
2151 "banner motd default",
2152 "Set banner string\n"
2153 "Strings for motd\n"
2154 "Default string\n")
2155 {
2156 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2157 return CMD_SUCCESS;
2158 }
2159
2160 DEFUN (no_banner_motd,
2161 no_banner_motd_cmd,
2162 "no banner motd",
2163 NO_STR
2164 "Set banner string\n"
2165 "Strings for motd\n")
2166 {
2167 host.motd = NULL;
2168 if (host.motdfile)
2169 XFREE(MTYPE_HOST, host.motdfile);
2170 host.motdfile = NULL;
2171 return CMD_SUCCESS;
2172 }
2173
2174 DEFUN(find,
2175 find_cmd,
2176 "find REGEX",
2177 "Find CLI command matching a regular expression\n"
2178 "Search pattern (POSIX regex)\n")
2179 {
2180 char *pattern = argv[1]->arg;
2181 const struct cmd_node *node;
2182 const struct cmd_element *cli;
2183 vector clis;
2184
2185 regex_t exp = {};
2186
2187 int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
2188
2189 if (cr != 0) {
2190 switch (cr) {
2191 case REG_BADBR:
2192 vty_out(vty, "%% Invalid {...} expression\n");
2193 break;
2194 case REG_BADRPT:
2195 vty_out(vty, "%% Bad repetition operator\n");
2196 break;
2197 case REG_BADPAT:
2198 vty_out(vty, "%% Regex syntax error\n");
2199 break;
2200 case REG_ECOLLATE:
2201 vty_out(vty, "%% Invalid collating element\n");
2202 break;
2203 case REG_ECTYPE:
2204 vty_out(vty, "%% Invalid character class name\n");
2205 break;
2206 case REG_EESCAPE:
2207 vty_out(vty,
2208 "%% Regex ended with escape character (\\)\n");
2209 break;
2210 case REG_ESUBREG:
2211 vty_out(vty,
2212 "%% Invalid number in \\digit construction\n");
2213 break;
2214 case REG_EBRACK:
2215 vty_out(vty, "%% Unbalanced square brackets\n");
2216 break;
2217 case REG_EPAREN:
2218 vty_out(vty, "%% Unbalanced parentheses\n");
2219 break;
2220 case REG_EBRACE:
2221 vty_out(vty, "%% Unbalanced braces\n");
2222 break;
2223 case REG_ERANGE:
2224 vty_out(vty,
2225 "%% Invalid endpoint in range expression\n");
2226 break;
2227 case REG_ESPACE:
2228 vty_out(vty, "%% Failed to compile (out of memory)\n");
2229 break;
2230 }
2231
2232 goto done;
2233 }
2234
2235
2236 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
2237 node = vector_slot(cmdvec, i);
2238 if (!node)
2239 continue;
2240 clis = node->cmd_vector;
2241 for (unsigned int j = 0; j < vector_active(clis); j++) {
2242 cli = vector_slot(clis, j);
2243
2244 if (regexec(&exp, cli->string, 0, NULL, 0) == 0)
2245 vty_out(vty, " (%s) %s\n",
2246 node->name, cli->string);
2247 }
2248 }
2249
2250 done:
2251 regfree(&exp);
2252 return CMD_SUCCESS;
2253 }
2254
2255 /* Set config filename. Called from vty.c */
host_config_set(const char * filename)2256 void host_config_set(const char *filename)
2257 {
2258 XFREE(MTYPE_HOST, host.config);
2259 host.config = XSTRDUP(MTYPE_HOST, filename);
2260 }
2261
host_config_get(void)2262 const char *host_config_get(void)
2263 {
2264 return host.config;
2265 }
2266
install_default(enum node_type node)2267 void install_default(enum node_type node)
2268 {
2269 install_element(node, &config_exit_cmd);
2270 install_element(node, &config_quit_cmd);
2271 install_element(node, &config_end_cmd);
2272 install_element(node, &config_help_cmd);
2273 install_element(node, &config_list_cmd);
2274 install_element(node, &show_cli_graph_cmd);
2275 install_element(node, &find_cmd);
2276
2277 install_element(node, &config_write_cmd);
2278 install_element(node, &show_running_config_cmd);
2279
2280 install_element(node, &autocomplete_cmd);
2281
2282 nb_cli_install_default(node);
2283 }
2284
2285 /* Initialize command interface. Install basic nodes and commands.
2286 *
2287 * terminal = 0 -- vtysh / no logging, no config control
2288 * terminal = 1 -- normal daemon
2289 * terminal = -1 -- watchfrr / no logging, but minimal config control */
cmd_init(int terminal)2290 void cmd_init(int terminal)
2291 {
2292 struct utsname names;
2293
2294 uname(&names);
2295 qobj_init();
2296
2297 /* register command preprocessors */
2298 hook_register(cmd_execute, handle_pipe_action);
2299 hook_register(cmd_execute_done, handle_pipe_action_done);
2300
2301 varhandlers = list_new();
2302
2303 /* Allocate initial top vector of commands. */
2304 cmdvec = vector_init(VECTOR_MIN_SIZE);
2305
2306 /* Default host value settings. */
2307 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2308 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2309 if ((strcmp(names.domainname, "(none)") == 0))
2310 host.domainname = NULL;
2311 else
2312 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
2313 #else
2314 host.domainname = NULL;
2315 #endif
2316 host.password = NULL;
2317 host.enable = NULL;
2318 host.config = NULL;
2319 host.noconfig = (terminal < 0);
2320 host.lines = -1;
2321 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2322 host.motdfile = NULL;
2323
2324 /* Install top nodes. */
2325 install_node(&view_node);
2326 install_node(&enable_node);
2327 install_node(&auth_node);
2328 install_node(&auth_enable_node);
2329 install_node(&config_node);
2330
2331 /* Each node's basic commands. */
2332 install_element(VIEW_NODE, &show_version_cmd);
2333 install_element(ENABLE_NODE, &show_startup_config_cmd);
2334
2335 if (terminal) {
2336 install_element(ENABLE_NODE, &debug_memstats_cmd);
2337
2338 install_element(VIEW_NODE, &config_list_cmd);
2339 install_element(VIEW_NODE, &config_exit_cmd);
2340 install_element(VIEW_NODE, &config_quit_cmd);
2341 install_element(VIEW_NODE, &config_help_cmd);
2342 install_element(VIEW_NODE, &config_enable_cmd);
2343 install_element(VIEW_NODE, &config_terminal_length_cmd);
2344 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2345 install_element(VIEW_NODE, &show_commandtree_cmd);
2346 install_element(VIEW_NODE, &echo_cmd);
2347 install_element(VIEW_NODE, &autocomplete_cmd);
2348 install_element(VIEW_NODE, &find_cmd);
2349
2350 install_element(ENABLE_NODE, &config_end_cmd);
2351 install_element(ENABLE_NODE, &config_disable_cmd);
2352 install_element(ENABLE_NODE, &config_terminal_cmd);
2353 install_element(ENABLE_NODE, ©_runningconf_startupconf_cmd);
2354 install_element(ENABLE_NODE, &config_write_cmd);
2355 install_element(ENABLE_NODE, &show_running_config_cmd);
2356 install_element(ENABLE_NODE, &config_logmsg_cmd);
2357
2358 install_default(CONFIG_NODE);
2359
2360 thread_cmd_init();
2361 workqueue_cmd_init();
2362 hash_cmd_init();
2363 }
2364
2365 install_element(CONFIG_NODE, &hostname_cmd);
2366 install_element(CONFIG_NODE, &no_hostname_cmd);
2367 install_element(CONFIG_NODE, &domainname_cmd);
2368 install_element(CONFIG_NODE, &no_domainname_cmd);
2369
2370 if (terminal > 0) {
2371 full_cli = true;
2372
2373 install_element(CONFIG_NODE, &debug_memstats_cmd);
2374
2375 install_element(CONFIG_NODE, &password_cmd);
2376 install_element(CONFIG_NODE, &no_password_cmd);
2377 install_element(CONFIG_NODE, &enable_password_cmd);
2378 install_element(CONFIG_NODE, &no_enable_password_cmd);
2379
2380 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2381 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2382 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2383 install_element(CONFIG_NODE, &banner_motd_file_cmd);
2384 install_element(CONFIG_NODE, &banner_motd_line_cmd);
2385 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2386 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2387 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2388
2389 log_cmd_init();
2390 vrf_install_commands();
2391 }
2392
2393 #ifdef DEV_BUILD
2394 grammar_sandbox_init();
2395 #endif
2396 }
2397
cmd_terminate(void)2398 void cmd_terminate(void)
2399 {
2400 struct cmd_node *cmd_node;
2401
2402 hook_unregister(cmd_execute, handle_pipe_action);
2403 hook_unregister(cmd_execute_done, handle_pipe_action_done);
2404
2405 if (cmdvec) {
2406 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2407 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2408 // deleting the graph delets the cmd_element as
2409 // well
2410 graph_delete_graph(cmd_node->cmdgraph);
2411 vector_free(cmd_node->cmd_vector);
2412 hash_clean(cmd_node->cmd_hash, NULL);
2413 hash_free(cmd_node->cmd_hash);
2414 cmd_node->cmd_hash = NULL;
2415 }
2416
2417 vector_free(cmdvec);
2418 cmdvec = NULL;
2419 }
2420
2421 XFREE(MTYPE_HOST, host.name);
2422 XFREE(MTYPE_HOST, host.domainname);
2423 XFREE(MTYPE_HOST, host.password);
2424 XFREE(MTYPE_HOST, host.password_encrypt);
2425 XFREE(MTYPE_HOST, host.enable);
2426 XFREE(MTYPE_HOST, host.enable_encrypt);
2427 XFREE(MTYPE_HOST, host.motdfile);
2428 XFREE(MTYPE_HOST, host.config);
2429 XFREE(MTYPE_HOST, host.motd);
2430
2431 list_delete(&varhandlers);
2432 qobj_finish();
2433 }
2434