1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * CLI backend interface.
6 * Copyright (C) 2016 Cumulus Networks, Inc.
7 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
8 * Copyright (C) 2013 by Open Source Routing.
9 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
13 #include <lib/version.h>
24 #include "workqueue.h"
26 #include "command_match.h"
27 #include "command_graph.h"
33 #include "lib_errors.h"
34 #include "northbound_cli.h"
38 #include "frrscript.h"
40 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config");
41 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item");
48 /* clang-format off */
49 const struct message tokennames
[] = {
54 item(IPV4_PREFIX_TKN
),
56 item(IPV6_PREFIX_TKN
),
69 /* Command vector which includes some level of command lists. Normally
70 each daemon maintains each own cmdvec. */
73 /* Host information structure. */
76 /* for vtysh, put together CLI trees only when switching into node */
77 static bool defer_cli_tree
;
80 * Returns host.name if any, otherwise
81 * it returns the system hostname.
83 const char *cmd_hostname_get(void)
89 * Returns unix domainname
91 const char *cmd_domainname_get(void)
93 return host
.domainname
;
96 const char *cmd_system_get(void)
101 const char *cmd_release_get(void)
106 const char *cmd_version_get(void)
111 bool cmd_allow_reserved_ranges_get(void)
113 return host
.allow_reserved_ranges
;
116 const char *cmd_software_version_get(void)
118 return FRR_FULL_NAME
"/" FRR_VERSION
;
121 static int root_on_exit(struct vty
*vty
);
123 /* Standard command node structures. */
124 static struct cmd_node auth_node
= {
127 .prompt
= "Password: ",
130 static struct cmd_node view_node
= {
134 .node_exit
= root_on_exit
,
137 static struct cmd_node auth_enable_node
= {
138 .name
= "auth enable",
139 .node
= AUTH_ENABLE_NODE
,
140 .prompt
= "Password: ",
143 static struct cmd_node enable_node
= {
147 .node_exit
= root_on_exit
,
150 static int config_write_host(struct vty
*vty
);
151 static struct cmd_node config_node
= {
154 .parent_node
= ENABLE_NODE
,
155 .prompt
= "%s(config)# ",
156 .config_write
= config_write_host
,
157 .node_exit
= vty_config_node_exit
,
160 /* This is called from main when a daemon is invoked with -v or --version. */
161 void print_version(const char *progname
)
163 printf("%s version %s\n", progname
, FRR_VERSION
);
164 printf("%s\n", FRR_COPYRIGHT
);
165 #ifdef ENABLE_VERSION_BUILD_CONFIG
166 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
170 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
172 int cnt
= MAX(argc
- shift
, 0);
173 const char *argstr
[cnt
+ 1];
178 for (int i
= 0; i
< cnt
; i
++)
179 argstr
[i
] = argv
[i
+ shift
]->arg
;
181 return frrstr_join(argstr
, cnt
, " ");
184 vector
cmd_make_strvec(const char *string
)
189 const char *copy
= string
;
191 /* skip leading whitespace */
192 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
195 /* if the entire string was whitespace or a comment, return */
196 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
199 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
201 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
202 if (strlen(vector_slot(result
, i
)) == 0) {
203 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
204 vector_unset(result
, i
);
208 vector_compact(result
);
213 void cmd_free_strvec(vector v
)
215 frrstr_strvec_free(v
);
219 * Convenience function for accessing argv data.
223 * @param text definition snippet of the desired token
224 * @param index the starting index, and where to store the
225 * index of the found token if it exists
226 * @return 1 if found, 0 otherwise
228 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
231 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
232 if ((found
= strmatch(text
, argv
[i
]->text
)))
237 static unsigned int cmd_hash_key(const void *p
)
239 int size
= sizeof(p
);
241 return jhash(p
, size
, 0);
244 static bool cmd_hash_cmp(const void *a
, const void *b
)
249 /* Install top node of command vector. */
250 void install_node(struct cmd_node
*node
)
252 #define CMD_HASH_STR_SIZE 256
253 char hash_name
[CMD_HASH_STR_SIZE
];
255 vector_set_index(cmdvec
, node
->node
, node
);
256 node
->cmdgraph
= graph_new();
257 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
259 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
260 graph_new_node(node
->cmdgraph
, token
,
261 (void (*)(void *)) & cmd_token_del
);
263 snprintf(hash_name
, sizeof(hash_name
), "Command Hash: %s", node
->name
);
265 hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
, hash_name
);
268 /* Return prompt character of specified node. */
269 const char *cmd_prompt(enum node_type node
)
271 struct cmd_node
*cnode
;
273 cnode
= vector_slot(cmdvec
, node
);
274 return cnode
->prompt
;
277 void cmd_defer_tree(bool val
)
279 defer_cli_tree
= val
;
282 /* Install a command into a node. */
283 void _install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
285 struct cmd_node
*cnode
;
287 /* cmd_init hasn't been called */
289 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
294 cnode
= vector_lookup(cmdvec
, ntype
);
299 "\tnode %d does not exist.\n"
300 "\tplease call install_node() before install_element()\n",
301 cmd
->name
, cmd
->string
, ntype
);
305 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
308 "\tnode %d (%s) already has this command installed.\n"
309 "\tduplicate install_element call?\n",
310 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
314 (void)hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
);
316 if (cnode
->graph_built
|| !defer_cli_tree
) {
317 struct graph
*graph
= graph_new();
318 struct cmd_token
*token
=
319 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
320 graph_new_node(graph
, token
,
321 (void (*)(void *)) & cmd_token_del
);
323 cmd_graph_parse(graph
, cmd
);
324 cmd_graph_names(graph
);
325 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
326 graph_delete_graph(graph
);
328 cnode
->graph_built
= true;
331 vector_set(cnode
->cmd_vector
, (void *)cmd
);
333 if (ntype
== VIEW_NODE
)
334 _install_element(ENABLE_NODE
, cmd
);
337 static void cmd_finalize_iter(struct hash_bucket
*hb
, void *arg
)
339 struct cmd_node
*cnode
= arg
;
340 const struct cmd_element
*cmd
= hb
->data
;
341 struct graph
*graph
= graph_new();
342 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
344 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
346 cmd_graph_parse(graph
, cmd
);
347 cmd_graph_names(graph
);
348 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
349 graph_delete_graph(graph
);
352 void cmd_finalize_node(struct cmd_node
*cnode
)
354 if (cnode
->graph_built
)
357 hash_iterate(cnode
->cmd_hash
, cmd_finalize_iter
, cnode
);
358 cnode
->graph_built
= true;
361 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
363 struct cmd_node
*cnode
;
365 /* cmd_init hasn't been called */
367 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
372 cnode
= vector_lookup(cmdvec
, ntype
);
377 "\tnode %d does not exist.\n"
378 "\tplease call install_node() before uninstall_element()\n",
379 cmd
->name
, cmd
->string
, ntype
);
383 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
386 "\tnode %d (%s) does not have this command installed.\n"
387 "\tduplicate uninstall_element call?\n",
388 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
392 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
394 if (cnode
->graph_built
) {
395 struct graph
*graph
= graph_new();
396 struct cmd_token
*token
=
397 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
398 graph_new_node(graph
, token
,
399 (void (*)(void *)) & cmd_token_del
);
401 cmd_graph_parse(graph
, cmd
);
402 cmd_graph_names(graph
);
403 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
404 graph_delete_graph(graph
);
407 if (ntype
== VIEW_NODE
)
408 uninstall_element(ENABLE_NODE
, cmd
);
412 static const unsigned char itoa64
[] =
413 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
415 static void to64(char *s
, long v
, int n
)
418 *s
++ = itoa64
[v
& 0x3f];
423 static char *zencrypt(const char *passwd
)
428 gettimeofday(&tv
, 0);
430 to64(&salt
[0], frr_weak_random(), 3);
431 to64(&salt
[3], tv
.tv_usec
, 3);
434 return crypt(passwd
, salt
);
437 static bool full_cli
;
439 /* This function write configuration of this host. */
440 static int config_write_host(struct vty
*vty
)
444 name
= cmd_hostname_get();
445 if (name
&& name
[0] != '\0')
446 vty_out(vty
, "hostname %s\n", name
);
448 name
= cmd_domainname_get();
449 if (name
&& name
[0] != '\0')
450 vty_out(vty
, "domainname %s\n", name
);
452 if (cmd_allow_reserved_ranges_get())
453 vty_out(vty
, "allow-reserved-ranges\n");
455 /* The following are all configuration commands that are not sent to
456 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
457 * we would always display 'log syslog informational' in the config
458 * which would cause other daemons to then switch to syslog when they
463 if (host
.password_encrypt
)
464 vty_out(vty
, "password 8 %s\n",
465 host
.password_encrypt
);
466 if (host
.enable_encrypt
)
467 vty_out(vty
, "enable password 8 %s\n",
468 host
.enable_encrypt
);
471 vty_out(vty
, "password %s\n", host
.password
);
473 vty_out(vty
, "enable password %s\n",
476 log_config_write(vty
);
478 /* print disable always, but enable only if default is flipped
479 * => prep for future removal of compile-time knob
481 if (!cputime_enabled
)
482 vty_out(vty
, "no service cputime-stats\n");
483 #ifdef EXCLUDE_CPU_TIME
485 vty_out(vty
, "service cputime-stats\n");
488 if (!cputime_threshold
)
489 vty_out(vty
, "no service cputime-warning\n");
490 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
491 else /* again, always print non-default */
493 else if (cputime_threshold
!= 5000000)
495 vty_out(vty
, "service cputime-warning %lu\n",
496 cputime_threshold
/ 1000);
498 if (!walltime_threshold
)
499 vty_out(vty
, "no service walltime-warning\n");
500 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
501 else /* again, always print non-default */
503 else if (walltime_threshold
!= 5000000)
505 vty_out(vty
, "service walltime-warning %lu\n",
506 walltime_threshold
/ 1000);
509 vty_out(vty
, "service advanced-vty\n");
512 vty_out(vty
, "service password-encryption\n");
515 vty_out(vty
, "service terminal-length %d\n",
519 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
521 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
523 vty_out(vty
, "banner motd line %s\n", host
.motd
);
525 vty_out(vty
, "no banner motd\n");
528 if (debug_memstats_at_exit
)
529 vty_out(vty
, "!\ndebug memstats-at-exit\n");
534 /* Utility function for getting command graph. */
535 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
537 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
539 cmd_finalize_node(cnode
);
540 return cnode
->cmdgraph
;
543 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
545 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
546 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
552 * Compare function for cmd_token.
553 * Used with qsort to sort command completions.
555 static int compare_completions(const void *fst
, const void *snd
)
557 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
558 *secnd
= *(const struct cmd_token
* const *)snd
;
559 return strcmp(first
->text
, secnd
->text
);
563 * Takes a list of completions returned by command_complete,
564 * dedeuplicates them based on both text and description,
565 * sorts them, and returns them as a vector.
567 * @param completions linked list of cmd_token
568 * @return deduplicated and sorted vector with
570 vector
completions_to_vec(struct list
*completions
)
572 vector comps
= vector_init(VECTOR_MIN_SIZE
);
575 struct cmd_token
*token
, *cr
= NULL
;
576 unsigned int i
, exists
;
577 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
578 if (token
->type
== END_TKN
&& (cr
= token
))
581 // linear search for token in completions vector
583 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
584 struct cmd_token
*curr
= vector_slot(comps
, i
);
586 exists
= !strcmp(curr
->text
, token
->text
)
587 && !strcmp(curr
->desc
, token
->desc
);
589 exists
= !strcmp(curr
->text
, token
->text
);
590 #endif /* VTYSH_DEBUG */
594 vector_set(comps
, token
);
598 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
599 &compare_completions
);
601 // make <cr> the first element, if it is present
603 vector_set_index(comps
, vector_active(comps
), NULL
);
604 memmove(comps
->index
+ 1, comps
->index
,
605 (comps
->alloced
- 1) * sizeof(void *));
606 vector_set_index(comps
, 0, cr
);
612 * Generates a vector of cmd_token representing possible completions
613 * on the current input.
615 * @param vline the vectorized input line
616 * @param vty the vty with the node to match on
617 * @param status pointer to matcher status code
618 * @return vector of struct cmd_token * with possible completions
620 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
623 struct list
*completions
;
624 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
626 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
628 if (MATCHER_ERROR(rv
)) {
629 *status
= CMD_ERR_NO_MATCH
;
633 vector comps
= completions_to_vec(completions
);
634 list_delete(&completions
);
636 // set status code appropriately
637 switch (vector_active(comps
)) {
639 *status
= CMD_ERR_NO_MATCH
;
642 *status
= CMD_COMPLETE_FULL_MATCH
;
645 *status
= CMD_COMPLETE_LIST_MATCH
;
651 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
655 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
656 enum node_type onode
;
657 int orig_xpath_index
;
658 vector shifted_vline
;
662 orig_xpath_index
= vty
->xpath_index
;
663 vty
->node
= ENABLE_NODE
;
664 vty
->xpath_index
= 0;
665 /* We can try it on enable node, cos' the vty is authenticated
668 shifted_vline
= vector_init(vector_count(vline
));
670 for (index
= 1; index
< vector_active(vline
); index
++) {
671 vector_set_index(shifted_vline
, index
- 1,
672 vector_lookup(vline
, index
));
675 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
677 vector_free(shifted_vline
);
679 vty
->xpath_index
= orig_xpath_index
;
683 return cmd_complete_command_real(vline
, vty
, status
);
686 static struct list
*varhandlers
= NULL
;
688 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
692 const struct cmd_variable_handler
*cvh
;
696 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
698 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
699 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
701 if (cvh
->varname
&& (!token
->varname
702 || strcmp(cvh
->varname
, token
->varname
)))
704 cvh
->completions(tmpcomps
, token
);
712 for (i
= vector_active(tmpcomps
); i
; i
--) {
713 char *item
= vector_slot(tmpcomps
, i
- 1);
714 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
715 vector_set(comps
, item
);
717 XFREE(MTYPE_COMPLETION
, item
);
719 vector_free(tmpcomps
);
722 #define AUTOCOMP_INDENT 5
724 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
727 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
728 int lc
= AUTOCOMP_INDENT
;
729 size_t cs
= AUTOCOMP_INDENT
;
731 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
732 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
733 char *item
= vector_slot(comps
, j
);
734 itemlen
= strlen(item
);
736 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
737 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
739 if (lc
+ itemlen
+ 1 >= cols
) {
740 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
741 AUTOCOMP_INDENT
, "");
742 lc
= AUTOCOMP_INDENT
;
745 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
748 XFREE(MTYPE_COMPLETION
, item
);
749 vector_set_index(comps
, j
, NULL
);
754 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
759 for (; cvh
->completions
; cvh
++)
760 listnode_add(varhandlers
, (void *)cvh
);
763 DEFUN_HIDDEN (autocomplete
,
765 "autocomplete TYPE TEXT VARNAME",
766 "Autocompletion handler (internal, for vtysh)\n"
769 "cmd_token->varname\n")
771 struct cmd_token tok
;
772 vector comps
= vector_init(32);
775 memset(&tok
, 0, sizeof(tok
));
776 tok
.type
= atoi(argv
[1]->arg
);
777 tok
.text
= argv
[2]->arg
;
778 tok
.varname
= argv
[3]->arg
;
779 if (!strcmp(tok
.varname
, "-"))
782 cmd_variable_complete(&tok
, NULL
, comps
);
784 for (i
= 0; i
< vector_active(comps
); i
++) {
785 char *text
= vector_slot(comps
, i
);
786 vty_out(vty
, "%s\n", text
);
787 XFREE(MTYPE_COMPLETION
, text
);
795 * Generate possible tab-completions for the given input. This function only
796 * returns results that would result in a valid command if used as Readline
797 * completions (as is the case in vtysh). For instance, if the passed vline ends
798 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
800 * @param vline vectorized input line
802 * @param status location to store matcher status code in
803 * @return set of valid strings for use with Readline as tab-completions.
806 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
809 int original_node
= vty
->node
;
810 vector input_line
= vector_init(vector_count(vline
));
812 // if the first token is 'do' we'll want to execute the command in the
814 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
815 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
817 // construct the input line we'll be matching on
818 unsigned int offset
= (do_shortcut
) ? 1 : 0;
819 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
820 vector_set_index(input_line
, index
,
821 vector_lookup(vline
, index
+ offset
));
823 // get token completions -- this is a copying operation
824 vector comps
= NULL
, initial_comps
;
825 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
827 if (!MATCHER_ERROR(*status
)) {
828 assert(initial_comps
);
829 // filter out everything that is not suitable for a
831 comps
= vector_init(VECTOR_MIN_SIZE
);
832 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
834 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
835 if (token
->type
== WORD_TKN
)
836 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
838 else if (IS_VARYING_TOKEN(token
->type
)) {
839 const char *ref
= vector_lookup(
840 vline
, vector_active(vline
) - 1);
841 cmd_variable_complete(token
, ref
, comps
);
844 vector_free(initial_comps
);
846 // since we filtered results, we need to re-set status code
847 switch (vector_active(comps
)) {
849 *status
= CMD_ERR_NO_MATCH
;
852 *status
= CMD_COMPLETE_FULL_MATCH
;
855 *status
= CMD_COMPLETE_LIST_MATCH
;
858 // copy completions text into an array of char*
859 ret
= XMALLOC(MTYPE_TMP
,
860 (vector_active(comps
) + 1) * sizeof(char *));
862 for (i
= 0; i
< vector_active(comps
); i
++) {
863 ret
[i
] = vector_slot(comps
, i
);
865 // set the last element to NULL, because this array is used in
866 // a Readline completion_generator function which expects NULL
867 // as a sentinel value
871 } else if (initial_comps
)
872 vector_free(initial_comps
);
874 // comps should always be null here
877 // free the adjusted input line
878 vector_free(input_line
);
880 // reset vty->node to its original value
881 vty
->node
= original_node
;
886 /* return parent node */
887 /* MUST eventually converge on CONFIG_NODE */
888 enum node_type
node_parent(enum node_type node
)
890 struct cmd_node
*cnode
;
892 assert(node
> CONFIG_NODE
);
894 cnode
= vector_lookup(cmdvec
, node
);
896 return cnode
->parent_node
;
899 /* Execute command by argument vline vector. */
900 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
902 const struct cmd_element
**cmd
,
903 unsigned int up_level
)
905 struct list
*argv_list
;
906 enum matcher_rv status
;
907 const struct cmd_element
*matched_element
= NULL
;
909 int xpath_index
= vty
->xpath_index
;
910 int node
= vty
->node
;
912 /* only happens for legacy split config file load; need to check for
913 * a match before calling node_exit handlers below
915 for (i
= 0; i
< up_level
; i
++) {
916 struct cmd_node
*cnode
;
918 if (node
<= CONFIG_NODE
)
919 return CMD_NO_LEVEL_UP
;
921 cnode
= vector_slot(cmdvec
, node
);
922 node
= node_parent(node
);
924 if (xpath_index
> 0 && !cnode
->no_xpath
)
928 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
929 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
932 *cmd
= matched_element
;
934 // if matcher error, return corresponding CMD_ERR
935 if (MATCHER_ERROR(status
)) {
937 list_delete(&argv_list
);
939 case MATCHER_INCOMPLETE
:
940 return CMD_ERR_INCOMPLETE
;
941 case MATCHER_AMBIGUOUS
:
942 return CMD_ERR_AMBIGUOUS
;
943 case MATCHER_NO_MATCH
:
945 return CMD_ERR_NO_MATCH
;
949 for (i
= 0; i
< up_level
; i
++)
952 // build argv array from argv list
953 struct cmd_token
**argv
= XMALLOC(
954 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
956 struct cmd_token
*token
;
959 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
962 int argc
= argv_list
->count
;
965 if (matched_element
->daemon
)
966 ret
= CMD_SUCCESS_DAEMON
;
969 /* Clear array of enqueued configuration changes. */
970 vty
->num_cfg_changes
= 0;
971 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
973 /* Regenerate candidate configuration if necessary. */
974 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
975 && running_config
->version
976 > vty
->candidate_config
->version
)
977 nb_config_replace(vty
->candidate_config
,
978 running_config
, true);
981 * Perform pending commit (if any) before executing
984 if (!(matched_element
->attr
& CMD_ATTR_YANG
))
985 (void)nb_cli_pending_commit_check(vty
);
988 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
991 // delete list and cmd_token's in it
992 list_delete(&argv_list
);
993 XFREE(MTYPE_TMP
, argv
);
999 * Execute a given command, handling things like "do ..." and checking
1000 * whether the given command might apply at a parent node if doesn't
1001 * apply for the current node.
1003 * @param vline Command line input, vector of char* where each element is
1005 * @param vty The vty context in which the command should be executed.
1006 * @param cmd Pointer where the struct cmd_element of the matched command
1007 * will be stored, if any. May be set to NULL if this info is
1009 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1010 * @return The status of the command that has been executed or an error code
1011 * as to why no command could be executed.
1013 int cmd_execute_command(vector vline
, struct vty
*vty
,
1014 const struct cmd_element
**cmd
, int vtysh
)
1016 int ret
, saved_ret
= 0;
1017 enum node_type onode
, try_node
;
1018 int orig_xpath_index
;
1020 onode
= try_node
= vty
->node
;
1021 orig_xpath_index
= vty
->xpath_index
;
1023 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1024 vector shifted_vline
;
1027 vty
->node
= ENABLE_NODE
;
1028 vty
->xpath_index
= 0;
1029 /* We can try it on enable node, cos' the vty is authenticated
1032 shifted_vline
= vector_init(vector_count(vline
));
1034 for (index
= 1; index
< vector_active(vline
); index
++)
1035 vector_set_index(shifted_vline
, index
- 1,
1036 vector_lookup(vline
, index
));
1038 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1041 vector_free(shifted_vline
);
1043 vty
->xpath_index
= orig_xpath_index
;
1048 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1053 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1054 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1055 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1056 /* This assumes all nodes above CONFIG_NODE are childs of
1058 while (vty
->node
> CONFIG_NODE
) {
1059 struct cmd_node
*cnode
= vector_slot(cmdvec
, try_node
);
1061 try_node
= node_parent(try_node
);
1062 vty
->node
= try_node
;
1063 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1066 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1068 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1069 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1070 || ret
== CMD_NOT_MY_INSTANCE
1071 || ret
== CMD_WARNING_CONFIG_FAILED
)
1074 /* no command succeeded, reset the vty to the original node */
1076 vty
->xpath_index
= orig_xpath_index
;
1079 /* return command status for original node */
1084 * Execute a given command, matching it strictly against the current node.
1085 * This mode is used when reading config files.
1087 * @param vline Command line input, vector of char* where each element is
1089 * @param vty The vty context in which the command should be executed.
1090 * @param cmd Pointer where the struct cmd_element* of the matched command
1091 * will be stored, if any. May be set to NULL if this info is
1093 * @return The status of the command that has been executed or an error code
1094 * as to why no command could be executed.
1096 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1097 const struct cmd_element
**cmd
)
1099 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1103 * Hook for preprocessing command string before executing.
1105 * All subscribers are called with the raw command string that is to be
1106 * executed. If any changes are to be made, a new string should be allocated
1107 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1108 * is then responsible for freeing this string.
1110 * All processing functions must be mutually exclusive in their action, i.e. if
1111 * one subscriber decides to modify the command, all others must not modify it
1112 * when called. Feeding the output of one processing command into a subsequent
1113 * one is not supported.
1115 * This hook is intentionally internal to the command processing system.
1118 * The raw command string.
1121 * The result of any processing.
1123 DECLARE_HOOK(cmd_execute
,
1124 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1125 (vty
, cmd_in
, cmd_out
));
1126 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1127 (vty
, cmd_in
, cmd_out
));
1129 /* Hook executed after a CLI command. */
1130 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1132 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1136 * cmd_execute hook subscriber to handle `|` actions.
1138 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1142 char *orig
, *working
, *token
, *u
;
1143 char *pipe
= strstr(cmd_in
, "| ");
1149 /* duplicate string for processing purposes, not including pipe */
1150 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1152 /* retrieve action */
1153 token
= strsep(&working
, " ");
1156 /* match result to known actions */
1157 if (strmatch(token
, "include")) {
1158 /* the remaining text should be a regexp */
1159 char *regexp
= working
;
1162 vty_out(vty
, "%% Need a regexp to filter with\n");
1167 bool succ
= vty_set_include(vty
, regexp
);
1170 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1174 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1178 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1184 XFREE(MTYPE_TMP
, orig
);
1188 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1191 vty_set_include(vty
, NULL
);
1196 int cmd_execute(struct vty
*vty
, const char *cmd
,
1197 const struct cmd_element
**matched
, int vtysh
)
1200 char *cmd_out
= NULL
;
1201 const char *cmd_exec
= NULL
;
1204 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1210 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1212 vline
= cmd_make_strvec(cmd_exec
);
1215 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1216 cmd_free_strvec(vline
);
1222 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1224 XFREE(MTYPE_TMP
, cmd_out
);
1231 * Parse one line of config, walking up the parse tree attempting to find a
1234 * @param vty The vty context in which the command should be executed.
1235 * @param cmd Pointer where the struct cmd_element* of the match command
1236 * will be stored, if any. May be set to NULL if this info is
1238 * @param use_daemon Boolean to control whether or not we match on
1239 * CMD_SUCCESS_DAEMON
1241 * @return The status of the command that has been executed or an error code
1242 * as to why no command could be executed.
1244 int command_config_read_one_line(struct vty
*vty
,
1245 const struct cmd_element
**cmd
,
1246 uint32_t line_num
, int use_daemon
)
1250 unsigned up_level
= 0;
1252 vline
= cmd_make_strvec(vty
->buf
);
1254 /* In case of comment line */
1258 /* Execute configuration command : this is strict match */
1259 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1261 /* The logic for trying parent nodes is in cmd_execute_command_real()
1262 * since calling ->node_exit() correctly is a bit involved. This is
1263 * also the only reason CMD_NO_LEVEL_UP exists.
1265 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1266 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1267 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1268 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1269 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1270 && ret
!= CMD_NO_LEVEL_UP
)
1271 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1274 if (ret
== CMD_NO_LEVEL_UP
)
1275 ret
= CMD_ERR_NO_MATCH
;
1277 if (ret
!= CMD_SUCCESS
&&
1278 ret
!= CMD_WARNING
&&
1279 ret
!= CMD_SUCCESS_DAEMON
) {
1280 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1282 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1283 ve
->line_num
= line_num
;
1285 vty
->error
= list_new();
1287 listnode_add(vty
->error
, ve
);
1290 cmd_free_strvec(vline
);
1295 /* Configuration make from file. */
1296 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1298 int ret
, error_ret
= 0;
1301 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1304 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1306 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1307 && ret
!= CMD_ERR_NOTHING_TODO
)
1318 /* Configuration from terminal */
1319 DEFUN (config_terminal
,
1320 config_terminal_cmd
,
1321 "configure [terminal]",
1322 "Configuration from vty interface\n"
1323 "Configuration terminal\n")
1325 return vty_config_enter(vty
, false, false);
1328 /* Enable command */
1332 "Turn on privileged mode command\n")
1334 /* If enable password is NULL, change to ENABLE_NODE */
1335 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1336 || vty
->type
== VTY_SHELL_SERV
)
1337 vty
->node
= ENABLE_NODE
;
1339 vty
->node
= AUTH_ENABLE_NODE
;
1344 /* Disable command */
1348 "Turn off privileged mode command\n")
1350 if (vty
->node
== ENABLE_NODE
)
1351 vty
->node
= VIEW_NODE
;
1355 /* Down vty node level. */
1359 "Exit current mode and down to previous mode\n")
1365 static int root_on_exit(struct vty
*vty
)
1370 vty
->status
= VTY_CLOSE
;
1374 void cmd_exit(struct vty
*vty
)
1376 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1378 if (cnode
->node_exit
) {
1379 if (!cnode
->node_exit(vty
))
1382 if (cnode
->parent_node
)
1383 vty
->node
= cnode
->parent_node
;
1384 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1392 "Exit current mode and down to previous mode\n")
1394 return config_exit(self
, vty
, argc
, argv
);
1398 /* End of configuration. */
1402 "End current mode and change to enable mode.\n")
1405 vty_config_exit(vty
);
1406 vty
->node
= ENABLE_NODE
;
1412 DEFUN (show_version
,
1416 "Displays zebra version\n")
1418 vty_out(vty
, "%s %s (%s) on %s(%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1419 cmd_hostname_get() ? cmd_hostname_get() : "", cmd_system_get(),
1421 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1422 #ifdef ENABLE_VERSION_BUILD_CONFIG
1423 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1428 /* Help display function for all node. */
1432 "Description of the interactive help system\n")
1435 "Quagga VTY provides advanced help feature. When you need help,\n\
1436 anytime at the command line please press '?'.\n\
1438 If nothing matches, the help list will be empty and you must backup\n\
1439 until entering a '?' shows the available options.\n\
1440 Two styles of help are provided:\n\
1441 1. Full help is available when you are ready to enter a\n\
1442 command argument (e.g. 'show ?') and describes each possible\n\
1444 2. Partial help is provided when an abbreviated argument is entered\n\
1445 and you want to know what arguments match the input\n\
1446 (e.g. 'show me?'.)\n\n");
1450 static void permute(struct graph_node
*start
, struct vty
*vty
)
1452 static struct list
*position
= NULL
;
1454 position
= list_new();
1456 struct cmd_token
*stok
= start
->data
;
1457 struct graph_node
*gnn
;
1458 struct listnode
*ln
;
1461 listnode_add(position
, start
);
1462 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1463 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1464 struct cmd_token
*tok
= gn
->data
;
1465 if (tok
->attr
& CMD_ATTR_HIDDEN
)
1467 else if (tok
->type
== END_TKN
|| gn
== start
) {
1469 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1470 struct cmd_token
*tt
= gnn
->data
;
1471 if (tt
->type
< SPECIAL_TKN
)
1472 vty_out(vty
, " %s", tt
->text
);
1475 vty_out(vty
, "...");
1479 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1480 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1489 list_delete_node(position
, listtail(position
));
1492 static void print_cmd(struct vty
*vty
, const char *cmd
)
1494 int i
, j
, len
= strlen(cmd
);
1499 for (i
= 0; i
< len
; i
++) {
1503 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1509 if (isspace(cmd
[i
])) {
1510 /* skip leading whitespace */
1513 /* skip trailing whitespace */
1516 /* skip all whitespace after opening brackets or pipe */
1517 if (strchr("(<[{|", cmd
[i
- 1])) {
1518 while (isspace(cmd
[i
+ 1]))
1522 /* skip repeated whitespace */
1523 if (isspace(cmd
[i
+ 1]))
1525 /* skip whitespace before closing brackets or pipe */
1526 if (strchr(")>]}|", cmd
[i
+ 1]))
1528 /* convert tabs to spaces */
1529 if (cmd
[i
] == '\t') {
1539 vty_out(vty
, "%s\n", buf
);
1542 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1544 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1547 cmd_finalize_node(node
);
1548 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1550 /* loop over all commands at this node */
1551 const struct cmd_element
*element
= NULL
;
1552 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1554 if ((element
= vector_slot(node
->cmd_vector
, i
)) &&
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\n", filename
);
2243 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2244 vty_out(vty
, "%s must be in %s\n", 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 DEFUN(allow_reserved_ranges
, allow_reserved_ranges_cmd
, "allow-reserved-ranges",
2295 "Allow using IPv4 (Class E) reserved IP space\n")
2297 host
.allow_reserved_ranges
= true;
2301 DEFUN(no_allow_reserved_ranges
, no_allow_reserved_ranges_cmd
,
2302 "no allow-reserved-ranges",
2303 NO_STR
"Allow using IPv4 (Class E) reserved IP space\n")
2305 host
.allow_reserved_ranges
= false;
2309 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2311 const struct cmd_node
*node
;
2312 const struct cmd_element
*cli
;
2317 char *pattern
= argv_concat(argv
, argc
, 1);
2318 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2319 XFREE(MTYPE_TMP
, pattern
);
2324 vty_out(vty
, "%% Invalid {...} expression\n");
2327 vty_out(vty
, "%% Bad repetition operator\n");
2330 vty_out(vty
, "%% Regex syntax error\n");
2333 vty_out(vty
, "%% Invalid collating element\n");
2336 vty_out(vty
, "%% Invalid character class name\n");
2340 "%% Regex ended with escape character (\\)\n");
2344 "%% Invalid number in \\digit construction\n");
2347 vty_out(vty
, "%% Unbalanced square brackets\n");
2350 vty_out(vty
, "%% Unbalanced parentheses\n");
2353 vty_out(vty
, "%% Unbalanced braces\n");
2357 "%% Invalid endpoint in range expression\n");
2360 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2368 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2369 node
= vector_slot(cmdvec
, i
);
2372 clis
= node
->cmd_vector
;
2373 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2374 cli
= vector_slot(clis
, j
);
2376 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2377 vty_out(vty
, " (%s) ", node
->name
);
2378 print_cmd(vty
, cli
->string
);
2391 "Find CLI command matching a regular expression\n"
2392 "Search pattern (POSIX regex)\n")
2394 return cmd_find_cmds(vty
, argv
, argc
);
2397 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2398 DEFUN(script
, script_cmd
, "script SCRIPT FUNCTION",
2399 "Test command - execute a function in a script\n"
2400 "Script name (same as filename in /etc/frr/scripts/)\n"
2401 "Function name (in the script)\n")
2405 (void)str2prefix("1.2.3.4/24", &p
);
2406 struct frrscript
*fs
= frrscript_new(argv
[1]->arg
);
2408 if (frrscript_load(fs
, argv
[2]->arg
, NULL
)) {
2410 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2411 argv
[1]->arg
, argv
[2]->arg
);
2414 int ret
= frrscript_call(fs
, argv
[2]->arg
, ("p", &p
));
2416 prefix2str(&p
, buf
, sizeof(buf
));
2417 vty_out(vty
, "p: %s\n", buf
);
2418 vty_out(vty
, "Script result: %d\n", ret
);
2420 frrscript_delete(fs
);
2426 /* Set config filename. Called from vty.c */
2427 void host_config_set(const char *filename
)
2429 XFREE(MTYPE_HOST
, host
.config
);
2430 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2433 const char *host_config_get(void)
2438 void cmd_show_lib_debugs(struct vty
*vty
)
2440 route_map_show_debug(vty
);
2443 void install_default(enum node_type node
)
2445 _install_element(node
, &config_exit_cmd
);
2446 _install_element(node
, &config_quit_cmd
);
2447 _install_element(node
, &config_end_cmd
);
2448 _install_element(node
, &config_help_cmd
);
2449 _install_element(node
, &config_list_cmd
);
2450 _install_element(node
, &show_cli_graph_cmd
);
2451 _install_element(node
, &find_cmd
);
2453 _install_element(node
, &config_write_cmd
);
2454 _install_element(node
, &show_running_config_cmd
);
2456 _install_element(node
, &autocomplete_cmd
);
2458 nb_cli_install_default(node
);
2461 /* Initialize command interface. Install basic nodes and commands.
2463 * terminal = 0 -- vtysh / no logging, no config control
2464 * terminal = 1 -- normal daemon
2465 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2466 void cmd_init(int terminal
)
2468 struct utsname names
;
2473 /* register command preprocessors */
2474 hook_register(cmd_execute
, handle_pipe_action
);
2475 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2477 varhandlers
= list_new();
2479 /* Allocate initial top vector of commands. */
2480 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2482 /* Default host value settings. */
2483 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2484 host
.system
= XSTRDUP(MTYPE_HOST
, names
.sysname
);
2485 host
.release
= XSTRDUP(MTYPE_HOST
, names
.release
);
2486 host
.version
= XSTRDUP(MTYPE_HOST
, names
.version
);
2488 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2489 if ((strcmp(names
.domainname
, "(none)") == 0))
2490 host
.domainname
= NULL
;
2492 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2494 host
.domainname
= NULL
;
2496 host
.password
= NULL
;
2499 host
.noconfig
= (terminal
< 0);
2501 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2502 host
.motdfile
= NULL
;
2503 host
.allow_reserved_ranges
= false;
2505 /* Install top nodes. */
2506 install_node(&view_node
);
2507 install_node(&enable_node
);
2508 install_node(&auth_node
);
2509 install_node(&auth_enable_node
);
2510 install_node(&config_node
);
2512 /* Each node's basic commands. */
2513 install_element(VIEW_NODE
, &show_version_cmd
);
2514 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2517 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2519 install_element(VIEW_NODE
, &config_list_cmd
);
2520 install_element(VIEW_NODE
, &config_exit_cmd
);
2521 install_element(VIEW_NODE
, &config_quit_cmd
);
2522 install_element(VIEW_NODE
, &config_help_cmd
);
2523 install_element(VIEW_NODE
, &config_enable_cmd
);
2524 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2525 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2526 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2527 install_element(VIEW_NODE
, &echo_cmd
);
2528 install_element(VIEW_NODE
, &autocomplete_cmd
);
2529 install_element(VIEW_NODE
, &find_cmd
);
2530 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2531 install_element(VIEW_NODE
, &script_cmd
);
2535 install_element(ENABLE_NODE
, &config_end_cmd
);
2536 install_element(ENABLE_NODE
, &config_disable_cmd
);
2537 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2538 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2539 install_element(ENABLE_NODE
, &config_write_cmd
);
2540 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2541 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2543 install_default(CONFIG_NODE
);
2546 workqueue_cmd_init();
2550 install_element(CONFIG_NODE
, &hostname_cmd
);
2551 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2552 install_element(CONFIG_NODE
, &domainname_cmd
);
2553 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2558 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2560 install_element(CONFIG_NODE
, &password_cmd
);
2561 install_element(CONFIG_NODE
, &no_password_cmd
);
2562 install_element(CONFIG_NODE
, &enable_password_cmd
);
2563 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2565 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2566 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2567 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2568 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2569 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2570 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2571 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2572 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2573 install_element(CONFIG_NODE
, &allow_reserved_ranges_cmd
);
2574 install_element(CONFIG_NODE
, &no_allow_reserved_ranges_cmd
);
2577 vrf_install_commands();
2581 grammar_sandbox_init();
2585 void cmd_terminate(void)
2587 struct cmd_node
*cmd_node
;
2589 hook_unregister(cmd_execute
, handle_pipe_action
);
2590 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2593 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2594 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2595 // deleting the graph delets the cmd_element as
2597 graph_delete_graph(cmd_node
->cmdgraph
);
2598 vector_free(cmd_node
->cmd_vector
);
2599 hash_clean_and_free(&cmd_node
->cmd_hash
, NULL
);
2602 vector_free(cmdvec
);
2606 XFREE(MTYPE_HOST
, host
.name
);
2607 XFREE(MTYPE_HOST
, host
.system
);
2608 XFREE(MTYPE_HOST
, host
.release
);
2609 XFREE(MTYPE_HOST
, host
.version
);
2610 XFREE(MTYPE_HOST
, host
.domainname
);
2611 XFREE(MTYPE_HOST
, host
.password
);
2612 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2613 XFREE(MTYPE_HOST
, host
.enable
);
2614 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2615 XFREE(MTYPE_HOST
, host
.motdfile
);
2616 XFREE(MTYPE_HOST
, host
.config
);
2617 XFREE(MTYPE_HOST
, host
.motd
);
2619 list_delete(&varhandlers
);