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
:
893 ret
= SEGMENT_ROUTING_NODE
;
899 ret
= SRV6_LOCS_NODE
;
909 /* Execute command by argument vline vector. */
910 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
912 const struct cmd_element
**cmd
,
913 unsigned int up_level
)
915 struct list
*argv_list
;
916 enum matcher_rv status
;
917 const struct cmd_element
*matched_element
= NULL
;
919 int xpath_index
= vty
->xpath_index
;
920 int node
= vty
->node
;
922 /* only happens for legacy split config file load; need to check for
923 * a match before calling node_exit handlers below
925 for (i
= 0; i
< up_level
; i
++) {
926 if (node
<= CONFIG_NODE
)
927 return CMD_NO_LEVEL_UP
;
929 node
= node_parent(node
);
932 && vty_check_node_for_xpath_decrement(node
, vty
->node
))
936 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
937 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
940 *cmd
= matched_element
;
942 // if matcher error, return corresponding CMD_ERR
943 if (MATCHER_ERROR(status
)) {
945 list_delete(&argv_list
);
947 case MATCHER_INCOMPLETE
:
948 return CMD_ERR_INCOMPLETE
;
949 case MATCHER_AMBIGUOUS
:
950 return CMD_ERR_AMBIGUOUS
;
952 return CMD_ERR_NO_MATCH
;
956 for (i
= 0; i
< up_level
; i
++)
959 // build argv array from argv list
960 struct cmd_token
**argv
= XMALLOC(
961 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
963 struct cmd_token
*token
;
966 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
969 int argc
= argv_list
->count
;
972 if (matched_element
->daemon
)
973 ret
= CMD_SUCCESS_DAEMON
;
976 /* Clear array of enqueued configuration changes. */
977 vty
->num_cfg_changes
= 0;
978 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
980 /* Regenerate candidate configuration if necessary. */
981 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
982 && running_config
->version
983 > vty
->candidate_config
->version
)
984 nb_config_replace(vty
->candidate_config
,
985 running_config
, true);
988 * Perform pending commit (if any) before executing
991 if (matched_element
->attr
!= CMD_ATTR_YANG
)
992 (void)nb_cli_pending_commit_check(vty
);
995 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
998 // delete list and cmd_token's in it
999 list_delete(&argv_list
);
1000 XFREE(MTYPE_TMP
, argv
);
1006 * Execute a given command, handling things like "do ..." and checking
1007 * whether the given command might apply at a parent node if doesn't
1008 * apply for the current node.
1010 * @param vline Command line input, vector of char* where each element is
1012 * @param vty The vty context in which the command should be executed.
1013 * @param cmd Pointer where the struct cmd_element of the matched command
1014 * will be stored, if any. May be set to NULL if this info is
1016 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1017 * @return The status of the command that has been executed or an error code
1018 * as to why no command could be executed.
1020 int cmd_execute_command(vector vline
, struct vty
*vty
,
1021 const struct cmd_element
**cmd
, int vtysh
)
1023 int ret
, saved_ret
= 0;
1024 enum node_type onode
, try_node
;
1025 int orig_xpath_index
;
1027 onode
= try_node
= vty
->node
;
1028 orig_xpath_index
= vty
->xpath_index
;
1030 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1031 vector shifted_vline
;
1034 vty
->node
= ENABLE_NODE
;
1035 vty
->xpath_index
= 0;
1036 /* We can try it on enable node, cos' the vty is authenticated
1039 shifted_vline
= vector_init(vector_count(vline
));
1041 for (index
= 1; index
< vector_active(vline
); index
++)
1042 vector_set_index(shifted_vline
, index
- 1,
1043 vector_lookup(vline
, index
));
1045 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1048 vector_free(shifted_vline
);
1050 vty
->xpath_index
= orig_xpath_index
;
1055 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1060 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1061 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1062 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1063 /* This assumes all nodes above CONFIG_NODE are childs of
1065 while (vty
->node
> CONFIG_NODE
) {
1066 try_node
= node_parent(try_node
);
1067 vty
->node
= try_node
;
1068 if (vty
->xpath_index
> 0
1069 && vty_check_node_for_xpath_decrement(try_node
,
1072 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1074 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1075 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1076 || ret
== CMD_NOT_MY_INSTANCE
1077 || ret
== CMD_WARNING_CONFIG_FAILED
)
1080 /* no command succeeded, reset the vty to the original node */
1082 vty
->xpath_index
= orig_xpath_index
;
1085 /* return command status for original node */
1090 * Execute a given command, matching it strictly against the current node.
1091 * This mode is used when reading config files.
1093 * @param vline Command line input, vector of char* where each element is
1095 * @param vty The vty context in which the command should be executed.
1096 * @param cmd Pointer where the struct cmd_element* of the matched command
1097 * will be stored, if any. May be set to NULL if this info is
1099 * @return The status of the command that has been executed or an error code
1100 * as to why no command could be executed.
1102 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1103 const struct cmd_element
**cmd
)
1105 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1109 * Hook for preprocessing command string before executing.
1111 * All subscribers are called with the raw command string that is to be
1112 * executed. If any changes are to be made, a new string should be allocated
1113 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1114 * is then responsible for freeing this string.
1116 * All processing functions must be mutually exclusive in their action, i.e. if
1117 * one subscriber decides to modify the command, all others must not modify it
1118 * when called. Feeding the output of one processing command into a subsequent
1119 * one is not supported.
1121 * This hook is intentionally internal to the command processing system.
1124 * The raw command string.
1127 * The result of any processing.
1129 DECLARE_HOOK(cmd_execute
,
1130 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1131 (vty
, cmd_in
, cmd_out
));
1132 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1133 (vty
, cmd_in
, cmd_out
));
1135 /* Hook executed after a CLI command. */
1136 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1138 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1142 * cmd_execute hook subscriber to handle `|` actions.
1144 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1148 char *orig
, *working
, *token
, *u
;
1149 char *pipe
= strstr(cmd_in
, "| ");
1155 /* duplicate string for processing purposes, not including pipe */
1156 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1158 /* retrieve action */
1159 token
= strsep(&working
, " ");
1162 /* match result to known actions */
1163 if (strmatch(token
, "include")) {
1164 /* the remaining text should be a regexp */
1165 char *regexp
= working
;
1168 vty_out(vty
, "%% Need a regexp to filter with\n");
1173 bool succ
= vty_set_include(vty
, regexp
);
1176 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1180 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1184 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1190 XFREE(MTYPE_TMP
, orig
);
1194 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1197 vty_set_include(vty
, NULL
);
1202 int cmd_execute(struct vty
*vty
, const char *cmd
,
1203 const struct cmd_element
**matched
, int vtysh
)
1206 char *cmd_out
= NULL
;
1207 const char *cmd_exec
= NULL
;
1210 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1216 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1218 vline
= cmd_make_strvec(cmd_exec
);
1221 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1222 cmd_free_strvec(vline
);
1228 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1230 XFREE(MTYPE_TMP
, cmd_out
);
1237 * Parse one line of config, walking up the parse tree attempting to find a
1240 * @param vty The vty context in which the command should be executed.
1241 * @param cmd Pointer where the struct cmd_element* of the match command
1242 * will be stored, if any. May be set to NULL if this info is
1244 * @param use_daemon Boolean to control whether or not we match on
1245 * CMD_SUCCESS_DAEMON
1247 * @return The status of the command that has been executed or an error code
1248 * as to why no command could be executed.
1250 int command_config_read_one_line(struct vty
*vty
,
1251 const struct cmd_element
**cmd
,
1252 uint32_t line_num
, int use_daemon
)
1256 unsigned up_level
= 0;
1258 vline
= cmd_make_strvec(vty
->buf
);
1260 /* In case of comment line */
1264 /* Execute configuration command : this is strict match */
1265 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1267 /* The logic for trying parent nodes is in cmd_execute_command_real()
1268 * since calling ->node_exit() correctly is a bit involved. This is
1269 * also the only reason CMD_NO_LEVEL_UP exists.
1271 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1272 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1273 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1274 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1275 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1276 && ret
!= CMD_NO_LEVEL_UP
)
1277 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1280 if (ret
== CMD_NO_LEVEL_UP
)
1281 ret
= CMD_ERR_NO_MATCH
;
1283 if (ret
!= CMD_SUCCESS
&&
1284 ret
!= CMD_WARNING
&&
1285 ret
!= CMD_SUCCESS_DAEMON
) {
1286 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1288 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1289 ve
->line_num
= line_num
;
1291 vty
->error
= list_new();
1293 listnode_add(vty
->error
, ve
);
1296 cmd_free_strvec(vline
);
1301 /* Configuration make from file. */
1302 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1304 int ret
, error_ret
= 0;
1307 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1310 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1312 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1313 && ret
!= CMD_ERR_NOTHING_TODO
)
1324 /* Configuration from terminal */
1325 DEFUN (config_terminal
,
1326 config_terminal_cmd
,
1327 "configure [terminal]",
1328 "Configuration from vty interface\n"
1329 "Configuration terminal\n")
1331 return vty_config_enter(vty
, false, false);
1334 /* Enable command */
1338 "Turn on privileged mode command\n")
1340 /* If enable password is NULL, change to ENABLE_NODE */
1341 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1342 || vty
->type
== VTY_SHELL_SERV
)
1343 vty
->node
= ENABLE_NODE
;
1345 vty
->node
= AUTH_ENABLE_NODE
;
1350 /* Disable command */
1354 "Turn off privileged mode command\n")
1356 if (vty
->node
== ENABLE_NODE
)
1357 vty
->node
= VIEW_NODE
;
1361 /* Down vty node level. */
1365 "Exit current mode and down to previous mode\n")
1371 static int root_on_exit(struct vty
*vty
)
1376 vty
->status
= VTY_CLOSE
;
1380 void cmd_exit(struct vty
*vty
)
1382 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1384 if (cnode
->node_exit
) {
1385 if (!cnode
->node_exit(vty
))
1388 if (cnode
->parent_node
)
1389 vty
->node
= cnode
->parent_node
;
1390 if (vty
->xpath_index
> 0
1391 && vty_check_node_for_xpath_decrement(vty
->node
, cnode
->node
))
1399 "Exit current mode and down to previous mode\n")
1401 return config_exit(self
, vty
, argc
, argv
);
1405 /* End of configuration. */
1409 "End current mode and change to enable mode.\n")
1412 vty_config_exit(vty
);
1413 vty
->node
= ENABLE_NODE
;
1419 DEFUN (show_version
,
1423 "Displays zebra version\n")
1425 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1426 cmd_hostname_get() ? cmd_hostname_get() : "");
1427 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1428 #ifdef ENABLE_VERSION_BUILD_CONFIG
1429 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1434 /* Help display function for all node. */
1438 "Description of the interactive help system\n")
1441 "Quagga VTY provides advanced help feature. When you need help,\n\
1442 anytime at the command line please press '?'.\n\
1444 If nothing matches, the help list will be empty and you must backup\n\
1445 until entering a '?' shows the available options.\n\
1446 Two styles of help are provided:\n\
1447 1. Full help is available when you are ready to enter a\n\
1448 command argument (e.g. 'show ?') and describes each possible\n\
1450 2. Partial help is provided when an abbreviated argument is entered\n\
1451 and you want to know what arguments match the input\n\
1452 (e.g. 'show me?'.)\n\n");
1456 static void permute(struct graph_node
*start
, struct vty
*vty
)
1458 static struct list
*position
= NULL
;
1460 position
= list_new();
1462 struct cmd_token
*stok
= start
->data
;
1463 struct graph_node
*gnn
;
1464 struct listnode
*ln
;
1467 listnode_add(position
, start
);
1468 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1469 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1470 struct cmd_token
*tok
= gn
->data
;
1471 if (tok
->attr
== CMD_ATTR_HIDDEN
1472 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1474 else if (tok
->type
== END_TKN
|| gn
== start
) {
1476 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1477 struct cmd_token
*tt
= gnn
->data
;
1478 if (tt
->type
< SPECIAL_TKN
)
1479 vty_out(vty
, " %s", tt
->text
);
1482 vty_out(vty
, "...");
1486 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1487 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1496 list_delete_node(position
, listtail(position
));
1499 static void print_cmd(struct vty
*vty
, const char *cmd
)
1501 int i
, j
, len
= strlen(cmd
);
1506 for (i
= 0; i
< len
; i
++) {
1510 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1516 if (isspace(cmd
[i
])) {
1517 /* skip leading whitespace */
1520 /* skip trailing whitespace */
1523 /* skip all whitespace after opening brackets or pipe */
1524 if (strchr("(<[{|", cmd
[i
- 1])) {
1525 while (isspace(cmd
[i
+ 1]))
1529 /* skip repeated whitespace */
1530 if (isspace(cmd
[i
+ 1]))
1532 /* skip whitespace before closing brackets or pipe */
1533 if (strchr(")>]}|", cmd
[i
+ 1]))
1535 /* convert tabs to spaces */
1536 if (cmd
[i
] == '\t') {
1546 vty_out(vty
, "%s\n", buf
);
1549 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1551 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1554 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1556 /* loop over all commands at this node */
1557 const struct cmd_element
*element
= NULL
;
1558 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1560 if ((element
= vector_slot(node
->cmd_vector
, i
))
1561 && element
->attr
!= CMD_ATTR_DEPRECATED
1562 && element
->attr
!= CMD_ATTR_HIDDEN
) {
1564 print_cmd(vty
, element
->string
);
1570 /* Help display function for all node. */
1573 "list [permutations]",
1574 "Print command list\n"
1575 "Print all possible command permutations\n")
1577 return cmd_list_cmds(vty
, argc
== 2);
1580 DEFUN (show_commandtree
,
1581 show_commandtree_cmd
,
1582 "show commandtree [permutations]",
1584 "Show command tree\n"
1585 "Permutations that we are interested in\n")
1587 return cmd_list_cmds(vty
, argc
== 3);
1590 DEFUN_HIDDEN(show_cli_graph
,
1595 "Dump current command space as DOT graph\n")
1597 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1598 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1600 vty_out(vty
, "%s\n", dot
);
1601 XFREE(MTYPE_TMP
, dot
);
1605 static int vty_write_config(struct vty
*vty
)
1608 struct cmd_node
*node
;
1613 nb_cli_show_config_prepare(running_config
, false);
1615 if (vty
->type
== VTY_TERM
) {
1616 vty_out(vty
, "\nCurrent configuration:\n");
1617 vty_out(vty
, "!\n");
1620 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1621 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1622 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1623 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1624 vty_out(vty
, "!\n");
1626 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1627 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1628 if ((*node
->config_write
)(vty
))
1629 vty_out(vty
, "!\n");
1632 if (vty
->type
== VTY_TERM
) {
1633 vty_out(vty
, "end\n");
1639 static int file_write_config(struct vty
*vty
)
1642 char *config_file
, *slash
;
1643 char *config_file_tmp
= NULL
;
1644 char *config_file_sav
= NULL
;
1645 int ret
= CMD_WARNING
;
1646 struct vty
*file_vty
;
1647 struct stat conf_stat
;
1652 /* Check and see if we are operating under vtysh configuration */
1653 if (host
.config
== NULL
) {
1655 "Can't save to configuration file, using vtysh.\n");
1660 config_file
= host
.config
;
1663 #define O_DIRECTORY 0
1665 slash
= strrchr(config_file
, '/');
1667 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1668 config_dir
[slash
- config_file
] = '\0';
1669 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1670 XFREE(MTYPE_TMP
, config_dir
);
1672 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1673 /* if dirfd is invalid, directory sync fails, but we're still OK */
1675 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1676 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1677 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1678 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1681 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1682 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1685 /* Open file to configuration write. */
1686 fd
= mkstemp(config_file_tmp
);
1688 vty_out(vty
, "Can't open configuration file %s.\n",
1692 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1693 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1694 config_file_tmp
, safe_strerror(errno
), errno
);
1698 /* Make vty for configuration file. */
1699 file_vty
= vty_new();
1701 file_vty
->type
= VTY_FILE
;
1703 /* Config file header print. */
1704 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1705 vty_time_print(file_vty
, 1);
1706 vty_out(file_vty
, "!\n");
1707 vty_write_config(file_vty
);
1708 vty_close(file_vty
);
1710 if (stat(config_file
, &conf_stat
) >= 0) {
1711 if (unlink(config_file_sav
) != 0)
1712 if (errno
!= ENOENT
) {
1714 "Can't unlink backup configuration file %s.\n",
1718 if (link(config_file
, config_file_sav
) != 0) {
1720 "Can't backup old configuration file %s.\n",
1727 if (rename(config_file_tmp
, config_file
) != 0) {
1728 vty_out(vty
, "Can't save configuration file %s.\n",
1735 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1739 if (ret
!= CMD_SUCCESS
)
1740 unlink(config_file_tmp
);
1743 XFREE(MTYPE_TMP
, config_file_tmp
);
1744 XFREE(MTYPE_TMP
, config_file_sav
);
1748 /* Write current configuration into file. */
1750 DEFUN (config_write
,
1752 "write [<file|memory|terminal>]",
1753 "Write running configuration to memory, network, or terminal\n"
1754 "Write to configuration file\n"
1755 "Write configuration currently in memory\n"
1756 "Write configuration to terminal\n")
1758 const int idx_type
= 1;
1760 // if command was 'write terminal' or 'write memory'
1761 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1762 return vty_write_config(vty
);
1765 return file_write_config(vty
);
1768 /* ALIAS_FIXME for 'write <terminal|memory>' */
1769 DEFUN (show_running_config
,
1770 show_running_config_cmd
,
1771 "show running-config",
1773 "running configuration (same as write terminal)\n")
1775 return vty_write_config(vty
);
1778 /* ALIAS_FIXME for 'write file' */
1779 DEFUN (copy_runningconf_startupconf
,
1780 copy_runningconf_startupconf_cmd
,
1781 "copy running-config startup-config",
1782 "Copy configuration\n"
1783 "Copy running config to... \n"
1784 "Copy running config to startup config (same as write file/memory)\n")
1786 return file_write_config(vty
);
1790 /* Write startup configuration into the terminal. */
1791 DEFUN (show_startup_config
,
1792 show_startup_config_cmd
,
1793 "show startup-config",
1795 "Contents of startup configuration\n")
1802 if (host
.config
== NULL
)
1805 confp
= fopen(host
.config
, "r");
1806 if (confp
== NULL
) {
1807 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1808 host
.config
, safe_strerror(errno
));
1812 while (fgets(buf
, BUFSIZ
, confp
)) {
1815 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1819 vty_out(vty
, "%s\n", buf
);
1827 int cmd_domainname_set(const char *domainname
)
1829 XFREE(MTYPE_HOST
, host
.domainname
);
1830 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1834 /* Hostname configuration */
1835 DEFUN(config_domainname
,
1838 "Set system's domain name\n"
1839 "This system's domain name\n")
1841 struct cmd_token
*word
= argv
[1];
1843 if (!isalpha((unsigned char)word
->arg
[0])) {
1844 vty_out(vty
, "Please specify string starting with alphabet\n");
1845 return CMD_WARNING_CONFIG_FAILED
;
1848 return cmd_domainname_set(word
->arg
);
1851 DEFUN(config_no_domainname
,
1853 "no domainname [DOMAINNAME]",
1855 "Reset system's domain name\n"
1856 "domain name of this router\n")
1858 return cmd_domainname_set(NULL
);
1861 int cmd_hostname_set(const char *hostname
)
1863 XFREE(MTYPE_HOST
, host
.name
);
1864 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1868 /* Hostname configuration */
1869 DEFUN (config_hostname
,
1872 "Set system's network name\n"
1873 "This system's network name\n")
1875 struct cmd_token
*word
= argv
[1];
1877 if (!isalnum((unsigned char)word
->arg
[0])) {
1879 "Please specify string starting with alphabet or number\n");
1880 return CMD_WARNING_CONFIG_FAILED
;
1883 /* With reference to RFC 1123 Section 2.1 */
1884 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1885 vty_out(vty
, "Hostname length should be less than %d chars\n",
1887 return CMD_WARNING_CONFIG_FAILED
;
1890 return cmd_hostname_set(word
->arg
);
1893 DEFUN (config_no_hostname
,
1895 "no hostname [HOSTNAME]",
1897 "Reset system's network name\n"
1898 "Host name of this router\n")
1900 return cmd_hostname_set(NULL
);
1903 /* VTY interface password set. */
1904 DEFUN (config_password
,
1906 "password [(8-8)] WORD",
1907 "Modify the terminal connection password\n"
1908 "Specifies a HIDDEN password will follow\n"
1909 "The password string\n")
1913 if (argc
== 3) // '8' was specified
1916 XFREE(MTYPE_HOST
, host
.password
);
1917 host
.password
= NULL
;
1918 if (host
.password_encrypt
)
1919 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1920 host
.password_encrypt
=
1921 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1925 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1927 "Please specify string starting with alphanumeric\n");
1928 return CMD_WARNING_CONFIG_FAILED
;
1932 XFREE(MTYPE_HOST
, host
.password
);
1933 host
.password
= NULL
;
1936 if (host
.password_encrypt
)
1937 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1938 host
.password_encrypt
=
1939 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1941 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1946 /* VTY interface password delete. */
1947 DEFUN (no_config_password
,
1951 "Modify the terminal connection password\n")
1953 bool warned
= false;
1955 if (host
.password
) {
1956 if (!vty_shell_serv(vty
)) {
1957 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1960 XFREE(MTYPE_HOST
, host
.password
);
1962 host
.password
= NULL
;
1964 if (host
.password_encrypt
) {
1965 if (!warned
&& !vty_shell_serv(vty
))
1966 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1967 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1969 host
.password_encrypt
= NULL
;
1974 /* VTY enable password set. */
1975 DEFUN (config_enable_password
,
1976 enable_password_cmd
,
1977 "enable password [(8-8)] WORD",
1978 "Modify enable password parameters\n"
1979 "Assign the privileged level password\n"
1980 "Specifies a HIDDEN password will follow\n"
1981 "The HIDDEN 'enable' password string\n")
1986 /* Crypt type is specified. */
1988 if (argv
[idx_8
]->arg
[0] == '8') {
1990 XFREE(MTYPE_HOST
, host
.enable
);
1993 if (host
.enable_encrypt
)
1994 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1995 host
.enable_encrypt
=
1996 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2000 vty_out(vty
, "Unknown encryption type.\n");
2001 return CMD_WARNING_CONFIG_FAILED
;
2005 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2007 "Please specify string starting with alphanumeric\n");
2008 return CMD_WARNING_CONFIG_FAILED
;
2012 XFREE(MTYPE_HOST
, host
.enable
);
2015 /* Plain password input. */
2017 if (host
.enable_encrypt
)
2018 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2019 host
.enable_encrypt
=
2020 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2022 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2027 /* VTY enable password delete. */
2028 DEFUN (no_config_enable_password
,
2029 no_enable_password_cmd
,
2030 "no enable password",
2032 "Modify enable password parameters\n"
2033 "Assign the privileged level password\n")
2035 bool warned
= false;
2038 if (!vty_shell_serv(vty
)) {
2039 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2042 XFREE(MTYPE_HOST
, host
.enable
);
2046 if (host
.enable_encrypt
) {
2047 if (!warned
&& !vty_shell_serv(vty
))
2048 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2049 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2051 host
.enable_encrypt
= NULL
;
2056 DEFUN (service_password_encrypt
,
2057 service_password_encrypt_cmd
,
2058 "service password-encryption",
2059 "Set up miscellaneous service\n"
2060 "Enable encrypted passwords\n")
2067 if (host
.password
) {
2068 if (host
.password_encrypt
)
2069 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2070 host
.password_encrypt
=
2071 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2074 if (host
.enable_encrypt
)
2075 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2076 host
.enable_encrypt
=
2077 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2083 DEFUN (no_service_password_encrypt
,
2084 no_service_password_encrypt_cmd
,
2085 "no service password-encryption",
2087 "Set up miscellaneous service\n"
2088 "Enable encrypted passwords\n")
2095 if (host
.password_encrypt
)
2096 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2097 host
.password_encrypt
= NULL
;
2099 if (host
.enable_encrypt
)
2100 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2101 host
.enable_encrypt
= NULL
;
2106 DEFUN (config_terminal_length
,
2107 config_terminal_length_cmd
,
2108 "terminal length (0-512)",
2109 "Set terminal line parameters\n"
2110 "Set number of lines on a screen\n"
2111 "Number of lines on screen (0 for no pausing)\n")
2115 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2120 DEFUN (config_terminal_no_length
,
2121 config_terminal_no_length_cmd
,
2122 "terminal no length",
2123 "Set terminal line parameters\n"
2125 "Set number of lines on a screen\n")
2131 DEFUN (service_terminal_length
,
2132 service_terminal_length_cmd
,
2133 "service terminal-length (0-512)",
2134 "Set up miscellaneous service\n"
2135 "System wide terminal length configuration\n"
2136 "Number of lines of VTY (0 means no line control)\n")
2140 host
.lines
= atoi(argv
[idx_number
]->arg
);
2145 DEFUN (no_service_terminal_length
,
2146 no_service_terminal_length_cmd
,
2147 "no service terminal-length [(0-512)]",
2149 "Set up miscellaneous service\n"
2150 "System wide terminal length configuration\n"
2151 "Number of lines of VTY (0 means no line control)\n")
2157 DEFUN_HIDDEN (do_echo
,
2160 "Echo a message back to the vty\n"
2161 "The message to echo\n")
2165 vty_out(vty
, "%s\n",
2166 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2168 XFREE(MTYPE_TMP
, message
);
2172 DEFUN (config_logmsg
,
2174 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2175 "Send a message to enabled logging destinations\n"
2177 "The message to send\n")
2179 int idx_log_level
= 1;
2180 int idx_message
= 2;
2184 level
= log_level_match(argv
[idx_log_level
]->arg
);
2185 if (level
== ZLOG_DISABLED
)
2186 return CMD_ERR_NO_MATCH
;
2189 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2191 XFREE(MTYPE_TMP
, message
);
2196 DEFUN (debug_memstats
,
2198 "[no] debug memstats-at-exit",
2201 "Print memory type statistics at exit\n")
2203 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2207 int cmd_banner_motd_file(const char *file
)
2209 int success
= CMD_SUCCESS
;
2214 rpath
= realpath(file
, p
);
2216 return CMD_ERR_NO_FILE
;
2217 in
= strstr(rpath
, SYSCONFDIR
);
2219 XFREE(MTYPE_HOST
, host
.motdfile
);
2220 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2222 success
= CMD_WARNING_CONFIG_FAILED
;
2227 void cmd_banner_motd_line(const char *line
)
2229 XFREE(MTYPE_HOST
, host
.motd
);
2230 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2233 DEFUN (banner_motd_file
,
2234 banner_motd_file_cmd
,
2235 "banner motd file FILE",
2238 "Banner from a file\n"
2242 const char *filename
= argv
[idx_file
]->arg
;
2243 int cmd
= cmd_banner_motd_file(filename
);
2245 if (cmd
== CMD_ERR_NO_FILE
)
2246 vty_out(vty
, "%s does not exist", filename
);
2247 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2248 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2253 DEFUN (banner_motd_line
,
2254 banner_motd_line_cmd
,
2255 "banner motd line LINE...",
2258 "Banner from an input\n"
2264 argv_find(argv
, argc
, "LINE", &idx
);
2265 motd
= argv_concat(argv
, argc
, idx
);
2267 cmd_banner_motd_line(motd
);
2268 XFREE(MTYPE_TMP
, motd
);
2273 DEFUN (banner_motd_default
,
2274 banner_motd_default_cmd
,
2275 "banner motd default",
2276 "Set banner string\n"
2277 "Strings for motd\n"
2280 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2284 DEFUN (no_banner_motd
,
2288 "Set banner string\n"
2289 "Strings for motd\n")
2293 XFREE(MTYPE_HOST
, host
.motdfile
);
2294 host
.motdfile
= NULL
;
2298 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2300 const struct cmd_node
*node
;
2301 const struct cmd_element
*cli
;
2306 char *pattern
= argv_concat(argv
, argc
, 1);
2307 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2308 XFREE(MTYPE_TMP
, pattern
);
2313 vty_out(vty
, "%% Invalid {...} expression\n");
2316 vty_out(vty
, "%% Bad repetition operator\n");
2319 vty_out(vty
, "%% Regex syntax error\n");
2322 vty_out(vty
, "%% Invalid collating element\n");
2325 vty_out(vty
, "%% Invalid character class name\n");
2329 "%% Regex ended with escape character (\\)\n");
2333 "%% Invalid number in \\digit construction\n");
2336 vty_out(vty
, "%% Unbalanced square brackets\n");
2339 vty_out(vty
, "%% Unbalanced parentheses\n");
2342 vty_out(vty
, "%% Unbalanced braces\n");
2346 "%% Invalid endpoint in range expression\n");
2349 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2357 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2358 node
= vector_slot(cmdvec
, i
);
2361 clis
= node
->cmd_vector
;
2362 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2363 cli
= vector_slot(clis
, j
);
2365 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2366 vty_out(vty
, " (%s) ", node
->name
);
2367 print_cmd(vty
, cli
->string
);
2380 "Find CLI command matching a regular expression\n"
2381 "Search pattern (POSIX regex)\n")
2383 return cmd_find_cmds(vty
, argv
, argc
);
2386 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2390 "Test command - execute a script\n"
2391 "Script name (same as filename in /etc/frr/scripts/\n")
2395 (void)str2prefix("1.2.3.4/24", &p
);
2396 struct frrscript
*fs
= frrscript_load(argv
[1]->arg
, NULL
);
2399 vty_out(vty
, "Script '/etc/frr/scripts/%s.lua' not found\n",
2402 int ret
= frrscript_call(fs
, ("p", &p
));
2404 prefix2str(&p
, buf
, sizeof(buf
));
2405 vty_out(vty
, "p: %s\n", buf
);
2406 vty_out(vty
, "Script result: %d\n", ret
);
2413 /* Set config filename. Called from vty.c */
2414 void host_config_set(const char *filename
)
2416 XFREE(MTYPE_HOST
, host
.config
);
2417 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2420 const char *host_config_get(void)
2425 void install_default(enum node_type node
)
2427 _install_element(node
, &config_exit_cmd
);
2428 _install_element(node
, &config_quit_cmd
);
2429 _install_element(node
, &config_end_cmd
);
2430 _install_element(node
, &config_help_cmd
);
2431 _install_element(node
, &config_list_cmd
);
2432 _install_element(node
, &show_cli_graph_cmd
);
2433 _install_element(node
, &find_cmd
);
2435 _install_element(node
, &config_write_cmd
);
2436 _install_element(node
, &show_running_config_cmd
);
2438 _install_element(node
, &autocomplete_cmd
);
2440 nb_cli_install_default(node
);
2443 /* Initialize command interface. Install basic nodes and commands.
2445 * terminal = 0 -- vtysh / no logging, no config control
2446 * terminal = 1 -- normal daemon
2447 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2448 void cmd_init(int terminal
)
2450 struct utsname names
;
2455 /* register command preprocessors */
2456 hook_register(cmd_execute
, handle_pipe_action
);
2457 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2459 varhandlers
= list_new();
2461 /* Allocate initial top vector of commands. */
2462 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2464 /* Default host value settings. */
2465 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2466 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2467 if ((strcmp(names
.domainname
, "(none)") == 0))
2468 host
.domainname
= NULL
;
2470 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2472 host
.domainname
= NULL
;
2474 host
.password
= NULL
;
2477 host
.noconfig
= (terminal
< 0);
2479 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2480 host
.motdfile
= NULL
;
2482 /* Install top nodes. */
2483 install_node(&view_node
);
2484 install_node(&enable_node
);
2485 install_node(&auth_node
);
2486 install_node(&auth_enable_node
);
2487 install_node(&config_node
);
2489 /* Each node's basic commands. */
2490 install_element(VIEW_NODE
, &show_version_cmd
);
2491 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2494 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2496 install_element(VIEW_NODE
, &config_list_cmd
);
2497 install_element(VIEW_NODE
, &config_exit_cmd
);
2498 install_element(VIEW_NODE
, &config_quit_cmd
);
2499 install_element(VIEW_NODE
, &config_help_cmd
);
2500 install_element(VIEW_NODE
, &config_enable_cmd
);
2501 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2502 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2503 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2504 install_element(VIEW_NODE
, &echo_cmd
);
2505 install_element(VIEW_NODE
, &autocomplete_cmd
);
2506 install_element(VIEW_NODE
, &find_cmd
);
2507 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2508 install_element(VIEW_NODE
, &script_cmd
);
2512 install_element(ENABLE_NODE
, &config_end_cmd
);
2513 install_element(ENABLE_NODE
, &config_disable_cmd
);
2514 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2515 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2516 install_element(ENABLE_NODE
, &config_write_cmd
);
2517 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2518 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2520 install_default(CONFIG_NODE
);
2523 workqueue_cmd_init();
2527 install_element(CONFIG_NODE
, &hostname_cmd
);
2528 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2529 install_element(CONFIG_NODE
, &domainname_cmd
);
2530 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2535 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2537 install_element(CONFIG_NODE
, &password_cmd
);
2538 install_element(CONFIG_NODE
, &no_password_cmd
);
2539 install_element(CONFIG_NODE
, &enable_password_cmd
);
2540 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2542 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2543 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2544 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2545 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2546 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2547 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2548 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2549 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2552 vrf_install_commands();
2556 grammar_sandbox_init();
2560 void cmd_terminate(void)
2562 struct cmd_node
*cmd_node
;
2564 hook_unregister(cmd_execute
, handle_pipe_action
);
2565 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2568 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2569 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2570 // deleting the graph delets the cmd_element as
2572 graph_delete_graph(cmd_node
->cmdgraph
);
2573 vector_free(cmd_node
->cmd_vector
);
2574 hash_clean(cmd_node
->cmd_hash
, NULL
);
2575 hash_free(cmd_node
->cmd_hash
);
2576 cmd_node
->cmd_hash
= NULL
;
2579 vector_free(cmdvec
);
2583 XFREE(MTYPE_HOST
, host
.name
);
2584 XFREE(MTYPE_HOST
, host
.domainname
);
2585 XFREE(MTYPE_HOST
, host
.password
);
2586 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2587 XFREE(MTYPE_HOST
, host
.enable
);
2588 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2589 XFREE(MTYPE_HOST
, host
.motdfile
);
2590 XFREE(MTYPE_HOST
, host
.config
);
2591 XFREE(MTYPE_HOST
, host
.motd
);
2593 list_delete(&varhandlers
);