2 * CLI backend interface.
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")
10 * This file is part of GNU Zebra.
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
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.
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
28 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
48 #include "lib_errors.h"
49 #include "northbound_cli.h"
52 #include "frrscript.h"
54 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config");
55 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item");
62 /* clang-format off */
63 const struct message tokennames
[] = {
68 item(IPV4_PREFIX_TKN
),
70 item(IPV6_PREFIX_TKN
),
82 /* Command vector which includes some level of command lists. Normally
83 each daemon maintains each own cmdvec. */
86 /* Host information structure. */
89 /* for vtysh, put together CLI trees only when switching into node */
90 static bool defer_cli_tree
;
93 * Returns host.name if any, otherwise
94 * it returns the system hostname.
96 const char *cmd_hostname_get(void)
102 * Returns unix domainname
104 const char *cmd_domainname_get(void)
106 return host
.domainname
;
109 const char *cmd_system_get(void)
114 const char *cmd_release_get(void)
119 const char *cmd_version_get(void)
124 static int root_on_exit(struct vty
*vty
);
126 /* Standard command node structures. */
127 static struct cmd_node auth_node
= {
130 .prompt
= "Password: ",
133 static struct cmd_node view_node
= {
137 .node_exit
= root_on_exit
,
140 static struct cmd_node auth_enable_node
= {
141 .name
= "auth enable",
142 .node
= AUTH_ENABLE_NODE
,
143 .prompt
= "Password: ",
146 static struct cmd_node enable_node
= {
150 .node_exit
= root_on_exit
,
153 static int config_write_host(struct vty
*vty
);
154 static struct cmd_node config_node
= {
157 .parent_node
= ENABLE_NODE
,
158 .prompt
= "%s(config)# ",
159 .config_write
= config_write_host
,
160 .node_exit
= vty_config_node_exit
,
163 /* This is called from main when a daemon is invoked with -v or --version. */
164 void print_version(const char *progname
)
166 printf("%s version %s\n", progname
, FRR_VERSION
);
167 printf("%s\n", FRR_COPYRIGHT
);
168 #ifdef ENABLE_VERSION_BUILD_CONFIG
169 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
173 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
175 int cnt
= MAX(argc
- shift
, 0);
176 const char *argstr
[cnt
+ 1];
181 for (int i
= 0; i
< cnt
; i
++)
182 argstr
[i
] = argv
[i
+ shift
]->arg
;
184 return frrstr_join(argstr
, cnt
, " ");
187 vector
cmd_make_strvec(const char *string
)
192 const char *copy
= string
;
194 /* skip leading whitespace */
195 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
198 /* if the entire string was whitespace or a comment, return */
199 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
202 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
204 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
205 if (strlen(vector_slot(result
, i
)) == 0) {
206 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
207 vector_unset(result
, i
);
211 vector_compact(result
);
216 void cmd_free_strvec(vector v
)
218 frrstr_strvec_free(v
);
222 * Convenience function for accessing argv data.
226 * @param text definition snippet of the desired token
227 * @param index the starting index, and where to store the
228 * index of the found token if it exists
229 * @return 1 if found, 0 otherwise
231 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
234 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
235 if ((found
= strmatch(text
, argv
[i
]->text
)))
240 static unsigned int cmd_hash_key(const void *p
)
242 int size
= sizeof(p
);
244 return jhash(p
, size
, 0);
247 static bool cmd_hash_cmp(const void *a
, const void *b
)
252 /* Install top node of command vector. */
253 void install_node(struct cmd_node
*node
)
255 #define CMD_HASH_STR_SIZE 256
256 char hash_name
[CMD_HASH_STR_SIZE
];
258 vector_set_index(cmdvec
, node
->node
, node
);
259 node
->cmdgraph
= graph_new();
260 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
262 struct cmd_token
*token
=
263 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
264 graph_new_node(node
->cmdgraph
, token
,
265 (void (*)(void *)) & cmd_token_del
);
267 snprintf(hash_name
, sizeof(hash_name
), "Command Hash: %s", node
->name
);
269 hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
, hash_name
);
272 /* Return prompt character of specified node. */
273 const char *cmd_prompt(enum node_type node
)
275 struct cmd_node
*cnode
;
277 cnode
= vector_slot(cmdvec
, node
);
278 return cnode
->prompt
;
281 void cmd_defer_tree(bool val
)
283 defer_cli_tree
= val
;
286 /* Install a command into a node. */
287 void _install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
289 struct cmd_node
*cnode
;
291 /* cmd_init hasn't been called */
293 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
298 cnode
= vector_lookup(cmdvec
, ntype
);
303 "\tnode %d does not exist.\n"
304 "\tplease call install_node() before install_element()\n",
305 cmd
->name
, cmd
->string
, ntype
);
309 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
312 "\tnode %d (%s) already has this command installed.\n"
313 "\tduplicate install_element call?\n",
314 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
318 assert(hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
));
320 if (cnode
->graph_built
|| !defer_cli_tree
) {
321 struct graph
*graph
= graph_new();
322 struct cmd_token
*token
=
323 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
324 graph_new_node(graph
, token
,
325 (void (*)(void *)) & cmd_token_del
);
327 cmd_graph_parse(graph
, cmd
);
328 cmd_graph_names(graph
);
329 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
330 graph_delete_graph(graph
);
332 cnode
->graph_built
= true;
335 vector_set(cnode
->cmd_vector
, (void *)cmd
);
337 if (ntype
== VIEW_NODE
)
338 _install_element(ENABLE_NODE
, cmd
);
341 static void cmd_finalize_iter(struct hash_bucket
*hb
, void *arg
)
343 struct cmd_node
*cnode
= arg
;
344 const struct cmd_element
*cmd
= hb
->data
;
345 struct graph
*graph
= graph_new();
346 struct cmd_token
*token
=
347 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
349 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
351 cmd_graph_parse(graph
, cmd
);
352 cmd_graph_names(graph
);
353 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
354 graph_delete_graph(graph
);
357 void cmd_finalize_node(struct cmd_node
*cnode
)
359 if (cnode
->graph_built
)
362 hash_iterate(cnode
->cmd_hash
, cmd_finalize_iter
, cnode
);
363 cnode
->graph_built
= true;
366 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
368 struct cmd_node
*cnode
;
370 /* cmd_init hasn't been called */
372 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
377 cnode
= vector_lookup(cmdvec
, ntype
);
382 "\tnode %d does not exist.\n"
383 "\tplease call install_node() before uninstall_element()\n",
384 cmd
->name
, cmd
->string
, ntype
);
388 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
391 "\tnode %d (%s) does not have this command installed.\n"
392 "\tduplicate uninstall_element call?\n",
393 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
397 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
399 if (cnode
->graph_built
) {
400 struct graph
*graph
= graph_new();
401 struct cmd_token
*token
=
402 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
403 graph_new_node(graph
, token
,
404 (void (*)(void *)) & cmd_token_del
);
406 cmd_graph_parse(graph
, cmd
);
407 cmd_graph_names(graph
);
408 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
409 graph_delete_graph(graph
);
412 if (ntype
== VIEW_NODE
)
413 uninstall_element(ENABLE_NODE
, cmd
);
417 static const unsigned char itoa64
[] =
418 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
420 static void to64(char *s
, long v
, int n
)
423 *s
++ = itoa64
[v
& 0x3f];
428 static char *zencrypt(const char *passwd
)
432 char *crypt(const char *, const char *);
434 gettimeofday(&tv
, 0);
436 to64(&salt
[0], frr_weak_random(), 3);
437 to64(&salt
[3], tv
.tv_usec
, 3);
440 return crypt(passwd
, salt
);
443 static bool full_cli
;
445 /* This function write configuration of this host. */
446 static int config_write_host(struct vty
*vty
)
448 if (cmd_hostname_get())
449 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
451 if (cmd_domainname_get())
452 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
454 /* The following are all configuration commands that are not sent to
455 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
456 * we would always display 'log syslog informational' in the config
457 * which would cause other daemons to then switch to syslog when they
462 if (host
.password_encrypt
)
463 vty_out(vty
, "password 8 %s\n",
464 host
.password_encrypt
);
465 if (host
.enable_encrypt
)
466 vty_out(vty
, "enable password 8 %s\n",
467 host
.enable_encrypt
);
470 vty_out(vty
, "password %s\n", host
.password
);
472 vty_out(vty
, "enable password %s\n",
475 log_config_write(vty
);
477 /* print disable always, but enable only if default is flipped
478 * => prep for future removal of compile-time knob
480 if (!cputime_enabled
)
481 vty_out(vty
, "no service cputime-stats\n");
482 #ifdef EXCLUDE_CPU_TIME
484 vty_out(vty
, "service cputime-stats\n");
487 if (!cputime_threshold
)
488 vty_out(vty
, "no service cputime-warning\n");
489 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
490 else /* again, always print non-default */
492 else if (cputime_threshold
!= 5000000)
494 vty_out(vty
, "service cputime-warning %lu\n",
497 if (!walltime_threshold
)
498 vty_out(vty
, "no service walltime-warning\n");
499 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
500 else /* again, always print non-default */
502 else if (walltime_threshold
!= 5000000)
504 vty_out(vty
, "service walltime-warning %lu\n",
508 vty_out(vty
, "service advanced-vty\n");
511 vty_out(vty
, "service password-encryption\n");
514 vty_out(vty
, "service terminal-length %d\n",
518 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
520 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
522 vty_out(vty
, "banner motd line %s\n", host
.motd
);
524 vty_out(vty
, "no banner motd\n");
527 if (debug_memstats_at_exit
)
528 vty_out(vty
, "!\ndebug memstats-at-exit\n");
533 /* Utility function for getting command graph. */
534 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
536 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
538 cmd_finalize_node(cnode
);
539 return cnode
->cmdgraph
;
542 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
544 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
545 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
551 * Compare function for cmd_token.
552 * Used with qsort to sort command completions.
554 static int compare_completions(const void *fst
, const void *snd
)
556 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
557 *secnd
= *(const struct cmd_token
* const *)snd
;
558 return strcmp(first
->text
, secnd
->text
);
562 * Takes a list of completions returned by command_complete,
563 * dedeuplicates them based on both text and description,
564 * sorts them, and returns them as a vector.
566 * @param completions linked list of cmd_token
567 * @return deduplicated and sorted vector with
569 vector
completions_to_vec(struct list
*completions
)
571 vector comps
= vector_init(VECTOR_MIN_SIZE
);
574 struct cmd_token
*token
, *cr
= NULL
;
575 unsigned int i
, exists
;
576 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
577 if (token
->type
== END_TKN
&& (cr
= token
))
580 // linear search for token in completions vector
582 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
583 struct cmd_token
*curr
= vector_slot(comps
, i
);
585 exists
= !strcmp(curr
->text
, token
->text
)
586 && !strcmp(curr
->desc
, token
->desc
);
588 exists
= !strcmp(curr
->text
, token
->text
);
589 #endif /* VTYSH_DEBUG */
593 vector_set(comps
, token
);
597 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
598 &compare_completions
);
600 // make <cr> the first element, if it is present
602 vector_set_index(comps
, vector_active(comps
), NULL
);
603 memmove(comps
->index
+ 1, comps
->index
,
604 (comps
->alloced
- 1) * sizeof(void *));
605 vector_set_index(comps
, 0, cr
);
611 * Generates a vector of cmd_token representing possible completions
612 * on the current input.
614 * @param vline the vectorized input line
615 * @param vty the vty with the node to match on
616 * @param status pointer to matcher status code
617 * @return vector of struct cmd_token * with possible completions
619 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
622 struct list
*completions
;
623 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
625 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
627 if (MATCHER_ERROR(rv
)) {
628 *status
= CMD_ERR_NO_MATCH
;
632 vector comps
= completions_to_vec(completions
);
633 list_delete(&completions
);
635 // set status code appropriately
636 switch (vector_active(comps
)) {
638 *status
= CMD_ERR_NO_MATCH
;
641 *status
= CMD_COMPLETE_FULL_MATCH
;
644 *status
= CMD_COMPLETE_LIST_MATCH
;
650 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
654 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
655 enum node_type onode
;
656 int orig_xpath_index
;
657 vector shifted_vline
;
661 orig_xpath_index
= vty
->xpath_index
;
662 vty
->node
= ENABLE_NODE
;
663 vty
->xpath_index
= 0;
664 /* We can try it on enable node, cos' the vty is authenticated
667 shifted_vline
= vector_init(vector_count(vline
));
669 for (index
= 1; index
< vector_active(vline
); index
++) {
670 vector_set_index(shifted_vline
, index
- 1,
671 vector_lookup(vline
, index
));
674 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
676 vector_free(shifted_vline
);
678 vty
->xpath_index
= orig_xpath_index
;
682 return cmd_complete_command_real(vline
, vty
, status
);
685 static struct list
*varhandlers
= NULL
;
687 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
691 const struct cmd_variable_handler
*cvh
;
695 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
697 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
698 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
700 if (cvh
->varname
&& (!token
->varname
701 || strcmp(cvh
->varname
, token
->varname
)))
703 cvh
->completions(tmpcomps
, token
);
711 for (i
= vector_active(tmpcomps
); i
; i
--) {
712 char *item
= vector_slot(tmpcomps
, i
- 1);
713 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
714 vector_set(comps
, item
);
716 XFREE(MTYPE_COMPLETION
, item
);
718 vector_free(tmpcomps
);
721 #define AUTOCOMP_INDENT 5
723 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
726 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
727 int lc
= AUTOCOMP_INDENT
;
728 size_t cs
= AUTOCOMP_INDENT
;
730 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
731 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
732 char *item
= vector_slot(comps
, j
);
733 itemlen
= strlen(item
);
735 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
736 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
738 if (lc
+ itemlen
+ 1 >= cols
) {
739 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
740 AUTOCOMP_INDENT
, "");
741 lc
= AUTOCOMP_INDENT
;
744 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
747 XFREE(MTYPE_COMPLETION
, item
);
748 vector_set_index(comps
, j
, NULL
);
753 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
758 for (; cvh
->completions
; cvh
++)
759 listnode_add(varhandlers
, (void *)cvh
);
762 DEFUN_HIDDEN (autocomplete
,
764 "autocomplete TYPE TEXT VARNAME",
765 "Autocompletion handler (internal, for vtysh)\n"
768 "cmd_token->varname\n")
770 struct cmd_token tok
;
771 vector comps
= vector_init(32);
774 memset(&tok
, 0, sizeof(tok
));
775 tok
.type
= atoi(argv
[1]->arg
);
776 tok
.text
= argv
[2]->arg
;
777 tok
.varname
= argv
[3]->arg
;
778 if (!strcmp(tok
.varname
, "-"))
781 cmd_variable_complete(&tok
, NULL
, comps
);
783 for (i
= 0; i
< vector_active(comps
); i
++) {
784 char *text
= vector_slot(comps
, i
);
785 vty_out(vty
, "%s\n", text
);
786 XFREE(MTYPE_COMPLETION
, text
);
794 * Generate possible tab-completions for the given input. This function only
795 * returns results that would result in a valid command if used as Readline
796 * completions (as is the case in vtysh). For instance, if the passed vline ends
797 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
799 * @param vline vectorized input line
801 * @param status location to store matcher status code in
802 * @return set of valid strings for use with Readline as tab-completions.
805 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
808 int original_node
= vty
->node
;
809 vector input_line
= vector_init(vector_count(vline
));
811 // if the first token is 'do' we'll want to execute the command in the
813 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
814 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
816 // construct the input line we'll be matching on
817 unsigned int offset
= (do_shortcut
) ? 1 : 0;
818 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
819 vector_set_index(input_line
, index
,
820 vector_lookup(vline
, index
+ offset
));
822 // get token completions -- this is a copying operation
823 vector comps
= NULL
, initial_comps
;
824 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
826 if (!MATCHER_ERROR(*status
)) {
827 assert(initial_comps
);
828 // filter out everything that is not suitable for a
830 comps
= vector_init(VECTOR_MIN_SIZE
);
831 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
833 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
834 if (token
->type
== WORD_TKN
)
835 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
837 else if (IS_VARYING_TOKEN(token
->type
)) {
838 const char *ref
= vector_lookup(
839 vline
, vector_active(vline
) - 1);
840 cmd_variable_complete(token
, ref
, comps
);
843 vector_free(initial_comps
);
845 // since we filtered results, we need to re-set status code
846 switch (vector_active(comps
)) {
848 *status
= CMD_ERR_NO_MATCH
;
851 *status
= CMD_COMPLETE_FULL_MATCH
;
854 *status
= CMD_COMPLETE_LIST_MATCH
;
857 // copy completions text into an array of char*
858 ret
= XMALLOC(MTYPE_TMP
,
859 (vector_active(comps
) + 1) * sizeof(char *));
861 for (i
= 0; i
< vector_active(comps
); i
++) {
862 ret
[i
] = vector_slot(comps
, i
);
864 // set the last element to NULL, because this array is used in
865 // a Readline completion_generator function which expects NULL
866 // as a sentinel value
870 } else if (initial_comps
)
871 vector_free(initial_comps
);
873 // comps should always be null here
876 // free the adjusted input line
877 vector_free(input_line
);
879 // reset vty->node to its original value
880 vty
->node
= original_node
;
885 /* return parent node */
886 /* MUST eventually converge on CONFIG_NODE */
887 enum node_type
node_parent(enum node_type node
)
889 struct cmd_node
*cnode
;
891 assert(node
> CONFIG_NODE
);
893 cnode
= vector_lookup(cmdvec
, node
);
895 return cnode
->parent_node
;
898 /* Execute command by argument vline vector. */
899 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
901 const struct cmd_element
**cmd
,
902 unsigned int up_level
)
904 struct list
*argv_list
;
905 enum matcher_rv status
;
906 const struct cmd_element
*matched_element
= NULL
;
908 int xpath_index
= vty
->xpath_index
;
909 int node
= vty
->node
;
911 /* only happens for legacy split config file load; need to check for
912 * a match before calling node_exit handlers below
914 for (i
= 0; i
< up_level
; i
++) {
915 struct cmd_node
*cnode
;
917 if (node
<= CONFIG_NODE
)
918 return CMD_NO_LEVEL_UP
;
920 cnode
= vector_slot(cmdvec
, node
);
921 node
= node_parent(node
);
923 if (xpath_index
> 0 && !cnode
->no_xpath
)
927 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
928 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
931 *cmd
= matched_element
;
933 // if matcher error, return corresponding CMD_ERR
934 if (MATCHER_ERROR(status
)) {
936 list_delete(&argv_list
);
938 case MATCHER_INCOMPLETE
:
939 return CMD_ERR_INCOMPLETE
;
940 case MATCHER_AMBIGUOUS
:
941 return CMD_ERR_AMBIGUOUS
;
943 return CMD_ERR_NO_MATCH
;
947 for (i
= 0; i
< up_level
; i
++)
950 // build argv array from argv list
951 struct cmd_token
**argv
= XMALLOC(
952 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
954 struct cmd_token
*token
;
957 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
960 int argc
= argv_list
->count
;
963 if (matched_element
->daemon
)
964 ret
= CMD_SUCCESS_DAEMON
;
967 /* Clear array of enqueued configuration changes. */
968 vty
->num_cfg_changes
= 0;
969 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
971 /* Regenerate candidate configuration if necessary. */
972 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
973 && running_config
->version
974 > vty
->candidate_config
->version
)
975 nb_config_replace(vty
->candidate_config
,
976 running_config
, true);
979 * Perform pending commit (if any) before executing
982 if (matched_element
->attr
!= CMD_ATTR_YANG
)
983 (void)nb_cli_pending_commit_check(vty
);
986 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
989 // delete list and cmd_token's in it
990 list_delete(&argv_list
);
991 XFREE(MTYPE_TMP
, argv
);
997 * Execute a given command, handling things like "do ..." and checking
998 * whether the given command might apply at a parent node if doesn't
999 * apply for the current node.
1001 * @param vline Command line input, vector of char* where each element is
1003 * @param vty The vty context in which the command should be executed.
1004 * @param cmd Pointer where the struct cmd_element of the matched command
1005 * will be stored, if any. May be set to NULL if this info is
1007 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1008 * @return The status of the command that has been executed or an error code
1009 * as to why no command could be executed.
1011 int cmd_execute_command(vector vline
, struct vty
*vty
,
1012 const struct cmd_element
**cmd
, int vtysh
)
1014 int ret
, saved_ret
= 0;
1015 enum node_type onode
, try_node
;
1016 int orig_xpath_index
;
1018 onode
= try_node
= vty
->node
;
1019 orig_xpath_index
= vty
->xpath_index
;
1021 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1022 vector shifted_vline
;
1025 vty
->node
= ENABLE_NODE
;
1026 vty
->xpath_index
= 0;
1027 /* We can try it on enable node, cos' the vty is authenticated
1030 shifted_vline
= vector_init(vector_count(vline
));
1032 for (index
= 1; index
< vector_active(vline
); index
++)
1033 vector_set_index(shifted_vline
, index
- 1,
1034 vector_lookup(vline
, index
));
1036 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1039 vector_free(shifted_vline
);
1041 vty
->xpath_index
= orig_xpath_index
;
1046 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1051 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1052 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1053 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1054 /* This assumes all nodes above CONFIG_NODE are childs of
1056 while (vty
->node
> CONFIG_NODE
) {
1057 struct cmd_node
*cnode
= vector_slot(cmdvec
, try_node
);
1059 try_node
= node_parent(try_node
);
1060 vty
->node
= try_node
;
1061 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1064 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1066 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1067 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1068 || ret
== CMD_NOT_MY_INSTANCE
1069 || ret
== CMD_WARNING_CONFIG_FAILED
)
1072 /* no command succeeded, reset the vty to the original node */
1074 vty
->xpath_index
= orig_xpath_index
;
1077 /* return command status for original node */
1082 * Execute a given command, matching it strictly against the current node.
1083 * This mode is used when reading config files.
1085 * @param vline Command line input, vector of char* where each element is
1087 * @param vty The vty context in which the command should be executed.
1088 * @param cmd Pointer where the struct cmd_element* of the matched command
1089 * will be stored, if any. May be set to NULL if this info is
1091 * @return The status of the command that has been executed or an error code
1092 * as to why no command could be executed.
1094 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1095 const struct cmd_element
**cmd
)
1097 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1101 * Hook for preprocessing command string before executing.
1103 * All subscribers are called with the raw command string that is to be
1104 * executed. If any changes are to be made, a new string should be allocated
1105 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1106 * is then responsible for freeing this string.
1108 * All processing functions must be mutually exclusive in their action, i.e. if
1109 * one subscriber decides to modify the command, all others must not modify it
1110 * when called. Feeding the output of one processing command into a subsequent
1111 * one is not supported.
1113 * This hook is intentionally internal to the command processing system.
1116 * The raw command string.
1119 * The result of any processing.
1121 DECLARE_HOOK(cmd_execute
,
1122 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1123 (vty
, cmd_in
, cmd_out
));
1124 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1125 (vty
, cmd_in
, cmd_out
));
1127 /* Hook executed after a CLI command. */
1128 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1130 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1134 * cmd_execute hook subscriber to handle `|` actions.
1136 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1140 char *orig
, *working
, *token
, *u
;
1141 char *pipe
= strstr(cmd_in
, "| ");
1147 /* duplicate string for processing purposes, not including pipe */
1148 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1150 /* retrieve action */
1151 token
= strsep(&working
, " ");
1154 /* match result to known actions */
1155 if (strmatch(token
, "include")) {
1156 /* the remaining text should be a regexp */
1157 char *regexp
= working
;
1160 vty_out(vty
, "%% Need a regexp to filter with\n");
1165 bool succ
= vty_set_include(vty
, regexp
);
1168 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1172 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1176 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1182 XFREE(MTYPE_TMP
, orig
);
1186 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1189 vty_set_include(vty
, NULL
);
1194 int cmd_execute(struct vty
*vty
, const char *cmd
,
1195 const struct cmd_element
**matched
, int vtysh
)
1198 char *cmd_out
= NULL
;
1199 const char *cmd_exec
= NULL
;
1202 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1208 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1210 vline
= cmd_make_strvec(cmd_exec
);
1213 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1214 cmd_free_strvec(vline
);
1220 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1222 XFREE(MTYPE_TMP
, cmd_out
);
1229 * Parse one line of config, walking up the parse tree attempting to find a
1232 * @param vty The vty context in which the command should be executed.
1233 * @param cmd Pointer where the struct cmd_element* of the match command
1234 * will be stored, if any. May be set to NULL if this info is
1236 * @param use_daemon Boolean to control whether or not we match on
1237 * CMD_SUCCESS_DAEMON
1239 * @return The status of the command that has been executed or an error code
1240 * as to why no command could be executed.
1242 int command_config_read_one_line(struct vty
*vty
,
1243 const struct cmd_element
**cmd
,
1244 uint32_t line_num
, int use_daemon
)
1248 unsigned up_level
= 0;
1250 vline
= cmd_make_strvec(vty
->buf
);
1252 /* In case of comment line */
1256 /* Execute configuration command : this is strict match */
1257 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1259 /* The logic for trying parent nodes is in cmd_execute_command_real()
1260 * since calling ->node_exit() correctly is a bit involved. This is
1261 * also the only reason CMD_NO_LEVEL_UP exists.
1263 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1264 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1265 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1266 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1267 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1268 && ret
!= CMD_NO_LEVEL_UP
)
1269 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1272 if (ret
== CMD_NO_LEVEL_UP
)
1273 ret
= CMD_ERR_NO_MATCH
;
1275 if (ret
!= CMD_SUCCESS
&&
1276 ret
!= CMD_WARNING
&&
1277 ret
!= CMD_SUCCESS_DAEMON
) {
1278 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1280 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1281 ve
->line_num
= line_num
;
1283 vty
->error
= list_new();
1285 listnode_add(vty
->error
, ve
);
1288 cmd_free_strvec(vline
);
1293 /* Configuration make from file. */
1294 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1296 int ret
, error_ret
= 0;
1299 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1302 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1304 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1305 && ret
!= CMD_ERR_NOTHING_TODO
)
1316 /* Configuration from terminal */
1317 DEFUN (config_terminal
,
1318 config_terminal_cmd
,
1319 "configure [terminal]",
1320 "Configuration from vty interface\n"
1321 "Configuration terminal\n")
1323 return vty_config_enter(vty
, false, false);
1326 /* Enable command */
1330 "Turn on privileged mode command\n")
1332 /* If enable password is NULL, change to ENABLE_NODE */
1333 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1334 || vty
->type
== VTY_SHELL_SERV
)
1335 vty
->node
= ENABLE_NODE
;
1337 vty
->node
= AUTH_ENABLE_NODE
;
1342 /* Disable command */
1346 "Turn off privileged mode command\n")
1348 if (vty
->node
== ENABLE_NODE
)
1349 vty
->node
= VIEW_NODE
;
1353 /* Down vty node level. */
1357 "Exit current mode and down to previous mode\n")
1363 static int root_on_exit(struct vty
*vty
)
1368 vty
->status
= VTY_CLOSE
;
1372 void cmd_exit(struct vty
*vty
)
1374 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1376 if (cnode
->node_exit
) {
1377 if (!cnode
->node_exit(vty
))
1380 if (cnode
->parent_node
)
1381 vty
->node
= cnode
->parent_node
;
1382 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1390 "Exit current mode and down to previous mode\n")
1392 return config_exit(self
, vty
, argc
, argv
);
1396 /* End of configuration. */
1400 "End current mode and change to enable mode.\n")
1403 vty_config_exit(vty
);
1404 vty
->node
= ENABLE_NODE
;
1410 DEFUN (show_version
,
1414 "Displays zebra version\n")
1416 vty_out(vty
, "%s %s (%s) on %s(%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1417 cmd_hostname_get() ? cmd_hostname_get() : "", cmd_system_get(),
1419 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1420 #ifdef ENABLE_VERSION_BUILD_CONFIG
1421 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1426 /* Help display function for all node. */
1430 "Description of the interactive help system\n")
1433 "Quagga VTY provides advanced help feature. When you need help,\n\
1434 anytime at the command line please press '?'.\n\
1436 If nothing matches, the help list will be empty and you must backup\n\
1437 until entering a '?' shows the available options.\n\
1438 Two styles of help are provided:\n\
1439 1. Full help is available when you are ready to enter a\n\
1440 command argument (e.g. 'show ?') and describes each possible\n\
1442 2. Partial help is provided when an abbreviated argument is entered\n\
1443 and you want to know what arguments match the input\n\
1444 (e.g. 'show me?'.)\n\n");
1448 static void permute(struct graph_node
*start
, struct vty
*vty
)
1450 static struct list
*position
= NULL
;
1452 position
= list_new();
1454 struct cmd_token
*stok
= start
->data
;
1455 struct graph_node
*gnn
;
1456 struct listnode
*ln
;
1459 listnode_add(position
, start
);
1460 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1461 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1462 struct cmd_token
*tok
= gn
->data
;
1463 if (tok
->attr
== CMD_ATTR_HIDDEN
1464 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1466 else if (tok
->type
== END_TKN
|| gn
== start
) {
1468 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1469 struct cmd_token
*tt
= gnn
->data
;
1470 if (tt
->type
< SPECIAL_TKN
)
1471 vty_out(vty
, " %s", tt
->text
);
1474 vty_out(vty
, "...");
1478 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1479 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1488 list_delete_node(position
, listtail(position
));
1491 static void print_cmd(struct vty
*vty
, const char *cmd
)
1493 int i
, j
, len
= strlen(cmd
);
1498 for (i
= 0; i
< len
; i
++) {
1502 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1508 if (isspace(cmd
[i
])) {
1509 /* skip leading whitespace */
1512 /* skip trailing whitespace */
1515 /* skip all whitespace after opening brackets or pipe */
1516 if (strchr("(<[{|", cmd
[i
- 1])) {
1517 while (isspace(cmd
[i
+ 1]))
1521 /* skip repeated whitespace */
1522 if (isspace(cmd
[i
+ 1]))
1524 /* skip whitespace before closing brackets or pipe */
1525 if (strchr(")>]}|", cmd
[i
+ 1]))
1527 /* convert tabs to spaces */
1528 if (cmd
[i
] == '\t') {
1538 vty_out(vty
, "%s\n", buf
);
1541 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1543 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1546 cmd_finalize_node(node
);
1547 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1549 /* loop over all commands at this node */
1550 const struct cmd_element
*element
= NULL
;
1551 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1553 if ((element
= vector_slot(node
->cmd_vector
, i
))
1554 && element
->attr
!= CMD_ATTR_DEPRECATED
1555 && element
->attr
!= CMD_ATTR_HIDDEN
) {
1557 print_cmd(vty
, element
->string
);
1563 /* Help display function for all node. */
1566 "list [permutations]",
1567 "Print command list\n"
1568 "Print all possible command permutations\n")
1570 return cmd_list_cmds(vty
, argc
== 2);
1573 DEFUN (show_commandtree
,
1574 show_commandtree_cmd
,
1575 "show commandtree [permutations]",
1577 "Show command tree\n"
1578 "Permutations that we are interested in\n")
1580 return cmd_list_cmds(vty
, argc
== 3);
1583 DEFUN_HIDDEN(show_cli_graph
,
1588 "Dump current command space as DOT graph\n")
1590 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1593 cmd_finalize_node(cn
);
1594 dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1596 vty_out(vty
, "%s\n", dot
);
1597 XFREE(MTYPE_TMP
, dot
);
1601 static int vty_write_config(struct vty
*vty
)
1604 struct cmd_node
*node
;
1609 nb_cli_show_config_prepare(running_config
, false);
1611 if (vty
->type
== VTY_TERM
) {
1612 vty_out(vty
, "\nCurrent configuration:\n");
1613 vty_out(vty
, "!\n");
1616 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1617 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1618 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1619 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1620 vty_out(vty
, "!\n");
1622 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1623 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1624 if ((*node
->config_write
)(vty
))
1625 vty_out(vty
, "!\n");
1628 if (vty
->type
== VTY_TERM
) {
1629 vty_out(vty
, "end\n");
1635 static int file_write_config(struct vty
*vty
)
1638 char *config_file
, *slash
;
1639 char *config_file_tmp
= NULL
;
1640 char *config_file_sav
= NULL
;
1641 int ret
= CMD_WARNING
;
1642 struct vty
*file_vty
;
1643 struct stat conf_stat
;
1648 /* Check and see if we are operating under vtysh configuration */
1649 if (host
.config
== NULL
) {
1651 "Can't save to configuration file, using vtysh.\n");
1656 config_file
= host
.config
;
1659 #define O_DIRECTORY 0
1661 slash
= strrchr(config_file
, '/');
1663 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1664 config_dir
[slash
- config_file
] = '\0';
1665 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1666 XFREE(MTYPE_TMP
, config_dir
);
1668 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1669 /* if dirfd is invalid, directory sync fails, but we're still OK */
1671 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1672 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1673 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1674 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1677 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1678 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1681 /* Open file to configuration write. */
1682 fd
= mkstemp(config_file_tmp
);
1684 vty_out(vty
, "Can't open configuration file %s.\n",
1688 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1689 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1690 config_file_tmp
, safe_strerror(errno
), errno
);
1694 /* Make vty for configuration file. */
1695 file_vty
= vty_new();
1697 file_vty
->type
= VTY_FILE
;
1699 /* Config file header print. */
1700 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1701 vty_time_print(file_vty
, 1);
1702 vty_out(file_vty
, "!\n");
1703 vty_write_config(file_vty
);
1704 vty_close(file_vty
);
1706 if (stat(config_file
, &conf_stat
) >= 0) {
1707 if (unlink(config_file_sav
) != 0)
1708 if (errno
!= ENOENT
) {
1710 "Can't unlink backup configuration file %s.\n",
1714 if (link(config_file
, config_file_sav
) != 0) {
1716 "Can't backup old configuration file %s.\n",
1723 if (rename(config_file_tmp
, config_file
) != 0) {
1724 vty_out(vty
, "Can't save configuration file %s.\n",
1731 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1735 if (ret
!= CMD_SUCCESS
)
1736 unlink(config_file_tmp
);
1739 XFREE(MTYPE_TMP
, config_file_tmp
);
1740 XFREE(MTYPE_TMP
, config_file_sav
);
1744 /* Write current configuration into file. */
1746 DEFUN (config_write
,
1748 "write [<file|memory|terminal>]",
1749 "Write running configuration to memory, network, or terminal\n"
1750 "Write to configuration file\n"
1751 "Write configuration currently in memory\n"
1752 "Write configuration to terminal\n")
1754 const int idx_type
= 1;
1756 // if command was 'write terminal' or 'write memory'
1757 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1758 return vty_write_config(vty
);
1761 return file_write_config(vty
);
1764 /* ALIAS_FIXME for 'write <terminal|memory>' */
1765 DEFUN (show_running_config
,
1766 show_running_config_cmd
,
1767 "show running-config",
1769 "running configuration (same as write terminal)\n")
1771 return vty_write_config(vty
);
1774 /* ALIAS_FIXME for 'write file' */
1775 DEFUN (copy_runningconf_startupconf
,
1776 copy_runningconf_startupconf_cmd
,
1777 "copy running-config startup-config",
1778 "Copy configuration\n"
1779 "Copy running config to... \n"
1780 "Copy running config to startup config (same as write file/memory)\n")
1782 return file_write_config(vty
);
1786 /* Write startup configuration into the terminal. */
1787 DEFUN (show_startup_config
,
1788 show_startup_config_cmd
,
1789 "show startup-config",
1791 "Contents of startup configuration\n")
1798 if (host
.config
== NULL
)
1801 confp
= fopen(host
.config
, "r");
1802 if (confp
== NULL
) {
1803 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1804 host
.config
, safe_strerror(errno
));
1808 while (fgets(buf
, BUFSIZ
, confp
)) {
1811 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1815 vty_out(vty
, "%s\n", buf
);
1823 int cmd_domainname_set(const char *domainname
)
1825 XFREE(MTYPE_HOST
, host
.domainname
);
1826 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1830 /* Hostname configuration */
1831 DEFUN(config_domainname
,
1834 "Set system's domain name\n"
1835 "This system's domain name\n")
1837 struct cmd_token
*word
= argv
[1];
1839 if (!isalpha((unsigned char)word
->arg
[0])) {
1840 vty_out(vty
, "Please specify string starting with alphabet\n");
1841 return CMD_WARNING_CONFIG_FAILED
;
1844 return cmd_domainname_set(word
->arg
);
1847 DEFUN(config_no_domainname
,
1849 "no domainname [DOMAINNAME]",
1851 "Reset system's domain name\n"
1852 "domain name of this router\n")
1854 return cmd_domainname_set(NULL
);
1857 int cmd_hostname_set(const char *hostname
)
1859 XFREE(MTYPE_HOST
, host
.name
);
1860 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1864 /* Hostname configuration */
1865 DEFUN (config_hostname
,
1868 "Set system's network name\n"
1869 "This system's network name\n")
1871 struct cmd_token
*word
= argv
[1];
1873 if (!isalnum((unsigned char)word
->arg
[0])) {
1875 "Please specify string starting with alphabet or number\n");
1876 return CMD_WARNING_CONFIG_FAILED
;
1879 /* With reference to RFC 1123 Section 2.1 */
1880 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1881 vty_out(vty
, "Hostname length should be less than %d chars\n",
1883 return CMD_WARNING_CONFIG_FAILED
;
1886 return cmd_hostname_set(word
->arg
);
1889 DEFUN (config_no_hostname
,
1891 "no hostname [HOSTNAME]",
1893 "Reset system's network name\n"
1894 "Host name of this router\n")
1896 return cmd_hostname_set(NULL
);
1899 /* VTY interface password set. */
1900 DEFUN (config_password
,
1902 "password [(8-8)] WORD",
1903 "Modify the terminal connection password\n"
1904 "Specifies a HIDDEN password will follow\n"
1905 "The password string\n")
1909 if (argc
== 3) // '8' was specified
1912 XFREE(MTYPE_HOST
, host
.password
);
1913 host
.password
= NULL
;
1914 if (host
.password_encrypt
)
1915 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1916 host
.password_encrypt
=
1917 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1921 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1923 "Please specify string starting with alphanumeric\n");
1924 return CMD_WARNING_CONFIG_FAILED
;
1928 XFREE(MTYPE_HOST
, host
.password
);
1929 host
.password
= NULL
;
1932 if (host
.password_encrypt
)
1933 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1934 host
.password_encrypt
=
1935 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1937 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1942 /* VTY interface password delete. */
1943 DEFUN (no_config_password
,
1947 "Modify the terminal connection password\n")
1949 bool warned
= false;
1951 if (host
.password
) {
1952 if (!vty_shell_serv(vty
)) {
1953 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1956 XFREE(MTYPE_HOST
, host
.password
);
1958 host
.password
= NULL
;
1960 if (host
.password_encrypt
) {
1961 if (!warned
&& !vty_shell_serv(vty
))
1962 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1963 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1965 host
.password_encrypt
= NULL
;
1970 /* VTY enable password set. */
1971 DEFUN (config_enable_password
,
1972 enable_password_cmd
,
1973 "enable password [(8-8)] WORD",
1974 "Modify enable password parameters\n"
1975 "Assign the privileged level password\n"
1976 "Specifies a HIDDEN password will follow\n"
1977 "The HIDDEN 'enable' password string\n")
1982 /* Crypt type is specified. */
1984 if (argv
[idx_8
]->arg
[0] == '8') {
1986 XFREE(MTYPE_HOST
, host
.enable
);
1989 if (host
.enable_encrypt
)
1990 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1991 host
.enable_encrypt
=
1992 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1996 vty_out(vty
, "Unknown encryption type.\n");
1997 return CMD_WARNING_CONFIG_FAILED
;
2001 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2003 "Please specify string starting with alphanumeric\n");
2004 return CMD_WARNING_CONFIG_FAILED
;
2008 XFREE(MTYPE_HOST
, host
.enable
);
2011 /* Plain password input. */
2013 if (host
.enable_encrypt
)
2014 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2015 host
.enable_encrypt
=
2016 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2018 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2023 /* VTY enable password delete. */
2024 DEFUN (no_config_enable_password
,
2025 no_enable_password_cmd
,
2026 "no enable password",
2028 "Modify enable password parameters\n"
2029 "Assign the privileged level password\n")
2031 bool warned
= false;
2034 if (!vty_shell_serv(vty
)) {
2035 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2038 XFREE(MTYPE_HOST
, host
.enable
);
2042 if (host
.enable_encrypt
) {
2043 if (!warned
&& !vty_shell_serv(vty
))
2044 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2045 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2047 host
.enable_encrypt
= NULL
;
2052 DEFUN (service_password_encrypt
,
2053 service_password_encrypt_cmd
,
2054 "service password-encryption",
2055 "Set up miscellaneous service\n"
2056 "Enable encrypted passwords\n")
2063 if (host
.password
) {
2064 if (host
.password_encrypt
)
2065 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2066 host
.password_encrypt
=
2067 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2070 if (host
.enable_encrypt
)
2071 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2072 host
.enable_encrypt
=
2073 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2079 DEFUN (no_service_password_encrypt
,
2080 no_service_password_encrypt_cmd
,
2081 "no service password-encryption",
2083 "Set up miscellaneous service\n"
2084 "Enable encrypted passwords\n")
2091 if (host
.password_encrypt
)
2092 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2093 host
.password_encrypt
= NULL
;
2095 if (host
.enable_encrypt
)
2096 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2097 host
.enable_encrypt
= NULL
;
2102 DEFUN (config_terminal_length
,
2103 config_terminal_length_cmd
,
2104 "terminal length (0-512)",
2105 "Set terminal line parameters\n"
2106 "Set number of lines on a screen\n"
2107 "Number of lines on screen (0 for no pausing)\n")
2111 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2116 DEFUN (config_terminal_no_length
,
2117 config_terminal_no_length_cmd
,
2118 "terminal no length",
2119 "Set terminal line parameters\n"
2121 "Set number of lines on a screen\n")
2127 DEFUN (service_terminal_length
,
2128 service_terminal_length_cmd
,
2129 "service terminal-length (0-512)",
2130 "Set up miscellaneous service\n"
2131 "System wide terminal length configuration\n"
2132 "Number of lines of VTY (0 means no line control)\n")
2136 host
.lines
= atoi(argv
[idx_number
]->arg
);
2141 DEFUN (no_service_terminal_length
,
2142 no_service_terminal_length_cmd
,
2143 "no service terminal-length [(0-512)]",
2145 "Set up miscellaneous service\n"
2146 "System wide terminal length configuration\n"
2147 "Number of lines of VTY (0 means no line control)\n")
2153 DEFUN_HIDDEN (do_echo
,
2156 "Echo a message back to the vty\n"
2157 "The message to echo\n")
2161 vty_out(vty
, "%s\n",
2162 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2164 XFREE(MTYPE_TMP
, message
);
2168 DEFUN (config_logmsg
,
2170 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2171 "Send a message to enabled logging destinations\n"
2173 "The message to send\n")
2175 int idx_log_level
= 1;
2176 int idx_message
= 2;
2180 level
= log_level_match(argv
[idx_log_level
]->arg
);
2181 if (level
== ZLOG_DISABLED
)
2182 return CMD_ERR_NO_MATCH
;
2185 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2187 XFREE(MTYPE_TMP
, message
);
2192 DEFUN (debug_memstats
,
2194 "[no] debug memstats-at-exit",
2197 "Print memory type statistics at exit\n")
2199 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2203 int cmd_banner_motd_file(const char *file
)
2205 int success
= CMD_SUCCESS
;
2210 rpath
= realpath(file
, p
);
2212 return CMD_ERR_NO_FILE
;
2213 in
= strstr(rpath
, SYSCONFDIR
);
2215 XFREE(MTYPE_HOST
, host
.motdfile
);
2216 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2218 success
= CMD_WARNING_CONFIG_FAILED
;
2223 void cmd_banner_motd_line(const char *line
)
2225 XFREE(MTYPE_HOST
, host
.motd
);
2226 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2229 DEFUN (banner_motd_file
,
2230 banner_motd_file_cmd
,
2231 "banner motd file FILE",
2234 "Banner from a file\n"
2238 const char *filename
= argv
[idx_file
]->arg
;
2239 int cmd
= cmd_banner_motd_file(filename
);
2241 if (cmd
== CMD_ERR_NO_FILE
)
2242 vty_out(vty
, "%s does not exist", filename
);
2243 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2244 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2249 DEFUN (banner_motd_line
,
2250 banner_motd_line_cmd
,
2251 "banner motd line LINE...",
2254 "Banner from an input\n"
2260 argv_find(argv
, argc
, "LINE", &idx
);
2261 motd
= argv_concat(argv
, argc
, idx
);
2263 cmd_banner_motd_line(motd
);
2264 XFREE(MTYPE_TMP
, motd
);
2269 DEFUN (banner_motd_default
,
2270 banner_motd_default_cmd
,
2271 "banner motd default",
2272 "Set banner string\n"
2273 "Strings for motd\n"
2276 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2280 DEFUN (no_banner_motd
,
2284 "Set banner string\n"
2285 "Strings for motd\n")
2289 XFREE(MTYPE_HOST
, host
.motdfile
);
2290 host
.motdfile
= NULL
;
2294 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2296 const struct cmd_node
*node
;
2297 const struct cmd_element
*cli
;
2302 char *pattern
= argv_concat(argv
, argc
, 1);
2303 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2304 XFREE(MTYPE_TMP
, pattern
);
2309 vty_out(vty
, "%% Invalid {...} expression\n");
2312 vty_out(vty
, "%% Bad repetition operator\n");
2315 vty_out(vty
, "%% Regex syntax error\n");
2318 vty_out(vty
, "%% Invalid collating element\n");
2321 vty_out(vty
, "%% Invalid character class name\n");
2325 "%% Regex ended with escape character (\\)\n");
2329 "%% Invalid number in \\digit construction\n");
2332 vty_out(vty
, "%% Unbalanced square brackets\n");
2335 vty_out(vty
, "%% Unbalanced parentheses\n");
2338 vty_out(vty
, "%% Unbalanced braces\n");
2342 "%% Invalid endpoint in range expression\n");
2345 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2353 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2354 node
= vector_slot(cmdvec
, i
);
2357 clis
= node
->cmd_vector
;
2358 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2359 cli
= vector_slot(clis
, j
);
2361 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2362 vty_out(vty
, " (%s) ", node
->name
);
2363 print_cmd(vty
, cli
->string
);
2376 "Find CLI command matching a regular expression\n"
2377 "Search pattern (POSIX regex)\n")
2379 return cmd_find_cmds(vty
, argv
, argc
);
2382 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2383 DEFUN(script
, script_cmd
, "script SCRIPT FUNCTION",
2384 "Test command - execute a function in a script\n"
2385 "Script name (same as filename in /etc/frr/scripts/)\n"
2386 "Function name (in the script)\n")
2390 (void)str2prefix("1.2.3.4/24", &p
);
2391 struct frrscript
*fs
= frrscript_new(argv
[1]->arg
);
2393 if (frrscript_load(fs
, argv
[2]->arg
, NULL
)) {
2395 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2396 argv
[1]->arg
, argv
[2]->arg
);
2399 int ret
= frrscript_call(fs
, argv
[2]->arg
, ("p", &p
));
2401 prefix2str(&p
, buf
, sizeof(buf
));
2402 vty_out(vty
, "p: %s\n", buf
);
2403 vty_out(vty
, "Script result: %d\n", ret
);
2405 frrscript_delete(fs
);
2411 /* Set config filename. Called from vty.c */
2412 void host_config_set(const char *filename
)
2414 XFREE(MTYPE_HOST
, host
.config
);
2415 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2418 const char *host_config_get(void)
2423 void install_default(enum node_type node
)
2425 _install_element(node
, &config_exit_cmd
);
2426 _install_element(node
, &config_quit_cmd
);
2427 _install_element(node
, &config_end_cmd
);
2428 _install_element(node
, &config_help_cmd
);
2429 _install_element(node
, &config_list_cmd
);
2430 _install_element(node
, &show_cli_graph_cmd
);
2431 _install_element(node
, &find_cmd
);
2433 _install_element(node
, &config_write_cmd
);
2434 _install_element(node
, &show_running_config_cmd
);
2436 _install_element(node
, &autocomplete_cmd
);
2438 nb_cli_install_default(node
);
2441 /* Initialize command interface. Install basic nodes and commands.
2443 * terminal = 0 -- vtysh / no logging, no config control
2444 * terminal = 1 -- normal daemon
2445 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2446 void cmd_init(int terminal
)
2448 struct utsname names
;
2453 /* register command preprocessors */
2454 hook_register(cmd_execute
, handle_pipe_action
);
2455 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2457 varhandlers
= list_new();
2459 /* Allocate initial top vector of commands. */
2460 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2462 /* Default host value settings. */
2463 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2464 host
.system
= XSTRDUP(MTYPE_HOST
, names
.sysname
);
2465 host
.release
= XSTRDUP(MTYPE_HOST
, names
.release
);
2466 host
.version
= XSTRDUP(MTYPE_HOST
, names
.version
);
2468 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2469 if ((strcmp(names
.domainname
, "(none)") == 0))
2470 host
.domainname
= NULL
;
2472 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2474 host
.domainname
= NULL
;
2476 host
.password
= NULL
;
2479 host
.noconfig
= (terminal
< 0);
2481 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2482 host
.motdfile
= NULL
;
2484 /* Install top nodes. */
2485 install_node(&view_node
);
2486 install_node(&enable_node
);
2487 install_node(&auth_node
);
2488 install_node(&auth_enable_node
);
2489 install_node(&config_node
);
2491 /* Each node's basic commands. */
2492 install_element(VIEW_NODE
, &show_version_cmd
);
2493 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2496 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2498 install_element(VIEW_NODE
, &config_list_cmd
);
2499 install_element(VIEW_NODE
, &config_exit_cmd
);
2500 install_element(VIEW_NODE
, &config_quit_cmd
);
2501 install_element(VIEW_NODE
, &config_help_cmd
);
2502 install_element(VIEW_NODE
, &config_enable_cmd
);
2503 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2504 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2505 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2506 install_element(VIEW_NODE
, &echo_cmd
);
2507 install_element(VIEW_NODE
, &autocomplete_cmd
);
2508 install_element(VIEW_NODE
, &find_cmd
);
2509 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2510 install_element(VIEW_NODE
, &script_cmd
);
2514 install_element(ENABLE_NODE
, &config_end_cmd
);
2515 install_element(ENABLE_NODE
, &config_disable_cmd
);
2516 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2517 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2518 install_element(ENABLE_NODE
, &config_write_cmd
);
2519 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2520 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2522 install_default(CONFIG_NODE
);
2525 workqueue_cmd_init();
2529 install_element(CONFIG_NODE
, &hostname_cmd
);
2530 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2531 install_element(CONFIG_NODE
, &domainname_cmd
);
2532 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2537 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2539 install_element(CONFIG_NODE
, &password_cmd
);
2540 install_element(CONFIG_NODE
, &no_password_cmd
);
2541 install_element(CONFIG_NODE
, &enable_password_cmd
);
2542 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2544 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2545 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2546 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2547 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2548 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2549 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2550 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2551 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2554 vrf_install_commands();
2558 grammar_sandbox_init();
2562 void cmd_terminate(void)
2564 struct cmd_node
*cmd_node
;
2566 hook_unregister(cmd_execute
, handle_pipe_action
);
2567 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2570 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2571 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2572 // deleting the graph delets the cmd_element as
2574 graph_delete_graph(cmd_node
->cmdgraph
);
2575 vector_free(cmd_node
->cmd_vector
);
2576 hash_clean(cmd_node
->cmd_hash
, NULL
);
2577 hash_free(cmd_node
->cmd_hash
);
2578 cmd_node
->cmd_hash
= NULL
;
2581 vector_free(cmdvec
);
2585 XFREE(MTYPE_HOST
, host
.name
);
2586 XFREE(MTYPE_HOST
, host
.system
);
2587 XFREE(MTYPE_HOST
, host
.release
);
2588 XFREE(MTYPE_HOST
, host
.version
);
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
);
2598 list_delete(&varhandlers
);