2 * CLI backend interface.
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
10 * This file is part of GNU Zebra.
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
48 #include "lib_errors.h"
49 #include "northbound_cli.h"
52 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config")
53 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item")
60 /* clang-format off */
61 const struct message tokennames
[] = {
66 item(IPV4_PREFIX_TKN
),
68 item(IPV6_PREFIX_TKN
),
79 /* Command vector which includes some level of command lists. Normally
80 each daemon maintains each own cmdvec. */
83 /* Host information structure. */
87 * Returns host.name if any, otherwise
88 * it returns the system hostname.
90 const char *cmd_hostname_get(void)
96 * Returns unix domainname
98 const char *cmd_domainname_get(void)
100 return host
.domainname
;
103 static int root_on_exit(struct vty
*vty
);
105 /* Standard command node structures. */
106 static struct cmd_node auth_node
= {
109 .prompt
= "Password: ",
112 static struct cmd_node view_node
= {
116 .node_exit
= root_on_exit
,
119 static struct cmd_node auth_enable_node
= {
120 .name
= "auth enable",
121 .node
= AUTH_ENABLE_NODE
,
122 .prompt
= "Password: ",
125 static struct cmd_node enable_node
= {
129 .node_exit
= root_on_exit
,
132 static int config_write_host(struct vty
*vty
);
133 static struct cmd_node config_node
= {
136 .parent_node
= ENABLE_NODE
,
137 .prompt
= "%s(config)# ",
138 .config_write
= config_write_host
,
139 .node_exit
= vty_config_node_exit
,
142 /* This is called from main when a daemon is invoked with -v or --version. */
143 void print_version(const char *progname
)
145 printf("%s version %s\n", progname
, FRR_VERSION
);
146 printf("%s\n", FRR_COPYRIGHT
);
147 #ifdef ENABLE_VERSION_BUILD_CONFIG
148 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
152 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
154 int cnt
= MAX(argc
- shift
, 0);
155 const char *argstr
[cnt
+ 1];
160 for (int i
= 0; i
< cnt
; i
++)
161 argstr
[i
] = argv
[i
+ shift
]->arg
;
163 return frrstr_join(argstr
, cnt
, " ");
166 vector
cmd_make_strvec(const char *string
)
171 const char *copy
= string
;
173 /* skip leading whitespace */
174 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
177 /* if the entire string was whitespace or a comment, return */
178 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
181 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
183 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
184 if (strlen(vector_slot(result
, i
)) == 0) {
185 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
186 vector_unset(result
, i
);
190 vector_compact(result
);
195 void cmd_free_strvec(vector v
)
197 frrstr_strvec_free(v
);
201 * Convenience function for accessing argv data.
205 * @param text definition snippet of the desired token
206 * @param index the starting index, and where to store the
207 * index of the found token if it exists
208 * @return 1 if found, 0 otherwise
210 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
213 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
214 if ((found
= strmatch(text
, argv
[i
]->text
)))
219 static unsigned int cmd_hash_key(const void *p
)
221 int size
= sizeof(p
);
223 return jhash(p
, size
, 0);
226 static bool cmd_hash_cmp(const void *a
, const void *b
)
231 /* Install top node of command vector. */
232 void install_node(struct cmd_node
*node
)
234 vector_set_index(cmdvec
, node
->node
, node
);
235 node
->cmdgraph
= graph_new();
236 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
238 struct cmd_token
*token
=
239 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
240 graph_new_node(node
->cmdgraph
, token
,
241 (void (*)(void *)) & cmd_token_del
);
242 node
->cmd_hash
= hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
,
246 /* Return prompt character of specified node. */
247 const char *cmd_prompt(enum node_type node
)
249 struct cmd_node
*cnode
;
251 cnode
= vector_slot(cmdvec
, node
);
252 return cnode
->prompt
;
255 /* Install a command into a node. */
256 void install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
258 struct cmd_node
*cnode
;
260 /* cmd_init hasn't been called */
262 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
267 cnode
= vector_lookup(cmdvec
, ntype
);
272 "\tnode %d does not exist.\n"
273 "\tplease call install_node() before install_element()\n",
274 cmd
->name
, cmd
->string
, ntype
);
278 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
281 "\tnode %d (%s) already has this command installed.\n"
282 "\tduplicate install_element call?\n",
283 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
287 assert(hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
));
289 struct graph
*graph
= graph_new();
290 struct cmd_token
*token
=
291 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
292 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
294 cmd_graph_parse(graph
, cmd
);
295 cmd_graph_names(graph
);
296 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
297 graph_delete_graph(graph
);
299 vector_set(cnode
->cmd_vector
, (void *)cmd
);
301 if (ntype
== VIEW_NODE
)
302 install_element(ENABLE_NODE
, cmd
);
305 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
307 struct cmd_node
*cnode
;
309 /* cmd_init hasn't been called */
311 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
316 cnode
= vector_lookup(cmdvec
, ntype
);
321 "\tnode %d does not exist.\n"
322 "\tplease call install_node() before uninstall_element()\n",
323 cmd
->name
, cmd
->string
, ntype
);
327 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
330 "\tnode %d (%s) does not have this command installed.\n"
331 "\tduplicate uninstall_element call?\n",
332 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
336 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
338 struct graph
*graph
= graph_new();
339 struct cmd_token
*token
=
340 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
341 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
343 cmd_graph_parse(graph
, cmd
);
344 cmd_graph_names(graph
);
345 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
346 graph_delete_graph(graph
);
348 if (ntype
== VIEW_NODE
)
349 uninstall_element(ENABLE_NODE
, cmd
);
353 static const unsigned char itoa64
[] =
354 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
356 static void to64(char *s
, long v
, int n
)
359 *s
++ = itoa64
[v
& 0x3f];
364 static char *zencrypt(const char *passwd
)
368 char *crypt(const char *, const char *);
370 gettimeofday(&tv
, 0);
372 to64(&salt
[0], frr_weak_random(), 3);
373 to64(&salt
[3], tv
.tv_usec
, 3);
376 return crypt(passwd
, salt
);
379 static bool full_cli
;
381 /* This function write configuration of this host. */
382 static int config_write_host(struct vty
*vty
)
384 if (cmd_hostname_get())
385 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
387 if (cmd_domainname_get())
388 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
390 /* The following are all configuration commands that are not sent to
391 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
392 * we would always display 'log syslog informational' in the config
393 * which would cause other daemons to then switch to syslog when they
398 if (host
.password_encrypt
)
399 vty_out(vty
, "password 8 %s\n",
400 host
.password_encrypt
);
401 if (host
.enable_encrypt
)
402 vty_out(vty
, "enable password 8 %s\n",
403 host
.enable_encrypt
);
406 vty_out(vty
, "password %s\n", host
.password
);
408 vty_out(vty
, "enable password %s\n",
411 log_config_write(vty
);
414 vty_out(vty
, "service advanced-vty\n");
417 vty_out(vty
, "service password-encryption\n");
420 vty_out(vty
, "service terminal-length %d\n",
424 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
426 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
428 vty_out(vty
, "banner motd line %s\n", host
.motd
);
430 vty_out(vty
, "no banner motd\n");
433 if (debug_memstats_at_exit
)
434 vty_out(vty
, "!\ndebug memstats-at-exit\n");
439 /* Utility function for getting command graph. */
440 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
442 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
443 return cnode
->cmdgraph
;
446 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
448 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
449 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
455 * Compare function for cmd_token.
456 * Used with qsort to sort command completions.
458 static int compare_completions(const void *fst
, const void *snd
)
460 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
461 *secnd
= *(const struct cmd_token
* const *)snd
;
462 return strcmp(first
->text
, secnd
->text
);
466 * Takes a list of completions returned by command_complete,
467 * dedeuplicates them based on both text and description,
468 * sorts them, and returns them as a vector.
470 * @param completions linked list of cmd_token
471 * @return deduplicated and sorted vector with
473 vector
completions_to_vec(struct list
*completions
)
475 vector comps
= vector_init(VECTOR_MIN_SIZE
);
478 struct cmd_token
*token
, *cr
= NULL
;
479 unsigned int i
, exists
;
480 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
481 if (token
->type
== END_TKN
&& (cr
= token
))
484 // linear search for token in completions vector
486 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
487 struct cmd_token
*curr
= vector_slot(comps
, i
);
489 exists
= !strcmp(curr
->text
, token
->text
)
490 && !strcmp(curr
->desc
, token
->desc
);
492 exists
= !strcmp(curr
->text
, token
->text
);
493 #endif /* VTYSH_DEBUG */
497 vector_set(comps
, token
);
501 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
502 &compare_completions
);
504 // make <cr> the first element, if it is present
506 vector_set_index(comps
, vector_active(comps
), NULL
);
507 memmove(comps
->index
+ 1, comps
->index
,
508 (comps
->alloced
- 1) * sizeof(void *));
509 vector_set_index(comps
, 0, cr
);
515 * Generates a vector of cmd_token representing possible completions
516 * on the current input.
518 * @param vline the vectorized input line
519 * @param vty the vty with the node to match on
520 * @param status pointer to matcher status code
521 * @return vector of struct cmd_token * with possible completions
523 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
526 struct list
*completions
;
527 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
529 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
531 if (MATCHER_ERROR(rv
)) {
532 *status
= CMD_ERR_NO_MATCH
;
536 vector comps
= completions_to_vec(completions
);
537 list_delete(&completions
);
539 // set status code appropriately
540 switch (vector_active(comps
)) {
542 *status
= CMD_ERR_NO_MATCH
;
545 *status
= CMD_COMPLETE_FULL_MATCH
;
548 *status
= CMD_COMPLETE_LIST_MATCH
;
554 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
558 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
559 enum node_type onode
;
560 int orig_xpath_index
;
561 vector shifted_vline
;
565 orig_xpath_index
= vty
->xpath_index
;
566 vty
->node
= ENABLE_NODE
;
567 vty
->xpath_index
= 0;
568 /* We can try it on enable node, cos' the vty is authenticated
571 shifted_vline
= vector_init(vector_count(vline
));
573 for (index
= 1; index
< vector_active(vline
); index
++) {
574 vector_set_index(shifted_vline
, index
- 1,
575 vector_lookup(vline
, index
));
578 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
580 vector_free(shifted_vline
);
582 vty
->xpath_index
= orig_xpath_index
;
586 return cmd_complete_command_real(vline
, vty
, status
);
589 static struct list
*varhandlers
= NULL
;
591 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
595 const struct cmd_variable_handler
*cvh
;
599 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
601 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
602 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
604 if (cvh
->varname
&& (!token
->varname
605 || strcmp(cvh
->varname
, token
->varname
)))
607 cvh
->completions(tmpcomps
, token
);
615 for (i
= vector_active(tmpcomps
); i
; i
--) {
616 char *item
= vector_slot(tmpcomps
, i
- 1);
617 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
618 vector_set(comps
, item
);
620 XFREE(MTYPE_COMPLETION
, item
);
622 vector_free(tmpcomps
);
625 #define AUTOCOMP_INDENT 5
627 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
630 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
631 int lc
= AUTOCOMP_INDENT
;
632 size_t cs
= AUTOCOMP_INDENT
;
634 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
635 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
636 char *item
= vector_slot(comps
, j
);
637 itemlen
= strlen(item
);
639 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
640 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
642 if (lc
+ itemlen
+ 1 >= cols
) {
643 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
644 AUTOCOMP_INDENT
, "");
645 lc
= AUTOCOMP_INDENT
;
648 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
651 XFREE(MTYPE_COMPLETION
, item
);
652 vector_set_index(comps
, j
, NULL
);
657 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
662 for (; cvh
->completions
; cvh
++)
663 listnode_add(varhandlers
, (void *)cvh
);
666 DEFUN_HIDDEN (autocomplete
,
668 "autocomplete TYPE TEXT VARNAME",
669 "Autocompletion handler (internal, for vtysh)\n"
672 "cmd_token->varname\n")
674 struct cmd_token tok
;
675 vector comps
= vector_init(32);
678 memset(&tok
, 0, sizeof(tok
));
679 tok
.type
= atoi(argv
[1]->arg
);
680 tok
.text
= argv
[2]->arg
;
681 tok
.varname
= argv
[3]->arg
;
682 if (!strcmp(tok
.varname
, "-"))
685 cmd_variable_complete(&tok
, NULL
, comps
);
687 for (i
= 0; i
< vector_active(comps
); i
++) {
688 char *text
= vector_slot(comps
, i
);
689 vty_out(vty
, "%s\n", text
);
690 XFREE(MTYPE_COMPLETION
, text
);
698 * Generate possible tab-completions for the given input. This function only
699 * returns results that would result in a valid command if used as Readline
700 * completions (as is the case in vtysh). For instance, if the passed vline ends
701 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
703 * @param vline vectorized input line
705 * @param status location to store matcher status code in
706 * @return set of valid strings for use with Readline as tab-completions.
709 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
712 int original_node
= vty
->node
;
713 vector input_line
= vector_init(vector_count(vline
));
715 // if the first token is 'do' we'll want to execute the command in the
717 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
718 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
720 // construct the input line we'll be matching on
721 unsigned int offset
= (do_shortcut
) ? 1 : 0;
722 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
723 vector_set_index(input_line
, index
,
724 vector_lookup(vline
, index
+ offset
));
726 // get token completions -- this is a copying operation
727 vector comps
= NULL
, initial_comps
;
728 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
730 if (!MATCHER_ERROR(*status
)) {
731 assert(initial_comps
);
732 // filter out everything that is not suitable for a
734 comps
= vector_init(VECTOR_MIN_SIZE
);
735 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
737 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
738 if (token
->type
== WORD_TKN
)
739 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
741 else if (IS_VARYING_TOKEN(token
->type
)) {
742 const char *ref
= vector_lookup(
743 vline
, vector_active(vline
) - 1);
744 cmd_variable_complete(token
, ref
, comps
);
747 vector_free(initial_comps
);
749 // since we filtered results, we need to re-set status code
750 switch (vector_active(comps
)) {
752 *status
= CMD_ERR_NO_MATCH
;
755 *status
= CMD_COMPLETE_FULL_MATCH
;
758 *status
= CMD_COMPLETE_LIST_MATCH
;
761 // copy completions text into an array of char*
762 ret
= XMALLOC(MTYPE_TMP
,
763 (vector_active(comps
) + 1) * sizeof(char *));
765 for (i
= 0; i
< vector_active(comps
); i
++) {
766 ret
[i
] = vector_slot(comps
, i
);
768 // set the last element to NULL, because this array is used in
769 // a Readline completion_generator function which expects NULL
770 // as a sentinel value
774 } else if (initial_comps
)
775 vector_free(initial_comps
);
777 // comps should always be null here
780 // free the adjusted input line
781 vector_free(input_line
);
783 // reset vty->node to its original value
784 vty
->node
= original_node
;
789 /* return parent node */
790 /* MUST eventually converge on CONFIG_NODE */
791 enum node_type
node_parent(enum node_type node
)
795 assert(node
> CONFIG_NODE
);
800 case BGP_FLOWSPECV4_NODE
:
801 case BGP_FLOWSPECV6_NODE
:
802 case BGP_VRF_POLICY_NODE
:
803 case BGP_VNC_DEFAULTS_NODE
:
804 case BGP_VNC_NVE_GROUP_NODE
:
805 case BGP_VNC_L2_GROUP_NODE
:
816 case BGP_EVPN_VNI_NODE
:
819 case KEYCHAIN_KEY_NODE
:
822 case LINK_PARAMS_NODE
:
823 ret
= INTERFACE_NODE
;
829 case LDP_IPV4_IFACE_NODE
:
832 case LDP_IPV6_IFACE_NODE
:
835 case LDP_PSEUDOWIRE_NODE
:
836 ret
= LDP_L2VPN_NODE
;
841 case BFD_PROFILE_NODE
:
852 /* Execute command by argument vline vector. */
853 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
855 const struct cmd_element
**cmd
)
857 struct list
*argv_list
;
858 enum matcher_rv status
;
859 const struct cmd_element
*matched_element
= NULL
;
861 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
862 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
865 *cmd
= matched_element
;
867 // if matcher error, return corresponding CMD_ERR
868 if (MATCHER_ERROR(status
)) {
870 list_delete(&argv_list
);
872 case MATCHER_INCOMPLETE
:
873 return CMD_ERR_INCOMPLETE
;
874 case MATCHER_AMBIGUOUS
:
875 return CMD_ERR_AMBIGUOUS
;
877 return CMD_ERR_NO_MATCH
;
881 // build argv array from argv list
882 struct cmd_token
**argv
= XMALLOC(
883 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
885 struct cmd_token
*token
;
887 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
890 int argc
= argv_list
->count
;
893 if (matched_element
->daemon
)
894 ret
= CMD_SUCCESS_DAEMON
;
897 /* Clear array of enqueued configuration changes. */
898 vty
->num_cfg_changes
= 0;
899 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
901 /* Regenerate candidate configuration if necessary. */
902 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
903 && running_config
->version
904 > vty
->candidate_config
->version
)
905 nb_config_replace(vty
->candidate_config
,
906 running_config
, true);
909 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
912 // delete list and cmd_token's in it
913 list_delete(&argv_list
);
914 XFREE(MTYPE_TMP
, argv
);
920 * Execute a given command, handling things like "do ..." and checking
921 * whether the given command might apply at a parent node if doesn't
922 * apply for the current node.
924 * @param vline Command line input, vector of char* where each element is
926 * @param vty The vty context in which the command should be executed.
927 * @param cmd Pointer where the struct cmd_element of the matched command
928 * will be stored, if any. May be set to NULL if this info is
930 * @param vtysh If set != 0, don't lookup the command at parent nodes.
931 * @return The status of the command that has been executed or an error code
932 * as to why no command could be executed.
934 int cmd_execute_command(vector vline
, struct vty
*vty
,
935 const struct cmd_element
**cmd
, int vtysh
)
937 int ret
, saved_ret
= 0;
938 enum node_type onode
, try_node
;
939 int orig_xpath_index
;
941 onode
= try_node
= vty
->node
;
942 orig_xpath_index
= vty
->xpath_index
;
944 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
945 vector shifted_vline
;
948 vty
->node
= ENABLE_NODE
;
949 vty
->xpath_index
= 0;
950 /* We can try it on enable node, cos' the vty is authenticated
953 shifted_vline
= vector_init(vector_count(vline
));
955 for (index
= 1; index
< vector_active(vline
); index
++)
956 vector_set_index(shifted_vline
, index
- 1,
957 vector_lookup(vline
, index
));
959 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
962 vector_free(shifted_vline
);
964 vty
->xpath_index
= orig_xpath_index
;
969 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
974 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
975 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
976 /* This assumes all nodes above CONFIG_NODE are childs of
978 while (vty
->node
> CONFIG_NODE
) {
979 try_node
= node_parent(try_node
);
980 vty
->node
= try_node
;
981 if (vty
->xpath_index
> 0)
983 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
985 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
986 || ret
== CMD_NOT_MY_INSTANCE
987 || ret
== CMD_WARNING_CONFIG_FAILED
)
990 /* no command succeeded, reset the vty to the original node */
992 vty
->xpath_index
= orig_xpath_index
;
995 /* return command status for original node */
1000 * Execute a given command, matching it strictly against the current node.
1001 * This mode is used when reading config files.
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 * @return The status of the command that has been executed or an error code
1010 * as to why no command could be executed.
1012 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1013 const struct cmd_element
**cmd
)
1015 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1019 * Hook for preprocessing command string before executing.
1021 * All subscribers are called with the raw command string that is to be
1022 * executed. If any changes are to be made, a new string should be allocated
1023 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1024 * is then responsible for freeing this string.
1026 * All processing functions must be mutually exclusive in their action, i.e. if
1027 * one subscriber decides to modify the command, all others must not modify it
1028 * when called. Feeding the output of one processing command into a subsequent
1029 * one is not supported.
1031 * This hook is intentionally internal to the command processing system.
1034 * The raw command string.
1037 * The result of any processing.
1039 DECLARE_HOOK(cmd_execute
,
1040 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1041 (vty
, cmd_in
, cmd_out
));
1042 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1043 (vty
, cmd_in
, cmd_out
));
1045 /* Hook executed after a CLI command. */
1046 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1048 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1052 * cmd_execute hook subscriber to handle `|` actions.
1054 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1058 char *orig
, *working
, *token
, *u
;
1059 char *pipe
= strstr(cmd_in
, "| ");
1064 /* duplicate string for processing purposes, not including pipe */
1065 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1067 /* retrieve action */
1068 token
= strsep(&working
, " ");
1071 /* match result to known actions */
1072 if (strmatch(token
, "include")) {
1073 /* the remaining text should be a regexp */
1074 char *regexp
= working
;
1077 vty_out(vty
, "%% Need a regexp to filter with\n");
1081 bool succ
= vty_set_include(vty
, regexp
);
1084 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1087 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1091 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1096 XFREE(MTYPE_TMP
, orig
);
1100 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1103 vty_set_include(vty
, NULL
);
1108 int cmd_execute(struct vty
*vty
, const char *cmd
,
1109 const struct cmd_element
**matched
, int vtysh
)
1112 char *cmd_out
= NULL
;
1113 const char *cmd_exec
;
1116 hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1117 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1119 vline
= cmd_make_strvec(cmd_exec
);
1122 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1123 cmd_free_strvec(vline
);
1128 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1130 XFREE(MTYPE_TMP
, cmd_out
);
1137 * Parse one line of config, walking up the parse tree attempting to find a
1140 * @param vty The vty context in which the command should be executed.
1141 * @param cmd Pointer where the struct cmd_element* of the match command
1142 * will be stored, if any. May be set to NULL if this info is
1144 * @param use_daemon Boolean to control whether or not we match on
1145 * CMD_SUCCESS_DAEMON
1147 * @return The status of the command that has been executed or an error code
1148 * as to why no command could be executed.
1150 int command_config_read_one_line(struct vty
*vty
,
1151 const struct cmd_element
**cmd
,
1152 uint32_t line_num
, int use_daemon
)
1157 vline
= cmd_make_strvec(vty
->buf
);
1159 /* In case of comment line */
1163 /* Execute configuration command : this is strict match */
1164 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1166 // Climb the tree and try the command again at each node
1167 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1168 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1169 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1170 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1171 && vty
->node
!= CONFIG_NODE
) {
1172 int saved_node
= vty
->node
;
1173 int saved_xpath_index
= vty
->xpath_index
;
1175 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1176 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1177 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1178 && vty
->node
> CONFIG_NODE
) {
1179 vty
->node
= node_parent(vty
->node
);
1180 if (vty
->xpath_index
> 0)
1182 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1185 // If climbing the tree did not work then ignore the command and
1186 // stay at the same node
1187 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1188 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1189 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1190 vty
->node
= saved_node
;
1191 vty
->xpath_index
= saved_xpath_index
;
1195 if (ret
!= CMD_SUCCESS
&&
1196 ret
!= CMD_WARNING
&&
1197 ret
!= CMD_SUCCESS_DAEMON
) {
1198 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1200 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1201 ve
->line_num
= line_num
;
1203 vty
->error
= list_new();
1205 listnode_add(vty
->error
, ve
);
1208 cmd_free_strvec(vline
);
1213 /* Configuration make from file. */
1214 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1216 int ret
, error_ret
= 0;
1219 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1222 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1224 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1225 && ret
!= CMD_ERR_NOTHING_TODO
)
1236 /* Configuration from terminal */
1237 DEFUN (config_terminal
,
1238 config_terminal_cmd
,
1239 "configure [terminal]",
1240 "Configuration from vty interface\n"
1241 "Configuration terminal\n")
1243 return vty_config_enter(vty
, false, false);
1246 /* Enable command */
1250 "Turn on privileged mode command\n")
1252 /* If enable password is NULL, change to ENABLE_NODE */
1253 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1254 || vty
->type
== VTY_SHELL_SERV
)
1255 vty
->node
= ENABLE_NODE
;
1257 vty
->node
= AUTH_ENABLE_NODE
;
1262 /* Disable command */
1266 "Turn off privileged mode command\n")
1268 if (vty
->node
== ENABLE_NODE
)
1269 vty
->node
= VIEW_NODE
;
1273 /* Down vty node level. */
1277 "Exit current mode and down to previous mode\n")
1283 static int root_on_exit(struct vty
*vty
)
1288 vty
->status
= VTY_CLOSE
;
1292 void cmd_exit(struct vty
*vty
)
1294 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1296 if (cnode
->node_exit
) {
1297 if (!cnode
->node_exit(vty
))
1300 if (cnode
->parent_node
)
1301 vty
->node
= cnode
->parent_node
;
1302 if (vty
->xpath_index
> 0)
1310 "Exit current mode and down to previous mode\n")
1312 return config_exit(self
, vty
, argc
, argv
);
1316 /* End of configuration. */
1320 "End current mode and change to enable mode.\n")
1323 vty_config_exit(vty
);
1324 vty
->node
= ENABLE_NODE
;
1330 DEFUN (show_version
,
1334 "Displays zebra version\n")
1336 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1337 cmd_hostname_get() ? cmd_hostname_get() : "");
1338 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1339 #ifdef ENABLE_VERSION_BUILD_CONFIG
1340 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1345 /* Help display function for all node. */
1349 "Description of the interactive help system\n")
1352 "Quagga VTY provides advanced help feature. When you need help,\n\
1353 anytime at the command line please press '?'.\n\
1355 If nothing matches, the help list will be empty and you must backup\n\
1356 until entering a '?' shows the available options.\n\
1357 Two styles of help are provided:\n\
1358 1. Full help is available when you are ready to enter a\n\
1359 command argument (e.g. 'show ?') and describes each possible\n\
1361 2. Partial help is provided when an abbreviated argument is entered\n\
1362 and you want to know what arguments match the input\n\
1363 (e.g. 'show me?'.)\n\n");
1367 static void permute(struct graph_node
*start
, struct vty
*vty
)
1369 static struct list
*position
= NULL
;
1371 position
= list_new();
1373 struct cmd_token
*stok
= start
->data
;
1374 struct graph_node
*gnn
;
1375 struct listnode
*ln
;
1378 listnode_add(position
, start
);
1379 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1380 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1381 struct cmd_token
*tok
= gn
->data
;
1382 if (tok
->attr
== CMD_ATTR_HIDDEN
1383 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1385 else if (tok
->type
== END_TKN
|| gn
== start
) {
1387 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1388 struct cmd_token
*tt
= gnn
->data
;
1389 if (tt
->type
< SPECIAL_TKN
)
1390 vty_out(vty
, " %s", tt
->text
);
1393 vty_out(vty
, "...");
1397 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1398 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1407 list_delete_node(position
, listtail(position
));
1410 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1412 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1415 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1417 /* loop over all commands at this node */
1418 const struct cmd_element
*element
= NULL
;
1419 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1421 if ((element
= vector_slot(node
->cmd_vector
, i
))
1422 && element
->attr
!= CMD_ATTR_DEPRECATED
1423 && element
->attr
!= CMD_ATTR_HIDDEN
)
1424 vty_out(vty
, " %s\n", element
->string
);
1429 /* Help display function for all node. */
1432 "list [permutations]",
1433 "Print command list\n"
1434 "Print all possible command permutations\n")
1436 return cmd_list_cmds(vty
, argc
== 2);
1439 DEFUN (show_commandtree
,
1440 show_commandtree_cmd
,
1441 "show commandtree [permutations]",
1443 "Show command tree\n"
1444 "Permutations that we are interested in\n")
1446 return cmd_list_cmds(vty
, argc
== 3);
1449 DEFUN_HIDDEN(show_cli_graph
,
1454 "Dump current command space as DOT graph\n")
1456 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1457 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1459 vty_out(vty
, "%s\n", dot
);
1460 XFREE(MTYPE_TMP
, dot
);
1464 static int vty_write_config(struct vty
*vty
)
1467 struct cmd_node
*node
;
1472 nb_cli_show_config_prepare(running_config
, false);
1474 if (vty
->type
== VTY_TERM
) {
1475 vty_out(vty
, "\nCurrent configuration:\n");
1476 vty_out(vty
, "!\n");
1479 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1480 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1481 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1482 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1483 vty_out(vty
, "!\n");
1485 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1486 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1487 if ((*node
->config_write
)(vty
))
1488 vty_out(vty
, "!\n");
1491 if (vty
->type
== VTY_TERM
) {
1492 vty_out(vty
, "end\n");
1498 static int file_write_config(struct vty
*vty
)
1501 char *config_file
, *slash
;
1502 char *config_file_tmp
= NULL
;
1503 char *config_file_sav
= NULL
;
1504 int ret
= CMD_WARNING
;
1505 struct vty
*file_vty
;
1506 struct stat conf_stat
;
1511 /* Check and see if we are operating under vtysh configuration */
1512 if (host
.config
== NULL
) {
1514 "Can't save to configuration file, using vtysh.\n");
1519 config_file
= host
.config
;
1522 #define O_DIRECTORY 0
1524 slash
= strrchr(config_file
, '/');
1526 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1527 config_dir
[slash
- config_file
] = '\0';
1528 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1529 XFREE(MTYPE_TMP
, config_dir
);
1531 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1532 /* if dirfd is invalid, directory sync fails, but we're still OK */
1534 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1535 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1536 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1537 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1540 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1541 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1544 /* Open file to configuration write. */
1545 fd
= mkstemp(config_file_tmp
);
1547 vty_out(vty
, "Can't open configuration file %s.\n",
1551 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1552 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1553 config_file_tmp
, safe_strerror(errno
), errno
);
1557 /* Make vty for configuration file. */
1558 file_vty
= vty_new();
1560 file_vty
->type
= VTY_FILE
;
1562 /* Config file header print. */
1563 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1564 vty_time_print(file_vty
, 1);
1565 vty_out(file_vty
, "!\n");
1566 vty_write_config(file_vty
);
1567 vty_close(file_vty
);
1569 if (stat(config_file
, &conf_stat
) >= 0) {
1570 if (unlink(config_file_sav
) != 0)
1571 if (errno
!= ENOENT
) {
1573 "Can't unlink backup configuration file %s.\n",
1577 if (link(config_file
, config_file_sav
) != 0) {
1579 "Can't backup old configuration file %s.\n",
1586 if (rename(config_file_tmp
, config_file
) != 0) {
1587 vty_out(vty
, "Can't save configuration file %s.\n",
1594 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1598 if (ret
!= CMD_SUCCESS
)
1599 unlink(config_file_tmp
);
1602 XFREE(MTYPE_TMP
, config_file_tmp
);
1603 XFREE(MTYPE_TMP
, config_file_sav
);
1607 /* Write current configuration into file. */
1609 DEFUN (config_write
,
1611 "write [<file|memory|terminal>]",
1612 "Write running configuration to memory, network, or terminal\n"
1613 "Write to configuration file\n"
1614 "Write configuration currently in memory\n"
1615 "Write configuration to terminal\n")
1617 const int idx_type
= 1;
1619 // if command was 'write terminal' or 'write memory'
1620 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1621 return vty_write_config(vty
);
1624 return file_write_config(vty
);
1627 /* ALIAS_FIXME for 'write <terminal|memory>' */
1628 DEFUN (show_running_config
,
1629 show_running_config_cmd
,
1630 "show running-config",
1632 "running configuration (same as write terminal)\n")
1634 return vty_write_config(vty
);
1637 /* ALIAS_FIXME for 'write file' */
1638 DEFUN (copy_runningconf_startupconf
,
1639 copy_runningconf_startupconf_cmd
,
1640 "copy running-config startup-config",
1641 "Copy configuration\n"
1642 "Copy running config to... \n"
1643 "Copy running config to startup config (same as write file/memory)\n")
1645 return file_write_config(vty
);
1649 /* Write startup configuration into the terminal. */
1650 DEFUN (show_startup_config
,
1651 show_startup_config_cmd
,
1652 "show startup-config",
1654 "Contents of startup configuration\n")
1661 if (host
.config
== NULL
)
1664 confp
= fopen(host
.config
, "r");
1665 if (confp
== NULL
) {
1666 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1667 host
.config
, safe_strerror(errno
));
1671 while (fgets(buf
, BUFSIZ
, confp
)) {
1674 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1678 vty_out(vty
, "%s\n", buf
);
1686 int cmd_domainname_set(const char *domainname
)
1688 XFREE(MTYPE_HOST
, host
.domainname
);
1689 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1693 /* Hostname configuration */
1694 DEFUN(config_domainname
,
1697 "Set system's domain name\n"
1698 "This system's domain name\n")
1700 struct cmd_token
*word
= argv
[1];
1702 if (!isalpha((unsigned char)word
->arg
[0])) {
1703 vty_out(vty
, "Please specify string starting with alphabet\n");
1704 return CMD_WARNING_CONFIG_FAILED
;
1707 return cmd_domainname_set(word
->arg
);
1710 DEFUN(config_no_domainname
,
1712 "no domainname [DOMAINNAME]",
1714 "Reset system's domain name\n"
1715 "domain name of this router\n")
1717 return cmd_domainname_set(NULL
);
1720 int cmd_hostname_set(const char *hostname
)
1722 XFREE(MTYPE_HOST
, host
.name
);
1723 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1727 /* Hostname configuration */
1728 DEFUN (config_hostname
,
1731 "Set system's network name\n"
1732 "This system's network name\n")
1734 struct cmd_token
*word
= argv
[1];
1736 if (!isalnum((unsigned char)word
->arg
[0])) {
1738 "Please specify string starting with alphabet or number\n");
1739 return CMD_WARNING_CONFIG_FAILED
;
1742 /* With reference to RFC 1123 Section 2.1 */
1743 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1744 vty_out(vty
, "Hostname length should be less than %d chars\n",
1746 return CMD_WARNING_CONFIG_FAILED
;
1749 return cmd_hostname_set(word
->arg
);
1752 DEFUN (config_no_hostname
,
1754 "no hostname [HOSTNAME]",
1756 "Reset system's network name\n"
1757 "Host name of this router\n")
1759 return cmd_hostname_set(NULL
);
1762 /* VTY interface password set. */
1763 DEFUN (config_password
,
1765 "password [(8-8)] WORD",
1766 "Modify the terminal connection password\n"
1767 "Specifies a HIDDEN password will follow\n"
1768 "The password string\n")
1772 if (argc
== 3) // '8' was specified
1775 XFREE(MTYPE_HOST
, host
.password
);
1776 host
.password
= NULL
;
1777 if (host
.password_encrypt
)
1778 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1779 host
.password_encrypt
=
1780 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1784 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1786 "Please specify string starting with alphanumeric\n");
1787 return CMD_WARNING_CONFIG_FAILED
;
1791 XFREE(MTYPE_HOST
, host
.password
);
1792 host
.password
= NULL
;
1795 if (host
.password_encrypt
)
1796 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1797 host
.password_encrypt
=
1798 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1800 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1805 /* VTY interface password delete. */
1806 DEFUN (no_config_password
,
1810 "Modify the terminal connection password\n")
1812 bool warned
= false;
1814 if (host
.password
) {
1815 if (!vty_shell_serv(vty
)) {
1816 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1819 XFREE(MTYPE_HOST
, host
.password
);
1821 host
.password
= NULL
;
1823 if (host
.password_encrypt
) {
1824 if (!warned
&& !vty_shell_serv(vty
))
1825 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1826 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1828 host
.password_encrypt
= NULL
;
1833 /* VTY enable password set. */
1834 DEFUN (config_enable_password
,
1835 enable_password_cmd
,
1836 "enable password [(8-8)] WORD",
1837 "Modify enable password parameters\n"
1838 "Assign the privileged level password\n"
1839 "Specifies a HIDDEN password will follow\n"
1840 "The HIDDEN 'enable' password string\n")
1845 /* Crypt type is specified. */
1847 if (argv
[idx_8
]->arg
[0] == '8') {
1849 XFREE(MTYPE_HOST
, host
.enable
);
1852 if (host
.enable_encrypt
)
1853 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1854 host
.enable_encrypt
=
1855 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1859 vty_out(vty
, "Unknown encryption type.\n");
1860 return CMD_WARNING_CONFIG_FAILED
;
1864 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1866 "Please specify string starting with alphanumeric\n");
1867 return CMD_WARNING_CONFIG_FAILED
;
1871 XFREE(MTYPE_HOST
, host
.enable
);
1874 /* Plain password input. */
1876 if (host
.enable_encrypt
)
1877 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1878 host
.enable_encrypt
=
1879 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1881 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1886 /* VTY enable password delete. */
1887 DEFUN (no_config_enable_password
,
1888 no_enable_password_cmd
,
1889 "no enable password",
1891 "Modify enable password parameters\n"
1892 "Assign the privileged level password\n")
1894 bool warned
= false;
1897 if (!vty_shell_serv(vty
)) {
1898 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1901 XFREE(MTYPE_HOST
, host
.enable
);
1905 if (host
.enable_encrypt
) {
1906 if (!warned
&& !vty_shell_serv(vty
))
1907 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1908 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1910 host
.enable_encrypt
= NULL
;
1915 DEFUN (service_password_encrypt
,
1916 service_password_encrypt_cmd
,
1917 "service password-encryption",
1918 "Set up miscellaneous service\n"
1919 "Enable encrypted passwords\n")
1926 if (host
.password
) {
1927 if (host
.password_encrypt
)
1928 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1929 host
.password_encrypt
=
1930 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
1933 if (host
.enable_encrypt
)
1934 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1935 host
.enable_encrypt
=
1936 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
1942 DEFUN (no_service_password_encrypt
,
1943 no_service_password_encrypt_cmd
,
1944 "no service password-encryption",
1946 "Set up miscellaneous service\n"
1947 "Enable encrypted passwords\n")
1954 if (host
.password_encrypt
)
1955 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1956 host
.password_encrypt
= NULL
;
1958 if (host
.enable_encrypt
)
1959 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1960 host
.enable_encrypt
= NULL
;
1965 DEFUN (config_terminal_length
,
1966 config_terminal_length_cmd
,
1967 "terminal length (0-512)",
1968 "Set terminal line parameters\n"
1969 "Set number of lines on a screen\n"
1970 "Number of lines on screen (0 for no pausing)\n")
1974 vty
->lines
= atoi(argv
[idx_number
]->arg
);
1979 DEFUN (config_terminal_no_length
,
1980 config_terminal_no_length_cmd
,
1981 "terminal no length",
1982 "Set terminal line parameters\n"
1984 "Set number of lines on a screen\n")
1990 DEFUN (service_terminal_length
,
1991 service_terminal_length_cmd
,
1992 "service terminal-length (0-512)",
1993 "Set up miscellaneous service\n"
1994 "System wide terminal length configuration\n"
1995 "Number of lines of VTY (0 means no line control)\n")
1999 host
.lines
= atoi(argv
[idx_number
]->arg
);
2004 DEFUN (no_service_terminal_length
,
2005 no_service_terminal_length_cmd
,
2006 "no service terminal-length [(0-512)]",
2008 "Set up miscellaneous service\n"
2009 "System wide terminal length configuration\n"
2010 "Number of lines of VTY (0 means no line control)\n")
2016 DEFUN_HIDDEN (do_echo
,
2019 "Echo a message back to the vty\n"
2020 "The message to echo\n")
2024 vty_out(vty
, "%s\n",
2025 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2027 XFREE(MTYPE_TMP
, message
);
2031 DEFUN (config_logmsg
,
2033 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2034 "Send a message to enabled logging destinations\n"
2036 "The message to send\n")
2038 int idx_log_level
= 1;
2039 int idx_message
= 2;
2043 level
= log_level_match(argv
[idx_log_level
]->arg
);
2044 if (level
== ZLOG_DISABLED
)
2045 return CMD_ERR_NO_MATCH
;
2048 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2050 XFREE(MTYPE_TMP
, message
);
2055 DEFUN (debug_memstats
,
2057 "[no] debug memstats-at-exit",
2060 "Print memory type statistics at exit\n")
2062 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2066 int cmd_banner_motd_file(const char *file
)
2068 int success
= CMD_SUCCESS
;
2073 rpath
= realpath(file
, p
);
2075 return CMD_ERR_NO_FILE
;
2076 in
= strstr(rpath
, SYSCONFDIR
);
2078 XFREE(MTYPE_HOST
, host
.motdfile
);
2079 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2081 success
= CMD_WARNING_CONFIG_FAILED
;
2086 void cmd_banner_motd_line(const char *line
)
2088 XFREE(MTYPE_HOST
, host
.motd
);
2089 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2092 DEFUN (banner_motd_file
,
2093 banner_motd_file_cmd
,
2094 "banner motd file FILE",
2097 "Banner from a file\n"
2101 const char *filename
= argv
[idx_file
]->arg
;
2102 int cmd
= cmd_banner_motd_file(filename
);
2104 if (cmd
== CMD_ERR_NO_FILE
)
2105 vty_out(vty
, "%s does not exist", filename
);
2106 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2107 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2112 DEFUN (banner_motd_line
,
2113 banner_motd_line_cmd
,
2114 "banner motd line LINE...",
2117 "Banner from an input\n"
2123 argv_find(argv
, argc
, "LINE", &idx
);
2124 motd
= argv_concat(argv
, argc
, idx
);
2126 cmd_banner_motd_line(motd
);
2127 XFREE(MTYPE_TMP
, motd
);
2132 DEFUN (banner_motd_default
,
2133 banner_motd_default_cmd
,
2134 "banner motd default",
2135 "Set banner string\n"
2136 "Strings for motd\n"
2139 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2143 DEFUN (no_banner_motd
,
2147 "Set banner string\n"
2148 "Strings for motd\n")
2152 XFREE(MTYPE_HOST
, host
.motdfile
);
2153 host
.motdfile
= NULL
;
2160 "Find CLI command matching a regular expression\n"
2161 "Search pattern (POSIX regex)\n")
2163 char *pattern
= argv
[1]->arg
;
2164 const struct cmd_node
*node
;
2165 const struct cmd_element
*cli
;
2170 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2175 vty_out(vty
, "%% Invalid {...} expression\n");
2178 vty_out(vty
, "%% Bad repetition operator\n");
2181 vty_out(vty
, "%% Regex syntax error\n");
2184 vty_out(vty
, "%% Invalid collating element\n");
2187 vty_out(vty
, "%% Invalid character class name\n");
2191 "%% Regex ended with escape character (\\)\n");
2195 "%% Invalid number in \\digit construction\n");
2198 vty_out(vty
, "%% Unbalanced square brackets\n");
2201 vty_out(vty
, "%% Unbalanced parentheses\n");
2204 vty_out(vty
, "%% Unbalanced braces\n");
2208 "%% Invalid endpoint in range expression\n");
2211 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2219 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2220 node
= vector_slot(cmdvec
, i
);
2223 clis
= node
->cmd_vector
;
2224 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2225 cli
= vector_slot(clis
, j
);
2227 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0)
2228 vty_out(vty
, " (%s) %s\n",
2229 node
->name
, cli
->string
);
2238 /* Set config filename. Called from vty.c */
2239 void host_config_set(const char *filename
)
2241 XFREE(MTYPE_HOST
, host
.config
);
2242 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2245 const char *host_config_get(void)
2250 void install_default(enum node_type node
)
2252 install_element(node
, &config_exit_cmd
);
2253 install_element(node
, &config_quit_cmd
);
2254 install_element(node
, &config_end_cmd
);
2255 install_element(node
, &config_help_cmd
);
2256 install_element(node
, &config_list_cmd
);
2257 install_element(node
, &show_cli_graph_cmd
);
2258 install_element(node
, &find_cmd
);
2260 install_element(node
, &config_write_cmd
);
2261 install_element(node
, &show_running_config_cmd
);
2263 install_element(node
, &autocomplete_cmd
);
2265 nb_cli_install_default(node
);
2268 /* Initialize command interface. Install basic nodes and commands.
2270 * terminal = 0 -- vtysh / no logging, no config control
2271 * terminal = 1 -- normal daemon
2272 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2273 void cmd_init(int terminal
)
2275 struct utsname names
;
2280 /* register command preprocessors */
2281 hook_register(cmd_execute
, handle_pipe_action
);
2282 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2284 varhandlers
= list_new();
2286 /* Allocate initial top vector of commands. */
2287 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2289 /* Default host value settings. */
2290 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2291 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2292 if ((strcmp(names
.domainname
, "(none)") == 0))
2293 host
.domainname
= NULL
;
2295 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2297 host
.domainname
= NULL
;
2299 host
.password
= NULL
;
2302 host
.noconfig
= (terminal
< 0);
2304 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2305 host
.motdfile
= NULL
;
2307 /* Install top nodes. */
2308 install_node(&view_node
);
2309 install_node(&enable_node
);
2310 install_node(&auth_node
);
2311 install_node(&auth_enable_node
);
2312 install_node(&config_node
);
2314 /* Each node's basic commands. */
2315 install_element(VIEW_NODE
, &show_version_cmd
);
2316 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2319 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2321 install_element(VIEW_NODE
, &config_list_cmd
);
2322 install_element(VIEW_NODE
, &config_exit_cmd
);
2323 install_element(VIEW_NODE
, &config_quit_cmd
);
2324 install_element(VIEW_NODE
, &config_help_cmd
);
2325 install_element(VIEW_NODE
, &config_enable_cmd
);
2326 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2327 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2328 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2329 install_element(VIEW_NODE
, &echo_cmd
);
2330 install_element(VIEW_NODE
, &autocomplete_cmd
);
2331 install_element(VIEW_NODE
, &find_cmd
);
2333 install_element(ENABLE_NODE
, &config_end_cmd
);
2334 install_element(ENABLE_NODE
, &config_disable_cmd
);
2335 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2336 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2337 install_element(ENABLE_NODE
, &config_write_cmd
);
2338 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2339 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2341 install_default(CONFIG_NODE
);
2344 workqueue_cmd_init();
2348 install_element(CONFIG_NODE
, &hostname_cmd
);
2349 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2350 install_element(CONFIG_NODE
, &domainname_cmd
);
2351 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2356 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2358 install_element(CONFIG_NODE
, &password_cmd
);
2359 install_element(CONFIG_NODE
, &no_password_cmd
);
2360 install_element(CONFIG_NODE
, &enable_password_cmd
);
2361 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2363 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2364 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2365 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2366 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2367 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2368 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2369 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2370 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2373 vrf_install_commands();
2377 grammar_sandbox_init();
2381 void cmd_terminate(void)
2383 struct cmd_node
*cmd_node
;
2385 hook_unregister(cmd_execute
, handle_pipe_action
);
2386 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2389 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2390 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2391 // deleting the graph delets the cmd_element as
2393 graph_delete_graph(cmd_node
->cmdgraph
);
2394 vector_free(cmd_node
->cmd_vector
);
2395 hash_clean(cmd_node
->cmd_hash
, NULL
);
2396 hash_free(cmd_node
->cmd_hash
);
2397 cmd_node
->cmd_hash
= NULL
;
2400 vector_free(cmdvec
);
2404 XFREE(MTYPE_HOST
, host
.name
);
2405 XFREE(MTYPE_HOST
, host
.domainname
);
2406 XFREE(MTYPE_HOST
, host
.password
);
2407 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2408 XFREE(MTYPE_HOST
, host
.enable
);
2409 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2410 XFREE(MTYPE_HOST
, host
.motdfile
);
2411 XFREE(MTYPE_HOST
, host
.config
);
2412 XFREE(MTYPE_HOST
, host
.motd
);
2414 list_delete(&varhandlers
);