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