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
)
905 struct list
*argv_list
;
906 enum matcher_rv status
;
907 const struct cmd_element
*matched_element
= NULL
;
909 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
910 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
913 *cmd
= matched_element
;
915 // if matcher error, return corresponding CMD_ERR
916 if (MATCHER_ERROR(status
)) {
918 list_delete(&argv_list
);
920 case MATCHER_INCOMPLETE
:
921 return CMD_ERR_INCOMPLETE
;
922 case MATCHER_AMBIGUOUS
:
923 return CMD_ERR_AMBIGUOUS
;
925 return CMD_ERR_NO_MATCH
;
929 // build argv array from argv list
930 struct cmd_token
**argv
= XMALLOC(
931 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
933 struct cmd_token
*token
;
935 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
938 int argc
= argv_list
->count
;
941 if (matched_element
->daemon
)
942 ret
= CMD_SUCCESS_DAEMON
;
945 /* Clear array of enqueued configuration changes. */
946 vty
->num_cfg_changes
= 0;
947 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
949 /* Regenerate candidate configuration if necessary. */
950 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
951 && running_config
->version
952 > vty
->candidate_config
->version
)
953 nb_config_replace(vty
->candidate_config
,
954 running_config
, true);
957 * Perform pending commit (if any) before executing
960 if (matched_element
->attr
!= CMD_ATTR_YANG
)
961 nb_cli_pending_commit_check(vty
);
964 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
967 // delete list and cmd_token's in it
968 list_delete(&argv_list
);
969 XFREE(MTYPE_TMP
, argv
);
975 * Execute a given command, handling things like "do ..." and checking
976 * whether the given command might apply at a parent node if doesn't
977 * apply for the current node.
979 * @param vline Command line input, vector of char* where each element is
981 * @param vty The vty context in which the command should be executed.
982 * @param cmd Pointer where the struct cmd_element of the matched command
983 * will be stored, if any. May be set to NULL if this info is
985 * @param vtysh If set != 0, don't lookup the command at parent nodes.
986 * @return The status of the command that has been executed or an error code
987 * as to why no command could be executed.
989 int cmd_execute_command(vector vline
, struct vty
*vty
,
990 const struct cmd_element
**cmd
, int vtysh
)
992 int ret
, saved_ret
= 0;
993 enum node_type onode
, try_node
;
994 int orig_xpath_index
;
996 onode
= try_node
= vty
->node
;
997 orig_xpath_index
= vty
->xpath_index
;
999 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1000 vector shifted_vline
;
1003 vty
->node
= ENABLE_NODE
;
1004 vty
->xpath_index
= 0;
1005 /* We can try it on enable node, cos' the vty is authenticated
1008 shifted_vline
= vector_init(vector_count(vline
));
1010 for (index
= 1; index
< vector_active(vline
); index
++)
1011 vector_set_index(shifted_vline
, index
- 1,
1012 vector_lookup(vline
, index
));
1014 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1017 vector_free(shifted_vline
);
1019 vty
->xpath_index
= orig_xpath_index
;
1024 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
1029 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1030 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1031 /* This assumes all nodes above CONFIG_NODE are childs of
1033 while (vty
->node
> CONFIG_NODE
) {
1034 try_node
= node_parent(try_node
);
1035 vty
->node
= try_node
;
1036 if (vty
->xpath_index
> 0
1037 && vty_check_node_for_xpath_decrement(try_node
,
1040 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1042 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1043 || ret
== CMD_NOT_MY_INSTANCE
1044 || ret
== CMD_WARNING_CONFIG_FAILED
)
1047 /* no command succeeded, reset the vty to the original node */
1049 vty
->xpath_index
= orig_xpath_index
;
1052 /* return command status for original node */
1057 * Execute a given command, matching it strictly against the current node.
1058 * This mode is used when reading config files.
1060 * @param vline Command line input, vector of char* where each element is
1062 * @param vty The vty context in which the command should be executed.
1063 * @param cmd Pointer where the struct cmd_element* of the matched command
1064 * will be stored, if any. May be set to NULL if this info is
1066 * @return The status of the command that has been executed or an error code
1067 * as to why no command could be executed.
1069 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1070 const struct cmd_element
**cmd
)
1072 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1076 * Hook for preprocessing command string before executing.
1078 * All subscribers are called with the raw command string that is to be
1079 * executed. If any changes are to be made, a new string should be allocated
1080 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1081 * is then responsible for freeing this string.
1083 * All processing functions must be mutually exclusive in their action, i.e. if
1084 * one subscriber decides to modify the command, all others must not modify it
1085 * when called. Feeding the output of one processing command into a subsequent
1086 * one is not supported.
1088 * This hook is intentionally internal to the command processing system.
1091 * The raw command string.
1094 * The result of any processing.
1096 DECLARE_HOOK(cmd_execute
,
1097 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1098 (vty
, cmd_in
, cmd_out
));
1099 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1100 (vty
, cmd_in
, cmd_out
));
1102 /* Hook executed after a CLI command. */
1103 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1105 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1109 * cmd_execute hook subscriber to handle `|` actions.
1111 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1115 char *orig
, *working
, *token
, *u
;
1116 char *pipe
= strstr(cmd_in
, "| ");
1122 /* duplicate string for processing purposes, not including pipe */
1123 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1125 /* retrieve action */
1126 token
= strsep(&working
, " ");
1129 /* match result to known actions */
1130 if (strmatch(token
, "include")) {
1131 /* the remaining text should be a regexp */
1132 char *regexp
= working
;
1135 vty_out(vty
, "%% Need a regexp to filter with\n");
1140 bool succ
= vty_set_include(vty
, regexp
);
1143 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1147 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1151 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1157 XFREE(MTYPE_TMP
, orig
);
1161 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1164 vty_set_include(vty
, NULL
);
1169 int cmd_execute(struct vty
*vty
, const char *cmd
,
1170 const struct cmd_element
**matched
, int vtysh
)
1173 char *cmd_out
= NULL
;
1174 const char *cmd_exec
= NULL
;
1177 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1183 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1185 vline
= cmd_make_strvec(cmd_exec
);
1188 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1189 cmd_free_strvec(vline
);
1195 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1197 XFREE(MTYPE_TMP
, cmd_out
);
1204 * Parse one line of config, walking up the parse tree attempting to find a
1207 * @param vty The vty context in which the command should be executed.
1208 * @param cmd Pointer where the struct cmd_element* of the match command
1209 * will be stored, if any. May be set to NULL if this info is
1211 * @param use_daemon Boolean to control whether or not we match on
1212 * CMD_SUCCESS_DAEMON
1214 * @return The status of the command that has been executed or an error code
1215 * as to why no command could be executed.
1217 int command_config_read_one_line(struct vty
*vty
,
1218 const struct cmd_element
**cmd
,
1219 uint32_t line_num
, int use_daemon
)
1224 vline
= cmd_make_strvec(vty
->buf
);
1226 /* In case of comment line */
1230 /* Execute configuration command : this is strict match */
1231 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1233 // Climb the tree and try the command again at each node
1234 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1235 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1236 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1237 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1238 && vty
->node
!= CONFIG_NODE
) {
1239 int saved_node
= vty
->node
;
1240 int saved_xpath_index
= vty
->xpath_index
;
1242 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1243 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1244 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1245 && vty
->node
> CONFIG_NODE
) {
1246 vty
->node
= node_parent(vty
->node
);
1247 if (vty
->xpath_index
> 0
1248 && vty_check_node_for_xpath_decrement(vty
->node
,
1251 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1254 // If climbing the tree did not work then ignore the command and
1255 // stay at the same node
1256 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1257 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1258 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1259 vty
->node
= saved_node
;
1260 vty
->xpath_index
= saved_xpath_index
;
1264 if (ret
!= CMD_SUCCESS
&&
1265 ret
!= CMD_WARNING
&&
1266 ret
!= CMD_SUCCESS_DAEMON
) {
1267 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1269 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1270 ve
->line_num
= line_num
;
1272 vty
->error
= list_new();
1274 listnode_add(vty
->error
, ve
);
1277 cmd_free_strvec(vline
);
1282 /* Configuration make from file. */
1283 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1285 int ret
, error_ret
= 0;
1288 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1291 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1293 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1294 && ret
!= CMD_ERR_NOTHING_TODO
)
1305 /* Configuration from terminal */
1306 DEFUN (config_terminal
,
1307 config_terminal_cmd
,
1308 "configure [terminal]",
1309 "Configuration from vty interface\n"
1310 "Configuration terminal\n")
1312 return vty_config_enter(vty
, false, false);
1315 /* Enable command */
1319 "Turn on privileged mode command\n")
1321 /* If enable password is NULL, change to ENABLE_NODE */
1322 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1323 || vty
->type
== VTY_SHELL_SERV
)
1324 vty
->node
= ENABLE_NODE
;
1326 vty
->node
= AUTH_ENABLE_NODE
;
1331 /* Disable command */
1335 "Turn off privileged mode command\n")
1337 if (vty
->node
== ENABLE_NODE
)
1338 vty
->node
= VIEW_NODE
;
1342 /* Down vty node level. */
1346 "Exit current mode and down to previous mode\n")
1352 static int root_on_exit(struct vty
*vty
)
1357 vty
->status
= VTY_CLOSE
;
1361 void cmd_exit(struct vty
*vty
)
1363 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1365 if (cnode
->node_exit
) {
1366 if (!cnode
->node_exit(vty
))
1369 if (cnode
->parent_node
)
1370 vty
->node
= cnode
->parent_node
;
1371 if (vty
->xpath_index
> 0
1372 && vty_check_node_for_xpath_decrement(vty
->node
, cnode
->node
))
1380 "Exit current mode and down to previous mode\n")
1382 return config_exit(self
, vty
, argc
, argv
);
1386 /* End of configuration. */
1390 "End current mode and change to enable mode.\n")
1393 vty_config_exit(vty
);
1394 vty
->node
= ENABLE_NODE
;
1400 DEFUN (show_version
,
1404 "Displays zebra version\n")
1406 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1407 cmd_hostname_get() ? cmd_hostname_get() : "");
1408 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1409 #ifdef ENABLE_VERSION_BUILD_CONFIG
1410 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1415 /* Help display function for all node. */
1419 "Description of the interactive help system\n")
1422 "Quagga VTY provides advanced help feature. When you need help,\n\
1423 anytime at the command line please press '?'.\n\
1425 If nothing matches, the help list will be empty and you must backup\n\
1426 until entering a '?' shows the available options.\n\
1427 Two styles of help are provided:\n\
1428 1. Full help is available when you are ready to enter a\n\
1429 command argument (e.g. 'show ?') and describes each possible\n\
1431 2. Partial help is provided when an abbreviated argument is entered\n\
1432 and you want to know what arguments match the input\n\
1433 (e.g. 'show me?'.)\n\n");
1437 static void permute(struct graph_node
*start
, struct vty
*vty
)
1439 static struct list
*position
= NULL
;
1441 position
= list_new();
1443 struct cmd_token
*stok
= start
->data
;
1444 struct graph_node
*gnn
;
1445 struct listnode
*ln
;
1448 listnode_add(position
, start
);
1449 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1450 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1451 struct cmd_token
*tok
= gn
->data
;
1452 if (tok
->attr
== CMD_ATTR_HIDDEN
1453 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1455 else if (tok
->type
== END_TKN
|| gn
== start
) {
1457 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1458 struct cmd_token
*tt
= gnn
->data
;
1459 if (tt
->type
< SPECIAL_TKN
)
1460 vty_out(vty
, " %s", tt
->text
);
1463 vty_out(vty
, "...");
1467 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1468 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1477 list_delete_node(position
, listtail(position
));
1480 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1482 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1485 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1487 /* loop over all commands at this node */
1488 const struct cmd_element
*element
= NULL
;
1489 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1491 if ((element
= vector_slot(node
->cmd_vector
, i
))
1492 && element
->attr
!= CMD_ATTR_DEPRECATED
1493 && element
->attr
!= CMD_ATTR_HIDDEN
)
1494 vty_out(vty
, " %s\n", element
->string
);
1499 /* Help display function for all node. */
1502 "list [permutations]",
1503 "Print command list\n"
1504 "Print all possible command permutations\n")
1506 return cmd_list_cmds(vty
, argc
== 2);
1509 DEFUN (show_commandtree
,
1510 show_commandtree_cmd
,
1511 "show commandtree [permutations]",
1513 "Show command tree\n"
1514 "Permutations that we are interested in\n")
1516 return cmd_list_cmds(vty
, argc
== 3);
1519 DEFUN_HIDDEN(show_cli_graph
,
1524 "Dump current command space as DOT graph\n")
1526 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1527 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1529 vty_out(vty
, "%s\n", dot
);
1530 XFREE(MTYPE_TMP
, dot
);
1534 static int vty_write_config(struct vty
*vty
)
1537 struct cmd_node
*node
;
1542 nb_cli_show_config_prepare(running_config
, false);
1544 if (vty
->type
== VTY_TERM
) {
1545 vty_out(vty
, "\nCurrent configuration:\n");
1546 vty_out(vty
, "!\n");
1549 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1550 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1551 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1552 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1553 vty_out(vty
, "!\n");
1555 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1556 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1557 if ((*node
->config_write
)(vty
))
1558 vty_out(vty
, "!\n");
1561 if (vty
->type
== VTY_TERM
) {
1562 vty_out(vty
, "end\n");
1568 static int file_write_config(struct vty
*vty
)
1571 char *config_file
, *slash
;
1572 char *config_file_tmp
= NULL
;
1573 char *config_file_sav
= NULL
;
1574 int ret
= CMD_WARNING
;
1575 struct vty
*file_vty
;
1576 struct stat conf_stat
;
1581 /* Check and see if we are operating under vtysh configuration */
1582 if (host
.config
== NULL
) {
1584 "Can't save to configuration file, using vtysh.\n");
1589 config_file
= host
.config
;
1592 #define O_DIRECTORY 0
1594 slash
= strrchr(config_file
, '/');
1596 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1597 config_dir
[slash
- config_file
] = '\0';
1598 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1599 XFREE(MTYPE_TMP
, config_dir
);
1601 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1602 /* if dirfd is invalid, directory sync fails, but we're still OK */
1604 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1605 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1606 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1607 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1610 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1611 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1614 /* Open file to configuration write. */
1615 fd
= mkstemp(config_file_tmp
);
1617 vty_out(vty
, "Can't open configuration file %s.\n",
1621 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1622 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1623 config_file_tmp
, safe_strerror(errno
), errno
);
1627 /* Make vty for configuration file. */
1628 file_vty
= vty_new();
1630 file_vty
->type
= VTY_FILE
;
1632 /* Config file header print. */
1633 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1634 vty_time_print(file_vty
, 1);
1635 vty_out(file_vty
, "!\n");
1636 vty_write_config(file_vty
);
1637 vty_close(file_vty
);
1639 if (stat(config_file
, &conf_stat
) >= 0) {
1640 if (unlink(config_file_sav
) != 0)
1641 if (errno
!= ENOENT
) {
1643 "Can't unlink backup configuration file %s.\n",
1647 if (link(config_file
, config_file_sav
) != 0) {
1649 "Can't backup old configuration file %s.\n",
1656 if (rename(config_file_tmp
, config_file
) != 0) {
1657 vty_out(vty
, "Can't save configuration file %s.\n",
1664 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1668 if (ret
!= CMD_SUCCESS
)
1669 unlink(config_file_tmp
);
1672 XFREE(MTYPE_TMP
, config_file_tmp
);
1673 XFREE(MTYPE_TMP
, config_file_sav
);
1677 /* Write current configuration into file. */
1679 DEFUN (config_write
,
1681 "write [<file|memory|terminal>]",
1682 "Write running configuration to memory, network, or terminal\n"
1683 "Write to configuration file\n"
1684 "Write configuration currently in memory\n"
1685 "Write configuration to terminal\n")
1687 const int idx_type
= 1;
1689 // if command was 'write terminal' or 'write memory'
1690 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1691 return vty_write_config(vty
);
1694 return file_write_config(vty
);
1697 /* ALIAS_FIXME for 'write <terminal|memory>' */
1698 DEFUN (show_running_config
,
1699 show_running_config_cmd
,
1700 "show running-config",
1702 "running configuration (same as write terminal)\n")
1704 return vty_write_config(vty
);
1707 /* ALIAS_FIXME for 'write file' */
1708 DEFUN (copy_runningconf_startupconf
,
1709 copy_runningconf_startupconf_cmd
,
1710 "copy running-config startup-config",
1711 "Copy configuration\n"
1712 "Copy running config to... \n"
1713 "Copy running config to startup config (same as write file/memory)\n")
1715 return file_write_config(vty
);
1719 /* Write startup configuration into the terminal. */
1720 DEFUN (show_startup_config
,
1721 show_startup_config_cmd
,
1722 "show startup-config",
1724 "Contents of startup configuration\n")
1731 if (host
.config
== NULL
)
1734 confp
= fopen(host
.config
, "r");
1735 if (confp
== NULL
) {
1736 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1737 host
.config
, safe_strerror(errno
));
1741 while (fgets(buf
, BUFSIZ
, confp
)) {
1744 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1748 vty_out(vty
, "%s\n", buf
);
1756 int cmd_domainname_set(const char *domainname
)
1758 XFREE(MTYPE_HOST
, host
.domainname
);
1759 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1763 /* Hostname configuration */
1764 DEFUN(config_domainname
,
1767 "Set system's domain name\n"
1768 "This system's domain name\n")
1770 struct cmd_token
*word
= argv
[1];
1772 if (!isalpha((unsigned char)word
->arg
[0])) {
1773 vty_out(vty
, "Please specify string starting with alphabet\n");
1774 return CMD_WARNING_CONFIG_FAILED
;
1777 return cmd_domainname_set(word
->arg
);
1780 DEFUN(config_no_domainname
,
1782 "no domainname [DOMAINNAME]",
1784 "Reset system's domain name\n"
1785 "domain name of this router\n")
1787 return cmd_domainname_set(NULL
);
1790 int cmd_hostname_set(const char *hostname
)
1792 XFREE(MTYPE_HOST
, host
.name
);
1793 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1797 /* Hostname configuration */
1798 DEFUN (config_hostname
,
1801 "Set system's network name\n"
1802 "This system's network name\n")
1804 struct cmd_token
*word
= argv
[1];
1806 if (!isalnum((unsigned char)word
->arg
[0])) {
1808 "Please specify string starting with alphabet or number\n");
1809 return CMD_WARNING_CONFIG_FAILED
;
1812 /* With reference to RFC 1123 Section 2.1 */
1813 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1814 vty_out(vty
, "Hostname length should be less than %d chars\n",
1816 return CMD_WARNING_CONFIG_FAILED
;
1819 return cmd_hostname_set(word
->arg
);
1822 DEFUN (config_no_hostname
,
1824 "no hostname [HOSTNAME]",
1826 "Reset system's network name\n"
1827 "Host name of this router\n")
1829 return cmd_hostname_set(NULL
);
1832 /* VTY interface password set. */
1833 DEFUN (config_password
,
1835 "password [(8-8)] WORD",
1836 "Modify the terminal connection password\n"
1837 "Specifies a HIDDEN password will follow\n"
1838 "The password string\n")
1842 if (argc
== 3) // '8' was specified
1845 XFREE(MTYPE_HOST
, host
.password
);
1846 host
.password
= NULL
;
1847 if (host
.password_encrypt
)
1848 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1849 host
.password_encrypt
=
1850 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1854 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1856 "Please specify string starting with alphanumeric\n");
1857 return CMD_WARNING_CONFIG_FAILED
;
1861 XFREE(MTYPE_HOST
, host
.password
);
1862 host
.password
= NULL
;
1865 if (host
.password_encrypt
)
1866 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1867 host
.password_encrypt
=
1868 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1870 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1875 /* VTY interface password delete. */
1876 DEFUN (no_config_password
,
1880 "Modify the terminal connection password\n")
1882 bool warned
= false;
1884 if (host
.password
) {
1885 if (!vty_shell_serv(vty
)) {
1886 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1889 XFREE(MTYPE_HOST
, host
.password
);
1891 host
.password
= NULL
;
1893 if (host
.password_encrypt
) {
1894 if (!warned
&& !vty_shell_serv(vty
))
1895 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1896 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1898 host
.password_encrypt
= NULL
;
1903 /* VTY enable password set. */
1904 DEFUN (config_enable_password
,
1905 enable_password_cmd
,
1906 "enable password [(8-8)] WORD",
1907 "Modify enable password parameters\n"
1908 "Assign the privileged level password\n"
1909 "Specifies a HIDDEN password will follow\n"
1910 "The HIDDEN 'enable' password string\n")
1915 /* Crypt type is specified. */
1917 if (argv
[idx_8
]->arg
[0] == '8') {
1919 XFREE(MTYPE_HOST
, host
.enable
);
1922 if (host
.enable_encrypt
)
1923 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1924 host
.enable_encrypt
=
1925 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1929 vty_out(vty
, "Unknown encryption type.\n");
1930 return CMD_WARNING_CONFIG_FAILED
;
1934 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1936 "Please specify string starting with alphanumeric\n");
1937 return CMD_WARNING_CONFIG_FAILED
;
1941 XFREE(MTYPE_HOST
, host
.enable
);
1944 /* Plain password input. */
1946 if (host
.enable_encrypt
)
1947 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1948 host
.enable_encrypt
=
1949 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1951 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1956 /* VTY enable password delete. */
1957 DEFUN (no_config_enable_password
,
1958 no_enable_password_cmd
,
1959 "no enable password",
1961 "Modify enable password parameters\n"
1962 "Assign the privileged level password\n")
1964 bool warned
= false;
1967 if (!vty_shell_serv(vty
)) {
1968 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1971 XFREE(MTYPE_HOST
, host
.enable
);
1975 if (host
.enable_encrypt
) {
1976 if (!warned
&& !vty_shell_serv(vty
))
1977 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1978 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1980 host
.enable_encrypt
= NULL
;
1985 DEFUN (service_password_encrypt
,
1986 service_password_encrypt_cmd
,
1987 "service password-encryption",
1988 "Set up miscellaneous service\n"
1989 "Enable encrypted passwords\n")
1996 if (host
.password
) {
1997 if (host
.password_encrypt
)
1998 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1999 host
.password_encrypt
=
2000 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2003 if (host
.enable_encrypt
)
2004 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2005 host
.enable_encrypt
=
2006 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2012 DEFUN (no_service_password_encrypt
,
2013 no_service_password_encrypt_cmd
,
2014 "no service password-encryption",
2016 "Set up miscellaneous service\n"
2017 "Enable encrypted passwords\n")
2024 if (host
.password_encrypt
)
2025 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2026 host
.password_encrypt
= NULL
;
2028 if (host
.enable_encrypt
)
2029 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2030 host
.enable_encrypt
= NULL
;
2035 DEFUN (config_terminal_length
,
2036 config_terminal_length_cmd
,
2037 "terminal length (0-512)",
2038 "Set terminal line parameters\n"
2039 "Set number of lines on a screen\n"
2040 "Number of lines on screen (0 for no pausing)\n")
2044 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2049 DEFUN (config_terminal_no_length
,
2050 config_terminal_no_length_cmd
,
2051 "terminal no length",
2052 "Set terminal line parameters\n"
2054 "Set number of lines on a screen\n")
2060 DEFUN (service_terminal_length
,
2061 service_terminal_length_cmd
,
2062 "service terminal-length (0-512)",
2063 "Set up miscellaneous service\n"
2064 "System wide terminal length configuration\n"
2065 "Number of lines of VTY (0 means no line control)\n")
2069 host
.lines
= atoi(argv
[idx_number
]->arg
);
2074 DEFUN (no_service_terminal_length
,
2075 no_service_terminal_length_cmd
,
2076 "no service terminal-length [(0-512)]",
2078 "Set up miscellaneous service\n"
2079 "System wide terminal length configuration\n"
2080 "Number of lines of VTY (0 means no line control)\n")
2086 DEFUN_HIDDEN (do_echo
,
2089 "Echo a message back to the vty\n"
2090 "The message to echo\n")
2094 vty_out(vty
, "%s\n",
2095 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2097 XFREE(MTYPE_TMP
, message
);
2101 DEFUN (config_logmsg
,
2103 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2104 "Send a message to enabled logging destinations\n"
2106 "The message to send\n")
2108 int idx_log_level
= 1;
2109 int idx_message
= 2;
2113 level
= log_level_match(argv
[idx_log_level
]->arg
);
2114 if (level
== ZLOG_DISABLED
)
2115 return CMD_ERR_NO_MATCH
;
2118 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2120 XFREE(MTYPE_TMP
, message
);
2125 DEFUN (debug_memstats
,
2127 "[no] debug memstats-at-exit",
2130 "Print memory type statistics at exit\n")
2132 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2136 int cmd_banner_motd_file(const char *file
)
2138 int success
= CMD_SUCCESS
;
2143 rpath
= realpath(file
, p
);
2145 return CMD_ERR_NO_FILE
;
2146 in
= strstr(rpath
, SYSCONFDIR
);
2148 XFREE(MTYPE_HOST
, host
.motdfile
);
2149 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2151 success
= CMD_WARNING_CONFIG_FAILED
;
2156 void cmd_banner_motd_line(const char *line
)
2158 XFREE(MTYPE_HOST
, host
.motd
);
2159 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2162 DEFUN (banner_motd_file
,
2163 banner_motd_file_cmd
,
2164 "banner motd file FILE",
2167 "Banner from a file\n"
2171 const char *filename
= argv
[idx_file
]->arg
;
2172 int cmd
= cmd_banner_motd_file(filename
);
2174 if (cmd
== CMD_ERR_NO_FILE
)
2175 vty_out(vty
, "%s does not exist", filename
);
2176 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2177 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2182 DEFUN (banner_motd_line
,
2183 banner_motd_line_cmd
,
2184 "banner motd line LINE...",
2187 "Banner from an input\n"
2193 argv_find(argv
, argc
, "LINE", &idx
);
2194 motd
= argv_concat(argv
, argc
, idx
);
2196 cmd_banner_motd_line(motd
);
2197 XFREE(MTYPE_TMP
, motd
);
2202 DEFUN (banner_motd_default
,
2203 banner_motd_default_cmd
,
2204 "banner motd default",
2205 "Set banner string\n"
2206 "Strings for motd\n"
2209 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2213 DEFUN (no_banner_motd
,
2217 "Set banner string\n"
2218 "Strings for motd\n")
2222 XFREE(MTYPE_HOST
, host
.motdfile
);
2223 host
.motdfile
= NULL
;
2230 "Find CLI command matching a regular expression\n"
2231 "Search pattern (POSIX regex)\n")
2233 char *pattern
= argv
[1]->arg
;
2234 const struct cmd_node
*node
;
2235 const struct cmd_element
*cli
;
2240 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2245 vty_out(vty
, "%% Invalid {...} expression\n");
2248 vty_out(vty
, "%% Bad repetition operator\n");
2251 vty_out(vty
, "%% Regex syntax error\n");
2254 vty_out(vty
, "%% Invalid collating element\n");
2257 vty_out(vty
, "%% Invalid character class name\n");
2261 "%% Regex ended with escape character (\\)\n");
2265 "%% Invalid number in \\digit construction\n");
2268 vty_out(vty
, "%% Unbalanced square brackets\n");
2271 vty_out(vty
, "%% Unbalanced parentheses\n");
2274 vty_out(vty
, "%% Unbalanced braces\n");
2278 "%% Invalid endpoint in range expression\n");
2281 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2289 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2290 node
= vector_slot(cmdvec
, i
);
2293 clis
= node
->cmd_vector
;
2294 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2295 cli
= vector_slot(clis
, j
);
2297 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0)
2298 vty_out(vty
, " (%s) %s\n",
2299 node
->name
, cli
->string
);
2308 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2312 "Test command - execute a script\n"
2313 "Script name (same as filename in /etc/frr/scripts/\n")
2317 (void)str2prefix("1.2.3.4/24", &p
);
2319 struct frrscript
*fs
= frrscript_load(argv
[1]->arg
, NULL
);
2322 vty_out(vty
, "Script '/etc/frr/scripts/%s.lua' not found\n",
2325 int ret
= frrscript_call(fs
, NULL
);
2326 vty_out(vty
, "Script result: %d\n", ret
);
2333 /* Set config filename. Called from vty.c */
2334 void host_config_set(const char *filename
)
2336 XFREE(MTYPE_HOST
, host
.config
);
2337 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2340 const char *host_config_get(void)
2345 void install_default(enum node_type node
)
2347 _install_element(node
, &config_exit_cmd
);
2348 _install_element(node
, &config_quit_cmd
);
2349 _install_element(node
, &config_end_cmd
);
2350 _install_element(node
, &config_help_cmd
);
2351 _install_element(node
, &config_list_cmd
);
2352 _install_element(node
, &show_cli_graph_cmd
);
2353 _install_element(node
, &find_cmd
);
2355 _install_element(node
, &config_write_cmd
);
2356 _install_element(node
, &show_running_config_cmd
);
2358 _install_element(node
, &autocomplete_cmd
);
2360 nb_cli_install_default(node
);
2363 /* Initialize command interface. Install basic nodes and commands.
2365 * terminal = 0 -- vtysh / no logging, no config control
2366 * terminal = 1 -- normal daemon
2367 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2368 void cmd_init(int terminal
)
2370 struct utsname names
;
2375 /* register command preprocessors */
2376 hook_register(cmd_execute
, handle_pipe_action
);
2377 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2379 varhandlers
= list_new();
2381 /* Allocate initial top vector of commands. */
2382 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2384 /* Default host value settings. */
2385 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2386 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2387 if ((strcmp(names
.domainname
, "(none)") == 0))
2388 host
.domainname
= NULL
;
2390 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2392 host
.domainname
= NULL
;
2394 host
.password
= NULL
;
2397 host
.noconfig
= (terminal
< 0);
2399 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2400 host
.motdfile
= NULL
;
2402 /* Install top nodes. */
2403 install_node(&view_node
);
2404 install_node(&enable_node
);
2405 install_node(&auth_node
);
2406 install_node(&auth_enable_node
);
2407 install_node(&config_node
);
2409 /* Each node's basic commands. */
2410 install_element(VIEW_NODE
, &show_version_cmd
);
2411 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2414 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2416 install_element(VIEW_NODE
, &config_list_cmd
);
2417 install_element(VIEW_NODE
, &config_exit_cmd
);
2418 install_element(VIEW_NODE
, &config_quit_cmd
);
2419 install_element(VIEW_NODE
, &config_help_cmd
);
2420 install_element(VIEW_NODE
, &config_enable_cmd
);
2421 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2422 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2423 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2424 install_element(VIEW_NODE
, &echo_cmd
);
2425 install_element(VIEW_NODE
, &autocomplete_cmd
);
2426 install_element(VIEW_NODE
, &find_cmd
);
2427 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2428 install_element(VIEW_NODE
, &script_cmd
);
2432 install_element(ENABLE_NODE
, &config_end_cmd
);
2433 install_element(ENABLE_NODE
, &config_disable_cmd
);
2434 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2435 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2436 install_element(ENABLE_NODE
, &config_write_cmd
);
2437 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2438 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2440 install_default(CONFIG_NODE
);
2443 workqueue_cmd_init();
2447 install_element(CONFIG_NODE
, &hostname_cmd
);
2448 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2449 install_element(CONFIG_NODE
, &domainname_cmd
);
2450 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2455 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2457 install_element(CONFIG_NODE
, &password_cmd
);
2458 install_element(CONFIG_NODE
, &no_password_cmd
);
2459 install_element(CONFIG_NODE
, &enable_password_cmd
);
2460 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2462 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2463 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2464 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2465 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2466 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2467 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2468 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2469 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2472 vrf_install_commands();
2476 grammar_sandbox_init();
2480 void cmd_terminate(void)
2482 struct cmd_node
*cmd_node
;
2484 hook_unregister(cmd_execute
, handle_pipe_action
);
2485 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2488 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2489 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2490 // deleting the graph delets the cmd_element as
2492 graph_delete_graph(cmd_node
->cmdgraph
);
2493 vector_free(cmd_node
->cmd_vector
);
2494 hash_clean(cmd_node
->cmd_hash
, NULL
);
2495 hash_free(cmd_node
->cmd_hash
);
2496 cmd_node
->cmd_hash
= NULL
;
2499 vector_free(cmdvec
);
2503 XFREE(MTYPE_HOST
, host
.name
);
2504 XFREE(MTYPE_HOST
, host
.domainname
);
2505 XFREE(MTYPE_HOST
, host
.password
);
2506 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2507 XFREE(MTYPE_HOST
, host
.enable
);
2508 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2509 XFREE(MTYPE_HOST
, host
.motdfile
);
2510 XFREE(MTYPE_HOST
, host
.config
);
2511 XFREE(MTYPE_HOST
, host
.motd
);
2513 list_delete(&varhandlers
);