2 * CLI backend interface.
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
10 * This file is part of GNU Zebra.
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
48 #include "lib_errors.h"
49 #include "northbound_cli.h"
52 #include "frrscript.h"
54 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config");
55 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item");
62 /* clang-format off */
63 const struct message tokennames
[] = {
68 item(IPV4_PREFIX_TKN
),
70 item(IPV6_PREFIX_TKN
),
81 /* Command vector which includes some level of command lists. Normally
82 each daemon maintains each own cmdvec. */
85 /* Host information structure. */
89 * Returns host.name if any, otherwise
90 * it returns the system hostname.
92 const char *cmd_hostname_get(void)
98 * Returns unix domainname
100 const char *cmd_domainname_get(void)
102 return host
.domainname
;
105 static int root_on_exit(struct vty
*vty
);
107 /* Standard command node structures. */
108 static struct cmd_node auth_node
= {
111 .prompt
= "Password: ",
114 static struct cmd_node view_node
= {
118 .node_exit
= root_on_exit
,
121 static struct cmd_node auth_enable_node
= {
122 .name
= "auth enable",
123 .node
= AUTH_ENABLE_NODE
,
124 .prompt
= "Password: ",
127 static struct cmd_node enable_node
= {
131 .node_exit
= root_on_exit
,
134 static int config_write_host(struct vty
*vty
);
135 static struct cmd_node config_node
= {
138 .parent_node
= ENABLE_NODE
,
139 .prompt
= "%s(config)# ",
140 .config_write
= config_write_host
,
141 .node_exit
= vty_config_node_exit
,
144 static bool vty_check_node_for_xpath_decrement(enum node_type target_node
,
147 /* bgp afi-safi (`address-family <afi> <safi>`) node
148 * does not increment xpath_index.
149 * In order to use (`router bgp`) BGP_NODE's xpath as a base,
150 * retain xpath_index as 1 upon exiting from
154 if (target_node
== BGP_NODE
155 && (node
== BGP_IPV4_NODE
|| node
== BGP_IPV6_NODE
156 || node
== BGP_IPV4M_NODE
|| node
== BGP_IPV6M_NODE
157 || node
== BGP_VPNV4_NODE
|| node
== BGP_VPNV6_NODE
158 || node
== BGP_EVPN_NODE
|| node
== BGP_IPV4L_NODE
159 || node
== BGP_IPV6L_NODE
|| node
== BGP_FLOWSPECV4_NODE
160 || node
== BGP_FLOWSPECV6_NODE
))
166 /* This is called from main when a daemon is invoked with -v or --version. */
167 void print_version(const char *progname
)
169 printf("%s version %s\n", progname
, FRR_VERSION
);
170 printf("%s\n", FRR_COPYRIGHT
);
171 #ifdef ENABLE_VERSION_BUILD_CONFIG
172 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
176 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
178 int cnt
= MAX(argc
- shift
, 0);
179 const char *argstr
[cnt
+ 1];
184 for (int i
= 0; i
< cnt
; i
++)
185 argstr
[i
] = argv
[i
+ shift
]->arg
;
187 return frrstr_join(argstr
, cnt
, " ");
190 vector
cmd_make_strvec(const char *string
)
195 const char *copy
= string
;
197 /* skip leading whitespace */
198 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
201 /* if the entire string was whitespace or a comment, return */
202 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
205 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
207 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
208 if (strlen(vector_slot(result
, i
)) == 0) {
209 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
210 vector_unset(result
, i
);
214 vector_compact(result
);
219 void cmd_free_strvec(vector v
)
221 frrstr_strvec_free(v
);
225 * Convenience function for accessing argv data.
229 * @param text definition snippet of the desired token
230 * @param index the starting index, and where to store the
231 * index of the found token if it exists
232 * @return 1 if found, 0 otherwise
234 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
237 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
238 if ((found
= strmatch(text
, argv
[i
]->text
)))
243 static unsigned int cmd_hash_key(const void *p
)
245 int size
= sizeof(p
);
247 return jhash(p
, size
, 0);
250 static bool cmd_hash_cmp(const void *a
, const void *b
)
255 /* Install top node of command vector. */
256 void install_node(struct cmd_node
*node
)
258 vector_set_index(cmdvec
, node
->node
, node
);
259 node
->cmdgraph
= graph_new();
260 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
262 struct cmd_token
*token
=
263 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
264 graph_new_node(node
->cmdgraph
, token
,
265 (void (*)(void *)) & cmd_token_del
);
266 node
->cmd_hash
= hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
,
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 /* Install a command into a node. */
280 void _install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
282 struct cmd_node
*cnode
;
284 /* cmd_init hasn't been called */
286 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
291 cnode
= vector_lookup(cmdvec
, ntype
);
296 "\tnode %d does not exist.\n"
297 "\tplease call install_node() before install_element()\n",
298 cmd
->name
, cmd
->string
, ntype
);
302 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
305 "\tnode %d (%s) already has this command installed.\n"
306 "\tduplicate install_element call?\n",
307 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
311 assert(hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
));
313 struct graph
*graph
= graph_new();
314 struct cmd_token
*token
=
315 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
316 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
318 cmd_graph_parse(graph
, cmd
);
319 cmd_graph_names(graph
);
320 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
321 graph_delete_graph(graph
);
323 vector_set(cnode
->cmd_vector
, (void *)cmd
);
325 if (ntype
== VIEW_NODE
)
326 _install_element(ENABLE_NODE
, cmd
);
329 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
331 struct cmd_node
*cnode
;
333 /* cmd_init hasn't been called */
335 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
340 cnode
= vector_lookup(cmdvec
, ntype
);
345 "\tnode %d does not exist.\n"
346 "\tplease call install_node() before uninstall_element()\n",
347 cmd
->name
, cmd
->string
, ntype
);
351 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
354 "\tnode %d (%s) does not have this command installed.\n"
355 "\tduplicate uninstall_element call?\n",
356 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
360 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
362 struct graph
*graph
= graph_new();
363 struct cmd_token
*token
=
364 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
365 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
367 cmd_graph_parse(graph
, cmd
);
368 cmd_graph_names(graph
);
369 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
370 graph_delete_graph(graph
);
372 if (ntype
== VIEW_NODE
)
373 uninstall_element(ENABLE_NODE
, cmd
);
377 static const unsigned char itoa64
[] =
378 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
380 static void to64(char *s
, long v
, int n
)
383 *s
++ = itoa64
[v
& 0x3f];
388 static char *zencrypt(const char *passwd
)
392 char *crypt(const char *, const char *);
394 gettimeofday(&tv
, 0);
396 to64(&salt
[0], frr_weak_random(), 3);
397 to64(&salt
[3], tv
.tv_usec
, 3);
400 return crypt(passwd
, salt
);
403 static bool full_cli
;
405 /* This function write configuration of this host. */
406 static int config_write_host(struct vty
*vty
)
408 if (cmd_hostname_get())
409 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
411 if (cmd_domainname_get())
412 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
414 /* The following are all configuration commands that are not sent to
415 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
416 * we would always display 'log syslog informational' in the config
417 * which would cause other daemons to then switch to syslog when they
422 if (host
.password_encrypt
)
423 vty_out(vty
, "password 8 %s\n",
424 host
.password_encrypt
);
425 if (host
.enable_encrypt
)
426 vty_out(vty
, "enable password 8 %s\n",
427 host
.enable_encrypt
);
430 vty_out(vty
, "password %s\n", host
.password
);
432 vty_out(vty
, "enable password %s\n",
435 log_config_write(vty
);
438 vty_out(vty
, "service advanced-vty\n");
441 vty_out(vty
, "service password-encryption\n");
444 vty_out(vty
, "service terminal-length %d\n",
448 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
450 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
452 vty_out(vty
, "banner motd line %s\n", host
.motd
);
454 vty_out(vty
, "no banner motd\n");
457 if (debug_memstats_at_exit
)
458 vty_out(vty
, "!\ndebug memstats-at-exit\n");
463 /* Utility function for getting command graph. */
464 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
466 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
467 return cnode
->cmdgraph
;
470 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
472 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
473 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
479 * Compare function for cmd_token.
480 * Used with qsort to sort command completions.
482 static int compare_completions(const void *fst
, const void *snd
)
484 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
485 *secnd
= *(const struct cmd_token
* const *)snd
;
486 return strcmp(first
->text
, secnd
->text
);
490 * Takes a list of completions returned by command_complete,
491 * dedeuplicates them based on both text and description,
492 * sorts them, and returns them as a vector.
494 * @param completions linked list of cmd_token
495 * @return deduplicated and sorted vector with
497 vector
completions_to_vec(struct list
*completions
)
499 vector comps
= vector_init(VECTOR_MIN_SIZE
);
502 struct cmd_token
*token
, *cr
= NULL
;
503 unsigned int i
, exists
;
504 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
505 if (token
->type
== END_TKN
&& (cr
= token
))
508 // linear search for token in completions vector
510 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
511 struct cmd_token
*curr
= vector_slot(comps
, i
);
513 exists
= !strcmp(curr
->text
, token
->text
)
514 && !strcmp(curr
->desc
, token
->desc
);
516 exists
= !strcmp(curr
->text
, token
->text
);
517 #endif /* VTYSH_DEBUG */
521 vector_set(comps
, token
);
525 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
526 &compare_completions
);
528 // make <cr> the first element, if it is present
530 vector_set_index(comps
, vector_active(comps
), NULL
);
531 memmove(comps
->index
+ 1, comps
->index
,
532 (comps
->alloced
- 1) * sizeof(void *));
533 vector_set_index(comps
, 0, cr
);
539 * Generates a vector of cmd_token representing possible completions
540 * on the current input.
542 * @param vline the vectorized input line
543 * @param vty the vty with the node to match on
544 * @param status pointer to matcher status code
545 * @return vector of struct cmd_token * with possible completions
547 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
550 struct list
*completions
;
551 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
553 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
555 if (MATCHER_ERROR(rv
)) {
556 *status
= CMD_ERR_NO_MATCH
;
560 vector comps
= completions_to_vec(completions
);
561 list_delete(&completions
);
563 // set status code appropriately
564 switch (vector_active(comps
)) {
566 *status
= CMD_ERR_NO_MATCH
;
569 *status
= CMD_COMPLETE_FULL_MATCH
;
572 *status
= CMD_COMPLETE_LIST_MATCH
;
578 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
582 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
583 enum node_type onode
;
584 int orig_xpath_index
;
585 vector shifted_vline
;
589 orig_xpath_index
= vty
->xpath_index
;
590 vty
->node
= ENABLE_NODE
;
591 vty
->xpath_index
= 0;
592 /* We can try it on enable node, cos' the vty is authenticated
595 shifted_vline
= vector_init(vector_count(vline
));
597 for (index
= 1; index
< vector_active(vline
); index
++) {
598 vector_set_index(shifted_vline
, index
- 1,
599 vector_lookup(vline
, index
));
602 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
604 vector_free(shifted_vline
);
606 vty
->xpath_index
= orig_xpath_index
;
610 return cmd_complete_command_real(vline
, vty
, status
);
613 static struct list
*varhandlers
= NULL
;
615 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
619 const struct cmd_variable_handler
*cvh
;
623 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
625 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
626 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
628 if (cvh
->varname
&& (!token
->varname
629 || strcmp(cvh
->varname
, token
->varname
)))
631 cvh
->completions(tmpcomps
, token
);
639 for (i
= vector_active(tmpcomps
); i
; i
--) {
640 char *item
= vector_slot(tmpcomps
, i
- 1);
641 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
642 vector_set(comps
, item
);
644 XFREE(MTYPE_COMPLETION
, item
);
646 vector_free(tmpcomps
);
649 #define AUTOCOMP_INDENT 5
651 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
654 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
655 int lc
= AUTOCOMP_INDENT
;
656 size_t cs
= AUTOCOMP_INDENT
;
658 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
659 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
660 char *item
= vector_slot(comps
, j
);
661 itemlen
= strlen(item
);
663 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
664 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
666 if (lc
+ itemlen
+ 1 >= cols
) {
667 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
668 AUTOCOMP_INDENT
, "");
669 lc
= AUTOCOMP_INDENT
;
672 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
675 XFREE(MTYPE_COMPLETION
, item
);
676 vector_set_index(comps
, j
, NULL
);
681 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
686 for (; cvh
->completions
; cvh
++)
687 listnode_add(varhandlers
, (void *)cvh
);
690 DEFUN_HIDDEN (autocomplete
,
692 "autocomplete TYPE TEXT VARNAME",
693 "Autocompletion handler (internal, for vtysh)\n"
696 "cmd_token->varname\n")
698 struct cmd_token tok
;
699 vector comps
= vector_init(32);
702 memset(&tok
, 0, sizeof(tok
));
703 tok
.type
= atoi(argv
[1]->arg
);
704 tok
.text
= argv
[2]->arg
;
705 tok
.varname
= argv
[3]->arg
;
706 if (!strcmp(tok
.varname
, "-"))
709 cmd_variable_complete(&tok
, NULL
, comps
);
711 for (i
= 0; i
< vector_active(comps
); i
++) {
712 char *text
= vector_slot(comps
, i
);
713 vty_out(vty
, "%s\n", text
);
714 XFREE(MTYPE_COMPLETION
, text
);
722 * Generate possible tab-completions for the given input. This function only
723 * returns results that would result in a valid command if used as Readline
724 * completions (as is the case in vtysh). For instance, if the passed vline ends
725 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
727 * @param vline vectorized input line
729 * @param status location to store matcher status code in
730 * @return set of valid strings for use with Readline as tab-completions.
733 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
736 int original_node
= vty
->node
;
737 vector input_line
= vector_init(vector_count(vline
));
739 // if the first token is 'do' we'll want to execute the command in the
741 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
742 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
744 // construct the input line we'll be matching on
745 unsigned int offset
= (do_shortcut
) ? 1 : 0;
746 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
747 vector_set_index(input_line
, index
,
748 vector_lookup(vline
, index
+ offset
));
750 // get token completions -- this is a copying operation
751 vector comps
= NULL
, initial_comps
;
752 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
754 if (!MATCHER_ERROR(*status
)) {
755 assert(initial_comps
);
756 // filter out everything that is not suitable for a
758 comps
= vector_init(VECTOR_MIN_SIZE
);
759 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
761 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
762 if (token
->type
== WORD_TKN
)
763 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
765 else if (IS_VARYING_TOKEN(token
->type
)) {
766 const char *ref
= vector_lookup(
767 vline
, vector_active(vline
) - 1);
768 cmd_variable_complete(token
, ref
, comps
);
771 vector_free(initial_comps
);
773 // since we filtered results, we need to re-set status code
774 switch (vector_active(comps
)) {
776 *status
= CMD_ERR_NO_MATCH
;
779 *status
= CMD_COMPLETE_FULL_MATCH
;
782 *status
= CMD_COMPLETE_LIST_MATCH
;
785 // copy completions text into an array of char*
786 ret
= XMALLOC(MTYPE_TMP
,
787 (vector_active(comps
) + 1) * sizeof(char *));
789 for (i
= 0; i
< vector_active(comps
); i
++) {
790 ret
[i
] = vector_slot(comps
, i
);
792 // set the last element to NULL, because this array is used in
793 // a Readline completion_generator function which expects NULL
794 // as a sentinel value
798 } else if (initial_comps
)
799 vector_free(initial_comps
);
801 // comps should always be null here
804 // free the adjusted input line
805 vector_free(input_line
);
807 // reset vty->node to its original value
808 vty
->node
= original_node
;
813 /* return parent node */
814 /* MUST eventually converge on CONFIG_NODE */
815 enum node_type
node_parent(enum node_type node
)
819 assert(node
> CONFIG_NODE
);
824 case BGP_FLOWSPECV4_NODE
:
825 case BGP_FLOWSPECV6_NODE
:
826 case BGP_VRF_POLICY_NODE
:
827 case BGP_VNC_DEFAULTS_NODE
:
828 case BGP_VNC_NVE_GROUP_NODE
:
829 case BGP_VNC_L2_GROUP_NODE
:
840 case BGP_EVPN_VNI_NODE
:
843 case KEYCHAIN_KEY_NODE
:
846 case LINK_PARAMS_NODE
:
847 ret
= INTERFACE_NODE
;
853 case LDP_IPV4_IFACE_NODE
:
856 case LDP_IPV6_IFACE_NODE
:
859 case LDP_PSEUDOWIRE_NODE
:
860 ret
= LDP_L2VPN_NODE
;
865 case BFD_PROFILE_NODE
:
868 case SR_TRAFFIC_ENG_NODE
:
869 ret
= SEGMENT_ROUTING_NODE
;
871 case SR_SEGMENT_LIST_NODE
:
872 ret
= SR_TRAFFIC_ENG_NODE
;
875 ret
= SR_TRAFFIC_ENG_NODE
;
877 case SR_CANDIDATE_DYN_NODE
:
878 ret
= SR_POLICY_NODE
;
881 ret
= SR_TRAFFIC_ENG_NODE
;
883 case PCEP_PCE_CONFIG_NODE
:
900 /* Execute command by argument vline vector. */
901 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
903 const struct cmd_element
**cmd
,
904 unsigned int up_level
)
906 struct list
*argv_list
;
907 enum matcher_rv status
;
908 const struct cmd_element
*matched_element
= NULL
;
910 int xpath_index
= vty
->xpath_index
;
911 int node
= vty
->node
;
913 /* only happens for legacy split config file load; need to check for
914 * a match before calling node_exit handlers below
916 for (i
= 0; i
< up_level
; i
++) {
917 if (node
<= CONFIG_NODE
)
918 return CMD_NO_LEVEL_UP
;
920 node
= node_parent(node
);
923 && vty_check_node_for_xpath_decrement(node
, vty
->node
))
927 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
928 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
931 *cmd
= matched_element
;
933 // if matcher error, return corresponding CMD_ERR
934 if (MATCHER_ERROR(status
)) {
936 list_delete(&argv_list
);
938 case MATCHER_INCOMPLETE
:
939 return CMD_ERR_INCOMPLETE
;
940 case MATCHER_AMBIGUOUS
:
941 return CMD_ERR_AMBIGUOUS
;
943 return CMD_ERR_NO_MATCH
;
947 for (i
= 0; i
< up_level
; i
++)
950 // build argv array from argv list
951 struct cmd_token
**argv
= XMALLOC(
952 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
954 struct cmd_token
*token
;
957 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
960 int argc
= argv_list
->count
;
963 if (matched_element
->daemon
)
964 ret
= CMD_SUCCESS_DAEMON
;
967 /* Clear array of enqueued configuration changes. */
968 vty
->num_cfg_changes
= 0;
969 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
971 /* Regenerate candidate configuration if necessary. */
972 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
973 && running_config
->version
974 > vty
->candidate_config
->version
)
975 nb_config_replace(vty
->candidate_config
,
976 running_config
, true);
979 * Perform pending commit (if any) before executing
982 if (matched_element
->attr
!= CMD_ATTR_YANG
)
983 nb_cli_pending_commit_check(vty
);
986 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
989 // delete list and cmd_token's in it
990 list_delete(&argv_list
);
991 XFREE(MTYPE_TMP
, argv
);
997 * Execute a given command, handling things like "do ..." and checking
998 * whether the given command might apply at a parent node if doesn't
999 * apply for the current node.
1001 * @param vline Command line input, vector of char* where each element is
1003 * @param vty The vty context in which the command should be executed.
1004 * @param cmd Pointer where the struct cmd_element of the matched command
1005 * will be stored, if any. May be set to NULL if this info is
1007 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1008 * @return The status of the command that has been executed or an error code
1009 * as to why no command could be executed.
1011 int cmd_execute_command(vector vline
, struct vty
*vty
,
1012 const struct cmd_element
**cmd
, int vtysh
)
1014 int ret
, saved_ret
= 0;
1015 enum node_type onode
, try_node
;
1016 int orig_xpath_index
;
1018 onode
= try_node
= vty
->node
;
1019 orig_xpath_index
= vty
->xpath_index
;
1021 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1022 vector shifted_vline
;
1025 vty
->node
= ENABLE_NODE
;
1026 vty
->xpath_index
= 0;
1027 /* We can try it on enable node, cos' the vty is authenticated
1030 shifted_vline
= vector_init(vector_count(vline
));
1032 for (index
= 1; index
< vector_active(vline
); index
++)
1033 vector_set_index(shifted_vline
, index
- 1,
1034 vector_lookup(vline
, index
));
1036 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1039 vector_free(shifted_vline
);
1041 vty
->xpath_index
= orig_xpath_index
;
1046 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1051 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1052 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1053 /* This assumes all nodes above CONFIG_NODE are childs of
1055 while (vty
->node
> CONFIG_NODE
) {
1056 try_node
= node_parent(try_node
);
1057 vty
->node
= try_node
;
1058 if (vty
->xpath_index
> 0
1059 && vty_check_node_for_xpath_decrement(try_node
,
1062 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1064 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1065 || ret
== CMD_NOT_MY_INSTANCE
1066 || ret
== CMD_WARNING_CONFIG_FAILED
)
1069 /* no command succeeded, reset the vty to the original node */
1071 vty
->xpath_index
= orig_xpath_index
;
1074 /* return command status for original node */
1079 * Execute a given command, matching it strictly against the current node.
1080 * This mode is used when reading config files.
1082 * @param vline Command line input, vector of char* where each element is
1084 * @param vty The vty context in which the command should be executed.
1085 * @param cmd Pointer where the struct cmd_element* of the matched command
1086 * will be stored, if any. May be set to NULL if this info is
1088 * @return The status of the command that has been executed or an error code
1089 * as to why no command could be executed.
1091 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1092 const struct cmd_element
**cmd
)
1094 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1098 * Hook for preprocessing command string before executing.
1100 * All subscribers are called with the raw command string that is to be
1101 * executed. If any changes are to be made, a new string should be allocated
1102 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1103 * is then responsible for freeing this string.
1105 * All processing functions must be mutually exclusive in their action, i.e. if
1106 * one subscriber decides to modify the command, all others must not modify it
1107 * when called. Feeding the output of one processing command into a subsequent
1108 * one is not supported.
1110 * This hook is intentionally internal to the command processing system.
1113 * The raw command string.
1116 * The result of any processing.
1118 DECLARE_HOOK(cmd_execute
,
1119 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1120 (vty
, cmd_in
, cmd_out
));
1121 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1122 (vty
, cmd_in
, cmd_out
));
1124 /* Hook executed after a CLI command. */
1125 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1127 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1131 * cmd_execute hook subscriber to handle `|` actions.
1133 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1137 char *orig
, *working
, *token
, *u
;
1138 char *pipe
= strstr(cmd_in
, "| ");
1144 /* duplicate string for processing purposes, not including pipe */
1145 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1147 /* retrieve action */
1148 token
= strsep(&working
, " ");
1151 /* match result to known actions */
1152 if (strmatch(token
, "include")) {
1153 /* the remaining text should be a regexp */
1154 char *regexp
= working
;
1157 vty_out(vty
, "%% Need a regexp to filter with\n");
1162 bool succ
= vty_set_include(vty
, regexp
);
1165 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1169 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1173 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1179 XFREE(MTYPE_TMP
, orig
);
1183 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1186 vty_set_include(vty
, NULL
);
1191 int cmd_execute(struct vty
*vty
, const char *cmd
,
1192 const struct cmd_element
**matched
, int vtysh
)
1195 char *cmd_out
= NULL
;
1196 const char *cmd_exec
= NULL
;
1199 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1205 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1207 vline
= cmd_make_strvec(cmd_exec
);
1210 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1211 cmd_free_strvec(vline
);
1217 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1219 XFREE(MTYPE_TMP
, cmd_out
);
1226 * Parse one line of config, walking up the parse tree attempting to find a
1229 * @param vty The vty context in which the command should be executed.
1230 * @param cmd Pointer where the struct cmd_element* of the match command
1231 * will be stored, if any. May be set to NULL if this info is
1233 * @param use_daemon Boolean to control whether or not we match on
1234 * CMD_SUCCESS_DAEMON
1236 * @return The status of the command that has been executed or an error code
1237 * as to why no command could be executed.
1239 int command_config_read_one_line(struct vty
*vty
,
1240 const struct cmd_element
**cmd
,
1241 uint32_t line_num
, int use_daemon
)
1245 unsigned up_level
= 0;
1247 vline
= cmd_make_strvec(vty
->buf
);
1249 /* In case of comment line */
1253 /* Execute configuration command : this is strict match */
1254 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1256 /* The logic for trying parent nodes is in cmd_execute_command_real()
1257 * since calling ->node_exit() correctly is a bit involved. This is
1258 * also the only reason CMD_NO_LEVEL_UP exists.
1260 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1261 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1262 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1263 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1264 && ret
!= CMD_NO_LEVEL_UP
)
1265 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1268 if (ret
== CMD_NO_LEVEL_UP
)
1269 ret
= CMD_ERR_NO_MATCH
;
1271 if (ret
!= CMD_SUCCESS
&&
1272 ret
!= CMD_WARNING
&&
1273 ret
!= CMD_SUCCESS_DAEMON
) {
1274 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1276 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1277 ve
->line_num
= line_num
;
1279 vty
->error
= list_new();
1281 listnode_add(vty
->error
, ve
);
1284 cmd_free_strvec(vline
);
1289 /* Configuration make from file. */
1290 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1292 int ret
, error_ret
= 0;
1295 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1298 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1300 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1301 && ret
!= CMD_ERR_NOTHING_TODO
)
1312 /* Configuration from terminal */
1313 DEFUN (config_terminal
,
1314 config_terminal_cmd
,
1315 "configure [terminal]",
1316 "Configuration from vty interface\n"
1317 "Configuration terminal\n")
1319 return vty_config_enter(vty
, false, false);
1322 /* Enable command */
1326 "Turn on privileged mode command\n")
1328 /* If enable password is NULL, change to ENABLE_NODE */
1329 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1330 || vty
->type
== VTY_SHELL_SERV
)
1331 vty
->node
= ENABLE_NODE
;
1333 vty
->node
= AUTH_ENABLE_NODE
;
1338 /* Disable command */
1342 "Turn off privileged mode command\n")
1344 if (vty
->node
== ENABLE_NODE
)
1345 vty
->node
= VIEW_NODE
;
1349 /* Down vty node level. */
1353 "Exit current mode and down to previous mode\n")
1359 static int root_on_exit(struct vty
*vty
)
1364 vty
->status
= VTY_CLOSE
;
1368 void cmd_exit(struct vty
*vty
)
1370 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1372 if (cnode
->node_exit
) {
1373 if (!cnode
->node_exit(vty
))
1376 if (cnode
->parent_node
)
1377 vty
->node
= cnode
->parent_node
;
1378 if (vty
->xpath_index
> 0
1379 && vty_check_node_for_xpath_decrement(vty
->node
, cnode
->node
))
1387 "Exit current mode and down to previous mode\n")
1389 return config_exit(self
, vty
, argc
, argv
);
1393 /* End of configuration. */
1397 "End current mode and change to enable mode.\n")
1400 vty_config_exit(vty
);
1401 vty
->node
= ENABLE_NODE
;
1407 DEFUN (show_version
,
1411 "Displays zebra version\n")
1413 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1414 cmd_hostname_get() ? cmd_hostname_get() : "");
1415 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1416 #ifdef ENABLE_VERSION_BUILD_CONFIG
1417 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1422 /* Help display function for all node. */
1426 "Description of the interactive help system\n")
1429 "Quagga VTY provides advanced help feature. When you need help,\n\
1430 anytime at the command line please press '?'.\n\
1432 If nothing matches, the help list will be empty and you must backup\n\
1433 until entering a '?' shows the available options.\n\
1434 Two styles of help are provided:\n\
1435 1. Full help is available when you are ready to enter a\n\
1436 command argument (e.g. 'show ?') and describes each possible\n\
1438 2. Partial help is provided when an abbreviated argument is entered\n\
1439 and you want to know what arguments match the input\n\
1440 (e.g. 'show me?'.)\n\n");
1444 static void permute(struct graph_node
*start
, struct vty
*vty
)
1446 static struct list
*position
= NULL
;
1448 position
= list_new();
1450 struct cmd_token
*stok
= start
->data
;
1451 struct graph_node
*gnn
;
1452 struct listnode
*ln
;
1455 listnode_add(position
, start
);
1456 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1457 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1458 struct cmd_token
*tok
= gn
->data
;
1459 if (tok
->attr
== CMD_ATTR_HIDDEN
1460 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1462 else if (tok
->type
== END_TKN
|| gn
== start
) {
1464 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1465 struct cmd_token
*tt
= gnn
->data
;
1466 if (tt
->type
< SPECIAL_TKN
)
1467 vty_out(vty
, " %s", tt
->text
);
1470 vty_out(vty
, "...");
1474 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1475 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1484 list_delete_node(position
, listtail(position
));
1487 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1489 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1492 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1494 /* loop over all commands at this node */
1495 const struct cmd_element
*element
= NULL
;
1496 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1498 if ((element
= vector_slot(node
->cmd_vector
, i
))
1499 && element
->attr
!= CMD_ATTR_DEPRECATED
1500 && element
->attr
!= CMD_ATTR_HIDDEN
)
1501 vty_out(vty
, " %s\n", element
->string
);
1506 /* Help display function for all node. */
1509 "list [permutations]",
1510 "Print command list\n"
1511 "Print all possible command permutations\n")
1513 return cmd_list_cmds(vty
, argc
== 2);
1516 DEFUN (show_commandtree
,
1517 show_commandtree_cmd
,
1518 "show commandtree [permutations]",
1520 "Show command tree\n"
1521 "Permutations that we are interested in\n")
1523 return cmd_list_cmds(vty
, argc
== 3);
1526 DEFUN_HIDDEN(show_cli_graph
,
1531 "Dump current command space as DOT graph\n")
1533 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1534 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1536 vty_out(vty
, "%s\n", dot
);
1537 XFREE(MTYPE_TMP
, dot
);
1541 static int vty_write_config(struct vty
*vty
)
1544 struct cmd_node
*node
;
1549 nb_cli_show_config_prepare(running_config
, false);
1551 if (vty
->type
== VTY_TERM
) {
1552 vty_out(vty
, "\nCurrent configuration:\n");
1553 vty_out(vty
, "!\n");
1556 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1557 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1558 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1559 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1560 vty_out(vty
, "!\n");
1562 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1563 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1564 if ((*node
->config_write
)(vty
))
1565 vty_out(vty
, "!\n");
1568 if (vty
->type
== VTY_TERM
) {
1569 vty_out(vty
, "end\n");
1575 static int file_write_config(struct vty
*vty
)
1578 char *config_file
, *slash
;
1579 char *config_file_tmp
= NULL
;
1580 char *config_file_sav
= NULL
;
1581 int ret
= CMD_WARNING
;
1582 struct vty
*file_vty
;
1583 struct stat conf_stat
;
1588 /* Check and see if we are operating under vtysh configuration */
1589 if (host
.config
== NULL
) {
1591 "Can't save to configuration file, using vtysh.\n");
1596 config_file
= host
.config
;
1599 #define O_DIRECTORY 0
1601 slash
= strrchr(config_file
, '/');
1603 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1604 config_dir
[slash
- config_file
] = '\0';
1605 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1606 XFREE(MTYPE_TMP
, config_dir
);
1608 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1609 /* if dirfd is invalid, directory sync fails, but we're still OK */
1611 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1612 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1613 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1614 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1617 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1618 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1621 /* Open file to configuration write. */
1622 fd
= mkstemp(config_file_tmp
);
1624 vty_out(vty
, "Can't open configuration file %s.\n",
1628 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1629 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1630 config_file_tmp
, safe_strerror(errno
), errno
);
1634 /* Make vty for configuration file. */
1635 file_vty
= vty_new();
1637 file_vty
->type
= VTY_FILE
;
1639 /* Config file header print. */
1640 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1641 vty_time_print(file_vty
, 1);
1642 vty_out(file_vty
, "!\n");
1643 vty_write_config(file_vty
);
1644 vty_close(file_vty
);
1646 if (stat(config_file
, &conf_stat
) >= 0) {
1647 if (unlink(config_file_sav
) != 0)
1648 if (errno
!= ENOENT
) {
1650 "Can't unlink backup configuration file %s.\n",
1654 if (link(config_file
, config_file_sav
) != 0) {
1656 "Can't backup old configuration file %s.\n",
1663 if (rename(config_file_tmp
, config_file
) != 0) {
1664 vty_out(vty
, "Can't save configuration file %s.\n",
1671 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1675 if (ret
!= CMD_SUCCESS
)
1676 unlink(config_file_tmp
);
1679 XFREE(MTYPE_TMP
, config_file_tmp
);
1680 XFREE(MTYPE_TMP
, config_file_sav
);
1684 /* Write current configuration into file. */
1686 DEFUN (config_write
,
1688 "write [<file|memory|terminal>]",
1689 "Write running configuration to memory, network, or terminal\n"
1690 "Write to configuration file\n"
1691 "Write configuration currently in memory\n"
1692 "Write configuration to terminal\n")
1694 const int idx_type
= 1;
1696 // if command was 'write terminal' or 'write memory'
1697 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1698 return vty_write_config(vty
);
1701 return file_write_config(vty
);
1704 /* ALIAS_FIXME for 'write <terminal|memory>' */
1705 DEFUN (show_running_config
,
1706 show_running_config_cmd
,
1707 "show running-config",
1709 "running configuration (same as write terminal)\n")
1711 return vty_write_config(vty
);
1714 /* ALIAS_FIXME for 'write file' */
1715 DEFUN (copy_runningconf_startupconf
,
1716 copy_runningconf_startupconf_cmd
,
1717 "copy running-config startup-config",
1718 "Copy configuration\n"
1719 "Copy running config to... \n"
1720 "Copy running config to startup config (same as write file/memory)\n")
1722 return file_write_config(vty
);
1726 /* Write startup configuration into the terminal. */
1727 DEFUN (show_startup_config
,
1728 show_startup_config_cmd
,
1729 "show startup-config",
1731 "Contents of startup configuration\n")
1738 if (host
.config
== NULL
)
1741 confp
= fopen(host
.config
, "r");
1742 if (confp
== NULL
) {
1743 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1744 host
.config
, safe_strerror(errno
));
1748 while (fgets(buf
, BUFSIZ
, confp
)) {
1751 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1755 vty_out(vty
, "%s\n", buf
);
1763 int cmd_domainname_set(const char *domainname
)
1765 XFREE(MTYPE_HOST
, host
.domainname
);
1766 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1770 /* Hostname configuration */
1771 DEFUN(config_domainname
,
1774 "Set system's domain name\n"
1775 "This system's domain name\n")
1777 struct cmd_token
*word
= argv
[1];
1779 if (!isalpha((unsigned char)word
->arg
[0])) {
1780 vty_out(vty
, "Please specify string starting with alphabet\n");
1781 return CMD_WARNING_CONFIG_FAILED
;
1784 return cmd_domainname_set(word
->arg
);
1787 DEFUN(config_no_domainname
,
1789 "no domainname [DOMAINNAME]",
1791 "Reset system's domain name\n"
1792 "domain name of this router\n")
1794 return cmd_domainname_set(NULL
);
1797 int cmd_hostname_set(const char *hostname
)
1799 XFREE(MTYPE_HOST
, host
.name
);
1800 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1804 /* Hostname configuration */
1805 DEFUN (config_hostname
,
1808 "Set system's network name\n"
1809 "This system's network name\n")
1811 struct cmd_token
*word
= argv
[1];
1813 if (!isalnum((unsigned char)word
->arg
[0])) {
1815 "Please specify string starting with alphabet or number\n");
1816 return CMD_WARNING_CONFIG_FAILED
;
1819 /* With reference to RFC 1123 Section 2.1 */
1820 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1821 vty_out(vty
, "Hostname length should be less than %d chars\n",
1823 return CMD_WARNING_CONFIG_FAILED
;
1826 return cmd_hostname_set(word
->arg
);
1829 DEFUN (config_no_hostname
,
1831 "no hostname [HOSTNAME]",
1833 "Reset system's network name\n"
1834 "Host name of this router\n")
1836 return cmd_hostname_set(NULL
);
1839 /* VTY interface password set. */
1840 DEFUN (config_password
,
1842 "password [(8-8)] WORD",
1843 "Modify the terminal connection password\n"
1844 "Specifies a HIDDEN password will follow\n"
1845 "The password string\n")
1849 if (argc
== 3) // '8' was specified
1852 XFREE(MTYPE_HOST
, host
.password
);
1853 host
.password
= NULL
;
1854 if (host
.password_encrypt
)
1855 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1856 host
.password_encrypt
=
1857 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1861 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1863 "Please specify string starting with alphanumeric\n");
1864 return CMD_WARNING_CONFIG_FAILED
;
1868 XFREE(MTYPE_HOST
, host
.password
);
1869 host
.password
= NULL
;
1872 if (host
.password_encrypt
)
1873 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1874 host
.password_encrypt
=
1875 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1877 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1882 /* VTY interface password delete. */
1883 DEFUN (no_config_password
,
1887 "Modify the terminal connection password\n")
1889 bool warned
= false;
1891 if (host
.password
) {
1892 if (!vty_shell_serv(vty
)) {
1893 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1896 XFREE(MTYPE_HOST
, host
.password
);
1898 host
.password
= NULL
;
1900 if (host
.password_encrypt
) {
1901 if (!warned
&& !vty_shell_serv(vty
))
1902 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1903 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1905 host
.password_encrypt
= NULL
;
1910 /* VTY enable password set. */
1911 DEFUN (config_enable_password
,
1912 enable_password_cmd
,
1913 "enable password [(8-8)] WORD",
1914 "Modify enable password parameters\n"
1915 "Assign the privileged level password\n"
1916 "Specifies a HIDDEN password will follow\n"
1917 "The HIDDEN 'enable' password string\n")
1922 /* Crypt type is specified. */
1924 if (argv
[idx_8
]->arg
[0] == '8') {
1926 XFREE(MTYPE_HOST
, host
.enable
);
1929 if (host
.enable_encrypt
)
1930 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1931 host
.enable_encrypt
=
1932 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1936 vty_out(vty
, "Unknown encryption type.\n");
1937 return CMD_WARNING_CONFIG_FAILED
;
1941 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1943 "Please specify string starting with alphanumeric\n");
1944 return CMD_WARNING_CONFIG_FAILED
;
1948 XFREE(MTYPE_HOST
, host
.enable
);
1951 /* Plain password input. */
1953 if (host
.enable_encrypt
)
1954 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1955 host
.enable_encrypt
=
1956 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1958 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1963 /* VTY enable password delete. */
1964 DEFUN (no_config_enable_password
,
1965 no_enable_password_cmd
,
1966 "no enable password",
1968 "Modify enable password parameters\n"
1969 "Assign the privileged level password\n")
1971 bool warned
= false;
1974 if (!vty_shell_serv(vty
)) {
1975 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1978 XFREE(MTYPE_HOST
, host
.enable
);
1982 if (host
.enable_encrypt
) {
1983 if (!warned
&& !vty_shell_serv(vty
))
1984 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1985 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1987 host
.enable_encrypt
= NULL
;
1992 DEFUN (service_password_encrypt
,
1993 service_password_encrypt_cmd
,
1994 "service password-encryption",
1995 "Set up miscellaneous service\n"
1996 "Enable encrypted passwords\n")
2003 if (host
.password
) {
2004 if (host
.password_encrypt
)
2005 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2006 host
.password_encrypt
=
2007 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2010 if (host
.enable_encrypt
)
2011 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2012 host
.enable_encrypt
=
2013 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2019 DEFUN (no_service_password_encrypt
,
2020 no_service_password_encrypt_cmd
,
2021 "no service password-encryption",
2023 "Set up miscellaneous service\n"
2024 "Enable encrypted passwords\n")
2031 if (host
.password_encrypt
)
2032 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2033 host
.password_encrypt
= NULL
;
2035 if (host
.enable_encrypt
)
2036 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2037 host
.enable_encrypt
= NULL
;
2042 DEFUN (config_terminal_length
,
2043 config_terminal_length_cmd
,
2044 "terminal length (0-512)",
2045 "Set terminal line parameters\n"
2046 "Set number of lines on a screen\n"
2047 "Number of lines on screen (0 for no pausing)\n")
2051 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2056 DEFUN (config_terminal_no_length
,
2057 config_terminal_no_length_cmd
,
2058 "terminal no length",
2059 "Set terminal line parameters\n"
2061 "Set number of lines on a screen\n")
2067 DEFUN (service_terminal_length
,
2068 service_terminal_length_cmd
,
2069 "service terminal-length (0-512)",
2070 "Set up miscellaneous service\n"
2071 "System wide terminal length configuration\n"
2072 "Number of lines of VTY (0 means no line control)\n")
2076 host
.lines
= atoi(argv
[idx_number
]->arg
);
2081 DEFUN (no_service_terminal_length
,
2082 no_service_terminal_length_cmd
,
2083 "no service terminal-length [(0-512)]",
2085 "Set up miscellaneous service\n"
2086 "System wide terminal length configuration\n"
2087 "Number of lines of VTY (0 means no line control)\n")
2093 DEFUN_HIDDEN (do_echo
,
2096 "Echo a message back to the vty\n"
2097 "The message to echo\n")
2101 vty_out(vty
, "%s\n",
2102 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2104 XFREE(MTYPE_TMP
, message
);
2108 DEFUN (config_logmsg
,
2110 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2111 "Send a message to enabled logging destinations\n"
2113 "The message to send\n")
2115 int idx_log_level
= 1;
2116 int idx_message
= 2;
2120 level
= log_level_match(argv
[idx_log_level
]->arg
);
2121 if (level
== ZLOG_DISABLED
)
2122 return CMD_ERR_NO_MATCH
;
2125 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2127 XFREE(MTYPE_TMP
, message
);
2132 DEFUN (debug_memstats
,
2134 "[no] debug memstats-at-exit",
2137 "Print memory type statistics at exit\n")
2139 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2143 int cmd_banner_motd_file(const char *file
)
2145 int success
= CMD_SUCCESS
;
2150 rpath
= realpath(file
, p
);
2152 return CMD_ERR_NO_FILE
;
2153 in
= strstr(rpath
, SYSCONFDIR
);
2155 XFREE(MTYPE_HOST
, host
.motdfile
);
2156 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2158 success
= CMD_WARNING_CONFIG_FAILED
;
2163 void cmd_banner_motd_line(const char *line
)
2165 XFREE(MTYPE_HOST
, host
.motd
);
2166 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2169 DEFUN (banner_motd_file
,
2170 banner_motd_file_cmd
,
2171 "banner motd file FILE",
2174 "Banner from a file\n"
2178 const char *filename
= argv
[idx_file
]->arg
;
2179 int cmd
= cmd_banner_motd_file(filename
);
2181 if (cmd
== CMD_ERR_NO_FILE
)
2182 vty_out(vty
, "%s does not exist", filename
);
2183 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2184 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2189 DEFUN (banner_motd_line
,
2190 banner_motd_line_cmd
,
2191 "banner motd line LINE...",
2194 "Banner from an input\n"
2200 argv_find(argv
, argc
, "LINE", &idx
);
2201 motd
= argv_concat(argv
, argc
, idx
);
2203 cmd_banner_motd_line(motd
);
2204 XFREE(MTYPE_TMP
, motd
);
2209 DEFUN (banner_motd_default
,
2210 banner_motd_default_cmd
,
2211 "banner motd default",
2212 "Set banner string\n"
2213 "Strings for motd\n"
2216 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2220 DEFUN (no_banner_motd
,
2224 "Set banner string\n"
2225 "Strings for motd\n")
2229 XFREE(MTYPE_HOST
, host
.motdfile
);
2230 host
.motdfile
= NULL
;
2237 "Find CLI command matching a regular expression\n"
2238 "Search pattern (POSIX regex)\n")
2240 const struct cmd_node
*node
;
2241 const struct cmd_element
*cli
;
2246 char *pattern
= argv_concat(argv
, argc
, 1);
2247 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2248 XFREE(MTYPE_TMP
, pattern
);
2253 vty_out(vty
, "%% Invalid {...} expression\n");
2256 vty_out(vty
, "%% Bad repetition operator\n");
2259 vty_out(vty
, "%% Regex syntax error\n");
2262 vty_out(vty
, "%% Invalid collating element\n");
2265 vty_out(vty
, "%% Invalid character class name\n");
2269 "%% Regex ended with escape character (\\)\n");
2273 "%% Invalid number in \\digit construction\n");
2276 vty_out(vty
, "%% Unbalanced square brackets\n");
2279 vty_out(vty
, "%% Unbalanced parentheses\n");
2282 vty_out(vty
, "%% Unbalanced braces\n");
2286 "%% Invalid endpoint in range expression\n");
2289 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2297 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2298 node
= vector_slot(cmdvec
, i
);
2301 clis
= node
->cmd_vector
;
2302 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2303 cli
= vector_slot(clis
, j
);
2305 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0)
2306 vty_out(vty
, " (%s) %s\n",
2307 node
->name
, cli
->string
);
2316 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2320 "Test command - execute a script\n"
2321 "Script name (same as filename in /etc/frr/scripts/\n")
2325 (void)str2prefix("1.2.3.4/24", &p
);
2327 struct frrscript
*fs
= frrscript_load(argv
[1]->arg
, NULL
);
2330 vty_out(vty
, "Script '/etc/frr/scripts/%s.lua' not found\n",
2333 int ret
= frrscript_call(fs
, NULL
);
2334 vty_out(vty
, "Script result: %d\n", ret
);
2341 /* Set config filename. Called from vty.c */
2342 void host_config_set(const char *filename
)
2344 XFREE(MTYPE_HOST
, host
.config
);
2345 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2348 const char *host_config_get(void)
2353 void install_default(enum node_type node
)
2355 _install_element(node
, &config_exit_cmd
);
2356 _install_element(node
, &config_quit_cmd
);
2357 _install_element(node
, &config_end_cmd
);
2358 _install_element(node
, &config_help_cmd
);
2359 _install_element(node
, &config_list_cmd
);
2360 _install_element(node
, &show_cli_graph_cmd
);
2361 _install_element(node
, &find_cmd
);
2363 _install_element(node
, &config_write_cmd
);
2364 _install_element(node
, &show_running_config_cmd
);
2366 _install_element(node
, &autocomplete_cmd
);
2368 nb_cli_install_default(node
);
2371 /* Initialize command interface. Install basic nodes and commands.
2373 * terminal = 0 -- vtysh / no logging, no config control
2374 * terminal = 1 -- normal daemon
2375 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2376 void cmd_init(int terminal
)
2378 struct utsname names
;
2383 /* register command preprocessors */
2384 hook_register(cmd_execute
, handle_pipe_action
);
2385 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2387 varhandlers
= list_new();
2389 /* Allocate initial top vector of commands. */
2390 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2392 /* Default host value settings. */
2393 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2394 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2395 if ((strcmp(names
.domainname
, "(none)") == 0))
2396 host
.domainname
= NULL
;
2398 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2400 host
.domainname
= NULL
;
2402 host
.password
= NULL
;
2405 host
.noconfig
= (terminal
< 0);
2407 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2408 host
.motdfile
= NULL
;
2410 /* Install top nodes. */
2411 install_node(&view_node
);
2412 install_node(&enable_node
);
2413 install_node(&auth_node
);
2414 install_node(&auth_enable_node
);
2415 install_node(&config_node
);
2417 /* Each node's basic commands. */
2418 install_element(VIEW_NODE
, &show_version_cmd
);
2419 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2422 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2424 install_element(VIEW_NODE
, &config_list_cmd
);
2425 install_element(VIEW_NODE
, &config_exit_cmd
);
2426 install_element(VIEW_NODE
, &config_quit_cmd
);
2427 install_element(VIEW_NODE
, &config_help_cmd
);
2428 install_element(VIEW_NODE
, &config_enable_cmd
);
2429 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2430 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2431 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2432 install_element(VIEW_NODE
, &echo_cmd
);
2433 install_element(VIEW_NODE
, &autocomplete_cmd
);
2434 install_element(VIEW_NODE
, &find_cmd
);
2435 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2436 install_element(VIEW_NODE
, &script_cmd
);
2440 install_element(ENABLE_NODE
, &config_end_cmd
);
2441 install_element(ENABLE_NODE
, &config_disable_cmd
);
2442 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2443 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2444 install_element(ENABLE_NODE
, &config_write_cmd
);
2445 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2446 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2448 install_default(CONFIG_NODE
);
2451 workqueue_cmd_init();
2455 install_element(CONFIG_NODE
, &hostname_cmd
);
2456 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2457 install_element(CONFIG_NODE
, &domainname_cmd
);
2458 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2463 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2465 install_element(CONFIG_NODE
, &password_cmd
);
2466 install_element(CONFIG_NODE
, &no_password_cmd
);
2467 install_element(CONFIG_NODE
, &enable_password_cmd
);
2468 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2470 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2471 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2472 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2473 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2474 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2475 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2476 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2477 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2480 vrf_install_commands();
2484 grammar_sandbox_init();
2488 void cmd_terminate(void)
2490 struct cmd_node
*cmd_node
;
2492 hook_unregister(cmd_execute
, handle_pipe_action
);
2493 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2496 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2497 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2498 // deleting the graph delets the cmd_element as
2500 graph_delete_graph(cmd_node
->cmdgraph
);
2501 vector_free(cmd_node
->cmd_vector
);
2502 hash_clean(cmd_node
->cmd_hash
, NULL
);
2503 hash_free(cmd_node
->cmd_hash
);
2504 cmd_node
->cmd_hash
= NULL
;
2507 vector_free(cmdvec
);
2511 XFREE(MTYPE_HOST
, host
.name
);
2512 XFREE(MTYPE_HOST
, host
.domainname
);
2513 XFREE(MTYPE_HOST
, host
.password
);
2514 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2515 XFREE(MTYPE_HOST
, host
.enable
);
2516 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2517 XFREE(MTYPE_HOST
, host
.motdfile
);
2518 XFREE(MTYPE_HOST
, host
.config
);
2519 XFREE(MTYPE_HOST
, host
.motd
);
2521 list_delete(&varhandlers
);