1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * CLI backend interface.
6 * Copyright (C) 2016 Cumulus Networks, Inc.
7 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
8 * Copyright (C) 2013 by Open Source Routing.
9 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
13 #include <lib/version.h>
24 #include "workqueue.h"
26 #include "command_match.h"
27 #include "command_graph.h"
33 #include "lib_errors.h"
34 #include "northbound_cli.h"
38 #include "frrscript.h"
40 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config");
41 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item");
48 /* clang-format off */
49 const struct message tokennames
[] = {
54 item(IPV4_PREFIX_TKN
),
56 item(IPV6_PREFIX_TKN
),
68 /* Command vector which includes some level of command lists. Normally
69 each daemon maintains each own cmdvec. */
72 /* Host information structure. */
75 /* for vtysh, put together CLI trees only when switching into node */
76 static bool defer_cli_tree
;
79 * Returns host.name if any, otherwise
80 * it returns the system hostname.
82 const char *cmd_hostname_get(void)
88 * Returns unix domainname
90 const char *cmd_domainname_get(void)
92 return host
.domainname
;
95 const char *cmd_system_get(void)
100 const char *cmd_release_get(void)
105 const char *cmd_version_get(void)
110 bool cmd_allow_reserved_ranges_get(void)
112 return host
.allow_reserved_ranges
;
115 static int root_on_exit(struct vty
*vty
);
117 /* Standard command node structures. */
118 static struct cmd_node auth_node
= {
121 .prompt
= "Password: ",
124 static struct cmd_node view_node
= {
128 .node_exit
= root_on_exit
,
131 static struct cmd_node auth_enable_node
= {
132 .name
= "auth enable",
133 .node
= AUTH_ENABLE_NODE
,
134 .prompt
= "Password: ",
137 static struct cmd_node enable_node
= {
141 .node_exit
= root_on_exit
,
144 static int config_write_host(struct vty
*vty
);
145 static struct cmd_node config_node
= {
148 .parent_node
= ENABLE_NODE
,
149 .prompt
= "%s(config)# ",
150 .config_write
= config_write_host
,
151 .node_exit
= vty_config_node_exit
,
154 /* This is called from main when a daemon is invoked with -v or --version. */
155 void print_version(const char *progname
)
157 printf("%s version %s\n", progname
, FRR_VERSION
);
158 printf("%s\n", FRR_COPYRIGHT
);
159 #ifdef ENABLE_VERSION_BUILD_CONFIG
160 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
164 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
166 int cnt
= MAX(argc
- shift
, 0);
167 const char *argstr
[cnt
+ 1];
172 for (int i
= 0; i
< cnt
; i
++)
173 argstr
[i
] = argv
[i
+ shift
]->arg
;
175 return frrstr_join(argstr
, cnt
, " ");
178 vector
cmd_make_strvec(const char *string
)
183 const char *copy
= string
;
185 /* skip leading whitespace */
186 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
189 /* if the entire string was whitespace or a comment, return */
190 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
193 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
195 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
196 if (strlen(vector_slot(result
, i
)) == 0) {
197 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
198 vector_unset(result
, i
);
202 vector_compact(result
);
207 void cmd_free_strvec(vector v
)
209 frrstr_strvec_free(v
);
213 * Convenience function for accessing argv data.
217 * @param text definition snippet of the desired token
218 * @param index the starting index, and where to store the
219 * index of the found token if it exists
220 * @return 1 if found, 0 otherwise
222 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
225 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
226 if ((found
= strmatch(text
, argv
[i
]->text
)))
231 static unsigned int cmd_hash_key(const void *p
)
233 int size
= sizeof(p
);
235 return jhash(p
, size
, 0);
238 static bool cmd_hash_cmp(const void *a
, const void *b
)
243 /* Install top node of command vector. */
244 void install_node(struct cmd_node
*node
)
246 #define CMD_HASH_STR_SIZE 256
247 char hash_name
[CMD_HASH_STR_SIZE
];
249 vector_set_index(cmdvec
, node
->node
, node
);
250 node
->cmdgraph
= graph_new();
251 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
253 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
254 graph_new_node(node
->cmdgraph
, token
,
255 (void (*)(void *)) & cmd_token_del
);
257 snprintf(hash_name
, sizeof(hash_name
), "Command Hash: %s", node
->name
);
259 hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
, hash_name
);
262 /* Return prompt character of specified node. */
263 const char *cmd_prompt(enum node_type node
)
265 struct cmd_node
*cnode
;
267 cnode
= vector_slot(cmdvec
, node
);
268 return cnode
->prompt
;
271 void cmd_defer_tree(bool val
)
273 defer_cli_tree
= val
;
276 /* Install a command into a node. */
277 void _install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
279 struct cmd_node
*cnode
;
281 /* cmd_init hasn't been called */
283 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
288 cnode
= vector_lookup(cmdvec
, ntype
);
293 "\tnode %d does not exist.\n"
294 "\tplease call install_node() before install_element()\n",
295 cmd
->name
, cmd
->string
, ntype
);
299 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
302 "\tnode %d (%s) already has this command installed.\n"
303 "\tduplicate install_element call?\n",
304 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
308 (void)hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
);
310 if (cnode
->graph_built
|| !defer_cli_tree
) {
311 struct graph
*graph
= graph_new();
312 struct cmd_token
*token
=
313 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
314 graph_new_node(graph
, token
,
315 (void (*)(void *)) & cmd_token_del
);
317 cmd_graph_parse(graph
, cmd
);
318 cmd_graph_names(graph
);
319 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
320 graph_delete_graph(graph
);
322 cnode
->graph_built
= true;
325 vector_set(cnode
->cmd_vector
, (void *)cmd
);
327 if (ntype
== VIEW_NODE
)
328 _install_element(ENABLE_NODE
, cmd
);
331 static void cmd_finalize_iter(struct hash_bucket
*hb
, void *arg
)
333 struct cmd_node
*cnode
= arg
;
334 const struct cmd_element
*cmd
= hb
->data
;
335 struct graph
*graph
= graph_new();
336 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
338 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
340 cmd_graph_parse(graph
, cmd
);
341 cmd_graph_names(graph
);
342 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
343 graph_delete_graph(graph
);
346 void cmd_finalize_node(struct cmd_node
*cnode
)
348 if (cnode
->graph_built
)
351 hash_iterate(cnode
->cmd_hash
, cmd_finalize_iter
, cnode
);
352 cnode
->graph_built
= true;
355 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
357 struct cmd_node
*cnode
;
359 /* cmd_init hasn't been called */
361 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
366 cnode
= vector_lookup(cmdvec
, ntype
);
371 "\tnode %d does not exist.\n"
372 "\tplease call install_node() before uninstall_element()\n",
373 cmd
->name
, cmd
->string
, ntype
);
377 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
380 "\tnode %d (%s) does not have this command installed.\n"
381 "\tduplicate uninstall_element call?\n",
382 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
386 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
388 if (cnode
->graph_built
) {
389 struct graph
*graph
= graph_new();
390 struct cmd_token
*token
=
391 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
392 graph_new_node(graph
, token
,
393 (void (*)(void *)) & cmd_token_del
);
395 cmd_graph_parse(graph
, cmd
);
396 cmd_graph_names(graph
);
397 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
398 graph_delete_graph(graph
);
401 if (ntype
== VIEW_NODE
)
402 uninstall_element(ENABLE_NODE
, cmd
);
406 static const unsigned char itoa64
[] =
407 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
409 static void to64(char *s
, long v
, int n
)
412 *s
++ = itoa64
[v
& 0x3f];
417 static char *zencrypt(const char *passwd
)
422 gettimeofday(&tv
, 0);
424 to64(&salt
[0], frr_weak_random(), 3);
425 to64(&salt
[3], tv
.tv_usec
, 3);
428 return crypt(passwd
, salt
);
431 static bool full_cli
;
433 /* This function write configuration of this host. */
434 static int config_write_host(struct vty
*vty
)
438 name
= cmd_hostname_get();
439 if (name
&& name
[0] != '\0')
440 vty_out(vty
, "hostname %s\n", name
);
442 name
= cmd_domainname_get();
443 if (name
&& name
[0] != '\0')
444 vty_out(vty
, "domainname %s\n", name
);
446 if (cmd_allow_reserved_ranges_get())
447 vty_out(vty
, "allow-reserved-ranges\n");
449 /* The following are all configuration commands that are not sent to
450 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
451 * we would always display 'log syslog informational' in the config
452 * which would cause other daemons to then switch to syslog when they
457 if (host
.password_encrypt
)
458 vty_out(vty
, "password 8 %s\n",
459 host
.password_encrypt
);
460 if (host
.enable_encrypt
)
461 vty_out(vty
, "enable password 8 %s\n",
462 host
.enable_encrypt
);
465 vty_out(vty
, "password %s\n", host
.password
);
467 vty_out(vty
, "enable password %s\n",
470 log_config_write(vty
);
472 /* print disable always, but enable only if default is flipped
473 * => prep for future removal of compile-time knob
475 if (!cputime_enabled
)
476 vty_out(vty
, "no service cputime-stats\n");
477 #ifdef EXCLUDE_CPU_TIME
479 vty_out(vty
, "service cputime-stats\n");
482 if (!cputime_threshold
)
483 vty_out(vty
, "no service cputime-warning\n");
484 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
485 else /* again, always print non-default */
487 else if (cputime_threshold
!= 5000000)
489 vty_out(vty
, "service cputime-warning %lu\n",
490 cputime_threshold
/ 1000);
492 if (!walltime_threshold
)
493 vty_out(vty
, "no service walltime-warning\n");
494 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
495 else /* again, always print non-default */
497 else if (walltime_threshold
!= 5000000)
499 vty_out(vty
, "service walltime-warning %lu\n",
500 walltime_threshold
/ 1000);
503 vty_out(vty
, "service advanced-vty\n");
506 vty_out(vty
, "service password-encryption\n");
509 vty_out(vty
, "service terminal-length %d\n",
513 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
515 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
517 vty_out(vty
, "banner motd line %s\n", host
.motd
);
519 vty_out(vty
, "no banner motd\n");
522 if (debug_memstats_at_exit
)
523 vty_out(vty
, "!\ndebug memstats-at-exit\n");
528 /* Utility function for getting command graph. */
529 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
531 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
533 cmd_finalize_node(cnode
);
534 return cnode
->cmdgraph
;
537 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
539 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
540 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
546 * Compare function for cmd_token.
547 * Used with qsort to sort command completions.
549 static int compare_completions(const void *fst
, const void *snd
)
551 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
552 *secnd
= *(const struct cmd_token
* const *)snd
;
553 return strcmp(first
->text
, secnd
->text
);
557 * Takes a list of completions returned by command_complete,
558 * dedeuplicates them based on both text and description,
559 * sorts them, and returns them as a vector.
561 * @param completions linked list of cmd_token
562 * @return deduplicated and sorted vector with
564 vector
completions_to_vec(struct list
*completions
)
566 vector comps
= vector_init(VECTOR_MIN_SIZE
);
569 struct cmd_token
*token
, *cr
= NULL
;
570 unsigned int i
, exists
;
571 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
572 if (token
->type
== END_TKN
&& (cr
= token
))
575 // linear search for token in completions vector
577 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
578 struct cmd_token
*curr
= vector_slot(comps
, i
);
580 exists
= !strcmp(curr
->text
, token
->text
)
581 && !strcmp(curr
->desc
, token
->desc
);
583 exists
= !strcmp(curr
->text
, token
->text
);
584 #endif /* VTYSH_DEBUG */
588 vector_set(comps
, token
);
592 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
593 &compare_completions
);
595 // make <cr> the first element, if it is present
597 vector_set_index(comps
, vector_active(comps
), NULL
);
598 memmove(comps
->index
+ 1, comps
->index
,
599 (comps
->alloced
- 1) * sizeof(void *));
600 vector_set_index(comps
, 0, cr
);
606 * Generates a vector of cmd_token representing possible completions
607 * on the current input.
609 * @param vline the vectorized input line
610 * @param vty the vty with the node to match on
611 * @param status pointer to matcher status code
612 * @return vector of struct cmd_token * with possible completions
614 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
617 struct list
*completions
;
618 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
620 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
622 if (MATCHER_ERROR(rv
)) {
623 *status
= CMD_ERR_NO_MATCH
;
627 vector comps
= completions_to_vec(completions
);
628 list_delete(&completions
);
630 // set status code appropriately
631 switch (vector_active(comps
)) {
633 *status
= CMD_ERR_NO_MATCH
;
636 *status
= CMD_COMPLETE_FULL_MATCH
;
639 *status
= CMD_COMPLETE_LIST_MATCH
;
645 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
649 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
650 enum node_type onode
;
651 int orig_xpath_index
;
652 vector shifted_vline
;
656 orig_xpath_index
= vty
->xpath_index
;
657 vty
->node
= ENABLE_NODE
;
658 vty
->xpath_index
= 0;
659 /* We can try it on enable node, cos' the vty is authenticated
662 shifted_vline
= vector_init(vector_count(vline
));
664 for (index
= 1; index
< vector_active(vline
); index
++) {
665 vector_set_index(shifted_vline
, index
- 1,
666 vector_lookup(vline
, index
));
669 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
671 vector_free(shifted_vline
);
673 vty
->xpath_index
= orig_xpath_index
;
677 return cmd_complete_command_real(vline
, vty
, status
);
680 static struct list
*varhandlers
= NULL
;
682 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
686 const struct cmd_variable_handler
*cvh
;
690 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
692 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
693 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
695 if (cvh
->varname
&& (!token
->varname
696 || strcmp(cvh
->varname
, token
->varname
)))
698 cvh
->completions(tmpcomps
, token
);
706 for (i
= vector_active(tmpcomps
); i
; i
--) {
707 char *item
= vector_slot(tmpcomps
, i
- 1);
708 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
709 vector_set(comps
, item
);
711 XFREE(MTYPE_COMPLETION
, item
);
713 vector_free(tmpcomps
);
716 #define AUTOCOMP_INDENT 5
718 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
721 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
722 int lc
= AUTOCOMP_INDENT
;
723 size_t cs
= AUTOCOMP_INDENT
;
725 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
726 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
727 char *item
= vector_slot(comps
, j
);
728 itemlen
= strlen(item
);
730 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
731 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
733 if (lc
+ itemlen
+ 1 >= cols
) {
734 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
735 AUTOCOMP_INDENT
, "");
736 lc
= AUTOCOMP_INDENT
;
739 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
742 XFREE(MTYPE_COMPLETION
, item
);
743 vector_set_index(comps
, j
, NULL
);
748 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
753 for (; cvh
->completions
; cvh
++)
754 listnode_add(varhandlers
, (void *)cvh
);
757 DEFUN_HIDDEN (autocomplete
,
759 "autocomplete TYPE TEXT VARNAME",
760 "Autocompletion handler (internal, for vtysh)\n"
763 "cmd_token->varname\n")
765 struct cmd_token tok
;
766 vector comps
= vector_init(32);
769 memset(&tok
, 0, sizeof(tok
));
770 tok
.type
= atoi(argv
[1]->arg
);
771 tok
.text
= argv
[2]->arg
;
772 tok
.varname
= argv
[3]->arg
;
773 if (!strcmp(tok
.varname
, "-"))
776 cmd_variable_complete(&tok
, NULL
, comps
);
778 for (i
= 0; i
< vector_active(comps
); i
++) {
779 char *text
= vector_slot(comps
, i
);
780 vty_out(vty
, "%s\n", text
);
781 XFREE(MTYPE_COMPLETION
, text
);
789 * Generate possible tab-completions for the given input. This function only
790 * returns results that would result in a valid command if used as Readline
791 * completions (as is the case in vtysh). For instance, if the passed vline ends
792 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
794 * @param vline vectorized input line
796 * @param status location to store matcher status code in
797 * @return set of valid strings for use with Readline as tab-completions.
800 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
803 int original_node
= vty
->node
;
804 vector input_line
= vector_init(vector_count(vline
));
806 // if the first token is 'do' we'll want to execute the command in the
808 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
809 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
811 // construct the input line we'll be matching on
812 unsigned int offset
= (do_shortcut
) ? 1 : 0;
813 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
814 vector_set_index(input_line
, index
,
815 vector_lookup(vline
, index
+ offset
));
817 // get token completions -- this is a copying operation
818 vector comps
= NULL
, initial_comps
;
819 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
821 if (!MATCHER_ERROR(*status
)) {
822 assert(initial_comps
);
823 // filter out everything that is not suitable for a
825 comps
= vector_init(VECTOR_MIN_SIZE
);
826 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
828 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
829 if (token
->type
== WORD_TKN
)
830 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
832 else if (IS_VARYING_TOKEN(token
->type
)) {
833 const char *ref
= vector_lookup(
834 vline
, vector_active(vline
) - 1);
835 cmd_variable_complete(token
, ref
, comps
);
838 vector_free(initial_comps
);
840 // since we filtered results, we need to re-set status code
841 switch (vector_active(comps
)) {
843 *status
= CMD_ERR_NO_MATCH
;
846 *status
= CMD_COMPLETE_FULL_MATCH
;
849 *status
= CMD_COMPLETE_LIST_MATCH
;
852 // copy completions text into an array of char*
853 ret
= XMALLOC(MTYPE_TMP
,
854 (vector_active(comps
) + 1) * sizeof(char *));
856 for (i
= 0; i
< vector_active(comps
); i
++) {
857 ret
[i
] = vector_slot(comps
, i
);
859 // set the last element to NULL, because this array is used in
860 // a Readline completion_generator function which expects NULL
861 // as a sentinel value
865 } else if (initial_comps
)
866 vector_free(initial_comps
);
868 // comps should always be null here
871 // free the adjusted input line
872 vector_free(input_line
);
874 // reset vty->node to its original value
875 vty
->node
= original_node
;
880 /* return parent node */
881 /* MUST eventually converge on CONFIG_NODE */
882 enum node_type
node_parent(enum node_type node
)
884 struct cmd_node
*cnode
;
886 assert(node
> CONFIG_NODE
);
888 cnode
= vector_lookup(cmdvec
, node
);
890 return cnode
->parent_node
;
893 /* Execute command by argument vline vector. */
894 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
896 const struct cmd_element
**cmd
,
897 unsigned int up_level
)
899 struct list
*argv_list
;
900 enum matcher_rv status
;
901 const struct cmd_element
*matched_element
= NULL
;
903 int xpath_index
= vty
->xpath_index
;
904 int node
= vty
->node
;
906 /* only happens for legacy split config file load; need to check for
907 * a match before calling node_exit handlers below
909 for (i
= 0; i
< up_level
; i
++) {
910 struct cmd_node
*cnode
;
912 if (node
<= CONFIG_NODE
)
913 return CMD_NO_LEVEL_UP
;
915 cnode
= vector_slot(cmdvec
, node
);
916 node
= node_parent(node
);
918 if (xpath_index
> 0 && !cnode
->no_xpath
)
922 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
923 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
926 *cmd
= matched_element
;
928 // if matcher error, return corresponding CMD_ERR
929 if (MATCHER_ERROR(status
)) {
931 list_delete(&argv_list
);
933 case MATCHER_INCOMPLETE
:
934 return CMD_ERR_INCOMPLETE
;
935 case MATCHER_AMBIGUOUS
:
936 return CMD_ERR_AMBIGUOUS
;
937 case MATCHER_NO_MATCH
:
939 return CMD_ERR_NO_MATCH
;
943 for (i
= 0; i
< up_level
; i
++)
946 // build argv array from argv list
947 struct cmd_token
**argv
= XMALLOC(
948 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
950 struct cmd_token
*token
;
953 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
956 int argc
= argv_list
->count
;
959 if (matched_element
->daemon
)
960 ret
= CMD_SUCCESS_DAEMON
;
963 /* Clear array of enqueued configuration changes. */
964 vty
->num_cfg_changes
= 0;
965 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
967 /* Regenerate candidate configuration if necessary. */
968 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
969 && running_config
->version
970 > vty
->candidate_config
->version
)
971 nb_config_replace(vty
->candidate_config
,
972 running_config
, true);
975 * Perform pending commit (if any) before executing
978 if (!(matched_element
->attr
& CMD_ATTR_YANG
))
979 (void)nb_cli_pending_commit_check(vty
);
982 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
985 // delete list and cmd_token's in it
986 list_delete(&argv_list
);
987 XFREE(MTYPE_TMP
, argv
);
993 * Execute a given command, handling things like "do ..." and checking
994 * whether the given command might apply at a parent node if doesn't
995 * apply for the current node.
997 * @param vline Command line input, vector of char* where each element is
999 * @param vty The vty context in which the command should be executed.
1000 * @param cmd Pointer where the struct cmd_element of the matched command
1001 * will be stored, if any. May be set to NULL if this info is
1003 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1004 * @return The status of the command that has been executed or an error code
1005 * as to why no command could be executed.
1007 int cmd_execute_command(vector vline
, struct vty
*vty
,
1008 const struct cmd_element
**cmd
, int vtysh
)
1010 int ret
, saved_ret
= 0;
1011 enum node_type onode
, try_node
;
1012 int orig_xpath_index
;
1014 onode
= try_node
= vty
->node
;
1015 orig_xpath_index
= vty
->xpath_index
;
1017 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1018 vector shifted_vline
;
1021 vty
->node
= ENABLE_NODE
;
1022 vty
->xpath_index
= 0;
1023 /* We can try it on enable node, cos' the vty is authenticated
1026 shifted_vline
= vector_init(vector_count(vline
));
1028 for (index
= 1; index
< vector_active(vline
); index
++)
1029 vector_set_index(shifted_vline
, index
- 1,
1030 vector_lookup(vline
, index
));
1032 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1035 vector_free(shifted_vline
);
1037 vty
->xpath_index
= orig_xpath_index
;
1042 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1047 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1048 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1049 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1050 /* This assumes all nodes above CONFIG_NODE are childs of
1052 while (vty
->node
> CONFIG_NODE
) {
1053 struct cmd_node
*cnode
= vector_slot(cmdvec
, try_node
);
1055 try_node
= node_parent(try_node
);
1056 vty
->node
= try_node
;
1057 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1060 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1062 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1063 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1064 || ret
== CMD_NOT_MY_INSTANCE
1065 || ret
== CMD_WARNING_CONFIG_FAILED
)
1068 /* no command succeeded, reset the vty to the original node */
1070 vty
->xpath_index
= orig_xpath_index
;
1073 /* return command status for original node */
1078 * Execute a given command, matching it strictly against the current node.
1079 * This mode is used when reading config files.
1081 * @param vline Command line input, vector of char* where each element is
1083 * @param vty The vty context in which the command should be executed.
1084 * @param cmd Pointer where the struct cmd_element* of the matched command
1085 * will be stored, if any. May be set to NULL if this info is
1087 * @return The status of the command that has been executed or an error code
1088 * as to why no command could be executed.
1090 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1091 const struct cmd_element
**cmd
)
1093 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1097 * Hook for preprocessing command string before executing.
1099 * All subscribers are called with the raw command string that is to be
1100 * executed. If any changes are to be made, a new string should be allocated
1101 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1102 * is then responsible for freeing this string.
1104 * All processing functions must be mutually exclusive in their action, i.e. if
1105 * one subscriber decides to modify the command, all others must not modify it
1106 * when called. Feeding the output of one processing command into a subsequent
1107 * one is not supported.
1109 * This hook is intentionally internal to the command processing system.
1112 * The raw command string.
1115 * The result of any processing.
1117 DECLARE_HOOK(cmd_execute
,
1118 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1119 (vty
, cmd_in
, cmd_out
));
1120 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1121 (vty
, cmd_in
, cmd_out
));
1123 /* Hook executed after a CLI command. */
1124 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1126 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1130 * cmd_execute hook subscriber to handle `|` actions.
1132 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1136 char *orig
, *working
, *token
, *u
;
1137 char *pipe
= strstr(cmd_in
, "| ");
1143 /* duplicate string for processing purposes, not including pipe */
1144 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1146 /* retrieve action */
1147 token
= strsep(&working
, " ");
1150 /* match result to known actions */
1151 if (strmatch(token
, "include")) {
1152 /* the remaining text should be a regexp */
1153 char *regexp
= working
;
1156 vty_out(vty
, "%% Need a regexp to filter with\n");
1161 bool succ
= vty_set_include(vty
, regexp
);
1164 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1168 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1172 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1178 XFREE(MTYPE_TMP
, orig
);
1182 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1185 vty_set_include(vty
, NULL
);
1190 int cmd_execute(struct vty
*vty
, const char *cmd
,
1191 const struct cmd_element
**matched
, int vtysh
)
1194 char *cmd_out
= NULL
;
1195 const char *cmd_exec
= NULL
;
1198 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1204 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1206 vline
= cmd_make_strvec(cmd_exec
);
1209 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1210 cmd_free_strvec(vline
);
1216 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1218 XFREE(MTYPE_TMP
, cmd_out
);
1225 * Parse one line of config, walking up the parse tree attempting to find a
1228 * @param vty The vty context in which the command should be executed.
1229 * @param cmd Pointer where the struct cmd_element* of the match command
1230 * will be stored, if any. May be set to NULL if this info is
1232 * @param use_daemon Boolean to control whether or not we match on
1233 * CMD_SUCCESS_DAEMON
1235 * @return The status of the command that has been executed or an error code
1236 * as to why no command could be executed.
1238 int command_config_read_one_line(struct vty
*vty
,
1239 const struct cmd_element
**cmd
,
1240 uint32_t line_num
, int use_daemon
)
1244 unsigned up_level
= 0;
1246 vline
= cmd_make_strvec(vty
->buf
);
1248 /* In case of comment line */
1252 /* Execute configuration command : this is strict match */
1253 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1255 /* The logic for trying parent nodes is in cmd_execute_command_real()
1256 * since calling ->node_exit() correctly is a bit involved. This is
1257 * also the only reason CMD_NO_LEVEL_UP exists.
1259 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1260 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1261 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1262 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1263 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1264 && ret
!= CMD_NO_LEVEL_UP
)
1265 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1268 if (ret
== CMD_NO_LEVEL_UP
)
1269 ret
= CMD_ERR_NO_MATCH
;
1271 if (ret
!= CMD_SUCCESS
&&
1272 ret
!= CMD_WARNING
&&
1273 ret
!= CMD_SUCCESS_DAEMON
) {
1274 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1276 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1277 ve
->line_num
= line_num
;
1279 vty
->error
= list_new();
1281 listnode_add(vty
->error
, ve
);
1284 cmd_free_strvec(vline
);
1289 /* Configuration make from file. */
1290 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1292 int ret
, error_ret
= 0;
1295 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1298 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1300 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1301 && ret
!= CMD_ERR_NOTHING_TODO
)
1312 /* Configuration from terminal */
1313 DEFUN (config_terminal
,
1314 config_terminal_cmd
,
1315 "configure [terminal]",
1316 "Configuration from vty interface\n"
1317 "Configuration terminal\n")
1319 return vty_config_enter(vty
, false, false);
1322 /* Enable command */
1326 "Turn on privileged mode command\n")
1328 /* If enable password is NULL, change to ENABLE_NODE */
1329 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1330 || vty
->type
== VTY_SHELL_SERV
)
1331 vty
->node
= ENABLE_NODE
;
1333 vty
->node
= AUTH_ENABLE_NODE
;
1338 /* Disable command */
1342 "Turn off privileged mode command\n")
1344 if (vty
->node
== ENABLE_NODE
)
1345 vty
->node
= VIEW_NODE
;
1349 /* Down vty node level. */
1353 "Exit current mode and down to previous mode\n")
1359 static int root_on_exit(struct vty
*vty
)
1364 vty
->status
= VTY_CLOSE
;
1368 void cmd_exit(struct vty
*vty
)
1370 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1372 if (cnode
->node_exit
) {
1373 if (!cnode
->node_exit(vty
))
1376 if (cnode
->parent_node
)
1377 vty
->node
= cnode
->parent_node
;
1378 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1386 "Exit current mode and down to previous mode\n")
1388 return config_exit(self
, vty
, argc
, argv
);
1392 /* End of configuration. */
1396 "End current mode and change to enable mode.\n")
1399 vty_config_exit(vty
);
1400 vty
->node
= ENABLE_NODE
;
1406 DEFUN (show_version
,
1410 "Displays zebra version\n")
1412 vty_out(vty
, "%s %s (%s) on %s(%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1413 cmd_hostname_get() ? cmd_hostname_get() : "", cmd_system_get(),
1415 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1416 #ifdef ENABLE_VERSION_BUILD_CONFIG
1417 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1422 /* Help display function for all node. */
1426 "Description of the interactive help system\n")
1429 "Quagga VTY provides advanced help feature. When you need help,\n\
1430 anytime at the command line please press '?'.\n\
1432 If nothing matches, the help list will be empty and you must backup\n\
1433 until entering a '?' shows the available options.\n\
1434 Two styles of help are provided:\n\
1435 1. Full help is available when you are ready to enter a\n\
1436 command argument (e.g. 'show ?') and describes each possible\n\
1438 2. Partial help is provided when an abbreviated argument is entered\n\
1439 and you want to know what arguments match the input\n\
1440 (e.g. 'show me?'.)\n\n");
1444 static void permute(struct graph_node
*start
, struct vty
*vty
)
1446 static struct list
*position
= NULL
;
1448 position
= list_new();
1450 struct cmd_token
*stok
= start
->data
;
1451 struct graph_node
*gnn
;
1452 struct listnode
*ln
;
1455 listnode_add(position
, start
);
1456 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1457 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1458 struct cmd_token
*tok
= gn
->data
;
1459 if (tok
->attr
& CMD_ATTR_HIDDEN
)
1461 else if (tok
->type
== END_TKN
|| gn
== start
) {
1463 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1464 struct cmd_token
*tt
= gnn
->data
;
1465 if (tt
->type
< SPECIAL_TKN
)
1466 vty_out(vty
, " %s", tt
->text
);
1469 vty_out(vty
, "...");
1473 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1474 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1483 list_delete_node(position
, listtail(position
));
1486 static void print_cmd(struct vty
*vty
, const char *cmd
)
1488 int i
, j
, len
= strlen(cmd
);
1493 for (i
= 0; i
< len
; i
++) {
1497 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1503 if (isspace(cmd
[i
])) {
1504 /* skip leading whitespace */
1507 /* skip trailing whitespace */
1510 /* skip all whitespace after opening brackets or pipe */
1511 if (strchr("(<[{|", cmd
[i
- 1])) {
1512 while (isspace(cmd
[i
+ 1]))
1516 /* skip repeated whitespace */
1517 if (isspace(cmd
[i
+ 1]))
1519 /* skip whitespace before closing brackets or pipe */
1520 if (strchr(")>]}|", cmd
[i
+ 1]))
1522 /* convert tabs to spaces */
1523 if (cmd
[i
] == '\t') {
1533 vty_out(vty
, "%s\n", buf
);
1536 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1538 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1541 cmd_finalize_node(node
);
1542 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1544 /* loop over all commands at this node */
1545 const struct cmd_element
*element
= NULL
;
1546 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1548 if ((element
= vector_slot(node
->cmd_vector
, i
)) &&
1549 !(element
->attr
& CMD_ATTR_HIDDEN
)) {
1551 print_cmd(vty
, element
->string
);
1557 /* Help display function for all node. */
1560 "list [permutations]",
1561 "Print command list\n"
1562 "Print all possible command permutations\n")
1564 return cmd_list_cmds(vty
, argc
== 2);
1567 DEFUN (show_commandtree
,
1568 show_commandtree_cmd
,
1569 "show commandtree [permutations]",
1571 "Show command tree\n"
1572 "Permutations that we are interested in\n")
1574 return cmd_list_cmds(vty
, argc
== 3);
1577 DEFUN_HIDDEN(show_cli_graph
,
1582 "Dump current command space as DOT graph\n")
1584 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1587 cmd_finalize_node(cn
);
1588 dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1590 vty_out(vty
, "%s\n", dot
);
1591 XFREE(MTYPE_TMP
, dot
);
1595 static int vty_write_config(struct vty
*vty
)
1598 struct cmd_node
*node
;
1603 nb_cli_show_config_prepare(running_config
, false);
1605 if (vty
->type
== VTY_TERM
) {
1606 vty_out(vty
, "\nCurrent configuration:\n");
1607 vty_out(vty
, "!\n");
1610 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1611 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1612 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1613 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1614 vty_out(vty
, "!\n");
1616 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1617 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1618 if ((*node
->config_write
)(vty
))
1619 vty_out(vty
, "!\n");
1622 if (vty
->type
== VTY_TERM
) {
1623 vty_out(vty
, "end\n");
1629 static int file_write_config(struct vty
*vty
)
1632 char *config_file
, *slash
;
1633 char *config_file_tmp
= NULL
;
1634 char *config_file_sav
= NULL
;
1635 int ret
= CMD_WARNING
;
1636 struct vty
*file_vty
;
1637 struct stat conf_stat
;
1642 /* Check and see if we are operating under vtysh configuration */
1643 if (host
.config
== NULL
) {
1645 "Can't save to configuration file, using vtysh.\n");
1650 config_file
= host
.config
;
1653 #define O_DIRECTORY 0
1655 slash
= strrchr(config_file
, '/');
1657 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1658 config_dir
[slash
- config_file
] = '\0';
1659 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1660 XFREE(MTYPE_TMP
, config_dir
);
1662 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1663 /* if dirfd is invalid, directory sync fails, but we're still OK */
1665 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1666 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1667 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1668 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1671 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1672 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1675 /* Open file to configuration write. */
1676 fd
= mkstemp(config_file_tmp
);
1678 vty_out(vty
, "Can't open configuration file %s.\n",
1682 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1683 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1684 config_file_tmp
, safe_strerror(errno
), errno
);
1688 /* Make vty for configuration file. */
1689 file_vty
= vty_new();
1691 file_vty
->type
= VTY_FILE
;
1693 /* Config file header print. */
1694 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1695 vty_time_print(file_vty
, 1);
1696 vty_out(file_vty
, "!\n");
1697 vty_write_config(file_vty
);
1698 vty_close(file_vty
);
1700 if (stat(config_file
, &conf_stat
) >= 0) {
1701 if (unlink(config_file_sav
) != 0)
1702 if (errno
!= ENOENT
) {
1704 "Can't unlink backup configuration file %s.\n",
1708 if (link(config_file
, config_file_sav
) != 0) {
1710 "Can't backup old configuration file %s.\n",
1717 if (rename(config_file_tmp
, config_file
) != 0) {
1718 vty_out(vty
, "Can't save configuration file %s.\n",
1725 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1729 if (ret
!= CMD_SUCCESS
)
1730 unlink(config_file_tmp
);
1733 XFREE(MTYPE_TMP
, config_file_tmp
);
1734 XFREE(MTYPE_TMP
, config_file_sav
);
1738 /* Write current configuration into file. */
1740 DEFUN (config_write
,
1742 "write [<file|memory|terminal>]",
1743 "Write running configuration to memory, network, or terminal\n"
1744 "Write to configuration file\n"
1745 "Write configuration currently in memory\n"
1746 "Write configuration to terminal\n")
1748 const int idx_type
= 1;
1750 // if command was 'write terminal' or 'write memory'
1751 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1752 return vty_write_config(vty
);
1755 return file_write_config(vty
);
1758 /* ALIAS_FIXME for 'write <terminal|memory>' */
1759 DEFUN (show_running_config
,
1760 show_running_config_cmd
,
1761 "show running-config",
1763 "running configuration (same as write terminal)\n")
1765 return vty_write_config(vty
);
1768 /* ALIAS_FIXME for 'write file' */
1769 DEFUN (copy_runningconf_startupconf
,
1770 copy_runningconf_startupconf_cmd
,
1771 "copy running-config startup-config",
1772 "Copy configuration\n"
1773 "Copy running config to... \n"
1774 "Copy running config to startup config (same as write file/memory)\n")
1776 return file_write_config(vty
);
1780 /* Write startup configuration into the terminal. */
1781 DEFUN (show_startup_config
,
1782 show_startup_config_cmd
,
1783 "show startup-config",
1785 "Contents of startup configuration\n")
1792 if (host
.config
== NULL
)
1795 confp
= fopen(host
.config
, "r");
1796 if (confp
== NULL
) {
1797 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1798 host
.config
, safe_strerror(errno
));
1802 while (fgets(buf
, BUFSIZ
, confp
)) {
1805 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1809 vty_out(vty
, "%s\n", buf
);
1817 int cmd_domainname_set(const char *domainname
)
1819 XFREE(MTYPE_HOST
, host
.domainname
);
1820 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1824 /* Hostname configuration */
1825 DEFUN(config_domainname
,
1828 "Set system's domain name\n"
1829 "This system's domain name\n")
1831 struct cmd_token
*word
= argv
[1];
1833 if (!isalpha((unsigned char)word
->arg
[0])) {
1834 vty_out(vty
, "Please specify string starting with alphabet\n");
1835 return CMD_WARNING_CONFIG_FAILED
;
1838 return cmd_domainname_set(word
->arg
);
1841 DEFUN(config_no_domainname
,
1843 "no domainname [DOMAINNAME]",
1845 "Reset system's domain name\n"
1846 "domain name of this router\n")
1848 return cmd_domainname_set(NULL
);
1851 int cmd_hostname_set(const char *hostname
)
1853 XFREE(MTYPE_HOST
, host
.name
);
1854 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1858 /* Hostname configuration */
1859 DEFUN (config_hostname
,
1862 "Set system's network name\n"
1863 "This system's network name\n")
1865 struct cmd_token
*word
= argv
[1];
1867 if (!isalnum((unsigned char)word
->arg
[0])) {
1869 "Please specify string starting with alphabet or number\n");
1870 return CMD_WARNING_CONFIG_FAILED
;
1873 /* With reference to RFC 1123 Section 2.1 */
1874 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1875 vty_out(vty
, "Hostname length should be less than %d chars\n",
1877 return CMD_WARNING_CONFIG_FAILED
;
1880 return cmd_hostname_set(word
->arg
);
1883 DEFUN (config_no_hostname
,
1885 "no hostname [HOSTNAME]",
1887 "Reset system's network name\n"
1888 "Host name of this router\n")
1890 return cmd_hostname_set(NULL
);
1893 /* VTY interface password set. */
1894 DEFUN (config_password
,
1896 "password [(8-8)] WORD",
1897 "Modify the terminal connection password\n"
1898 "Specifies a HIDDEN password will follow\n"
1899 "The password string\n")
1903 if (argc
== 3) // '8' was specified
1906 XFREE(MTYPE_HOST
, host
.password
);
1907 host
.password
= NULL
;
1908 if (host
.password_encrypt
)
1909 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1910 host
.password_encrypt
=
1911 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1915 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1917 "Please specify string starting with alphanumeric\n");
1918 return CMD_WARNING_CONFIG_FAILED
;
1922 XFREE(MTYPE_HOST
, host
.password
);
1923 host
.password
= NULL
;
1926 if (host
.password_encrypt
)
1927 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1928 host
.password_encrypt
=
1929 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1931 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1936 /* VTY interface password delete. */
1937 DEFUN (no_config_password
,
1941 "Modify the terminal connection password\n")
1943 bool warned
= false;
1945 if (host
.password
) {
1946 if (!vty_shell_serv(vty
)) {
1947 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1950 XFREE(MTYPE_HOST
, host
.password
);
1952 host
.password
= NULL
;
1954 if (host
.password_encrypt
) {
1955 if (!warned
&& !vty_shell_serv(vty
))
1956 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1957 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1959 host
.password_encrypt
= NULL
;
1964 /* VTY enable password set. */
1965 DEFUN (config_enable_password
,
1966 enable_password_cmd
,
1967 "enable password [(8-8)] WORD",
1968 "Modify enable password parameters\n"
1969 "Assign the privileged level password\n"
1970 "Specifies a HIDDEN password will follow\n"
1971 "The HIDDEN 'enable' password string\n")
1976 /* Crypt type is specified. */
1978 if (argv
[idx_8
]->arg
[0] == '8') {
1980 XFREE(MTYPE_HOST
, host
.enable
);
1983 if (host
.enable_encrypt
)
1984 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1985 host
.enable_encrypt
=
1986 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1990 vty_out(vty
, "Unknown encryption type.\n");
1991 return CMD_WARNING_CONFIG_FAILED
;
1995 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1997 "Please specify string starting with alphanumeric\n");
1998 return CMD_WARNING_CONFIG_FAILED
;
2002 XFREE(MTYPE_HOST
, host
.enable
);
2005 /* Plain password input. */
2007 if (host
.enable_encrypt
)
2008 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2009 host
.enable_encrypt
=
2010 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2012 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2017 /* VTY enable password delete. */
2018 DEFUN (no_config_enable_password
,
2019 no_enable_password_cmd
,
2020 "no enable password",
2022 "Modify enable password parameters\n"
2023 "Assign the privileged level password\n")
2025 bool warned
= false;
2028 if (!vty_shell_serv(vty
)) {
2029 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2032 XFREE(MTYPE_HOST
, host
.enable
);
2036 if (host
.enable_encrypt
) {
2037 if (!warned
&& !vty_shell_serv(vty
))
2038 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2039 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2041 host
.enable_encrypt
= NULL
;
2046 DEFUN (service_password_encrypt
,
2047 service_password_encrypt_cmd
,
2048 "service password-encryption",
2049 "Set up miscellaneous service\n"
2050 "Enable encrypted passwords\n")
2057 if (host
.password
) {
2058 if (host
.password_encrypt
)
2059 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2060 host
.password_encrypt
=
2061 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2064 if (host
.enable_encrypt
)
2065 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2066 host
.enable_encrypt
=
2067 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2073 DEFUN (no_service_password_encrypt
,
2074 no_service_password_encrypt_cmd
,
2075 "no service password-encryption",
2077 "Set up miscellaneous service\n"
2078 "Enable encrypted passwords\n")
2085 if (host
.password_encrypt
)
2086 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2087 host
.password_encrypt
= NULL
;
2089 if (host
.enable_encrypt
)
2090 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2091 host
.enable_encrypt
= NULL
;
2096 DEFUN (config_terminal_length
,
2097 config_terminal_length_cmd
,
2098 "terminal length (0-512)",
2099 "Set terminal line parameters\n"
2100 "Set number of lines on a screen\n"
2101 "Number of lines on screen (0 for no pausing)\n")
2105 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2110 DEFUN (config_terminal_no_length
,
2111 config_terminal_no_length_cmd
,
2112 "terminal no length",
2113 "Set terminal line parameters\n"
2115 "Set number of lines on a screen\n")
2121 DEFUN (service_terminal_length
,
2122 service_terminal_length_cmd
,
2123 "service terminal-length (0-512)",
2124 "Set up miscellaneous service\n"
2125 "System wide terminal length configuration\n"
2126 "Number of lines of VTY (0 means no line control)\n")
2130 host
.lines
= atoi(argv
[idx_number
]->arg
);
2135 DEFUN (no_service_terminal_length
,
2136 no_service_terminal_length_cmd
,
2137 "no service terminal-length [(0-512)]",
2139 "Set up miscellaneous service\n"
2140 "System wide terminal length configuration\n"
2141 "Number of lines of VTY (0 means no line control)\n")
2147 DEFUN_HIDDEN (do_echo
,
2150 "Echo a message back to the vty\n"
2151 "The message to echo\n")
2155 vty_out(vty
, "%s\n",
2156 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2158 XFREE(MTYPE_TMP
, message
);
2162 DEFUN (config_logmsg
,
2164 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2165 "Send a message to enabled logging destinations\n"
2167 "The message to send\n")
2169 int idx_log_level
= 1;
2170 int idx_message
= 2;
2174 level
= log_level_match(argv
[idx_log_level
]->arg
);
2175 if (level
== ZLOG_DISABLED
)
2176 return CMD_ERR_NO_MATCH
;
2179 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2181 XFREE(MTYPE_TMP
, message
);
2186 DEFUN (debug_memstats
,
2188 "[no] debug memstats-at-exit",
2191 "Print memory type statistics at exit\n")
2193 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2197 int cmd_banner_motd_file(const char *file
)
2199 int success
= CMD_SUCCESS
;
2204 rpath
= realpath(file
, p
);
2206 return CMD_ERR_NO_FILE
;
2207 in
= strstr(rpath
, SYSCONFDIR
);
2209 XFREE(MTYPE_HOST
, host
.motdfile
);
2210 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2212 success
= CMD_WARNING_CONFIG_FAILED
;
2217 void cmd_banner_motd_line(const char *line
)
2219 XFREE(MTYPE_HOST
, host
.motd
);
2220 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2223 DEFUN (banner_motd_file
,
2224 banner_motd_file_cmd
,
2225 "banner motd file FILE",
2228 "Banner from a file\n"
2232 const char *filename
= argv
[idx_file
]->arg
;
2233 int cmd
= cmd_banner_motd_file(filename
);
2235 if (cmd
== CMD_ERR_NO_FILE
)
2236 vty_out(vty
, "%s does not exist\n", filename
);
2237 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2238 vty_out(vty
, "%s must be in %s\n", filename
, SYSCONFDIR
);
2243 DEFUN (banner_motd_line
,
2244 banner_motd_line_cmd
,
2245 "banner motd line LINE...",
2248 "Banner from an input\n"
2254 argv_find(argv
, argc
, "LINE", &idx
);
2255 motd
= argv_concat(argv
, argc
, idx
);
2257 cmd_banner_motd_line(motd
);
2258 XFREE(MTYPE_TMP
, motd
);
2263 DEFUN (banner_motd_default
,
2264 banner_motd_default_cmd
,
2265 "banner motd default",
2266 "Set banner string\n"
2267 "Strings for motd\n"
2270 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2274 DEFUN (no_banner_motd
,
2278 "Set banner string\n"
2279 "Strings for motd\n")
2283 XFREE(MTYPE_HOST
, host
.motdfile
);
2284 host
.motdfile
= NULL
;
2288 DEFUN(allow_reserved_ranges
, allow_reserved_ranges_cmd
, "allow-reserved-ranges",
2289 "Allow using IPv4 (Class E) reserved IP space\n")
2291 host
.allow_reserved_ranges
= true;
2295 DEFUN(no_allow_reserved_ranges
, no_allow_reserved_ranges_cmd
,
2296 "no allow-reserved-ranges",
2297 NO_STR
"Allow using IPv4 (Class E) reserved IP space\n")
2299 host
.allow_reserved_ranges
= false;
2303 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2305 const struct cmd_node
*node
;
2306 const struct cmd_element
*cli
;
2311 char *pattern
= argv_concat(argv
, argc
, 1);
2312 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2313 XFREE(MTYPE_TMP
, pattern
);
2318 vty_out(vty
, "%% Invalid {...} expression\n");
2321 vty_out(vty
, "%% Bad repetition operator\n");
2324 vty_out(vty
, "%% Regex syntax error\n");
2327 vty_out(vty
, "%% Invalid collating element\n");
2330 vty_out(vty
, "%% Invalid character class name\n");
2334 "%% Regex ended with escape character (\\)\n");
2338 "%% Invalid number in \\digit construction\n");
2341 vty_out(vty
, "%% Unbalanced square brackets\n");
2344 vty_out(vty
, "%% Unbalanced parentheses\n");
2347 vty_out(vty
, "%% Unbalanced braces\n");
2351 "%% Invalid endpoint in range expression\n");
2354 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2362 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2363 node
= vector_slot(cmdvec
, i
);
2366 clis
= node
->cmd_vector
;
2367 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2368 cli
= vector_slot(clis
, j
);
2370 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2371 vty_out(vty
, " (%s) ", node
->name
);
2372 print_cmd(vty
, cli
->string
);
2385 "Find CLI command matching a regular expression\n"
2386 "Search pattern (POSIX regex)\n")
2388 return cmd_find_cmds(vty
, argv
, argc
);
2391 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2392 DEFUN(script
, script_cmd
, "script SCRIPT FUNCTION",
2393 "Test command - execute a function in a script\n"
2394 "Script name (same as filename in /etc/frr/scripts/)\n"
2395 "Function name (in the script)\n")
2399 (void)str2prefix("1.2.3.4/24", &p
);
2400 struct frrscript
*fs
= frrscript_new(argv
[1]->arg
);
2402 if (frrscript_load(fs
, argv
[2]->arg
, NULL
)) {
2404 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2405 argv
[1]->arg
, argv
[2]->arg
);
2408 int ret
= frrscript_call(fs
, argv
[2]->arg
, ("p", &p
));
2410 prefix2str(&p
, buf
, sizeof(buf
));
2411 vty_out(vty
, "p: %s\n", buf
);
2412 vty_out(vty
, "Script result: %d\n", ret
);
2414 frrscript_delete(fs
);
2420 /* Set config filename. Called from vty.c */
2421 void host_config_set(const char *filename
)
2423 XFREE(MTYPE_HOST
, host
.config
);
2424 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2427 const char *host_config_get(void)
2432 void cmd_show_lib_debugs(struct vty
*vty
)
2434 route_map_show_debug(vty
);
2437 void install_default(enum node_type node
)
2439 _install_element(node
, &config_exit_cmd
);
2440 _install_element(node
, &config_quit_cmd
);
2441 _install_element(node
, &config_end_cmd
);
2442 _install_element(node
, &config_help_cmd
);
2443 _install_element(node
, &config_list_cmd
);
2444 _install_element(node
, &show_cli_graph_cmd
);
2445 _install_element(node
, &find_cmd
);
2447 _install_element(node
, &config_write_cmd
);
2448 _install_element(node
, &show_running_config_cmd
);
2450 _install_element(node
, &autocomplete_cmd
);
2452 nb_cli_install_default(node
);
2455 /* Initialize command interface. Install basic nodes and commands.
2457 * terminal = 0 -- vtysh / no logging, no config control
2458 * terminal = 1 -- normal daemon
2459 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2460 void cmd_init(int terminal
)
2462 struct utsname names
;
2467 /* register command preprocessors */
2468 hook_register(cmd_execute
, handle_pipe_action
);
2469 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2471 varhandlers
= list_new();
2473 /* Allocate initial top vector of commands. */
2474 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2476 /* Default host value settings. */
2477 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2478 host
.system
= XSTRDUP(MTYPE_HOST
, names
.sysname
);
2479 host
.release
= XSTRDUP(MTYPE_HOST
, names
.release
);
2480 host
.version
= XSTRDUP(MTYPE_HOST
, names
.version
);
2482 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2483 if ((strcmp(names
.domainname
, "(none)") == 0))
2484 host
.domainname
= NULL
;
2486 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2488 host
.domainname
= NULL
;
2490 host
.password
= NULL
;
2493 host
.noconfig
= (terminal
< 0);
2495 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2496 host
.motdfile
= NULL
;
2497 host
.allow_reserved_ranges
= false;
2499 /* Install top nodes. */
2500 install_node(&view_node
);
2501 install_node(&enable_node
);
2502 install_node(&auth_node
);
2503 install_node(&auth_enable_node
);
2504 install_node(&config_node
);
2506 /* Each node's basic commands. */
2507 install_element(VIEW_NODE
, &show_version_cmd
);
2508 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2511 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2513 install_element(VIEW_NODE
, &config_list_cmd
);
2514 install_element(VIEW_NODE
, &config_exit_cmd
);
2515 install_element(VIEW_NODE
, &config_quit_cmd
);
2516 install_element(VIEW_NODE
, &config_help_cmd
);
2517 install_element(VIEW_NODE
, &config_enable_cmd
);
2518 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2519 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2520 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2521 install_element(VIEW_NODE
, &echo_cmd
);
2522 install_element(VIEW_NODE
, &autocomplete_cmd
);
2523 install_element(VIEW_NODE
, &find_cmd
);
2524 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2525 install_element(VIEW_NODE
, &script_cmd
);
2529 install_element(ENABLE_NODE
, &config_end_cmd
);
2530 install_element(ENABLE_NODE
, &config_disable_cmd
);
2531 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2532 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2533 install_element(ENABLE_NODE
, &config_write_cmd
);
2534 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2535 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2537 install_default(CONFIG_NODE
);
2540 workqueue_cmd_init();
2544 install_element(CONFIG_NODE
, &hostname_cmd
);
2545 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2546 install_element(CONFIG_NODE
, &domainname_cmd
);
2547 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2552 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2554 install_element(CONFIG_NODE
, &password_cmd
);
2555 install_element(CONFIG_NODE
, &no_password_cmd
);
2556 install_element(CONFIG_NODE
, &enable_password_cmd
);
2557 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2559 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2560 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2561 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2562 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2563 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2564 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2565 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2566 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2567 install_element(CONFIG_NODE
, &allow_reserved_ranges_cmd
);
2568 install_element(CONFIG_NODE
, &no_allow_reserved_ranges_cmd
);
2571 vrf_install_commands();
2575 grammar_sandbox_init();
2579 void cmd_terminate(void)
2581 struct cmd_node
*cmd_node
;
2583 hook_unregister(cmd_execute
, handle_pipe_action
);
2584 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2587 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2588 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2589 // deleting the graph delets the cmd_element as
2591 graph_delete_graph(cmd_node
->cmdgraph
);
2592 vector_free(cmd_node
->cmd_vector
);
2593 hash_clean(cmd_node
->cmd_hash
, NULL
);
2594 hash_free(cmd_node
->cmd_hash
);
2595 cmd_node
->cmd_hash
= NULL
;
2598 vector_free(cmdvec
);
2602 XFREE(MTYPE_HOST
, host
.name
);
2603 XFREE(MTYPE_HOST
, host
.system
);
2604 XFREE(MTYPE_HOST
, host
.release
);
2605 XFREE(MTYPE_HOST
, host
.version
);
2606 XFREE(MTYPE_HOST
, host
.domainname
);
2607 XFREE(MTYPE_HOST
, host
.password
);
2608 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2609 XFREE(MTYPE_HOST
, host
.enable
);
2610 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2611 XFREE(MTYPE_HOST
, host
.motdfile
);
2612 XFREE(MTYPE_HOST
, host
.config
);
2613 XFREE(MTYPE_HOST
, host
.motd
);
2615 list_delete(&varhandlers
);