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 "mgmt_be_client.h"
35 #include "mgmt_fe_client.h"
36 #include "northbound_cli.h"
40 #include "frrscript.h"
42 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config");
43 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item");
50 /* clang-format off */
51 const struct message tokennames
[] = {
56 item(IPV4_PREFIX_TKN
),
58 item(IPV6_PREFIX_TKN
),
71 /* Command vector which includes some level of command lists. Normally
72 each daemon maintains each own cmdvec. */
75 /* Host information structure. */
78 /* for vtysh, put together CLI trees only when switching into node */
79 static bool defer_cli_tree
;
82 * Returns host.name if any, otherwise
83 * it returns the system hostname.
85 const char *cmd_hostname_get(void)
91 * Returns unix domainname
93 const char *cmd_domainname_get(void)
95 return host
.domainname
;
98 const char *cmd_system_get(void)
103 const char *cmd_release_get(void)
108 const char *cmd_version_get(void)
113 bool cmd_allow_reserved_ranges_get(void)
115 return host
.allow_reserved_ranges
;
118 const char *cmd_software_version_get(void)
120 return FRR_FULL_NAME
"/" FRR_VERSION
;
123 static int root_on_exit(struct vty
*vty
);
125 /* Standard command node structures. */
126 static struct cmd_node auth_node
= {
129 .prompt
= "Password: ",
132 static struct cmd_node view_node
= {
136 .node_exit
= root_on_exit
,
139 static struct cmd_node auth_enable_node
= {
140 .name
= "auth enable",
141 .node
= AUTH_ENABLE_NODE
,
142 .prompt
= "Password: ",
145 static struct cmd_node enable_node
= {
149 .node_exit
= root_on_exit
,
152 static int config_write_host(struct vty
*vty
);
153 static struct cmd_node config_node
= {
156 .parent_node
= ENABLE_NODE
,
157 .prompt
= "%s(config)# ",
158 .config_write
= config_write_host
,
159 .node_exit
= vty_config_node_exit
,
162 /* This is called from main when a daemon is invoked with -v or --version. */
163 void print_version(const char *progname
)
165 printf("%s version %s\n", progname
, FRR_VERSION
);
166 printf("%s\n", FRR_COPYRIGHT
);
167 #ifdef ENABLE_VERSION_BUILD_CONFIG
168 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
172 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
174 int cnt
= MAX(argc
- shift
, 0);
175 const char *argstr
[cnt
+ 1];
180 for (int i
= 0; i
< cnt
; i
++)
181 argstr
[i
] = argv
[i
+ shift
]->arg
;
183 return frrstr_join(argstr
, cnt
, " ");
186 vector
cmd_make_strvec(const char *string
)
191 const char *copy
= string
;
193 /* skip leading whitespace */
194 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
197 /* if the entire string was whitespace or a comment, return */
198 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
201 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
203 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
204 if (strlen(vector_slot(result
, i
)) == 0) {
205 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
206 vector_unset(result
, i
);
210 vector_compact(result
);
215 void cmd_free_strvec(vector v
)
217 frrstr_strvec_free(v
);
221 * Convenience function for accessing argv data.
225 * @param text definition snippet of the desired token
226 * @param index the starting index, and where to store the
227 * index of the found token if it exists
228 * @return 1 if found, 0 otherwise
230 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
233 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
234 if ((found
= strmatch(text
, argv
[i
]->text
)))
239 static unsigned int cmd_hash_key(const void *p
)
241 int size
= sizeof(p
);
243 return jhash(p
, size
, 0);
246 static bool cmd_hash_cmp(const void *a
, const void *b
)
251 /* Install top node of command vector. */
252 void install_node(struct cmd_node
*node
)
254 #define CMD_HASH_STR_SIZE 256
255 char hash_name
[CMD_HASH_STR_SIZE
];
257 vector_set_index(cmdvec
, node
->node
, node
);
258 node
->cmdgraph
= graph_new();
259 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
261 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
262 graph_new_node(node
->cmdgraph
, token
,
263 (void (*)(void *)) & cmd_token_del
);
265 snprintf(hash_name
, sizeof(hash_name
), "Command Hash: %s", node
->name
);
267 hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
, hash_name
);
270 /* Return prompt character of specified node. */
271 const char *cmd_prompt(enum node_type node
)
273 struct cmd_node
*cnode
;
275 cnode
= vector_slot(cmdvec
, node
);
276 return cnode
->prompt
;
279 void cmd_defer_tree(bool val
)
281 defer_cli_tree
= val
;
284 /* Install a command into a node. */
285 void _install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
287 struct cmd_node
*cnode
;
289 /* cmd_init hasn't been called */
291 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
296 cnode
= vector_lookup(cmdvec
, ntype
);
301 "\tnode %d does not exist.\n"
302 "\tplease call install_node() before install_element()\n",
303 cmd
->name
, cmd
->string
, ntype
);
307 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
310 "\tnode %d (%s) already has this command installed.\n"
311 "\tduplicate install_element call?\n",
312 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
316 (void)hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
);
318 if (cnode
->graph_built
|| !defer_cli_tree
) {
319 struct graph
*graph
= graph_new();
320 struct cmd_token
*token
=
321 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
322 graph_new_node(graph
, token
,
323 (void (*)(void *)) & cmd_token_del
);
325 cmd_graph_parse(graph
, cmd
);
326 cmd_graph_names(graph
);
327 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
328 graph_delete_graph(graph
);
330 cnode
->graph_built
= true;
333 vector_set(cnode
->cmd_vector
, (void *)cmd
);
335 if (ntype
== VIEW_NODE
)
336 _install_element(ENABLE_NODE
, cmd
);
339 static void cmd_finalize_iter(struct hash_bucket
*hb
, void *arg
)
341 struct cmd_node
*cnode
= arg
;
342 const struct cmd_element
*cmd
= hb
->data
;
343 struct graph
*graph
= graph_new();
344 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
346 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
348 cmd_graph_parse(graph
, cmd
);
349 cmd_graph_names(graph
);
350 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
351 graph_delete_graph(graph
);
354 void cmd_finalize_node(struct cmd_node
*cnode
)
356 if (cnode
->graph_built
)
359 hash_iterate(cnode
->cmd_hash
, cmd_finalize_iter
, cnode
);
360 cnode
->graph_built
= true;
363 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
365 struct cmd_node
*cnode
;
367 /* cmd_init hasn't been called */
369 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
374 cnode
= vector_lookup(cmdvec
, ntype
);
379 "\tnode %d does not exist.\n"
380 "\tplease call install_node() before uninstall_element()\n",
381 cmd
->name
, cmd
->string
, ntype
);
385 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
388 "\tnode %d (%s) does not have this command installed.\n"
389 "\tduplicate uninstall_element call?\n",
390 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
394 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
396 if (cnode
->graph_built
) {
397 struct graph
*graph
= graph_new();
398 struct cmd_token
*token
=
399 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
400 graph_new_node(graph
, token
,
401 (void (*)(void *)) & cmd_token_del
);
403 cmd_graph_parse(graph
, cmd
);
404 cmd_graph_names(graph
);
405 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
406 graph_delete_graph(graph
);
409 if (ntype
== VIEW_NODE
)
410 uninstall_element(ENABLE_NODE
, cmd
);
414 static const unsigned char itoa64
[] =
415 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
417 static void to64(char *s
, long v
, int n
)
420 *s
++ = itoa64
[v
& 0x3f];
425 static char *zencrypt(const char *passwd
)
430 gettimeofday(&tv
, 0);
432 to64(&salt
[0], frr_weak_random(), 3);
433 to64(&salt
[3], tv
.tv_usec
, 3);
436 return crypt(passwd
, salt
);
439 static bool full_cli
;
441 /* This function write configuration of this host. */
442 static int config_write_host(struct vty
*vty
)
446 name
= cmd_hostname_get();
447 if (name
&& name
[0] != '\0')
448 vty_out(vty
, "hostname %s\n", name
);
450 name
= cmd_domainname_get();
451 if (name
&& name
[0] != '\0')
452 vty_out(vty
, "domainname %s\n", name
);
454 if (cmd_allow_reserved_ranges_get())
455 vty_out(vty
, "allow-reserved-ranges\n");
457 /* The following are all configuration commands that are not sent to
458 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
459 * we would always display 'log syslog informational' in the config
460 * which would cause other daemons to then switch to syslog when they
465 if (host
.password_encrypt
)
466 vty_out(vty
, "password 8 %s\n",
467 host
.password_encrypt
);
468 if (host
.enable_encrypt
)
469 vty_out(vty
, "enable password 8 %s\n",
470 host
.enable_encrypt
);
473 vty_out(vty
, "password %s\n", host
.password
);
475 vty_out(vty
, "enable password %s\n",
478 log_config_write(vty
);
480 /* print disable always, but enable only if default is flipped
481 * => prep for future removal of compile-time knob
483 if (!cputime_enabled
)
484 vty_out(vty
, "no service cputime-stats\n");
485 #ifdef EXCLUDE_CPU_TIME
487 vty_out(vty
, "service cputime-stats\n");
490 if (!cputime_threshold
)
491 vty_out(vty
, "no service cputime-warning\n");
492 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
493 else /* again, always print non-default */
495 else if (cputime_threshold
!= 5000000)
497 vty_out(vty
, "service cputime-warning %lu\n",
498 cputime_threshold
/ 1000);
500 if (!walltime_threshold
)
501 vty_out(vty
, "no service walltime-warning\n");
502 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
503 else /* again, always print non-default */
505 else if (walltime_threshold
!= 5000000)
507 vty_out(vty
, "service walltime-warning %lu\n",
508 walltime_threshold
/ 1000);
511 vty_out(vty
, "service advanced-vty\n");
514 vty_out(vty
, "service password-encryption\n");
517 vty_out(vty
, "service terminal-length %d\n",
521 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
523 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
525 vty_out(vty
, "banner motd line %s\n", host
.motd
);
527 vty_out(vty
, "no banner motd\n");
530 if (debug_memstats_at_exit
)
531 vty_out(vty
, "!\ndebug memstats-at-exit\n");
536 /* Utility function for getting command graph. */
537 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
539 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
541 cmd_finalize_node(cnode
);
542 return cnode
->cmdgraph
;
545 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
547 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
548 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
554 * Compare function for cmd_token.
555 * Used with qsort to sort command completions.
557 static int compare_completions(const void *fst
, const void *snd
)
559 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
560 *secnd
= *(const struct cmd_token
* const *)snd
;
561 return strcmp(first
->text
, secnd
->text
);
565 * Takes a list of completions returned by command_complete,
566 * dedeuplicates them based on both text and description,
567 * sorts them, and returns them as a vector.
569 * @param completions linked list of cmd_token
570 * @return deduplicated and sorted vector with
572 vector
completions_to_vec(struct list
*completions
)
574 vector comps
= vector_init(VECTOR_MIN_SIZE
);
577 struct cmd_token
*token
, *cr
= NULL
;
578 unsigned int i
, exists
;
579 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
580 if (token
->type
== END_TKN
&& (cr
= token
))
583 // linear search for token in completions vector
585 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
586 struct cmd_token
*curr
= vector_slot(comps
, i
);
588 exists
= !strcmp(curr
->text
, token
->text
)
589 && !strcmp(curr
->desc
, token
->desc
);
591 exists
= !strcmp(curr
->text
, token
->text
);
592 #endif /* VTYSH_DEBUG */
596 vector_set(comps
, token
);
600 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
601 &compare_completions
);
603 // make <cr> the first element, if it is present
605 vector_set_index(comps
, vector_active(comps
), NULL
);
606 memmove(comps
->index
+ 1, comps
->index
,
607 (comps
->alloced
- 1) * sizeof(void *));
608 vector_set_index(comps
, 0, cr
);
614 * Generates a vector of cmd_token representing possible completions
615 * on the current input.
617 * @param vline the vectorized input line
618 * @param vty the vty with the node to match on
619 * @param status pointer to matcher status code
620 * @return vector of struct cmd_token * with possible completions
622 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
625 struct list
*completions
;
626 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
628 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
630 if (MATCHER_ERROR(rv
)) {
631 *status
= CMD_ERR_NO_MATCH
;
635 vector comps
= completions_to_vec(completions
);
636 list_delete(&completions
);
638 // set status code appropriately
639 switch (vector_active(comps
)) {
641 *status
= CMD_ERR_NO_MATCH
;
644 *status
= CMD_COMPLETE_FULL_MATCH
;
647 *status
= CMD_COMPLETE_LIST_MATCH
;
653 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
657 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
658 enum node_type onode
;
659 int orig_xpath_index
;
660 vector shifted_vline
;
664 orig_xpath_index
= vty
->xpath_index
;
665 vty
->node
= ENABLE_NODE
;
666 vty
->xpath_index
= 0;
667 /* We can try it on enable node, cos' the vty is authenticated
670 shifted_vline
= vector_init(vector_count(vline
));
672 for (index
= 1; index
< vector_active(vline
); index
++) {
673 vector_set_index(shifted_vline
, index
- 1,
674 vector_lookup(vline
, index
));
677 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
679 vector_free(shifted_vline
);
681 vty
->xpath_index
= orig_xpath_index
;
685 return cmd_complete_command_real(vline
, vty
, status
);
688 static struct list
*varhandlers
= NULL
;
690 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
694 const struct cmd_variable_handler
*cvh
;
698 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
700 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
701 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
703 if (cvh
->varname
&& (!token
->varname
704 || strcmp(cvh
->varname
, token
->varname
)))
706 cvh
->completions(tmpcomps
, token
);
714 for (i
= vector_active(tmpcomps
); i
; i
--) {
715 char *item
= vector_slot(tmpcomps
, i
- 1);
716 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
717 vector_set(comps
, item
);
719 XFREE(MTYPE_COMPLETION
, item
);
721 vector_free(tmpcomps
);
724 #define AUTOCOMP_INDENT 5
726 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
729 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
730 int lc
= AUTOCOMP_INDENT
;
731 size_t cs
= AUTOCOMP_INDENT
;
733 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
734 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
735 char *item
= vector_slot(comps
, j
);
736 itemlen
= strlen(item
);
738 size_t next_sz
= cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3;
741 /* Make sure the buf size is large enough */
743 buf
= XREALLOC(MTYPE_TMP
, buf
, bsz
);
745 if (lc
+ itemlen
+ 1 >= cols
) {
746 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
747 AUTOCOMP_INDENT
, "");
748 lc
= AUTOCOMP_INDENT
;
751 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
754 XFREE(MTYPE_COMPLETION
, item
);
755 vector_set_index(comps
, j
, NULL
);
760 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
765 for (; cvh
->completions
; cvh
++)
766 listnode_add(varhandlers
, (void *)cvh
);
769 DEFUN_HIDDEN (autocomplete
,
771 "autocomplete TYPE TEXT VARNAME",
772 "Autocompletion handler (internal, for vtysh)\n"
775 "cmd_token->varname\n")
777 struct cmd_token tok
;
778 vector comps
= vector_init(32);
781 memset(&tok
, 0, sizeof(tok
));
782 tok
.type
= atoi(argv
[1]->arg
);
783 tok
.text
= argv
[2]->arg
;
784 tok
.varname
= argv
[3]->arg
;
785 if (!strcmp(tok
.varname
, "-"))
788 cmd_variable_complete(&tok
, NULL
, comps
);
790 for (i
= 0; i
< vector_active(comps
); i
++) {
791 char *text
= vector_slot(comps
, i
);
792 vty_out(vty
, "%s\n", text
);
793 XFREE(MTYPE_COMPLETION
, text
);
801 * Generate possible tab-completions for the given input. This function only
802 * returns results that would result in a valid command if used as Readline
803 * completions (as is the case in vtysh). For instance, if the passed vline ends
804 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
806 * @param vline vectorized input line
808 * @param status location to store matcher status code in
809 * @return set of valid strings for use with Readline as tab-completions.
812 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
815 int original_node
= vty
->node
;
816 vector input_line
= vector_init(vector_count(vline
));
818 // if the first token is 'do' we'll want to execute the command in the
820 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
821 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
823 // construct the input line we'll be matching on
824 unsigned int offset
= (do_shortcut
) ? 1 : 0;
825 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
826 vector_set_index(input_line
, index
,
827 vector_lookup(vline
, index
+ offset
));
829 // get token completions -- this is a copying operation
830 vector comps
= NULL
, initial_comps
;
831 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
833 if (!MATCHER_ERROR(*status
)) {
834 assert(initial_comps
);
835 // filter out everything that is not suitable for a
837 comps
= vector_init(VECTOR_MIN_SIZE
);
838 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
840 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
841 if (token
->type
== WORD_TKN
)
842 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
844 else if (IS_VARYING_TOKEN(token
->type
)) {
845 const char *ref
= vector_lookup(
846 vline
, vector_active(vline
) - 1);
847 cmd_variable_complete(token
, ref
, comps
);
850 vector_free(initial_comps
);
852 // since we filtered results, we need to re-set status code
853 switch (vector_active(comps
)) {
855 *status
= CMD_ERR_NO_MATCH
;
858 *status
= CMD_COMPLETE_FULL_MATCH
;
861 *status
= CMD_COMPLETE_LIST_MATCH
;
864 // copy completions text into an array of char*
865 ret
= XMALLOC(MTYPE_TMP
,
866 (vector_active(comps
) + 1) * sizeof(char *));
868 for (i
= 0; i
< vector_active(comps
); i
++) {
869 ret
[i
] = vector_slot(comps
, i
);
871 // set the last element to NULL, because this array is used in
872 // a Readline completion_generator function which expects NULL
873 // as a sentinel value
877 } else if (initial_comps
)
878 vector_free(initial_comps
);
880 // comps should always be null here
883 // free the adjusted input line
884 vector_free(input_line
);
886 // reset vty->node to its original value
887 vty
->node
= original_node
;
892 /* return parent node */
893 /* MUST eventually converge on CONFIG_NODE */
894 enum node_type
node_parent(enum node_type node
)
896 struct cmd_node
*cnode
;
898 assert(node
> CONFIG_NODE
);
900 cnode
= vector_lookup(cmdvec
, node
);
902 return cnode
->parent_node
;
905 /* Execute command by argument vline vector. */
906 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
908 const struct cmd_element
**cmd
,
909 unsigned int up_level
)
911 struct list
*argv_list
;
912 enum matcher_rv status
;
913 const struct cmd_element
*matched_element
= NULL
;
915 int xpath_index
= vty
->xpath_index
;
916 int node
= vty
->node
;
918 /* only happens for legacy split config file load; need to check for
919 * a match before calling node_exit handlers below
921 for (i
= 0; i
< up_level
; i
++) {
922 struct cmd_node
*cnode
;
924 if (node
<= CONFIG_NODE
)
925 return CMD_NO_LEVEL_UP
;
927 cnode
= vector_slot(cmdvec
, node
);
928 node
= node_parent(node
);
930 if (xpath_index
> 0 && !cnode
->no_xpath
)
934 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
935 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
938 *cmd
= matched_element
;
940 // if matcher error, return corresponding CMD_ERR
941 if (MATCHER_ERROR(status
)) {
943 list_delete(&argv_list
);
945 case MATCHER_INCOMPLETE
:
946 return CMD_ERR_INCOMPLETE
;
947 case MATCHER_AMBIGUOUS
:
948 return CMD_ERR_AMBIGUOUS
;
949 case MATCHER_NO_MATCH
:
951 return CMD_ERR_NO_MATCH
;
955 for (i
= 0; i
< up_level
; i
++)
958 // build argv array from argv list
959 struct cmd_token
**argv
= XMALLOC(
960 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
962 struct cmd_token
*token
;
965 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
968 int argc
= argv_list
->count
;
971 if (matched_element
->daemon
)
972 ret
= CMD_SUCCESS_DAEMON
;
975 /* Clear array of enqueued configuration changes. */
976 vty
->num_cfg_changes
= 0;
977 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
979 /* Regenerate candidate configuration if necessary. */
980 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
981 && running_config
->version
982 > vty
->candidate_config
->version
)
983 nb_config_replace(vty
->candidate_config
,
984 running_config
, true);
987 * Perform pending commit (if any) before executing
990 if (!(matched_element
->attr
& CMD_ATTR_YANG
))
991 (void)nb_cli_pending_commit_check(vty
);
994 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
997 // delete list and cmd_token's in it
998 list_delete(&argv_list
);
999 XFREE(MTYPE_TMP
, argv
);
1005 * Execute a given command, handling things like "do ..." and checking
1006 * whether the given command might apply at a parent node if doesn't
1007 * apply for the current node.
1009 * @param vline Command line input, vector of char* where each element is
1011 * @param vty The vty context in which the command should be executed.
1012 * @param cmd Pointer where the struct cmd_element of the matched command
1013 * will be stored, if any. May be set to NULL if this info is
1015 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1016 * @return The status of the command that has been executed or an error code
1017 * as to why no command could be executed.
1019 int cmd_execute_command(vector vline
, struct vty
*vty
,
1020 const struct cmd_element
**cmd
, int vtysh
)
1022 int ret
, saved_ret
= 0;
1023 enum node_type onode
, try_node
;
1024 int orig_xpath_index
;
1026 onode
= try_node
= vty
->node
;
1027 orig_xpath_index
= vty
->xpath_index
;
1029 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1030 vector shifted_vline
;
1033 vty
->node
= ENABLE_NODE
;
1034 vty
->xpath_index
= 0;
1035 /* We can try it on enable node, cos' the vty is authenticated
1038 shifted_vline
= vector_init(vector_count(vline
));
1040 for (index
= 1; index
< vector_active(vline
); index
++)
1041 vector_set_index(shifted_vline
, index
- 1,
1042 vector_lookup(vline
, index
));
1044 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1047 vector_free(shifted_vline
);
1049 vty
->xpath_index
= orig_xpath_index
;
1054 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1059 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1060 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1061 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1062 /* This assumes all nodes above CONFIG_NODE are childs of
1064 while (vty
->node
> CONFIG_NODE
) {
1065 struct cmd_node
*cnode
= vector_slot(cmdvec
, try_node
);
1067 try_node
= node_parent(try_node
);
1068 vty
->node
= try_node
;
1069 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1072 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1074 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1075 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1076 || ret
== CMD_NOT_MY_INSTANCE
1077 || ret
== CMD_WARNING_CONFIG_FAILED
)
1080 /* no command succeeded, reset the vty to the original node */
1082 vty
->xpath_index
= orig_xpath_index
;
1085 /* return command status for original node */
1090 * Execute a given command, matching it strictly against the current node.
1091 * This mode is used when reading config files.
1093 * @param vline Command line input, vector of char* where each element is
1095 * @param vty The vty context in which the command should be executed.
1096 * @param cmd Pointer where the struct cmd_element* of the matched command
1097 * will be stored, if any. May be set to NULL if this info is
1099 * @return The status of the command that has been executed or an error code
1100 * as to why no command could be executed.
1102 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1103 const struct cmd_element
**cmd
)
1105 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1109 * Hook for preprocessing command string before executing.
1111 * All subscribers are called with the raw command string that is to be
1112 * executed. If any changes are to be made, a new string should be allocated
1113 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1114 * is then responsible for freeing this string.
1116 * All processing functions must be mutually exclusive in their action, i.e. if
1117 * one subscriber decides to modify the command, all others must not modify it
1118 * when called. Feeding the output of one processing command into a subsequent
1119 * one is not supported.
1121 * This hook is intentionally internal to the command processing system.
1124 * The raw command string.
1127 * The result of any processing.
1129 DECLARE_HOOK(cmd_execute
,
1130 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1131 (vty
, cmd_in
, cmd_out
));
1132 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1133 (vty
, cmd_in
, cmd_out
));
1135 /* Hook executed after a CLI command. */
1136 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1138 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1142 * cmd_execute hook subscriber to handle `|` actions.
1144 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1148 char *orig
, *working
, *token
, *u
;
1149 char *pipe
= strstr(cmd_in
, "| ");
1155 /* duplicate string for processing purposes, not including pipe */
1156 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1158 /* retrieve action */
1159 token
= strsep(&working
, " ");
1162 /* match result to known actions */
1163 if (strmatch(token
, "include")) {
1164 /* the remaining text should be a regexp */
1165 char *regexp
= working
;
1168 vty_out(vty
, "%% Need a regexp to filter with\n");
1173 bool succ
= vty_set_include(vty
, regexp
);
1176 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1180 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1184 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1190 XFREE(MTYPE_TMP
, orig
);
1194 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1197 vty_set_include(vty
, NULL
);
1202 int cmd_execute(struct vty
*vty
, const char *cmd
,
1203 const struct cmd_element
**matched
, int vtysh
)
1206 char *cmd_out
= NULL
;
1207 const char *cmd_exec
= NULL
;
1210 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1216 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1218 vline
= cmd_make_strvec(cmd_exec
);
1221 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1222 cmd_free_strvec(vline
);
1228 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1230 XFREE(MTYPE_TMP
, cmd_out
);
1237 * Parse one line of config, walking up the parse tree attempting to find a
1240 * @param vty The vty context in which the command should be executed.
1241 * @param cmd Pointer where the struct cmd_element* of the match command
1242 * will be stored, if any. May be set to NULL if this info is
1244 * @param use_daemon Boolean to control whether or not we match on
1245 * CMD_SUCCESS_DAEMON
1247 * @return The status of the command that has been executed or an error code
1248 * as to why no command could be executed.
1250 int command_config_read_one_line(struct vty
*vty
,
1251 const struct cmd_element
**cmd
,
1252 uint32_t line_num
, int use_daemon
)
1256 unsigned up_level
= 0;
1258 vline
= cmd_make_strvec(vty
->buf
);
1260 /* In case of comment line */
1264 /* Execute configuration command : this is strict match */
1265 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1267 /* The logic for trying parent nodes is in cmd_execute_command_real()
1268 * since calling ->node_exit() correctly is a bit involved. This is
1269 * also the only reason CMD_NO_LEVEL_UP exists.
1271 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1272 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1273 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1274 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1275 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1276 && ret
!= CMD_NO_LEVEL_UP
)
1277 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1280 if (ret
== CMD_NO_LEVEL_UP
)
1281 ret
= CMD_ERR_NO_MATCH
;
1283 if (ret
!= CMD_SUCCESS
&&
1284 ret
!= CMD_WARNING
&&
1285 ret
!= CMD_SUCCESS_DAEMON
) {
1286 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1288 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1289 ve
->line_num
= line_num
;
1292 vty
->error
= list_new();
1294 listnode_add(vty
->error
, ve
);
1297 cmd_free_strvec(vline
);
1302 /* Configuration make from file. */
1303 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1305 int ret
, error_ret
= 0;
1308 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1311 if (vty_log_commands
) {
1312 int len
= strlen(vty
->buf
);
1314 /* now log the command */
1315 zlog_notice("config-from-file# %.*s", len
? len
- 1 : 0,
1319 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1321 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1322 && ret
!= CMD_ERR_NOTHING_TODO
)
1333 /* Configuration from terminal */
1334 DEFUN (config_terminal
,
1335 config_terminal_cmd
,
1336 "configure [terminal]",
1337 "Configuration from vty interface\n"
1338 "Configuration terminal\n")
1340 return vty_config_enter(vty
, false, false);
1343 /* Enable command */
1347 "Turn on privileged mode command\n")
1349 /* If enable password is NULL, change to ENABLE_NODE */
1350 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1351 || vty
->type
== VTY_SHELL_SERV
)
1352 vty
->node
= ENABLE_NODE
;
1354 vty
->node
= AUTH_ENABLE_NODE
;
1359 /* Disable command */
1363 "Turn off privileged mode command\n")
1365 if (vty
->node
== ENABLE_NODE
)
1366 vty
->node
= VIEW_NODE
;
1370 /* Down vty node level. */
1374 "Exit current mode and down to previous mode\n")
1380 static int root_on_exit(struct vty
*vty
)
1385 vty
->status
= VTY_CLOSE
;
1389 void cmd_exit(struct vty
*vty
)
1391 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1393 if (cnode
->node_exit
) {
1394 if (!cnode
->node_exit(vty
))
1397 if (cnode
->parent_node
)
1398 vty
->node
= cnode
->parent_node
;
1399 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1407 "Exit current mode and down to previous mode\n")
1409 return config_exit(self
, vty
, argc
, argv
);
1413 /* End of configuration. */
1417 "End current mode and change to enable mode.\n")
1420 vty_config_exit(vty
);
1421 vty
->node
= ENABLE_NODE
;
1427 DEFUN (show_version
,
1431 "Displays zebra version\n")
1433 vty_out(vty
, "%s %s (%s) on %s(%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1434 cmd_hostname_get() ? cmd_hostname_get() : "", cmd_system_get(),
1436 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1437 #ifdef ENABLE_VERSION_BUILD_CONFIG
1438 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1443 /* Help display function for all node. */
1447 "Description of the interactive help system\n")
1450 "Quagga VTY provides advanced help feature. When you need help,\n\
1451 anytime at the command line please press '?'.\n\
1453 If nothing matches, the help list will be empty and you must backup\n\
1454 until entering a '?' shows the available options.\n\
1455 Two styles of help are provided:\n\
1456 1. Full help is available when you are ready to enter a\n\
1457 command argument (e.g. 'show ?') and describes each possible\n\
1459 2. Partial help is provided when an abbreviated argument is entered\n\
1460 and you want to know what arguments match the input\n\
1461 (e.g. 'show me?'.)\n\n");
1465 static void permute(struct graph_node
*start
, struct vty
*vty
)
1467 static struct list
*position
= NULL
;
1469 position
= list_new();
1471 struct cmd_token
*stok
= start
->data
;
1472 struct graph_node
*gnn
;
1473 struct listnode
*ln
;
1476 listnode_add(position
, start
);
1477 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1478 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1479 struct cmd_token
*tok
= gn
->data
;
1480 if (tok
->attr
& CMD_ATTR_HIDDEN
)
1482 else if (tok
->type
== END_TKN
|| gn
== start
) {
1484 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1485 struct cmd_token
*tt
= gnn
->data
;
1486 if (tt
->type
< SPECIAL_TKN
)
1487 vty_out(vty
, " %s", tt
->text
);
1490 vty_out(vty
, "...");
1494 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1495 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1504 list_delete_node(position
, listtail(position
));
1507 static void print_cmd(struct vty
*vty
, const char *cmd
)
1509 int i
, j
, len
= strlen(cmd
);
1514 for (i
= 0; i
< len
; i
++) {
1518 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1524 if (isspace(cmd
[i
])) {
1525 /* skip leading whitespace */
1528 /* skip trailing whitespace */
1531 /* skip all whitespace after opening brackets or pipe */
1532 if (strchr("(<[{|", cmd
[i
- 1])) {
1533 while (isspace(cmd
[i
+ 1]))
1537 /* skip repeated whitespace */
1538 if (isspace(cmd
[i
+ 1]))
1540 /* skip whitespace before closing brackets or pipe */
1541 if (strchr(")>]}|", cmd
[i
+ 1]))
1543 /* convert tabs to spaces */
1544 if (cmd
[i
] == '\t') {
1554 vty_out(vty
, "%s\n", buf
);
1557 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1559 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1562 cmd_finalize_node(node
);
1563 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1565 /* loop over all commands at this node */
1566 const struct cmd_element
*element
= NULL
;
1567 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1569 if ((element
= vector_slot(node
->cmd_vector
, i
)) &&
1570 !(element
->attr
& CMD_ATTR_HIDDEN
)) {
1572 print_cmd(vty
, element
->string
);
1578 /* Help display function for all node. */
1581 "list [permutations]",
1582 "Print command list\n"
1583 "Print all possible command permutations\n")
1585 return cmd_list_cmds(vty
, argc
== 2);
1588 DEFUN (show_commandtree
,
1589 show_commandtree_cmd
,
1590 "show commandtree [permutations]",
1592 "Show command tree\n"
1593 "Permutations that we are interested in\n")
1595 return cmd_list_cmds(vty
, argc
== 3);
1598 DEFUN_HIDDEN(show_cli_graph
,
1603 "Dump current command space as DOT graph\n")
1605 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1608 cmd_finalize_node(cn
);
1609 dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1611 vty_out(vty
, "%s\n", dot
);
1612 XFREE(MTYPE_TMP
, dot
);
1616 static int vty_write_config(struct vty
*vty
)
1619 struct cmd_node
*node
;
1624 nb_cli_show_config_prepare(running_config
, false);
1626 if (vty
->type
== VTY_TERM
) {
1627 vty_out(vty
, "\nCurrent configuration:\n");
1628 vty_out(vty
, "!\n");
1631 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1632 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1633 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1634 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1635 vty_out(vty
, "!\n");
1637 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1638 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1639 if ((*node
->config_write
)(vty
))
1640 vty_out(vty
, "!\n");
1643 if (vty
->type
== VTY_TERM
) {
1644 vty_out(vty
, "end\n");
1650 static int file_write_config(struct vty
*vty
)
1653 char *config_file
, *slash
;
1654 char *config_file_tmp
= NULL
;
1655 char *config_file_sav
= NULL
;
1656 int ret
= CMD_WARNING
;
1657 struct vty
*file_vty
;
1658 struct stat conf_stat
;
1663 /* Check and see if we are operating under vtysh configuration */
1664 if (host
.config
== NULL
) {
1666 "Can't save to configuration file, using vtysh.\n");
1671 config_file
= host
.config
;
1674 #define O_DIRECTORY 0
1676 slash
= strrchr(config_file
, '/');
1678 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1679 config_dir
[slash
- config_file
] = '\0';
1680 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1681 XFREE(MTYPE_TMP
, config_dir
);
1683 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1684 /* if dirfd is invalid, directory sync fails, but we're still OK */
1686 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1687 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1688 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1689 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1692 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1693 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1696 /* Open file to configuration write. */
1697 fd
= mkstemp(config_file_tmp
);
1699 vty_out(vty
, "Can't open configuration file %s.\n",
1703 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1704 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1705 config_file_tmp
, safe_strerror(errno
), errno
);
1709 /* Make vty for configuration file. */
1710 file_vty
= vty_new();
1712 file_vty
->type
= VTY_FILE
;
1714 /* Config file header print. */
1715 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1716 vty_time_print(file_vty
, 1);
1717 vty_out(file_vty
, "!\n");
1718 vty_write_config(file_vty
);
1719 vty_close(file_vty
);
1721 if (stat(config_file
, &conf_stat
) >= 0) {
1722 if (unlink(config_file_sav
) != 0)
1723 if (errno
!= ENOENT
) {
1725 "Can't unlink backup configuration file %s.\n",
1729 if (link(config_file
, config_file_sav
) != 0) {
1731 "Can't backup old configuration file %s.\n",
1738 if (rename(config_file_tmp
, config_file
) != 0) {
1739 vty_out(vty
, "Can't save configuration file %s.\n",
1746 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1750 if (ret
!= CMD_SUCCESS
)
1751 unlink(config_file_tmp
);
1754 XFREE(MTYPE_TMP
, config_file_tmp
);
1755 XFREE(MTYPE_TMP
, config_file_sav
);
1759 /* Write current configuration into file. */
1761 DEFUN (config_write
,
1763 "write [<file|memory|terminal>]",
1764 "Write running configuration to memory, network, or terminal\n"
1765 "Write to configuration file\n"
1766 "Write configuration currently in memory\n"
1767 "Write configuration to terminal\n")
1769 const int idx_type
= 1;
1771 // if command was 'write terminal' or 'write memory'
1772 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1773 return vty_write_config(vty
);
1776 return file_write_config(vty
);
1779 /* ALIAS_FIXME for 'write <terminal|memory>' */
1780 DEFUN (show_running_config
,
1781 show_running_config_cmd
,
1782 "show running-config",
1784 "running configuration (same as write terminal)\n")
1786 return vty_write_config(vty
);
1789 /* ALIAS_FIXME for 'write file' */
1790 DEFUN (copy_runningconf_startupconf
,
1791 copy_runningconf_startupconf_cmd
,
1792 "copy running-config startup-config",
1793 "Copy configuration\n"
1794 "Copy running config to... \n"
1795 "Copy running config to startup config (same as write file/memory)\n")
1797 return file_write_config(vty
);
1801 /* Write startup configuration into the terminal. */
1802 DEFUN (show_startup_config
,
1803 show_startup_config_cmd
,
1804 "show startup-config",
1806 "Contents of startup configuration\n")
1813 if (host
.config
== NULL
)
1816 confp
= fopen(host
.config
, "r");
1817 if (confp
== NULL
) {
1818 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1819 host
.config
, safe_strerror(errno
));
1823 while (fgets(buf
, BUFSIZ
, confp
)) {
1826 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1830 vty_out(vty
, "%s\n", buf
);
1838 int cmd_domainname_set(const char *domainname
)
1840 XFREE(MTYPE_HOST
, host
.domainname
);
1841 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1845 /* Hostname configuration */
1846 DEFUN(config_domainname
,
1849 "Set system's domain name\n"
1850 "This system's domain name\n")
1852 struct cmd_token
*word
= argv
[1];
1854 if (!isalpha((unsigned char)word
->arg
[0])) {
1855 vty_out(vty
, "Please specify string starting with alphabet\n");
1856 return CMD_WARNING_CONFIG_FAILED
;
1859 return cmd_domainname_set(word
->arg
);
1862 DEFUN(config_no_domainname
,
1864 "no domainname [DOMAINNAME]",
1866 "Reset system's domain name\n"
1867 "domain name of this router\n")
1869 return cmd_domainname_set(NULL
);
1872 int cmd_hostname_set(const char *hostname
)
1874 XFREE(MTYPE_HOST
, host
.name
);
1875 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1879 /* Hostname configuration */
1880 DEFUN (config_hostname
,
1883 "Set system's network name\n"
1884 "This system's network name\n")
1886 struct cmd_token
*word
= argv
[1];
1888 if (!isalnum((unsigned char)word
->arg
[0])) {
1890 "Please specify string starting with alphabet or number\n");
1891 return CMD_WARNING_CONFIG_FAILED
;
1894 /* With reference to RFC 1123 Section 2.1 */
1895 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1896 vty_out(vty
, "Hostname length should be less than %d chars\n",
1898 return CMD_WARNING_CONFIG_FAILED
;
1901 return cmd_hostname_set(word
->arg
);
1904 DEFUN (config_no_hostname
,
1906 "no hostname [HOSTNAME]",
1908 "Reset system's network name\n"
1909 "Host name of this router\n")
1911 return cmd_hostname_set(NULL
);
1914 /* VTY interface password set. */
1915 DEFUN (config_password
,
1917 "password [(8-8)] WORD",
1918 "Modify the terminal connection password\n"
1919 "Specifies a HIDDEN password will follow\n"
1920 "The password string\n")
1924 if (argc
== 3) // '8' was specified
1927 XFREE(MTYPE_HOST
, host
.password
);
1928 host
.password
= NULL
;
1929 if (host
.password_encrypt
)
1930 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1931 host
.password_encrypt
=
1932 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1936 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1938 "Please specify string starting with alphanumeric\n");
1939 return CMD_WARNING_CONFIG_FAILED
;
1943 XFREE(MTYPE_HOST
, host
.password
);
1944 host
.password
= NULL
;
1947 if (host
.password_encrypt
)
1948 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1949 host
.password_encrypt
=
1950 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1952 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1957 /* VTY interface password delete. */
1958 DEFUN (no_config_password
,
1962 "Modify the terminal connection password\n")
1964 bool warned
= false;
1966 if (host
.password
) {
1967 if (!vty_shell_serv(vty
)) {
1968 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1971 XFREE(MTYPE_HOST
, host
.password
);
1973 host
.password
= NULL
;
1975 if (host
.password_encrypt
) {
1976 if (!warned
&& !vty_shell_serv(vty
))
1977 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1978 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1980 host
.password_encrypt
= NULL
;
1985 /* VTY enable password set. */
1986 DEFUN (config_enable_password
,
1987 enable_password_cmd
,
1988 "enable password [(8-8)] WORD",
1989 "Modify enable password parameters\n"
1990 "Assign the privileged level password\n"
1991 "Specifies a HIDDEN password will follow\n"
1992 "The HIDDEN 'enable' password string\n")
1997 /* Crypt type is specified. */
1999 if (argv
[idx_8
]->arg
[0] == '8') {
2001 XFREE(MTYPE_HOST
, host
.enable
);
2004 if (host
.enable_encrypt
)
2005 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2006 host
.enable_encrypt
=
2007 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2011 vty_out(vty
, "Unknown encryption type.\n");
2012 return CMD_WARNING_CONFIG_FAILED
;
2016 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2018 "Please specify string starting with alphanumeric\n");
2019 return CMD_WARNING_CONFIG_FAILED
;
2023 XFREE(MTYPE_HOST
, host
.enable
);
2026 /* Plain password input. */
2028 if (host
.enable_encrypt
)
2029 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2030 host
.enable_encrypt
=
2031 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2033 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2038 /* VTY enable password delete. */
2039 DEFUN (no_config_enable_password
,
2040 no_enable_password_cmd
,
2041 "no enable password",
2043 "Modify enable password parameters\n"
2044 "Assign the privileged level password\n")
2046 bool warned
= false;
2049 if (!vty_shell_serv(vty
)) {
2050 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2053 XFREE(MTYPE_HOST
, host
.enable
);
2057 if (host
.enable_encrypt
) {
2058 if (!warned
&& !vty_shell_serv(vty
))
2059 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2060 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2062 host
.enable_encrypt
= NULL
;
2067 DEFUN (service_password_encrypt
,
2068 service_password_encrypt_cmd
,
2069 "service password-encryption",
2070 "Set up miscellaneous service\n"
2071 "Enable encrypted passwords\n")
2078 if (host
.password
) {
2079 if (host
.password_encrypt
)
2080 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2081 host
.password_encrypt
=
2082 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2085 if (host
.enable_encrypt
)
2086 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2087 host
.enable_encrypt
=
2088 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2094 DEFUN (no_service_password_encrypt
,
2095 no_service_password_encrypt_cmd
,
2096 "no service password-encryption",
2098 "Set up miscellaneous service\n"
2099 "Enable encrypted passwords\n")
2106 if (host
.password_encrypt
)
2107 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2108 host
.password_encrypt
= NULL
;
2110 if (host
.enable_encrypt
)
2111 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2112 host
.enable_encrypt
= NULL
;
2117 DEFUN (config_terminal_length
,
2118 config_terminal_length_cmd
,
2119 "terminal length (0-512)",
2120 "Set terminal line parameters\n"
2121 "Set number of lines on a screen\n"
2122 "Number of lines on screen (0 for no pausing)\n")
2126 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2131 DEFUN (config_terminal_no_length
,
2132 config_terminal_no_length_cmd
,
2133 "terminal no length",
2134 "Set terminal line parameters\n"
2136 "Set number of lines on a screen\n")
2142 DEFUN (service_terminal_length
,
2143 service_terminal_length_cmd
,
2144 "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")
2151 host
.lines
= atoi(argv
[idx_number
]->arg
);
2156 DEFUN (no_service_terminal_length
,
2157 no_service_terminal_length_cmd
,
2158 "no service terminal-length [(0-512)]",
2160 "Set up miscellaneous service\n"
2161 "System wide terminal length configuration\n"
2162 "Number of lines of VTY (0 means no line control)\n")
2168 DEFUN_HIDDEN (do_echo
,
2171 "Echo a message back to the vty\n"
2172 "The message to echo\n")
2176 vty_out(vty
, "%s\n",
2177 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2179 XFREE(MTYPE_TMP
, message
);
2183 DEFUN (config_logmsg
,
2185 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2186 "Send a message to enabled logging destinations\n"
2188 "The message to send\n")
2190 int idx_log_level
= 1;
2191 int idx_message
= 2;
2195 level
= log_level_match(argv
[idx_log_level
]->arg
);
2196 if (level
== ZLOG_DISABLED
)
2197 return CMD_ERR_NO_MATCH
;
2200 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2202 XFREE(MTYPE_TMP
, message
);
2207 DEFUN (debug_memstats
,
2209 "[no] debug memstats-at-exit",
2212 "Print memory type statistics at exit\n")
2214 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2218 int cmd_banner_motd_file(const char *file
)
2220 int success
= CMD_SUCCESS
;
2225 rpath
= realpath(file
, p
);
2227 return CMD_ERR_NO_FILE
;
2228 in
= strstr(rpath
, SYSCONFDIR
);
2230 XFREE(MTYPE_HOST
, host
.motdfile
);
2231 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2233 success
= CMD_WARNING_CONFIG_FAILED
;
2238 void cmd_banner_motd_line(const char *line
)
2240 XFREE(MTYPE_HOST
, host
.motd
);
2241 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2244 DEFUN (banner_motd_file
,
2245 banner_motd_file_cmd
,
2246 "banner motd file FILE",
2249 "Banner from a file\n"
2253 const char *filename
= argv
[idx_file
]->arg
;
2254 int cmd
= cmd_banner_motd_file(filename
);
2256 if (cmd
== CMD_ERR_NO_FILE
)
2257 vty_out(vty
, "%s does not exist\n", filename
);
2258 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2259 vty_out(vty
, "%s must be in %s\n", filename
, SYSCONFDIR
);
2264 DEFUN (banner_motd_line
,
2265 banner_motd_line_cmd
,
2266 "banner motd line LINE...",
2269 "Banner from an input\n"
2275 argv_find(argv
, argc
, "LINE", &idx
);
2276 motd
= argv_concat(argv
, argc
, idx
);
2278 cmd_banner_motd_line(motd
);
2279 XFREE(MTYPE_TMP
, motd
);
2284 DEFUN (banner_motd_default
,
2285 banner_motd_default_cmd
,
2286 "banner motd default",
2287 "Set banner string\n"
2288 "Strings for motd\n"
2291 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2295 DEFUN (no_banner_motd
,
2299 "Set banner string\n"
2300 "Strings for motd\n")
2304 XFREE(MTYPE_HOST
, host
.motdfile
);
2305 host
.motdfile
= NULL
;
2309 DEFUN(allow_reserved_ranges
, allow_reserved_ranges_cmd
, "allow-reserved-ranges",
2310 "Allow using IPv4 (Class E) reserved IP space\n")
2312 host
.allow_reserved_ranges
= true;
2316 DEFUN(no_allow_reserved_ranges
, no_allow_reserved_ranges_cmd
,
2317 "no allow-reserved-ranges",
2318 NO_STR
"Allow using IPv4 (Class E) reserved IP space\n")
2320 host
.allow_reserved_ranges
= false;
2324 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2326 const struct cmd_node
*node
;
2327 const struct cmd_element
*cli
;
2332 char *pattern
= argv_concat(argv
, argc
, 1);
2333 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2334 XFREE(MTYPE_TMP
, pattern
);
2339 vty_out(vty
, "%% Invalid {...} expression\n");
2342 vty_out(vty
, "%% Bad repetition operator\n");
2345 vty_out(vty
, "%% Regex syntax error\n");
2348 vty_out(vty
, "%% Invalid collating element\n");
2351 vty_out(vty
, "%% Invalid character class name\n");
2355 "%% Regex ended with escape character (\\)\n");
2359 "%% Invalid number in \\digit construction\n");
2362 vty_out(vty
, "%% Unbalanced square brackets\n");
2365 vty_out(vty
, "%% Unbalanced parentheses\n");
2368 vty_out(vty
, "%% Unbalanced braces\n");
2372 "%% Invalid endpoint in range expression\n");
2375 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2383 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2384 node
= vector_slot(cmdvec
, i
);
2387 clis
= node
->cmd_vector
;
2388 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2389 cli
= vector_slot(clis
, j
);
2391 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2392 vty_out(vty
, " (%s) ", node
->name
);
2393 print_cmd(vty
, cli
->string
);
2406 "Find CLI command matching a regular expression\n"
2407 "Search pattern (POSIX regex)\n")
2409 return cmd_find_cmds(vty
, argv
, argc
);
2412 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2413 DEFUN(script
, script_cmd
, "script SCRIPT FUNCTION",
2414 "Test command - execute a function in a script\n"
2415 "Script name (same as filename in /etc/frr/scripts/)\n"
2416 "Function name (in the script)\n")
2420 (void)str2prefix("1.2.3.4/24", &p
);
2421 struct frrscript
*fs
= frrscript_new(argv
[1]->arg
);
2423 if (frrscript_load(fs
, argv
[2]->arg
, NULL
)) {
2425 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2426 argv
[1]->arg
, argv
[2]->arg
);
2429 int ret
= frrscript_call(fs
, argv
[2]->arg
, ("p", &p
));
2431 prefix2str(&p
, buf
, sizeof(buf
));
2432 vty_out(vty
, "p: %s\n", buf
);
2433 vty_out(vty
, "Script result: %d\n", ret
);
2435 frrscript_delete(fs
);
2441 /* Set config filename. Called from vty.c */
2442 void host_config_set(const char *filename
)
2444 XFREE(MTYPE_HOST
, host
.config
);
2445 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2448 const char *host_config_get(void)
2453 void cmd_show_lib_debugs(struct vty
*vty
)
2455 route_map_show_debug(vty
);
2456 mgmt_debug_be_client_show_debug(vty
);
2457 mgmt_debug_fe_client_show_debug(vty
);
2460 void install_default(enum node_type node
)
2462 _install_element(node
, &config_exit_cmd
);
2463 _install_element(node
, &config_quit_cmd
);
2464 _install_element(node
, &config_end_cmd
);
2465 _install_element(node
, &config_help_cmd
);
2466 _install_element(node
, &config_list_cmd
);
2467 _install_element(node
, &show_cli_graph_cmd
);
2468 _install_element(node
, &find_cmd
);
2470 _install_element(node
, &config_write_cmd
);
2471 _install_element(node
, &show_running_config_cmd
);
2473 _install_element(node
, &autocomplete_cmd
);
2475 nb_cli_install_default(node
);
2478 /* Initialize command interface. Install basic nodes and commands.
2480 * terminal = 0 -- vtysh / no logging, no config control
2481 * terminal = 1 -- normal daemon
2482 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2483 void cmd_init(int terminal
)
2485 struct utsname names
;
2490 /* register command preprocessors */
2491 hook_register(cmd_execute
, handle_pipe_action
);
2492 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2494 varhandlers
= list_new();
2496 /* Allocate initial top vector of commands. */
2497 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2499 /* Default host value settings. */
2500 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2501 host
.system
= XSTRDUP(MTYPE_HOST
, names
.sysname
);
2502 host
.release
= XSTRDUP(MTYPE_HOST
, names
.release
);
2503 host
.version
= XSTRDUP(MTYPE_HOST
, names
.version
);
2505 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2506 if ((strcmp(names
.domainname
, "(none)") == 0))
2507 host
.domainname
= NULL
;
2509 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2511 host
.domainname
= NULL
;
2513 host
.password
= NULL
;
2516 host
.noconfig
= (terminal
< 0);
2518 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2519 host
.motdfile
= NULL
;
2520 host
.allow_reserved_ranges
= false;
2522 /* Install top nodes. */
2523 install_node(&view_node
);
2524 install_node(&enable_node
);
2525 install_node(&auth_node
);
2526 install_node(&auth_enable_node
);
2527 install_node(&config_node
);
2529 /* Each node's basic commands. */
2530 install_element(VIEW_NODE
, &show_version_cmd
);
2531 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2534 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2536 install_element(VIEW_NODE
, &config_list_cmd
);
2537 install_element(VIEW_NODE
, &config_exit_cmd
);
2538 install_element(VIEW_NODE
, &config_quit_cmd
);
2539 install_element(VIEW_NODE
, &config_help_cmd
);
2540 install_element(VIEW_NODE
, &config_enable_cmd
);
2541 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2542 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2543 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2544 install_element(VIEW_NODE
, &echo_cmd
);
2545 install_element(VIEW_NODE
, &autocomplete_cmd
);
2546 install_element(VIEW_NODE
, &find_cmd
);
2547 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2548 install_element(VIEW_NODE
, &script_cmd
);
2552 install_element(ENABLE_NODE
, &config_end_cmd
);
2553 install_element(ENABLE_NODE
, &config_disable_cmd
);
2554 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2555 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2556 install_element(ENABLE_NODE
, &config_write_cmd
);
2557 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2558 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2560 install_default(CONFIG_NODE
);
2563 workqueue_cmd_init();
2567 install_element(CONFIG_NODE
, &hostname_cmd
);
2568 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2569 install_element(CONFIG_NODE
, &domainname_cmd
);
2570 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2575 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2577 install_element(CONFIG_NODE
, &password_cmd
);
2578 install_element(CONFIG_NODE
, &no_password_cmd
);
2579 install_element(CONFIG_NODE
, &enable_password_cmd
);
2580 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2582 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2583 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2584 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2585 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2586 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2587 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2588 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2589 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2590 install_element(CONFIG_NODE
, &allow_reserved_ranges_cmd
);
2591 install_element(CONFIG_NODE
, &no_allow_reserved_ranges_cmd
);
2594 vrf_install_commands();
2598 grammar_sandbox_init();
2602 void cmd_terminate(void)
2604 struct cmd_node
*cmd_node
;
2606 hook_unregister(cmd_execute
, handle_pipe_action
);
2607 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2610 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2611 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2612 // deleting the graph delets the cmd_element as
2614 graph_delete_graph(cmd_node
->cmdgraph
);
2615 vector_free(cmd_node
->cmd_vector
);
2616 hash_clean_and_free(&cmd_node
->cmd_hash
, NULL
);
2619 vector_free(cmdvec
);
2623 XFREE(MTYPE_HOST
, host
.name
);
2624 XFREE(MTYPE_HOST
, host
.system
);
2625 XFREE(MTYPE_HOST
, host
.release
);
2626 XFREE(MTYPE_HOST
, host
.version
);
2627 XFREE(MTYPE_HOST
, host
.domainname
);
2628 XFREE(MTYPE_HOST
, host
.password
);
2629 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2630 XFREE(MTYPE_HOST
, host
.enable
);
2631 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2632 XFREE(MTYPE_HOST
, host
.motdfile
);
2633 XFREE(MTYPE_HOST
, host
.config
);
2634 XFREE(MTYPE_HOST
, host
.motd
);
2636 list_delete(&varhandlers
);