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
),
82 /* Command vector which includes some level of command lists. Normally
83 each daemon maintains each own cmdvec. */
86 /* Host information structure. */
89 /* for vtysh, put together CLI trees only when switching into node */
90 static bool defer_cli_tree
;
93 * Returns host.name if any, otherwise
94 * it returns the system hostname.
96 const char *cmd_hostname_get(void)
102 * Returns unix domainname
104 const char *cmd_domainname_get(void)
106 return host
.domainname
;
109 static int root_on_exit(struct vty
*vty
);
111 /* Standard command node structures. */
112 static struct cmd_node auth_node
= {
115 .prompt
= "Password: ",
118 static struct cmd_node view_node
= {
122 .node_exit
= root_on_exit
,
125 static struct cmd_node auth_enable_node
= {
126 .name
= "auth enable",
127 .node
= AUTH_ENABLE_NODE
,
128 .prompt
= "Password: ",
131 static struct cmd_node enable_node
= {
135 .node_exit
= root_on_exit
,
138 static int config_write_host(struct vty
*vty
);
139 static struct cmd_node config_node
= {
142 .parent_node
= ENABLE_NODE
,
143 .prompt
= "%s(config)# ",
144 .config_write
= config_write_host
,
145 .node_exit
= vty_config_node_exit
,
148 static bool vty_check_node_for_xpath_decrement(enum node_type target_node
,
151 /* bgp afi-safi (`address-family <afi> <safi>`) node
152 * does not increment xpath_index.
153 * In order to use (`router bgp`) BGP_NODE's xpath as a base,
154 * retain xpath_index as 1 upon exiting from
158 if (target_node
== BGP_NODE
159 && (node
== BGP_IPV4_NODE
|| node
== BGP_IPV6_NODE
160 || node
== BGP_IPV4M_NODE
|| node
== BGP_IPV6M_NODE
161 || node
== BGP_VPNV4_NODE
|| node
== BGP_VPNV6_NODE
162 || node
== BGP_EVPN_NODE
|| node
== BGP_IPV4L_NODE
163 || node
== BGP_IPV6L_NODE
|| node
== BGP_FLOWSPECV4_NODE
164 || node
== BGP_FLOWSPECV6_NODE
))
167 if (target_node
== INTERFACE_NODE
&& node
== LINK_PARAMS_NODE
)
173 /* This is called from main when a daemon is invoked with -v or --version. */
174 void print_version(const char *progname
)
176 printf("%s version %s\n", progname
, FRR_VERSION
);
177 printf("%s\n", FRR_COPYRIGHT
);
178 #ifdef ENABLE_VERSION_BUILD_CONFIG
179 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
183 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
185 int cnt
= MAX(argc
- shift
, 0);
186 const char *argstr
[cnt
+ 1];
191 for (int i
= 0; i
< cnt
; i
++)
192 argstr
[i
] = argv
[i
+ shift
]->arg
;
194 return frrstr_join(argstr
, cnt
, " ");
197 vector
cmd_make_strvec(const char *string
)
202 const char *copy
= string
;
204 /* skip leading whitespace */
205 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
208 /* if the entire string was whitespace or a comment, return */
209 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
212 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
214 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
215 if (strlen(vector_slot(result
, i
)) == 0) {
216 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
217 vector_unset(result
, i
);
221 vector_compact(result
);
226 void cmd_free_strvec(vector v
)
228 frrstr_strvec_free(v
);
232 * Convenience function for accessing argv data.
236 * @param text definition snippet of the desired token
237 * @param index the starting index, and where to store the
238 * index of the found token if it exists
239 * @return 1 if found, 0 otherwise
241 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
244 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
245 if ((found
= strmatch(text
, argv
[i
]->text
)))
250 static unsigned int cmd_hash_key(const void *p
)
252 int size
= sizeof(p
);
254 return jhash(p
, size
, 0);
257 static bool cmd_hash_cmp(const void *a
, const void *b
)
262 /* Install top node of command vector. */
263 void install_node(struct cmd_node
*node
)
265 #define CMD_HASH_STR_SIZE 256
266 char hash_name
[CMD_HASH_STR_SIZE
];
268 vector_set_index(cmdvec
, node
->node
, node
);
269 node
->cmdgraph
= graph_new();
270 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
272 struct cmd_token
*token
=
273 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
274 graph_new_node(node
->cmdgraph
, token
,
275 (void (*)(void *)) & cmd_token_del
);
277 snprintf(hash_name
, sizeof(hash_name
), "Command Hash: %s", node
->name
);
279 hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
, hash_name
);
282 /* Return prompt character of specified node. */
283 const char *cmd_prompt(enum node_type node
)
285 struct cmd_node
*cnode
;
287 cnode
= vector_slot(cmdvec
, node
);
288 return cnode
->prompt
;
291 void cmd_defer_tree(bool val
)
293 defer_cli_tree
= val
;
296 /* Install a command into a node. */
297 void _install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
299 struct cmd_node
*cnode
;
301 /* cmd_init hasn't been called */
303 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
308 cnode
= vector_lookup(cmdvec
, ntype
);
313 "\tnode %d does not exist.\n"
314 "\tplease call install_node() before install_element()\n",
315 cmd
->name
, cmd
->string
, ntype
);
319 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
322 "\tnode %d (%s) already has this command installed.\n"
323 "\tduplicate install_element call?\n",
324 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
328 assert(hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
));
330 if (cnode
->graph_built
|| !defer_cli_tree
) {
331 struct graph
*graph
= graph_new();
332 struct cmd_token
*token
=
333 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
334 graph_new_node(graph
, token
,
335 (void (*)(void *)) & cmd_token_del
);
337 cmd_graph_parse(graph
, cmd
);
338 cmd_graph_names(graph
);
339 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
340 graph_delete_graph(graph
);
342 cnode
->graph_built
= true;
345 vector_set(cnode
->cmd_vector
, (void *)cmd
);
347 if (ntype
== VIEW_NODE
)
348 _install_element(ENABLE_NODE
, cmd
);
351 static void cmd_finalize_iter(struct hash_bucket
*hb
, void *arg
)
353 struct cmd_node
*cnode
= arg
;
354 const struct cmd_element
*cmd
= hb
->data
;
355 struct graph
*graph
= graph_new();
356 struct cmd_token
*token
=
357 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
359 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
361 cmd_graph_parse(graph
, cmd
);
362 cmd_graph_names(graph
);
363 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
364 graph_delete_graph(graph
);
367 void cmd_finalize_node(struct cmd_node
*cnode
)
369 if (cnode
->graph_built
)
372 hash_iterate(cnode
->cmd_hash
, cmd_finalize_iter
, cnode
);
373 cnode
->graph_built
= true;
376 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
378 struct cmd_node
*cnode
;
380 /* cmd_init hasn't been called */
382 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
387 cnode
= vector_lookup(cmdvec
, ntype
);
392 "\tnode %d does not exist.\n"
393 "\tplease call install_node() before uninstall_element()\n",
394 cmd
->name
, cmd
->string
, ntype
);
398 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
401 "\tnode %d (%s) does not have this command installed.\n"
402 "\tduplicate uninstall_element call?\n",
403 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
407 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
409 if (cnode
->graph_built
) {
410 struct graph
*graph
= graph_new();
411 struct cmd_token
*token
=
412 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
413 graph_new_node(graph
, token
,
414 (void (*)(void *)) & cmd_token_del
);
416 cmd_graph_parse(graph
, cmd
);
417 cmd_graph_names(graph
);
418 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
419 graph_delete_graph(graph
);
422 if (ntype
== VIEW_NODE
)
423 uninstall_element(ENABLE_NODE
, cmd
);
427 static const unsigned char itoa64
[] =
428 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
430 static void to64(char *s
, long v
, int n
)
433 *s
++ = itoa64
[v
& 0x3f];
438 static char *zencrypt(const char *passwd
)
442 char *crypt(const char *, const char *);
444 gettimeofday(&tv
, 0);
446 to64(&salt
[0], frr_weak_random(), 3);
447 to64(&salt
[3], tv
.tv_usec
, 3);
450 return crypt(passwd
, salt
);
453 static bool full_cli
;
455 /* This function write configuration of this host. */
456 static int config_write_host(struct vty
*vty
)
458 if (cmd_hostname_get())
459 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
461 if (cmd_domainname_get())
462 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
464 /* The following are all configuration commands that are not sent to
465 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
466 * we would always display 'log syslog informational' in the config
467 * which would cause other daemons to then switch to syslog when they
472 if (host
.password_encrypt
)
473 vty_out(vty
, "password 8 %s\n",
474 host
.password_encrypt
);
475 if (host
.enable_encrypt
)
476 vty_out(vty
, "enable password 8 %s\n",
477 host
.enable_encrypt
);
480 vty_out(vty
, "password %s\n", host
.password
);
482 vty_out(vty
, "enable password %s\n",
485 log_config_write(vty
);
487 /* print disable always, but enable only if default is flipped
488 * => prep for future removal of compile-time knob
490 if (!cputime_enabled
)
491 vty_out(vty
, "no service cputime-stats\n");
492 #ifdef EXCLUDE_CPU_TIME
494 vty_out(vty
, "service cputime-stats\n");
497 if (!cputime_threshold
)
498 vty_out(vty
, "no service cputime-warning\n");
499 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
500 else /* again, always print non-default */
502 else if (cputime_threshold
!= 5000000)
504 vty_out(vty
, "service cputime-warning %lu\n",
507 if (!walltime_threshold
)
508 vty_out(vty
, "no service walltime-warning\n");
509 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
510 else /* again, always print non-default */
512 else if (walltime_threshold
!= 5000000)
514 vty_out(vty
, "service walltime-warning %lu\n",
518 vty_out(vty
, "service advanced-vty\n");
521 vty_out(vty
, "service password-encryption\n");
524 vty_out(vty
, "service terminal-length %d\n",
528 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
530 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
532 vty_out(vty
, "banner motd line %s\n", host
.motd
);
534 vty_out(vty
, "no banner motd\n");
537 if (debug_memstats_at_exit
)
538 vty_out(vty
, "!\ndebug memstats-at-exit\n");
543 /* Utility function for getting command graph. */
544 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
546 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
548 cmd_finalize_node(cnode
);
549 return cnode
->cmdgraph
;
552 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
554 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
555 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
561 * Compare function for cmd_token.
562 * Used with qsort to sort command completions.
564 static int compare_completions(const void *fst
, const void *snd
)
566 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
567 *secnd
= *(const struct cmd_token
* const *)snd
;
568 return strcmp(first
->text
, secnd
->text
);
572 * Takes a list of completions returned by command_complete,
573 * dedeuplicates them based on both text and description,
574 * sorts them, and returns them as a vector.
576 * @param completions linked list of cmd_token
577 * @return deduplicated and sorted vector with
579 vector
completions_to_vec(struct list
*completions
)
581 vector comps
= vector_init(VECTOR_MIN_SIZE
);
584 struct cmd_token
*token
, *cr
= NULL
;
585 unsigned int i
, exists
;
586 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
587 if (token
->type
== END_TKN
&& (cr
= token
))
590 // linear search for token in completions vector
592 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
593 struct cmd_token
*curr
= vector_slot(comps
, i
);
595 exists
= !strcmp(curr
->text
, token
->text
)
596 && !strcmp(curr
->desc
, token
->desc
);
598 exists
= !strcmp(curr
->text
, token
->text
);
599 #endif /* VTYSH_DEBUG */
603 vector_set(comps
, token
);
607 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
608 &compare_completions
);
610 // make <cr> the first element, if it is present
612 vector_set_index(comps
, vector_active(comps
), NULL
);
613 memmove(comps
->index
+ 1, comps
->index
,
614 (comps
->alloced
- 1) * sizeof(void *));
615 vector_set_index(comps
, 0, cr
);
621 * Generates a vector of cmd_token representing possible completions
622 * on the current input.
624 * @param vline the vectorized input line
625 * @param vty the vty with the node to match on
626 * @param status pointer to matcher status code
627 * @return vector of struct cmd_token * with possible completions
629 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
632 struct list
*completions
;
633 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
635 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
637 if (MATCHER_ERROR(rv
)) {
638 *status
= CMD_ERR_NO_MATCH
;
642 vector comps
= completions_to_vec(completions
);
643 list_delete(&completions
);
645 // set status code appropriately
646 switch (vector_active(comps
)) {
648 *status
= CMD_ERR_NO_MATCH
;
651 *status
= CMD_COMPLETE_FULL_MATCH
;
654 *status
= CMD_COMPLETE_LIST_MATCH
;
660 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
664 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
665 enum node_type onode
;
666 int orig_xpath_index
;
667 vector shifted_vline
;
671 orig_xpath_index
= vty
->xpath_index
;
672 vty
->node
= ENABLE_NODE
;
673 vty
->xpath_index
= 0;
674 /* We can try it on enable node, cos' the vty is authenticated
677 shifted_vline
= vector_init(vector_count(vline
));
679 for (index
= 1; index
< vector_active(vline
); index
++) {
680 vector_set_index(shifted_vline
, index
- 1,
681 vector_lookup(vline
, index
));
684 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
686 vector_free(shifted_vline
);
688 vty
->xpath_index
= orig_xpath_index
;
692 return cmd_complete_command_real(vline
, vty
, status
);
695 static struct list
*varhandlers
= NULL
;
697 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
701 const struct cmd_variable_handler
*cvh
;
705 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
707 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
708 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
710 if (cvh
->varname
&& (!token
->varname
711 || strcmp(cvh
->varname
, token
->varname
)))
713 cvh
->completions(tmpcomps
, token
);
721 for (i
= vector_active(tmpcomps
); i
; i
--) {
722 char *item
= vector_slot(tmpcomps
, i
- 1);
723 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
724 vector_set(comps
, item
);
726 XFREE(MTYPE_COMPLETION
, item
);
728 vector_free(tmpcomps
);
731 #define AUTOCOMP_INDENT 5
733 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
736 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
737 int lc
= AUTOCOMP_INDENT
;
738 size_t cs
= AUTOCOMP_INDENT
;
740 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
741 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
742 char *item
= vector_slot(comps
, j
);
743 itemlen
= strlen(item
);
745 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
746 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
748 if (lc
+ itemlen
+ 1 >= cols
) {
749 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
750 AUTOCOMP_INDENT
, "");
751 lc
= AUTOCOMP_INDENT
;
754 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
757 XFREE(MTYPE_COMPLETION
, item
);
758 vector_set_index(comps
, j
, NULL
);
763 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
768 for (; cvh
->completions
; cvh
++)
769 listnode_add(varhandlers
, (void *)cvh
);
772 DEFUN_HIDDEN (autocomplete
,
774 "autocomplete TYPE TEXT VARNAME",
775 "Autocompletion handler (internal, for vtysh)\n"
778 "cmd_token->varname\n")
780 struct cmd_token tok
;
781 vector comps
= vector_init(32);
784 memset(&tok
, 0, sizeof(tok
));
785 tok
.type
= atoi(argv
[1]->arg
);
786 tok
.text
= argv
[2]->arg
;
787 tok
.varname
= argv
[3]->arg
;
788 if (!strcmp(tok
.varname
, "-"))
791 cmd_variable_complete(&tok
, NULL
, comps
);
793 for (i
= 0; i
< vector_active(comps
); i
++) {
794 char *text
= vector_slot(comps
, i
);
795 vty_out(vty
, "%s\n", text
);
796 XFREE(MTYPE_COMPLETION
, text
);
804 * Generate possible tab-completions for the given input. This function only
805 * returns results that would result in a valid command if used as Readline
806 * completions (as is the case in vtysh). For instance, if the passed vline ends
807 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
809 * @param vline vectorized input line
811 * @param status location to store matcher status code in
812 * @return set of valid strings for use with Readline as tab-completions.
815 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
818 int original_node
= vty
->node
;
819 vector input_line
= vector_init(vector_count(vline
));
821 // if the first token is 'do' we'll want to execute the command in the
823 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
824 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
826 // construct the input line we'll be matching on
827 unsigned int offset
= (do_shortcut
) ? 1 : 0;
828 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
829 vector_set_index(input_line
, index
,
830 vector_lookup(vline
, index
+ offset
));
832 // get token completions -- this is a copying operation
833 vector comps
= NULL
, initial_comps
;
834 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
836 if (!MATCHER_ERROR(*status
)) {
837 assert(initial_comps
);
838 // filter out everything that is not suitable for a
840 comps
= vector_init(VECTOR_MIN_SIZE
);
841 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
843 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
844 if (token
->type
== WORD_TKN
)
845 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
847 else if (IS_VARYING_TOKEN(token
->type
)) {
848 const char *ref
= vector_lookup(
849 vline
, vector_active(vline
) - 1);
850 cmd_variable_complete(token
, ref
, comps
);
853 vector_free(initial_comps
);
855 // since we filtered results, we need to re-set status code
856 switch (vector_active(comps
)) {
858 *status
= CMD_ERR_NO_MATCH
;
861 *status
= CMD_COMPLETE_FULL_MATCH
;
864 *status
= CMD_COMPLETE_LIST_MATCH
;
867 // copy completions text into an array of char*
868 ret
= XMALLOC(MTYPE_TMP
,
869 (vector_active(comps
) + 1) * sizeof(char *));
871 for (i
= 0; i
< vector_active(comps
); i
++) {
872 ret
[i
] = vector_slot(comps
, i
);
874 // set the last element to NULL, because this array is used in
875 // a Readline completion_generator function which expects NULL
876 // as a sentinel value
880 } else if (initial_comps
)
881 vector_free(initial_comps
);
883 // comps should always be null here
886 // free the adjusted input line
887 vector_free(input_line
);
889 // reset vty->node to its original value
890 vty
->node
= original_node
;
895 /* return parent node */
896 /* MUST eventually converge on CONFIG_NODE */
897 enum node_type
node_parent(enum node_type node
)
899 struct cmd_node
*cnode
;
901 assert(node
> CONFIG_NODE
);
903 cnode
= vector_lookup(cmdvec
, node
);
905 return cnode
->parent_node
;
908 /* Execute command by argument vline vector. */
909 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
911 const struct cmd_element
**cmd
,
912 unsigned int up_level
)
914 struct list
*argv_list
;
915 enum matcher_rv status
;
916 const struct cmd_element
*matched_element
= NULL
;
918 int xpath_index
= vty
->xpath_index
;
919 int node
= vty
->node
;
921 /* only happens for legacy split config file load; need to check for
922 * a match before calling node_exit handlers below
924 for (i
= 0; i
< up_level
; i
++) {
925 if (node
<= CONFIG_NODE
)
926 return CMD_NO_LEVEL_UP
;
928 node
= node_parent(node
);
931 && vty_check_node_for_xpath_decrement(node
, vty
->node
))
935 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
936 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
939 *cmd
= matched_element
;
941 // if matcher error, return corresponding CMD_ERR
942 if (MATCHER_ERROR(status
)) {
944 list_delete(&argv_list
);
946 case MATCHER_INCOMPLETE
:
947 return CMD_ERR_INCOMPLETE
;
948 case MATCHER_AMBIGUOUS
:
949 return CMD_ERR_AMBIGUOUS
;
951 return CMD_ERR_NO_MATCH
;
955 for (i
= 0; i
< up_level
; i
++)
958 // build argv array from argv list
959 struct cmd_token
**argv
= XMALLOC(
960 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
962 struct cmd_token
*token
;
965 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
968 int argc
= argv_list
->count
;
971 if (matched_element
->daemon
)
972 ret
= CMD_SUCCESS_DAEMON
;
975 /* Clear array of enqueued configuration changes. */
976 vty
->num_cfg_changes
= 0;
977 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
979 /* Regenerate candidate configuration if necessary. */
980 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
981 && running_config
->version
982 > vty
->candidate_config
->version
)
983 nb_config_replace(vty
->candidate_config
,
984 running_config
, true);
987 * Perform pending commit (if any) before executing
990 if (matched_element
->attr
!= CMD_ATTR_YANG
)
991 (void)nb_cli_pending_commit_check(vty
);
994 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
997 // delete list and cmd_token's in it
998 list_delete(&argv_list
);
999 XFREE(MTYPE_TMP
, argv
);
1005 * Execute a given command, handling things like "do ..." and checking
1006 * whether the given command might apply at a parent node if doesn't
1007 * apply for the current node.
1009 * @param vline Command line input, vector of char* where each element is
1011 * @param vty The vty context in which the command should be executed.
1012 * @param cmd Pointer where the struct cmd_element of the matched command
1013 * will be stored, if any. May be set to NULL if this info is
1015 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1016 * @return The status of the command that has been executed or an error code
1017 * as to why no command could be executed.
1019 int cmd_execute_command(vector vline
, struct vty
*vty
,
1020 const struct cmd_element
**cmd
, int vtysh
)
1022 int ret
, saved_ret
= 0;
1023 enum node_type onode
, try_node
;
1024 int orig_xpath_index
;
1026 onode
= try_node
= vty
->node
;
1027 orig_xpath_index
= vty
->xpath_index
;
1029 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1030 vector shifted_vline
;
1033 vty
->node
= ENABLE_NODE
;
1034 vty
->xpath_index
= 0;
1035 /* We can try it on enable node, cos' the vty is authenticated
1038 shifted_vline
= vector_init(vector_count(vline
));
1040 for (index
= 1; index
< vector_active(vline
); index
++)
1041 vector_set_index(shifted_vline
, index
- 1,
1042 vector_lookup(vline
, index
));
1044 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1047 vector_free(shifted_vline
);
1049 vty
->xpath_index
= orig_xpath_index
;
1054 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1059 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1060 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1061 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1062 /* This assumes all nodes above CONFIG_NODE are childs of
1064 while (vty
->node
> CONFIG_NODE
) {
1065 try_node
= node_parent(try_node
);
1066 vty
->node
= try_node
;
1067 if (vty
->xpath_index
> 0
1068 && vty_check_node_for_xpath_decrement(try_node
,
1071 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1073 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1074 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1075 || ret
== CMD_NOT_MY_INSTANCE
1076 || ret
== CMD_WARNING_CONFIG_FAILED
)
1079 /* no command succeeded, reset the vty to the original node */
1081 vty
->xpath_index
= orig_xpath_index
;
1084 /* return command status for original node */
1089 * Execute a given command, matching it strictly against the current node.
1090 * This mode is used when reading config files.
1092 * @param vline Command line input, vector of char* where each element is
1094 * @param vty The vty context in which the command should be executed.
1095 * @param cmd Pointer where the struct cmd_element* of the matched command
1096 * will be stored, if any. May be set to NULL if this info is
1098 * @return The status of the command that has been executed or an error code
1099 * as to why no command could be executed.
1101 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1102 const struct cmd_element
**cmd
)
1104 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1108 * Hook for preprocessing command string before executing.
1110 * All subscribers are called with the raw command string that is to be
1111 * executed. If any changes are to be made, a new string should be allocated
1112 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1113 * is then responsible for freeing this string.
1115 * All processing functions must be mutually exclusive in their action, i.e. if
1116 * one subscriber decides to modify the command, all others must not modify it
1117 * when called. Feeding the output of one processing command into a subsequent
1118 * one is not supported.
1120 * This hook is intentionally internal to the command processing system.
1123 * The raw command string.
1126 * The result of any processing.
1128 DECLARE_HOOK(cmd_execute
,
1129 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1130 (vty
, cmd_in
, cmd_out
));
1131 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1132 (vty
, cmd_in
, cmd_out
));
1134 /* Hook executed after a CLI command. */
1135 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1137 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1141 * cmd_execute hook subscriber to handle `|` actions.
1143 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1147 char *orig
, *working
, *token
, *u
;
1148 char *pipe
= strstr(cmd_in
, "| ");
1154 /* duplicate string for processing purposes, not including pipe */
1155 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1157 /* retrieve action */
1158 token
= strsep(&working
, " ");
1161 /* match result to known actions */
1162 if (strmatch(token
, "include")) {
1163 /* the remaining text should be a regexp */
1164 char *regexp
= working
;
1167 vty_out(vty
, "%% Need a regexp to filter with\n");
1172 bool succ
= vty_set_include(vty
, regexp
);
1175 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1179 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1183 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1189 XFREE(MTYPE_TMP
, orig
);
1193 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1196 vty_set_include(vty
, NULL
);
1201 int cmd_execute(struct vty
*vty
, const char *cmd
,
1202 const struct cmd_element
**matched
, int vtysh
)
1205 char *cmd_out
= NULL
;
1206 const char *cmd_exec
= NULL
;
1209 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1215 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1217 vline
= cmd_make_strvec(cmd_exec
);
1220 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1221 cmd_free_strvec(vline
);
1227 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1229 XFREE(MTYPE_TMP
, cmd_out
);
1236 * Parse one line of config, walking up the parse tree attempting to find a
1239 * @param vty The vty context in which the command should be executed.
1240 * @param cmd Pointer where the struct cmd_element* of the match command
1241 * will be stored, if any. May be set to NULL if this info is
1243 * @param use_daemon Boolean to control whether or not we match on
1244 * CMD_SUCCESS_DAEMON
1246 * @return The status of the command that has been executed or an error code
1247 * as to why no command could be executed.
1249 int command_config_read_one_line(struct vty
*vty
,
1250 const struct cmd_element
**cmd
,
1251 uint32_t line_num
, int use_daemon
)
1255 unsigned up_level
= 0;
1257 vline
= cmd_make_strvec(vty
->buf
);
1259 /* In case of comment line */
1263 /* Execute configuration command : this is strict match */
1264 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1266 /* The logic for trying parent nodes is in cmd_execute_command_real()
1267 * since calling ->node_exit() correctly is a bit involved. This is
1268 * also the only reason CMD_NO_LEVEL_UP exists.
1270 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1271 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1272 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1273 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1274 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1275 && ret
!= CMD_NO_LEVEL_UP
)
1276 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1279 if (ret
== CMD_NO_LEVEL_UP
)
1280 ret
= CMD_ERR_NO_MATCH
;
1282 if (ret
!= CMD_SUCCESS
&&
1283 ret
!= CMD_WARNING
&&
1284 ret
!= CMD_SUCCESS_DAEMON
) {
1285 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1287 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1288 ve
->line_num
= line_num
;
1290 vty
->error
= list_new();
1292 listnode_add(vty
->error
, ve
);
1295 cmd_free_strvec(vline
);
1300 /* Configuration make from file. */
1301 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1303 int ret
, error_ret
= 0;
1306 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1309 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1311 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1312 && ret
!= CMD_ERR_NOTHING_TODO
)
1323 /* Configuration from terminal */
1324 DEFUN (config_terminal
,
1325 config_terminal_cmd
,
1326 "configure [terminal]",
1327 "Configuration from vty interface\n"
1328 "Configuration terminal\n")
1330 return vty_config_enter(vty
, false, false);
1333 /* Enable command */
1337 "Turn on privileged mode command\n")
1339 /* If enable password is NULL, change to ENABLE_NODE */
1340 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1341 || vty
->type
== VTY_SHELL_SERV
)
1342 vty
->node
= ENABLE_NODE
;
1344 vty
->node
= AUTH_ENABLE_NODE
;
1349 /* Disable command */
1353 "Turn off privileged mode command\n")
1355 if (vty
->node
== ENABLE_NODE
)
1356 vty
->node
= VIEW_NODE
;
1360 /* Down vty node level. */
1364 "Exit current mode and down to previous mode\n")
1370 static int root_on_exit(struct vty
*vty
)
1375 vty
->status
= VTY_CLOSE
;
1379 void cmd_exit(struct vty
*vty
)
1381 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1383 if (cnode
->node_exit
) {
1384 if (!cnode
->node_exit(vty
))
1387 if (cnode
->parent_node
)
1388 vty
->node
= cnode
->parent_node
;
1389 if (vty
->xpath_index
> 0
1390 && vty_check_node_for_xpath_decrement(vty
->node
, cnode
->node
))
1398 "Exit current mode and down to previous mode\n")
1400 return config_exit(self
, vty
, argc
, argv
);
1404 /* End of configuration. */
1408 "End current mode and change to enable mode.\n")
1411 vty_config_exit(vty
);
1412 vty
->node
= ENABLE_NODE
;
1418 DEFUN (show_version
,
1422 "Displays zebra version\n")
1424 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1425 cmd_hostname_get() ? cmd_hostname_get() : "");
1426 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1427 #ifdef ENABLE_VERSION_BUILD_CONFIG
1428 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1433 /* Help display function for all node. */
1437 "Description of the interactive help system\n")
1440 "Quagga VTY provides advanced help feature. When you need help,\n\
1441 anytime at the command line please press '?'.\n\
1443 If nothing matches, the help list will be empty and you must backup\n\
1444 until entering a '?' shows the available options.\n\
1445 Two styles of help are provided:\n\
1446 1. Full help is available when you are ready to enter a\n\
1447 command argument (e.g. 'show ?') and describes each possible\n\
1449 2. Partial help is provided when an abbreviated argument is entered\n\
1450 and you want to know what arguments match the input\n\
1451 (e.g. 'show me?'.)\n\n");
1455 static void permute(struct graph_node
*start
, struct vty
*vty
)
1457 static struct list
*position
= NULL
;
1459 position
= list_new();
1461 struct cmd_token
*stok
= start
->data
;
1462 struct graph_node
*gnn
;
1463 struct listnode
*ln
;
1466 listnode_add(position
, start
);
1467 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1468 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1469 struct cmd_token
*tok
= gn
->data
;
1470 if (tok
->attr
== CMD_ATTR_HIDDEN
1471 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1473 else if (tok
->type
== END_TKN
|| gn
== start
) {
1475 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1476 struct cmd_token
*tt
= gnn
->data
;
1477 if (tt
->type
< SPECIAL_TKN
)
1478 vty_out(vty
, " %s", tt
->text
);
1481 vty_out(vty
, "...");
1485 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1486 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1495 list_delete_node(position
, listtail(position
));
1498 static void print_cmd(struct vty
*vty
, const char *cmd
)
1500 int i
, j
, len
= strlen(cmd
);
1505 for (i
= 0; i
< len
; i
++) {
1509 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1515 if (isspace(cmd
[i
])) {
1516 /* skip leading whitespace */
1519 /* skip trailing whitespace */
1522 /* skip all whitespace after opening brackets or pipe */
1523 if (strchr("(<[{|", cmd
[i
- 1])) {
1524 while (isspace(cmd
[i
+ 1]))
1528 /* skip repeated whitespace */
1529 if (isspace(cmd
[i
+ 1]))
1531 /* skip whitespace before closing brackets or pipe */
1532 if (strchr(")>]}|", cmd
[i
+ 1]))
1534 /* convert tabs to spaces */
1535 if (cmd
[i
] == '\t') {
1545 vty_out(vty
, "%s\n", buf
);
1548 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1550 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1553 cmd_finalize_node(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
);
1600 cmd_finalize_node(cn
);
1601 dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1603 vty_out(vty
, "%s\n", dot
);
1604 XFREE(MTYPE_TMP
, dot
);
1608 static int vty_write_config(struct vty
*vty
)
1611 struct cmd_node
*node
;
1616 nb_cli_show_config_prepare(running_config
, false);
1618 if (vty
->type
== VTY_TERM
) {
1619 vty_out(vty
, "\nCurrent configuration:\n");
1620 vty_out(vty
, "!\n");
1623 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1624 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1625 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1626 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1627 vty_out(vty
, "!\n");
1629 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1630 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1631 if ((*node
->config_write
)(vty
))
1632 vty_out(vty
, "!\n");
1635 if (vty
->type
== VTY_TERM
) {
1636 vty_out(vty
, "end\n");
1642 static int file_write_config(struct vty
*vty
)
1645 char *config_file
, *slash
;
1646 char *config_file_tmp
= NULL
;
1647 char *config_file_sav
= NULL
;
1648 int ret
= CMD_WARNING
;
1649 struct vty
*file_vty
;
1650 struct stat conf_stat
;
1655 /* Check and see if we are operating under vtysh configuration */
1656 if (host
.config
== NULL
) {
1658 "Can't save to configuration file, using vtysh.\n");
1663 config_file
= host
.config
;
1666 #define O_DIRECTORY 0
1668 slash
= strrchr(config_file
, '/');
1670 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1671 config_dir
[slash
- config_file
] = '\0';
1672 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1673 XFREE(MTYPE_TMP
, config_dir
);
1675 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1676 /* if dirfd is invalid, directory sync fails, but we're still OK */
1678 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1679 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1680 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1681 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1684 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1685 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1688 /* Open file to configuration write. */
1689 fd
= mkstemp(config_file_tmp
);
1691 vty_out(vty
, "Can't open configuration file %s.\n",
1695 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1696 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1697 config_file_tmp
, safe_strerror(errno
), errno
);
1701 /* Make vty for configuration file. */
1702 file_vty
= vty_new();
1704 file_vty
->type
= VTY_FILE
;
1706 /* Config file header print. */
1707 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1708 vty_time_print(file_vty
, 1);
1709 vty_out(file_vty
, "!\n");
1710 vty_write_config(file_vty
);
1711 vty_close(file_vty
);
1713 if (stat(config_file
, &conf_stat
) >= 0) {
1714 if (unlink(config_file_sav
) != 0)
1715 if (errno
!= ENOENT
) {
1717 "Can't unlink backup configuration file %s.\n",
1721 if (link(config_file
, config_file_sav
) != 0) {
1723 "Can't backup old configuration file %s.\n",
1730 if (rename(config_file_tmp
, config_file
) != 0) {
1731 vty_out(vty
, "Can't save configuration file %s.\n",
1738 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1742 if (ret
!= CMD_SUCCESS
)
1743 unlink(config_file_tmp
);
1746 XFREE(MTYPE_TMP
, config_file_tmp
);
1747 XFREE(MTYPE_TMP
, config_file_sav
);
1751 /* Write current configuration into file. */
1753 DEFUN (config_write
,
1755 "write [<file|memory|terminal>]",
1756 "Write running configuration to memory, network, or terminal\n"
1757 "Write to configuration file\n"
1758 "Write configuration currently in memory\n"
1759 "Write configuration to terminal\n")
1761 const int idx_type
= 1;
1763 // if command was 'write terminal' or 'write memory'
1764 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1765 return vty_write_config(vty
);
1768 return file_write_config(vty
);
1771 /* ALIAS_FIXME for 'write <terminal|memory>' */
1772 DEFUN (show_running_config
,
1773 show_running_config_cmd
,
1774 "show running-config",
1776 "running configuration (same as write terminal)\n")
1778 return vty_write_config(vty
);
1781 /* ALIAS_FIXME for 'write file' */
1782 DEFUN (copy_runningconf_startupconf
,
1783 copy_runningconf_startupconf_cmd
,
1784 "copy running-config startup-config",
1785 "Copy configuration\n"
1786 "Copy running config to... \n"
1787 "Copy running config to startup config (same as write file/memory)\n")
1789 return file_write_config(vty
);
1793 /* Write startup configuration into the terminal. */
1794 DEFUN (show_startup_config
,
1795 show_startup_config_cmd
,
1796 "show startup-config",
1798 "Contents of startup configuration\n")
1805 if (host
.config
== NULL
)
1808 confp
= fopen(host
.config
, "r");
1809 if (confp
== NULL
) {
1810 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1811 host
.config
, safe_strerror(errno
));
1815 while (fgets(buf
, BUFSIZ
, confp
)) {
1818 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1822 vty_out(vty
, "%s\n", buf
);
1830 int cmd_domainname_set(const char *domainname
)
1832 XFREE(MTYPE_HOST
, host
.domainname
);
1833 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1837 /* Hostname configuration */
1838 DEFUN(config_domainname
,
1841 "Set system's domain name\n"
1842 "This system's domain name\n")
1844 struct cmd_token
*word
= argv
[1];
1846 if (!isalpha((unsigned char)word
->arg
[0])) {
1847 vty_out(vty
, "Please specify string starting with alphabet\n");
1848 return CMD_WARNING_CONFIG_FAILED
;
1851 return cmd_domainname_set(word
->arg
);
1854 DEFUN(config_no_domainname
,
1856 "no domainname [DOMAINNAME]",
1858 "Reset system's domain name\n"
1859 "domain name of this router\n")
1861 return cmd_domainname_set(NULL
);
1864 int cmd_hostname_set(const char *hostname
)
1866 XFREE(MTYPE_HOST
, host
.name
);
1867 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1871 /* Hostname configuration */
1872 DEFUN (config_hostname
,
1875 "Set system's network name\n"
1876 "This system's network name\n")
1878 struct cmd_token
*word
= argv
[1];
1880 if (!isalnum((unsigned char)word
->arg
[0])) {
1882 "Please specify string starting with alphabet or number\n");
1883 return CMD_WARNING_CONFIG_FAILED
;
1886 /* With reference to RFC 1123 Section 2.1 */
1887 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1888 vty_out(vty
, "Hostname length should be less than %d chars\n",
1890 return CMD_WARNING_CONFIG_FAILED
;
1893 return cmd_hostname_set(word
->arg
);
1896 DEFUN (config_no_hostname
,
1898 "no hostname [HOSTNAME]",
1900 "Reset system's network name\n"
1901 "Host name of this router\n")
1903 return cmd_hostname_set(NULL
);
1906 /* VTY interface password set. */
1907 DEFUN (config_password
,
1909 "password [(8-8)] WORD",
1910 "Modify the terminal connection password\n"
1911 "Specifies a HIDDEN password will follow\n"
1912 "The password string\n")
1916 if (argc
== 3) // '8' was specified
1919 XFREE(MTYPE_HOST
, host
.password
);
1920 host
.password
= NULL
;
1921 if (host
.password_encrypt
)
1922 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1923 host
.password_encrypt
=
1924 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1928 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1930 "Please specify string starting with alphanumeric\n");
1931 return CMD_WARNING_CONFIG_FAILED
;
1935 XFREE(MTYPE_HOST
, host
.password
);
1936 host
.password
= NULL
;
1939 if (host
.password_encrypt
)
1940 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1941 host
.password_encrypt
=
1942 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1944 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1949 /* VTY interface password delete. */
1950 DEFUN (no_config_password
,
1954 "Modify the terminal connection password\n")
1956 bool warned
= false;
1958 if (host
.password
) {
1959 if (!vty_shell_serv(vty
)) {
1960 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1963 XFREE(MTYPE_HOST
, host
.password
);
1965 host
.password
= NULL
;
1967 if (host
.password_encrypt
) {
1968 if (!warned
&& !vty_shell_serv(vty
))
1969 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1970 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1972 host
.password_encrypt
= NULL
;
1977 /* VTY enable password set. */
1978 DEFUN (config_enable_password
,
1979 enable_password_cmd
,
1980 "enable password [(8-8)] WORD",
1981 "Modify enable password parameters\n"
1982 "Assign the privileged level password\n"
1983 "Specifies a HIDDEN password will follow\n"
1984 "The HIDDEN 'enable' password string\n")
1989 /* Crypt type is specified. */
1991 if (argv
[idx_8
]->arg
[0] == '8') {
1993 XFREE(MTYPE_HOST
, host
.enable
);
1996 if (host
.enable_encrypt
)
1997 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1998 host
.enable_encrypt
=
1999 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2003 vty_out(vty
, "Unknown encryption type.\n");
2004 return CMD_WARNING_CONFIG_FAILED
;
2008 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2010 "Please specify string starting with alphanumeric\n");
2011 return CMD_WARNING_CONFIG_FAILED
;
2015 XFREE(MTYPE_HOST
, host
.enable
);
2018 /* Plain password input. */
2020 if (host
.enable_encrypt
)
2021 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2022 host
.enable_encrypt
=
2023 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2025 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2030 /* VTY enable password delete. */
2031 DEFUN (no_config_enable_password
,
2032 no_enable_password_cmd
,
2033 "no enable password",
2035 "Modify enable password parameters\n"
2036 "Assign the privileged level password\n")
2038 bool warned
= false;
2041 if (!vty_shell_serv(vty
)) {
2042 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2045 XFREE(MTYPE_HOST
, host
.enable
);
2049 if (host
.enable_encrypt
) {
2050 if (!warned
&& !vty_shell_serv(vty
))
2051 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2052 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2054 host
.enable_encrypt
= NULL
;
2059 DEFUN (service_password_encrypt
,
2060 service_password_encrypt_cmd
,
2061 "service password-encryption",
2062 "Set up miscellaneous service\n"
2063 "Enable encrypted passwords\n")
2070 if (host
.password
) {
2071 if (host
.password_encrypt
)
2072 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2073 host
.password_encrypt
=
2074 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2077 if (host
.enable_encrypt
)
2078 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2079 host
.enable_encrypt
=
2080 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2086 DEFUN (no_service_password_encrypt
,
2087 no_service_password_encrypt_cmd
,
2088 "no service password-encryption",
2090 "Set up miscellaneous service\n"
2091 "Enable encrypted passwords\n")
2098 if (host
.password_encrypt
)
2099 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2100 host
.password_encrypt
= NULL
;
2102 if (host
.enable_encrypt
)
2103 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2104 host
.enable_encrypt
= NULL
;
2109 DEFUN (config_terminal_length
,
2110 config_terminal_length_cmd
,
2111 "terminal length (0-512)",
2112 "Set terminal line parameters\n"
2113 "Set number of lines on a screen\n"
2114 "Number of lines on screen (0 for no pausing)\n")
2118 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2123 DEFUN (config_terminal_no_length
,
2124 config_terminal_no_length_cmd
,
2125 "terminal no length",
2126 "Set terminal line parameters\n"
2128 "Set number of lines on a screen\n")
2134 DEFUN (service_terminal_length
,
2135 service_terminal_length_cmd
,
2136 "service terminal-length (0-512)",
2137 "Set up miscellaneous service\n"
2138 "System wide terminal length configuration\n"
2139 "Number of lines of VTY (0 means no line control)\n")
2143 host
.lines
= atoi(argv
[idx_number
]->arg
);
2148 DEFUN (no_service_terminal_length
,
2149 no_service_terminal_length_cmd
,
2150 "no service terminal-length [(0-512)]",
2152 "Set up miscellaneous service\n"
2153 "System wide terminal length configuration\n"
2154 "Number of lines of VTY (0 means no line control)\n")
2160 DEFUN_HIDDEN (do_echo
,
2163 "Echo a message back to the vty\n"
2164 "The message to echo\n")
2168 vty_out(vty
, "%s\n",
2169 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2171 XFREE(MTYPE_TMP
, message
);
2175 DEFUN (config_logmsg
,
2177 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2178 "Send a message to enabled logging destinations\n"
2180 "The message to send\n")
2182 int idx_log_level
= 1;
2183 int idx_message
= 2;
2187 level
= log_level_match(argv
[idx_log_level
]->arg
);
2188 if (level
== ZLOG_DISABLED
)
2189 return CMD_ERR_NO_MATCH
;
2192 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2194 XFREE(MTYPE_TMP
, message
);
2199 DEFUN (debug_memstats
,
2201 "[no] debug memstats-at-exit",
2204 "Print memory type statistics at exit\n")
2206 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2210 int cmd_banner_motd_file(const char *file
)
2212 int success
= CMD_SUCCESS
;
2217 rpath
= realpath(file
, p
);
2219 return CMD_ERR_NO_FILE
;
2220 in
= strstr(rpath
, SYSCONFDIR
);
2222 XFREE(MTYPE_HOST
, host
.motdfile
);
2223 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2225 success
= CMD_WARNING_CONFIG_FAILED
;
2230 void cmd_banner_motd_line(const char *line
)
2232 XFREE(MTYPE_HOST
, host
.motd
);
2233 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2236 DEFUN (banner_motd_file
,
2237 banner_motd_file_cmd
,
2238 "banner motd file FILE",
2241 "Banner from a file\n"
2245 const char *filename
= argv
[idx_file
]->arg
;
2246 int cmd
= cmd_banner_motd_file(filename
);
2248 if (cmd
== CMD_ERR_NO_FILE
)
2249 vty_out(vty
, "%s does not exist", filename
);
2250 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2251 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2256 DEFUN (banner_motd_line
,
2257 banner_motd_line_cmd
,
2258 "banner motd line LINE...",
2261 "Banner from an input\n"
2267 argv_find(argv
, argc
, "LINE", &idx
);
2268 motd
= argv_concat(argv
, argc
, idx
);
2270 cmd_banner_motd_line(motd
);
2271 XFREE(MTYPE_TMP
, motd
);
2276 DEFUN (banner_motd_default
,
2277 banner_motd_default_cmd
,
2278 "banner motd default",
2279 "Set banner string\n"
2280 "Strings for motd\n"
2283 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2287 DEFUN (no_banner_motd
,
2291 "Set banner string\n"
2292 "Strings for motd\n")
2296 XFREE(MTYPE_HOST
, host
.motdfile
);
2297 host
.motdfile
= NULL
;
2301 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2303 const struct cmd_node
*node
;
2304 const struct cmd_element
*cli
;
2309 char *pattern
= argv_concat(argv
, argc
, 1);
2310 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2311 XFREE(MTYPE_TMP
, pattern
);
2316 vty_out(vty
, "%% Invalid {...} expression\n");
2319 vty_out(vty
, "%% Bad repetition operator\n");
2322 vty_out(vty
, "%% Regex syntax error\n");
2325 vty_out(vty
, "%% Invalid collating element\n");
2328 vty_out(vty
, "%% Invalid character class name\n");
2332 "%% Regex ended with escape character (\\)\n");
2336 "%% Invalid number in \\digit construction\n");
2339 vty_out(vty
, "%% Unbalanced square brackets\n");
2342 vty_out(vty
, "%% Unbalanced parentheses\n");
2345 vty_out(vty
, "%% Unbalanced braces\n");
2349 "%% Invalid endpoint in range expression\n");
2352 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2360 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2361 node
= vector_slot(cmdvec
, i
);
2364 clis
= node
->cmd_vector
;
2365 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2366 cli
= vector_slot(clis
, j
);
2368 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2369 vty_out(vty
, " (%s) ", node
->name
);
2370 print_cmd(vty
, cli
->string
);
2383 "Find CLI command matching a regular expression\n"
2384 "Search pattern (POSIX regex)\n")
2386 return cmd_find_cmds(vty
, argv
, argc
);
2389 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2390 DEFUN(script
, script_cmd
, "script SCRIPT FUNCTION",
2391 "Test command - execute a function in a script\n"
2392 "Script name (same as filename in /etc/frr/scripts/)\n"
2393 "Function name (in the script)\n")
2397 (void)str2prefix("1.2.3.4/24", &p
);
2398 struct frrscript
*fs
= frrscript_new(argv
[1]->arg
);
2400 if (frrscript_load(fs
, argv
[2]->arg
, NULL
)) {
2402 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2403 argv
[1]->arg
, argv
[2]->arg
);
2406 int ret
= frrscript_call(fs
, argv
[2]->arg
, ("p", &p
));
2408 prefix2str(&p
, buf
, sizeof(buf
));
2409 vty_out(vty
, "p: %s\n", buf
);
2410 vty_out(vty
, "Script result: %d\n", ret
);
2412 frrscript_delete(fs
);
2418 /* Set config filename. Called from vty.c */
2419 void host_config_set(const char *filename
)
2421 XFREE(MTYPE_HOST
, host
.config
);
2422 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2425 const char *host_config_get(void)
2430 void install_default(enum node_type node
)
2432 _install_element(node
, &config_exit_cmd
);
2433 _install_element(node
, &config_quit_cmd
);
2434 _install_element(node
, &config_end_cmd
);
2435 _install_element(node
, &config_help_cmd
);
2436 _install_element(node
, &config_list_cmd
);
2437 _install_element(node
, &show_cli_graph_cmd
);
2438 _install_element(node
, &find_cmd
);
2440 _install_element(node
, &config_write_cmd
);
2441 _install_element(node
, &show_running_config_cmd
);
2443 _install_element(node
, &autocomplete_cmd
);
2445 nb_cli_install_default(node
);
2448 /* Initialize command interface. Install basic nodes and commands.
2450 * terminal = 0 -- vtysh / no logging, no config control
2451 * terminal = 1 -- normal daemon
2452 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2453 void cmd_init(int terminal
)
2455 struct utsname names
;
2460 /* register command preprocessors */
2461 hook_register(cmd_execute
, handle_pipe_action
);
2462 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2464 varhandlers
= list_new();
2466 /* Allocate initial top vector of commands. */
2467 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2469 /* Default host value settings. */
2470 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2471 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2472 if ((strcmp(names
.domainname
, "(none)") == 0))
2473 host
.domainname
= NULL
;
2475 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2477 host
.domainname
= NULL
;
2479 host
.password
= NULL
;
2482 host
.noconfig
= (terminal
< 0);
2484 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2485 host
.motdfile
= NULL
;
2487 /* Install top nodes. */
2488 install_node(&view_node
);
2489 install_node(&enable_node
);
2490 install_node(&auth_node
);
2491 install_node(&auth_enable_node
);
2492 install_node(&config_node
);
2494 /* Each node's basic commands. */
2495 install_element(VIEW_NODE
, &show_version_cmd
);
2496 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2499 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2501 install_element(VIEW_NODE
, &config_list_cmd
);
2502 install_element(VIEW_NODE
, &config_exit_cmd
);
2503 install_element(VIEW_NODE
, &config_quit_cmd
);
2504 install_element(VIEW_NODE
, &config_help_cmd
);
2505 install_element(VIEW_NODE
, &config_enable_cmd
);
2506 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2507 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2508 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2509 install_element(VIEW_NODE
, &echo_cmd
);
2510 install_element(VIEW_NODE
, &autocomplete_cmd
);
2511 install_element(VIEW_NODE
, &find_cmd
);
2512 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2513 install_element(VIEW_NODE
, &script_cmd
);
2517 install_element(ENABLE_NODE
, &config_end_cmd
);
2518 install_element(ENABLE_NODE
, &config_disable_cmd
);
2519 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2520 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2521 install_element(ENABLE_NODE
, &config_write_cmd
);
2522 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2523 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2525 install_default(CONFIG_NODE
);
2528 workqueue_cmd_init();
2532 install_element(CONFIG_NODE
, &hostname_cmd
);
2533 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2534 install_element(CONFIG_NODE
, &domainname_cmd
);
2535 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2540 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2542 install_element(CONFIG_NODE
, &password_cmd
);
2543 install_element(CONFIG_NODE
, &no_password_cmd
);
2544 install_element(CONFIG_NODE
, &enable_password_cmd
);
2545 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2547 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2548 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2549 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2550 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2551 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2552 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2553 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2554 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2557 vrf_install_commands();
2561 grammar_sandbox_init();
2565 void cmd_terminate(void)
2567 struct cmd_node
*cmd_node
;
2569 hook_unregister(cmd_execute
, handle_pipe_action
);
2570 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2573 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2574 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2575 // deleting the graph delets the cmd_element as
2577 graph_delete_graph(cmd_node
->cmdgraph
);
2578 vector_free(cmd_node
->cmd_vector
);
2579 hash_clean(cmd_node
->cmd_hash
, NULL
);
2580 hash_free(cmd_node
->cmd_hash
);
2581 cmd_node
->cmd_hash
= NULL
;
2584 vector_free(cmdvec
);
2588 XFREE(MTYPE_HOST
, host
.name
);
2589 XFREE(MTYPE_HOST
, host
.domainname
);
2590 XFREE(MTYPE_HOST
, host
.password
);
2591 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2592 XFREE(MTYPE_HOST
, host
.enable
);
2593 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2594 XFREE(MTYPE_HOST
, host
.motdfile
);
2595 XFREE(MTYPE_HOST
, host
.config
);
2596 XFREE(MTYPE_HOST
, host
.motd
);
2598 list_delete(&varhandlers
);