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"
53 #include "frrscript.h"
55 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config");
56 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item");
63 /* clang-format off */
64 const struct message tokennames
[] = {
69 item(IPV4_PREFIX_TKN
),
71 item(IPV6_PREFIX_TKN
),
83 /* Command vector which includes some level of command lists. Normally
84 each daemon maintains each own cmdvec. */
87 /* Host information structure. */
90 /* for vtysh, put together CLI trees only when switching into node */
91 static bool defer_cli_tree
;
94 * Returns host.name if any, otherwise
95 * it returns the system hostname.
97 const char *cmd_hostname_get(void)
103 * Returns unix domainname
105 const char *cmd_domainname_get(void)
107 return host
.domainname
;
110 const char *cmd_system_get(void)
115 const char *cmd_release_get(void)
120 const char *cmd_version_get(void)
125 bool cmd_allow_reserved_ranges_get(void)
127 return host
.allow_reserved_ranges
;
130 const char *cmd_software_version_get(void)
132 return FRR_FULL_NAME
"/" FRR_VERSION
;
135 static int root_on_exit(struct vty
*vty
);
137 /* Standard command node structures. */
138 static struct cmd_node auth_node
= {
141 .prompt
= "Password: ",
144 static struct cmd_node view_node
= {
148 .node_exit
= root_on_exit
,
151 static struct cmd_node auth_enable_node
= {
152 .name
= "auth enable",
153 .node
= AUTH_ENABLE_NODE
,
154 .prompt
= "Password: ",
157 static struct cmd_node enable_node
= {
161 .node_exit
= root_on_exit
,
164 static int config_write_host(struct vty
*vty
);
165 static struct cmd_node config_node
= {
168 .parent_node
= ENABLE_NODE
,
169 .prompt
= "%s(config)# ",
170 .config_write
= config_write_host
,
171 .node_exit
= vty_config_node_exit
,
174 /* This is called from main when a daemon is invoked with -v or --version. */
175 void print_version(const char *progname
)
177 printf("%s version %s\n", progname
, FRR_VERSION
);
178 printf("%s\n", FRR_COPYRIGHT
);
179 #ifdef ENABLE_VERSION_BUILD_CONFIG
180 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
184 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
186 int cnt
= MAX(argc
- shift
, 0);
187 const char *argstr
[cnt
+ 1];
192 for (int i
= 0; i
< cnt
; i
++)
193 argstr
[i
] = argv
[i
+ shift
]->arg
;
195 return frrstr_join(argstr
, cnt
, " ");
198 vector
cmd_make_strvec(const char *string
)
203 const char *copy
= string
;
205 /* skip leading whitespace */
206 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
209 /* if the entire string was whitespace or a comment, return */
210 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
213 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
215 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
216 if (strlen(vector_slot(result
, i
)) == 0) {
217 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
218 vector_unset(result
, i
);
222 vector_compact(result
);
227 void cmd_free_strvec(vector v
)
229 frrstr_strvec_free(v
);
233 * Convenience function for accessing argv data.
237 * @param text definition snippet of the desired token
238 * @param index the starting index, and where to store the
239 * index of the found token if it exists
240 * @return 1 if found, 0 otherwise
242 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
245 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
246 if ((found
= strmatch(text
, argv
[i
]->text
)))
251 static unsigned int cmd_hash_key(const void *p
)
253 int size
= sizeof(p
);
255 return jhash(p
, size
, 0);
258 static bool cmd_hash_cmp(const void *a
, const void *b
)
263 /* Install top node of command vector. */
264 void install_node(struct cmd_node
*node
)
266 #define CMD_HASH_STR_SIZE 256
267 char hash_name
[CMD_HASH_STR_SIZE
];
269 vector_set_index(cmdvec
, node
->node
, node
);
270 node
->cmdgraph
= graph_new();
271 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
273 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, 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 (void)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
, 0, 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
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
358 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
360 cmd_graph_parse(graph
, cmd
);
361 cmd_graph_names(graph
);
362 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
363 graph_delete_graph(graph
);
366 void cmd_finalize_node(struct cmd_node
*cnode
)
368 if (cnode
->graph_built
)
371 hash_iterate(cnode
->cmd_hash
, cmd_finalize_iter
, cnode
);
372 cnode
->graph_built
= true;
375 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
377 struct cmd_node
*cnode
;
379 /* cmd_init hasn't been called */
381 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
386 cnode
= vector_lookup(cmdvec
, ntype
);
391 "\tnode %d does not exist.\n"
392 "\tplease call install_node() before uninstall_element()\n",
393 cmd
->name
, cmd
->string
, ntype
);
397 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
400 "\tnode %d (%s) does not have this command installed.\n"
401 "\tduplicate uninstall_element call?\n",
402 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
406 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
408 if (cnode
->graph_built
) {
409 struct graph
*graph
= graph_new();
410 struct cmd_token
*token
=
411 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
412 graph_new_node(graph
, token
,
413 (void (*)(void *)) & cmd_token_del
);
415 cmd_graph_parse(graph
, cmd
);
416 cmd_graph_names(graph
);
417 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
418 graph_delete_graph(graph
);
421 if (ntype
== VIEW_NODE
)
422 uninstall_element(ENABLE_NODE
, cmd
);
426 static const unsigned char itoa64
[] =
427 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
429 static void to64(char *s
, long v
, int n
)
432 *s
++ = itoa64
[v
& 0x3f];
437 static char *zencrypt(const char *passwd
)
442 gettimeofday(&tv
, 0);
444 to64(&salt
[0], frr_weak_random(), 3);
445 to64(&salt
[3], tv
.tv_usec
, 3);
448 return crypt(passwd
, salt
);
451 static bool full_cli
;
453 /* This function write configuration of this host. */
454 static int config_write_host(struct vty
*vty
)
458 name
= cmd_hostname_get();
459 if (name
&& name
[0] != '\0')
460 vty_out(vty
, "hostname %s\n", name
);
462 name
= cmd_domainname_get();
463 if (name
&& name
[0] != '\0')
464 vty_out(vty
, "domainname %s\n", name
);
466 if (cmd_allow_reserved_ranges_get())
467 vty_out(vty
, "allow-reserved-ranges\n");
469 /* The following are all configuration commands that are not sent to
470 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
471 * we would always display 'log syslog informational' in the config
472 * which would cause other daemons to then switch to syslog when they
477 if (host
.password_encrypt
)
478 vty_out(vty
, "password 8 %s\n",
479 host
.password_encrypt
);
480 if (host
.enable_encrypt
)
481 vty_out(vty
, "enable password 8 %s\n",
482 host
.enable_encrypt
);
485 vty_out(vty
, "password %s\n", host
.password
);
487 vty_out(vty
, "enable password %s\n",
490 log_config_write(vty
);
492 /* print disable always, but enable only if default is flipped
493 * => prep for future removal of compile-time knob
495 if (!cputime_enabled
)
496 vty_out(vty
, "no service cputime-stats\n");
497 #ifdef EXCLUDE_CPU_TIME
499 vty_out(vty
, "service cputime-stats\n");
502 if (!cputime_threshold
)
503 vty_out(vty
, "no service cputime-warning\n");
504 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
505 else /* again, always print non-default */
507 else if (cputime_threshold
!= 5000000)
509 vty_out(vty
, "service cputime-warning %lu\n",
510 cputime_threshold
/ 1000);
512 if (!walltime_threshold
)
513 vty_out(vty
, "no service walltime-warning\n");
514 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
515 else /* again, always print non-default */
517 else if (walltime_threshold
!= 5000000)
519 vty_out(vty
, "service walltime-warning %lu\n",
520 walltime_threshold
/ 1000);
523 vty_out(vty
, "service advanced-vty\n");
526 vty_out(vty
, "service password-encryption\n");
529 vty_out(vty
, "service terminal-length %d\n",
533 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
535 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
537 vty_out(vty
, "banner motd line %s\n", host
.motd
);
539 vty_out(vty
, "no banner motd\n");
542 if (debug_memstats_at_exit
)
543 vty_out(vty
, "!\ndebug memstats-at-exit\n");
548 /* Utility function for getting command graph. */
549 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
551 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
553 cmd_finalize_node(cnode
);
554 return cnode
->cmdgraph
;
557 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
559 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
560 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
566 * Compare function for cmd_token.
567 * Used with qsort to sort command completions.
569 static int compare_completions(const void *fst
, const void *snd
)
571 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
572 *secnd
= *(const struct cmd_token
* const *)snd
;
573 return strcmp(first
->text
, secnd
->text
);
577 * Takes a list of completions returned by command_complete,
578 * dedeuplicates them based on both text and description,
579 * sorts them, and returns them as a vector.
581 * @param completions linked list of cmd_token
582 * @return deduplicated and sorted vector with
584 vector
completions_to_vec(struct list
*completions
)
586 vector comps
= vector_init(VECTOR_MIN_SIZE
);
589 struct cmd_token
*token
, *cr
= NULL
;
590 unsigned int i
, exists
;
591 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
592 if (token
->type
== END_TKN
&& (cr
= token
))
595 // linear search for token in completions vector
597 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
598 struct cmd_token
*curr
= vector_slot(comps
, i
);
600 exists
= !strcmp(curr
->text
, token
->text
)
601 && !strcmp(curr
->desc
, token
->desc
);
603 exists
= !strcmp(curr
->text
, token
->text
);
604 #endif /* VTYSH_DEBUG */
608 vector_set(comps
, token
);
612 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
613 &compare_completions
);
615 // make <cr> the first element, if it is present
617 vector_set_index(comps
, vector_active(comps
), NULL
);
618 memmove(comps
->index
+ 1, comps
->index
,
619 (comps
->alloced
- 1) * sizeof(void *));
620 vector_set_index(comps
, 0, cr
);
626 * Generates a vector of cmd_token representing possible completions
627 * on the current input.
629 * @param vline the vectorized input line
630 * @param vty the vty with the node to match on
631 * @param status pointer to matcher status code
632 * @return vector of struct cmd_token * with possible completions
634 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
637 struct list
*completions
;
638 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
640 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
642 if (MATCHER_ERROR(rv
)) {
643 *status
= CMD_ERR_NO_MATCH
;
647 vector comps
= completions_to_vec(completions
);
648 list_delete(&completions
);
650 // set status code appropriately
651 switch (vector_active(comps
)) {
653 *status
= CMD_ERR_NO_MATCH
;
656 *status
= CMD_COMPLETE_FULL_MATCH
;
659 *status
= CMD_COMPLETE_LIST_MATCH
;
665 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
669 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
670 enum node_type onode
;
671 int orig_xpath_index
;
672 vector shifted_vline
;
676 orig_xpath_index
= vty
->xpath_index
;
677 vty
->node
= ENABLE_NODE
;
678 vty
->xpath_index
= 0;
679 /* We can try it on enable node, cos' the vty is authenticated
682 shifted_vline
= vector_init(vector_count(vline
));
684 for (index
= 1; index
< vector_active(vline
); index
++) {
685 vector_set_index(shifted_vline
, index
- 1,
686 vector_lookup(vline
, index
));
689 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
691 vector_free(shifted_vline
);
693 vty
->xpath_index
= orig_xpath_index
;
697 return cmd_complete_command_real(vline
, vty
, status
);
700 static struct list
*varhandlers
= NULL
;
702 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
706 const struct cmd_variable_handler
*cvh
;
710 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
712 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
713 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
715 if (cvh
->varname
&& (!token
->varname
716 || strcmp(cvh
->varname
, token
->varname
)))
718 cvh
->completions(tmpcomps
, token
);
726 for (i
= vector_active(tmpcomps
); i
; i
--) {
727 char *item
= vector_slot(tmpcomps
, i
- 1);
728 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
729 vector_set(comps
, item
);
731 XFREE(MTYPE_COMPLETION
, item
);
733 vector_free(tmpcomps
);
736 #define AUTOCOMP_INDENT 5
738 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
741 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
742 int lc
= AUTOCOMP_INDENT
;
743 size_t cs
= AUTOCOMP_INDENT
;
745 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
746 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
747 char *item
= vector_slot(comps
, j
);
748 itemlen
= strlen(item
);
750 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
751 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
753 if (lc
+ itemlen
+ 1 >= cols
) {
754 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
755 AUTOCOMP_INDENT
, "");
756 lc
= AUTOCOMP_INDENT
;
759 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
762 XFREE(MTYPE_COMPLETION
, item
);
763 vector_set_index(comps
, j
, NULL
);
768 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
773 for (; cvh
->completions
; cvh
++)
774 listnode_add(varhandlers
, (void *)cvh
);
777 DEFUN_HIDDEN (autocomplete
,
779 "autocomplete TYPE TEXT VARNAME",
780 "Autocompletion handler (internal, for vtysh)\n"
783 "cmd_token->varname\n")
785 struct cmd_token tok
;
786 vector comps
= vector_init(32);
789 memset(&tok
, 0, sizeof(tok
));
790 tok
.type
= atoi(argv
[1]->arg
);
791 tok
.text
= argv
[2]->arg
;
792 tok
.varname
= argv
[3]->arg
;
793 if (!strcmp(tok
.varname
, "-"))
796 cmd_variable_complete(&tok
, NULL
, comps
);
798 for (i
= 0; i
< vector_active(comps
); i
++) {
799 char *text
= vector_slot(comps
, i
);
800 vty_out(vty
, "%s\n", text
);
801 XFREE(MTYPE_COMPLETION
, text
);
809 * Generate possible tab-completions for the given input. This function only
810 * returns results that would result in a valid command if used as Readline
811 * completions (as is the case in vtysh). For instance, if the passed vline ends
812 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
814 * @param vline vectorized input line
816 * @param status location to store matcher status code in
817 * @return set of valid strings for use with Readline as tab-completions.
820 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
823 int original_node
= vty
->node
;
824 vector input_line
= vector_init(vector_count(vline
));
826 // if the first token is 'do' we'll want to execute the command in the
828 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
829 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
831 // construct the input line we'll be matching on
832 unsigned int offset
= (do_shortcut
) ? 1 : 0;
833 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
834 vector_set_index(input_line
, index
,
835 vector_lookup(vline
, index
+ offset
));
837 // get token completions -- this is a copying operation
838 vector comps
= NULL
, initial_comps
;
839 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
841 if (!MATCHER_ERROR(*status
)) {
842 assert(initial_comps
);
843 // filter out everything that is not suitable for a
845 comps
= vector_init(VECTOR_MIN_SIZE
);
846 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
848 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
849 if (token
->type
== WORD_TKN
)
850 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
852 else if (IS_VARYING_TOKEN(token
->type
)) {
853 const char *ref
= vector_lookup(
854 vline
, vector_active(vline
) - 1);
855 cmd_variable_complete(token
, ref
, comps
);
858 vector_free(initial_comps
);
860 // since we filtered results, we need to re-set status code
861 switch (vector_active(comps
)) {
863 *status
= CMD_ERR_NO_MATCH
;
866 *status
= CMD_COMPLETE_FULL_MATCH
;
869 *status
= CMD_COMPLETE_LIST_MATCH
;
872 // copy completions text into an array of char*
873 ret
= XMALLOC(MTYPE_TMP
,
874 (vector_active(comps
) + 1) * sizeof(char *));
876 for (i
= 0; i
< vector_active(comps
); i
++) {
877 ret
[i
] = vector_slot(comps
, i
);
879 // set the last element to NULL, because this array is used in
880 // a Readline completion_generator function which expects NULL
881 // as a sentinel value
885 } else if (initial_comps
)
886 vector_free(initial_comps
);
888 // comps should always be null here
891 // free the adjusted input line
892 vector_free(input_line
);
894 // reset vty->node to its original value
895 vty
->node
= original_node
;
900 /* return parent node */
901 /* MUST eventually converge on CONFIG_NODE */
902 enum node_type
node_parent(enum node_type node
)
904 struct cmd_node
*cnode
;
906 assert(node
> CONFIG_NODE
);
908 cnode
= vector_lookup(cmdvec
, node
);
910 return cnode
->parent_node
;
913 /* Execute command by argument vline vector. */
914 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
916 const struct cmd_element
**cmd
,
917 unsigned int up_level
)
919 struct list
*argv_list
;
920 enum matcher_rv status
;
921 const struct cmd_element
*matched_element
= NULL
;
923 int xpath_index
= vty
->xpath_index
;
924 int node
= vty
->node
;
926 /* only happens for legacy split config file load; need to check for
927 * a match before calling node_exit handlers below
929 for (i
= 0; i
< up_level
; i
++) {
930 struct cmd_node
*cnode
;
932 if (node
<= CONFIG_NODE
)
933 return CMD_NO_LEVEL_UP
;
935 cnode
= vector_slot(cmdvec
, node
);
936 node
= node_parent(node
);
938 if (xpath_index
> 0 && !cnode
->no_xpath
)
942 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
943 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
946 *cmd
= matched_element
;
948 // if matcher error, return corresponding CMD_ERR
949 if (MATCHER_ERROR(status
)) {
951 list_delete(&argv_list
);
953 case MATCHER_INCOMPLETE
:
954 return CMD_ERR_INCOMPLETE
;
955 case MATCHER_AMBIGUOUS
:
956 return CMD_ERR_AMBIGUOUS
;
957 case MATCHER_NO_MATCH
:
959 return CMD_ERR_NO_MATCH
;
963 for (i
= 0; i
< up_level
; i
++)
966 // build argv array from argv list
967 struct cmd_token
**argv
= XMALLOC(
968 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
970 struct cmd_token
*token
;
973 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
976 int argc
= argv_list
->count
;
979 if (matched_element
->daemon
)
980 ret
= CMD_SUCCESS_DAEMON
;
983 /* Clear array of enqueued configuration changes. */
984 vty
->num_cfg_changes
= 0;
985 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
987 /* Regenerate candidate configuration if necessary. */
988 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
989 && running_config
->version
990 > vty
->candidate_config
->version
)
991 nb_config_replace(vty
->candidate_config
,
992 running_config
, true);
995 * Perform pending commit (if any) before executing
998 if (!(matched_element
->attr
& CMD_ATTR_YANG
))
999 (void)nb_cli_pending_commit_check(vty
);
1002 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
1005 // delete list and cmd_token's in it
1006 list_delete(&argv_list
);
1007 XFREE(MTYPE_TMP
, argv
);
1013 * Execute a given command, handling things like "do ..." and checking
1014 * whether the given command might apply at a parent node if doesn't
1015 * apply for the current node.
1017 * @param vline Command line input, vector of char* where each element is
1019 * @param vty The vty context in which the command should be executed.
1020 * @param cmd Pointer where the struct cmd_element of the matched command
1021 * will be stored, if any. May be set to NULL if this info is
1023 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1024 * @return The status of the command that has been executed or an error code
1025 * as to why no command could be executed.
1027 int cmd_execute_command(vector vline
, struct vty
*vty
,
1028 const struct cmd_element
**cmd
, int vtysh
)
1030 int ret
, saved_ret
= 0;
1031 enum node_type onode
, try_node
;
1032 int orig_xpath_index
;
1034 onode
= try_node
= vty
->node
;
1035 orig_xpath_index
= vty
->xpath_index
;
1037 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1038 vector shifted_vline
;
1041 vty
->node
= ENABLE_NODE
;
1042 vty
->xpath_index
= 0;
1043 /* We can try it on enable node, cos' the vty is authenticated
1046 shifted_vline
= vector_init(vector_count(vline
));
1048 for (index
= 1; index
< vector_active(vline
); index
++)
1049 vector_set_index(shifted_vline
, index
- 1,
1050 vector_lookup(vline
, index
));
1052 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1055 vector_free(shifted_vline
);
1057 vty
->xpath_index
= orig_xpath_index
;
1062 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1067 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1068 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1069 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1070 /* This assumes all nodes above CONFIG_NODE are childs of
1072 while (vty
->node
> CONFIG_NODE
) {
1073 struct cmd_node
*cnode
= vector_slot(cmdvec
, try_node
);
1075 try_node
= node_parent(try_node
);
1076 vty
->node
= try_node
;
1077 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1080 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1082 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1083 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1084 || ret
== CMD_NOT_MY_INSTANCE
1085 || ret
== CMD_WARNING_CONFIG_FAILED
)
1088 /* no command succeeded, reset the vty to the original node */
1090 vty
->xpath_index
= orig_xpath_index
;
1093 /* return command status for original node */
1098 * Execute a given command, matching it strictly against the current node.
1099 * This mode is used when reading config files.
1101 * @param vline Command line input, vector of char* where each element is
1103 * @param vty The vty context in which the command should be executed.
1104 * @param cmd Pointer where the struct cmd_element* of the matched command
1105 * will be stored, if any. May be set to NULL if this info is
1107 * @return The status of the command that has been executed or an error code
1108 * as to why no command could be executed.
1110 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1111 const struct cmd_element
**cmd
)
1113 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1117 * Hook for preprocessing command string before executing.
1119 * All subscribers are called with the raw command string that is to be
1120 * executed. If any changes are to be made, a new string should be allocated
1121 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1122 * is then responsible for freeing this string.
1124 * All processing functions must be mutually exclusive in their action, i.e. if
1125 * one subscriber decides to modify the command, all others must not modify it
1126 * when called. Feeding the output of one processing command into a subsequent
1127 * one is not supported.
1129 * This hook is intentionally internal to the command processing system.
1132 * The raw command string.
1135 * The result of any processing.
1137 DECLARE_HOOK(cmd_execute
,
1138 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1139 (vty
, cmd_in
, cmd_out
));
1140 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1141 (vty
, cmd_in
, cmd_out
));
1143 /* Hook executed after a CLI command. */
1144 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1146 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1150 * cmd_execute hook subscriber to handle `|` actions.
1152 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1156 char *orig
, *working
, *token
, *u
;
1157 char *pipe
= strstr(cmd_in
, "| ");
1163 /* duplicate string for processing purposes, not including pipe */
1164 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1166 /* retrieve action */
1167 token
= strsep(&working
, " ");
1170 /* match result to known actions */
1171 if (strmatch(token
, "include")) {
1172 /* the remaining text should be a regexp */
1173 char *regexp
= working
;
1176 vty_out(vty
, "%% Need a regexp to filter with\n");
1181 bool succ
= vty_set_include(vty
, regexp
);
1184 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1188 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1192 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1198 XFREE(MTYPE_TMP
, orig
);
1202 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1205 vty_set_include(vty
, NULL
);
1210 int cmd_execute(struct vty
*vty
, const char *cmd
,
1211 const struct cmd_element
**matched
, int vtysh
)
1214 char *cmd_out
= NULL
;
1215 const char *cmd_exec
= NULL
;
1218 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1224 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1226 vline
= cmd_make_strvec(cmd_exec
);
1229 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1230 cmd_free_strvec(vline
);
1236 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1238 XFREE(MTYPE_TMP
, cmd_out
);
1245 * Parse one line of config, walking up the parse tree attempting to find a
1248 * @param vty The vty context in which the command should be executed.
1249 * @param cmd Pointer where the struct cmd_element* of the match command
1250 * will be stored, if any. May be set to NULL if this info is
1252 * @param use_daemon Boolean to control whether or not we match on
1253 * CMD_SUCCESS_DAEMON
1255 * @return The status of the command that has been executed or an error code
1256 * as to why no command could be executed.
1258 int command_config_read_one_line(struct vty
*vty
,
1259 const struct cmd_element
**cmd
,
1260 uint32_t line_num
, int use_daemon
)
1264 unsigned up_level
= 0;
1266 vline
= cmd_make_strvec(vty
->buf
);
1268 /* In case of comment line */
1272 /* Execute configuration command : this is strict match */
1273 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1275 /* The logic for trying parent nodes is in cmd_execute_command_real()
1276 * since calling ->node_exit() correctly is a bit involved. This is
1277 * also the only reason CMD_NO_LEVEL_UP exists.
1279 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1280 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1281 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1282 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1283 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1284 && ret
!= CMD_NO_LEVEL_UP
)
1285 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1288 if (ret
== CMD_NO_LEVEL_UP
)
1289 ret
= CMD_ERR_NO_MATCH
;
1291 if (ret
!= CMD_SUCCESS
&&
1292 ret
!= CMD_WARNING
&&
1293 ret
!= CMD_SUCCESS_DAEMON
) {
1294 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1296 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1297 ve
->line_num
= line_num
;
1299 vty
->error
= list_new();
1301 listnode_add(vty
->error
, ve
);
1304 cmd_free_strvec(vline
);
1309 /* Configuration make from file. */
1310 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1312 int ret
, error_ret
= 0;
1315 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1318 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1320 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1321 && ret
!= CMD_ERR_NOTHING_TODO
)
1332 /* Configuration from terminal */
1333 DEFUN (config_terminal
,
1334 config_terminal_cmd
,
1335 "configure [terminal]",
1336 "Configuration from vty interface\n"
1337 "Configuration terminal\n")
1339 return vty_config_enter(vty
, false, false);
1342 /* Enable command */
1346 "Turn on privileged mode command\n")
1348 /* If enable password is NULL, change to ENABLE_NODE */
1349 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1350 || vty
->type
== VTY_SHELL_SERV
)
1351 vty
->node
= ENABLE_NODE
;
1353 vty
->node
= AUTH_ENABLE_NODE
;
1358 /* Disable command */
1362 "Turn off privileged mode command\n")
1364 if (vty
->node
== ENABLE_NODE
)
1365 vty
->node
= VIEW_NODE
;
1369 /* Down vty node level. */
1373 "Exit current mode and down to previous mode\n")
1379 static int root_on_exit(struct vty
*vty
)
1384 vty
->status
= VTY_CLOSE
;
1388 void cmd_exit(struct vty
*vty
)
1390 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1392 if (cnode
->node_exit
) {
1393 if (!cnode
->node_exit(vty
))
1396 if (cnode
->parent_node
)
1397 vty
->node
= cnode
->parent_node
;
1398 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1406 "Exit current mode and down to previous mode\n")
1408 return config_exit(self
, vty
, argc
, argv
);
1412 /* End of configuration. */
1416 "End current mode and change to enable mode.\n")
1419 vty_config_exit(vty
);
1420 vty
->node
= ENABLE_NODE
;
1426 DEFUN (show_version
,
1430 "Displays zebra version\n")
1432 vty_out(vty
, "%s %s (%s) on %s(%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1433 cmd_hostname_get() ? cmd_hostname_get() : "", cmd_system_get(),
1435 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1436 #ifdef ENABLE_VERSION_BUILD_CONFIG
1437 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1442 /* Help display function for all node. */
1446 "Description of the interactive help system\n")
1449 "Quagga VTY provides advanced help feature. When you need help,\n\
1450 anytime at the command line please press '?'.\n\
1452 If nothing matches, the help list will be empty and you must backup\n\
1453 until entering a '?' shows the available options.\n\
1454 Two styles of help are provided:\n\
1455 1. Full help is available when you are ready to enter a\n\
1456 command argument (e.g. 'show ?') and describes each possible\n\
1458 2. Partial help is provided when an abbreviated argument is entered\n\
1459 and you want to know what arguments match the input\n\
1460 (e.g. 'show me?'.)\n\n");
1464 static void permute(struct graph_node
*start
, struct vty
*vty
)
1466 static struct list
*position
= NULL
;
1468 position
= list_new();
1470 struct cmd_token
*stok
= start
->data
;
1471 struct graph_node
*gnn
;
1472 struct listnode
*ln
;
1475 listnode_add(position
, start
);
1476 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1477 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1478 struct cmd_token
*tok
= gn
->data
;
1479 if (tok
->attr
& CMD_ATTR_HIDDEN
)
1481 else if (tok
->type
== END_TKN
|| gn
== start
) {
1483 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1484 struct cmd_token
*tt
= gnn
->data
;
1485 if (tt
->type
< SPECIAL_TKN
)
1486 vty_out(vty
, " %s", tt
->text
);
1489 vty_out(vty
, "...");
1493 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1494 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1503 list_delete_node(position
, listtail(position
));
1506 static void print_cmd(struct vty
*vty
, const char *cmd
)
1508 int i
, j
, len
= strlen(cmd
);
1513 for (i
= 0; i
< len
; i
++) {
1517 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1523 if (isspace(cmd
[i
])) {
1524 /* skip leading whitespace */
1527 /* skip trailing whitespace */
1530 /* skip all whitespace after opening brackets or pipe */
1531 if (strchr("(<[{|", cmd
[i
- 1])) {
1532 while (isspace(cmd
[i
+ 1]))
1536 /* skip repeated whitespace */
1537 if (isspace(cmd
[i
+ 1]))
1539 /* skip whitespace before closing brackets or pipe */
1540 if (strchr(")>]}|", cmd
[i
+ 1]))
1542 /* convert tabs to spaces */
1543 if (cmd
[i
] == '\t') {
1553 vty_out(vty
, "%s\n", buf
);
1556 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1558 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1561 cmd_finalize_node(node
);
1562 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1564 /* loop over all commands at this node */
1565 const struct cmd_element
*element
= NULL
;
1566 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1568 if ((element
= vector_slot(node
->cmd_vector
, i
)) &&
1569 !(element
->attr
& CMD_ATTR_HIDDEN
)) {
1571 print_cmd(vty
, element
->string
);
1577 /* Help display function for all node. */
1580 "list [permutations]",
1581 "Print command list\n"
1582 "Print all possible command permutations\n")
1584 return cmd_list_cmds(vty
, argc
== 2);
1587 DEFUN (show_commandtree
,
1588 show_commandtree_cmd
,
1589 "show commandtree [permutations]",
1591 "Show command tree\n"
1592 "Permutations that we are interested in\n")
1594 return cmd_list_cmds(vty
, argc
== 3);
1597 DEFUN_HIDDEN(show_cli_graph
,
1602 "Dump current command space as DOT graph\n")
1604 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1607 cmd_finalize_node(cn
);
1608 dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1610 vty_out(vty
, "%s\n", dot
);
1611 XFREE(MTYPE_TMP
, dot
);
1615 static int vty_write_config(struct vty
*vty
)
1618 struct cmd_node
*node
;
1623 nb_cli_show_config_prepare(running_config
, false);
1625 if (vty
->type
== VTY_TERM
) {
1626 vty_out(vty
, "\nCurrent configuration:\n");
1627 vty_out(vty
, "!\n");
1630 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1631 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1632 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1633 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1634 vty_out(vty
, "!\n");
1636 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1637 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1638 if ((*node
->config_write
)(vty
))
1639 vty_out(vty
, "!\n");
1642 if (vty
->type
== VTY_TERM
) {
1643 vty_out(vty
, "end\n");
1649 static int file_write_config(struct vty
*vty
)
1652 char *config_file
, *slash
;
1653 char *config_file_tmp
= NULL
;
1654 char *config_file_sav
= NULL
;
1655 int ret
= CMD_WARNING
;
1656 struct vty
*file_vty
;
1657 struct stat conf_stat
;
1662 /* Check and see if we are operating under vtysh configuration */
1663 if (host
.config
== NULL
) {
1665 "Can't save to configuration file, using vtysh.\n");
1670 config_file
= host
.config
;
1673 #define O_DIRECTORY 0
1675 slash
= strrchr(config_file
, '/');
1677 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1678 config_dir
[slash
- config_file
] = '\0';
1679 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1680 XFREE(MTYPE_TMP
, config_dir
);
1682 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1683 /* if dirfd is invalid, directory sync fails, but we're still OK */
1685 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1686 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1687 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1688 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1691 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1692 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1695 /* Open file to configuration write. */
1696 fd
= mkstemp(config_file_tmp
);
1698 vty_out(vty
, "Can't open configuration file %s.\n",
1702 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1703 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1704 config_file_tmp
, safe_strerror(errno
), errno
);
1708 /* Make vty for configuration file. */
1709 file_vty
= vty_new();
1711 file_vty
->type
= VTY_FILE
;
1713 /* Config file header print. */
1714 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1715 vty_time_print(file_vty
, 1);
1716 vty_out(file_vty
, "!\n");
1717 vty_write_config(file_vty
);
1718 vty_close(file_vty
);
1720 if (stat(config_file
, &conf_stat
) >= 0) {
1721 if (unlink(config_file_sav
) != 0)
1722 if (errno
!= ENOENT
) {
1724 "Can't unlink backup configuration file %s.\n",
1728 if (link(config_file
, config_file_sav
) != 0) {
1730 "Can't backup old configuration file %s.\n",
1737 if (rename(config_file_tmp
, config_file
) != 0) {
1738 vty_out(vty
, "Can't save configuration file %s.\n",
1745 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1749 if (ret
!= CMD_SUCCESS
)
1750 unlink(config_file_tmp
);
1753 XFREE(MTYPE_TMP
, config_file_tmp
);
1754 XFREE(MTYPE_TMP
, config_file_sav
);
1758 /* Write current configuration into file. */
1760 DEFUN (config_write
,
1762 "write [<file|memory|terminal>]",
1763 "Write running configuration to memory, network, or terminal\n"
1764 "Write to configuration file\n"
1765 "Write configuration currently in memory\n"
1766 "Write configuration to terminal\n")
1768 const int idx_type
= 1;
1770 // if command was 'write terminal' or 'write memory'
1771 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1772 return vty_write_config(vty
);
1775 return file_write_config(vty
);
1778 /* ALIAS_FIXME for 'write <terminal|memory>' */
1779 DEFUN (show_running_config
,
1780 show_running_config_cmd
,
1781 "show running-config",
1783 "running configuration (same as write terminal)\n")
1785 return vty_write_config(vty
);
1788 /* ALIAS_FIXME for 'write file' */
1789 DEFUN (copy_runningconf_startupconf
,
1790 copy_runningconf_startupconf_cmd
,
1791 "copy running-config startup-config",
1792 "Copy configuration\n"
1793 "Copy running config to... \n"
1794 "Copy running config to startup config (same as write file/memory)\n")
1796 return file_write_config(vty
);
1800 /* Write startup configuration into the terminal. */
1801 DEFUN (show_startup_config
,
1802 show_startup_config_cmd
,
1803 "show startup-config",
1805 "Contents of startup configuration\n")
1812 if (host
.config
== NULL
)
1815 confp
= fopen(host
.config
, "r");
1816 if (confp
== NULL
) {
1817 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1818 host
.config
, safe_strerror(errno
));
1822 while (fgets(buf
, BUFSIZ
, confp
)) {
1825 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1829 vty_out(vty
, "%s\n", buf
);
1837 int cmd_domainname_set(const char *domainname
)
1839 XFREE(MTYPE_HOST
, host
.domainname
);
1840 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1844 /* Hostname configuration */
1845 DEFUN(config_domainname
,
1848 "Set system's domain name\n"
1849 "This system's domain name\n")
1851 struct cmd_token
*word
= argv
[1];
1853 if (!isalpha((unsigned char)word
->arg
[0])) {
1854 vty_out(vty
, "Please specify string starting with alphabet\n");
1855 return CMD_WARNING_CONFIG_FAILED
;
1858 return cmd_domainname_set(word
->arg
);
1861 DEFUN(config_no_domainname
,
1863 "no domainname [DOMAINNAME]",
1865 "Reset system's domain name\n"
1866 "domain name of this router\n")
1868 return cmd_domainname_set(NULL
);
1871 int cmd_hostname_set(const char *hostname
)
1873 XFREE(MTYPE_HOST
, host
.name
);
1874 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1878 /* Hostname configuration */
1879 DEFUN (config_hostname
,
1882 "Set system's network name\n"
1883 "This system's network name\n")
1885 struct cmd_token
*word
= argv
[1];
1887 if (!isalnum((unsigned char)word
->arg
[0])) {
1889 "Please specify string starting with alphabet or number\n");
1890 return CMD_WARNING_CONFIG_FAILED
;
1893 /* With reference to RFC 1123 Section 2.1 */
1894 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1895 vty_out(vty
, "Hostname length should be less than %d chars\n",
1897 return CMD_WARNING_CONFIG_FAILED
;
1900 return cmd_hostname_set(word
->arg
);
1903 DEFUN (config_no_hostname
,
1905 "no hostname [HOSTNAME]",
1907 "Reset system's network name\n"
1908 "Host name of this router\n")
1910 return cmd_hostname_set(NULL
);
1913 /* VTY interface password set. */
1914 DEFUN (config_password
,
1916 "password [(8-8)] WORD",
1917 "Modify the terminal connection password\n"
1918 "Specifies a HIDDEN password will follow\n"
1919 "The password string\n")
1923 if (argc
== 3) // '8' was specified
1926 XFREE(MTYPE_HOST
, host
.password
);
1927 host
.password
= NULL
;
1928 if (host
.password_encrypt
)
1929 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1930 host
.password_encrypt
=
1931 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1935 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1937 "Please specify string starting with alphanumeric\n");
1938 return CMD_WARNING_CONFIG_FAILED
;
1942 XFREE(MTYPE_HOST
, host
.password
);
1943 host
.password
= NULL
;
1946 if (host
.password_encrypt
)
1947 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1948 host
.password_encrypt
=
1949 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1951 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1956 /* VTY interface password delete. */
1957 DEFUN (no_config_password
,
1961 "Modify the terminal connection password\n")
1963 bool warned
= false;
1965 if (host
.password
) {
1966 if (!vty_shell_serv(vty
)) {
1967 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1970 XFREE(MTYPE_HOST
, host
.password
);
1972 host
.password
= NULL
;
1974 if (host
.password_encrypt
) {
1975 if (!warned
&& !vty_shell_serv(vty
))
1976 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1977 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1979 host
.password_encrypt
= NULL
;
1984 /* VTY enable password set. */
1985 DEFUN (config_enable_password
,
1986 enable_password_cmd
,
1987 "enable password [(8-8)] WORD",
1988 "Modify enable password parameters\n"
1989 "Assign the privileged level password\n"
1990 "Specifies a HIDDEN password will follow\n"
1991 "The HIDDEN 'enable' password string\n")
1996 /* Crypt type is specified. */
1998 if (argv
[idx_8
]->arg
[0] == '8') {
2000 XFREE(MTYPE_HOST
, host
.enable
);
2003 if (host
.enable_encrypt
)
2004 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2005 host
.enable_encrypt
=
2006 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2010 vty_out(vty
, "Unknown encryption type.\n");
2011 return CMD_WARNING_CONFIG_FAILED
;
2015 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2017 "Please specify string starting with alphanumeric\n");
2018 return CMD_WARNING_CONFIG_FAILED
;
2022 XFREE(MTYPE_HOST
, host
.enable
);
2025 /* Plain password input. */
2027 if (host
.enable_encrypt
)
2028 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2029 host
.enable_encrypt
=
2030 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2032 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2037 /* VTY enable password delete. */
2038 DEFUN (no_config_enable_password
,
2039 no_enable_password_cmd
,
2040 "no enable password",
2042 "Modify enable password parameters\n"
2043 "Assign the privileged level password\n")
2045 bool warned
= false;
2048 if (!vty_shell_serv(vty
)) {
2049 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2052 XFREE(MTYPE_HOST
, host
.enable
);
2056 if (host
.enable_encrypt
) {
2057 if (!warned
&& !vty_shell_serv(vty
))
2058 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2059 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2061 host
.enable_encrypt
= NULL
;
2066 DEFUN (service_password_encrypt
,
2067 service_password_encrypt_cmd
,
2068 "service password-encryption",
2069 "Set up miscellaneous service\n"
2070 "Enable encrypted passwords\n")
2077 if (host
.password
) {
2078 if (host
.password_encrypt
)
2079 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2080 host
.password_encrypt
=
2081 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2084 if (host
.enable_encrypt
)
2085 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2086 host
.enable_encrypt
=
2087 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2093 DEFUN (no_service_password_encrypt
,
2094 no_service_password_encrypt_cmd
,
2095 "no service password-encryption",
2097 "Set up miscellaneous service\n"
2098 "Enable encrypted passwords\n")
2105 if (host
.password_encrypt
)
2106 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2107 host
.password_encrypt
= NULL
;
2109 if (host
.enable_encrypt
)
2110 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2111 host
.enable_encrypt
= NULL
;
2116 DEFUN (config_terminal_length
,
2117 config_terminal_length_cmd
,
2118 "terminal length (0-512)",
2119 "Set terminal line parameters\n"
2120 "Set number of lines on a screen\n"
2121 "Number of lines on screen (0 for no pausing)\n")
2125 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2130 DEFUN (config_terminal_no_length
,
2131 config_terminal_no_length_cmd
,
2132 "terminal no length",
2133 "Set terminal line parameters\n"
2135 "Set number of lines on a screen\n")
2141 DEFUN (service_terminal_length
,
2142 service_terminal_length_cmd
,
2143 "service terminal-length (0-512)",
2144 "Set up miscellaneous service\n"
2145 "System wide terminal length configuration\n"
2146 "Number of lines of VTY (0 means no line control)\n")
2150 host
.lines
= atoi(argv
[idx_number
]->arg
);
2155 DEFUN (no_service_terminal_length
,
2156 no_service_terminal_length_cmd
,
2157 "no service terminal-length [(0-512)]",
2159 "Set up miscellaneous service\n"
2160 "System wide terminal length configuration\n"
2161 "Number of lines of VTY (0 means no line control)\n")
2167 DEFUN_HIDDEN (do_echo
,
2170 "Echo a message back to the vty\n"
2171 "The message to echo\n")
2175 vty_out(vty
, "%s\n",
2176 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2178 XFREE(MTYPE_TMP
, message
);
2182 DEFUN (config_logmsg
,
2184 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2185 "Send a message to enabled logging destinations\n"
2187 "The message to send\n")
2189 int idx_log_level
= 1;
2190 int idx_message
= 2;
2194 level
= log_level_match(argv
[idx_log_level
]->arg
);
2195 if (level
== ZLOG_DISABLED
)
2196 return CMD_ERR_NO_MATCH
;
2199 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2201 XFREE(MTYPE_TMP
, message
);
2206 DEFUN (debug_memstats
,
2208 "[no] debug memstats-at-exit",
2211 "Print memory type statistics at exit\n")
2213 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2217 int cmd_banner_motd_file(const char *file
)
2219 int success
= CMD_SUCCESS
;
2224 rpath
= realpath(file
, p
);
2226 return CMD_ERR_NO_FILE
;
2227 in
= strstr(rpath
, SYSCONFDIR
);
2229 XFREE(MTYPE_HOST
, host
.motdfile
);
2230 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2232 success
= CMD_WARNING_CONFIG_FAILED
;
2237 void cmd_banner_motd_line(const char *line
)
2239 XFREE(MTYPE_HOST
, host
.motd
);
2240 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2243 DEFUN (banner_motd_file
,
2244 banner_motd_file_cmd
,
2245 "banner motd file FILE",
2248 "Banner from a file\n"
2252 const char *filename
= argv
[idx_file
]->arg
;
2253 int cmd
= cmd_banner_motd_file(filename
);
2255 if (cmd
== CMD_ERR_NO_FILE
)
2256 vty_out(vty
, "%s does not exist\n", filename
);
2257 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2258 vty_out(vty
, "%s must be in %s\n", filename
, SYSCONFDIR
);
2263 DEFUN (banner_motd_line
,
2264 banner_motd_line_cmd
,
2265 "banner motd line LINE...",
2268 "Banner from an input\n"
2274 argv_find(argv
, argc
, "LINE", &idx
);
2275 motd
= argv_concat(argv
, argc
, idx
);
2277 cmd_banner_motd_line(motd
);
2278 XFREE(MTYPE_TMP
, motd
);
2283 DEFUN (banner_motd_default
,
2284 banner_motd_default_cmd
,
2285 "banner motd default",
2286 "Set banner string\n"
2287 "Strings for motd\n"
2290 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2294 DEFUN (no_banner_motd
,
2298 "Set banner string\n"
2299 "Strings for motd\n")
2303 XFREE(MTYPE_HOST
, host
.motdfile
);
2304 host
.motdfile
= NULL
;
2308 DEFUN(allow_reserved_ranges
, allow_reserved_ranges_cmd
, "allow-reserved-ranges",
2309 "Allow using IPv4 (Class E) reserved IP space\n")
2311 host
.allow_reserved_ranges
= true;
2315 DEFUN(no_allow_reserved_ranges
, no_allow_reserved_ranges_cmd
,
2316 "no allow-reserved-ranges",
2317 NO_STR
"Allow using IPv4 (Class E) reserved IP space\n")
2319 host
.allow_reserved_ranges
= false;
2323 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2325 const struct cmd_node
*node
;
2326 const struct cmd_element
*cli
;
2331 char *pattern
= argv_concat(argv
, argc
, 1);
2332 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2333 XFREE(MTYPE_TMP
, pattern
);
2338 vty_out(vty
, "%% Invalid {...} expression\n");
2341 vty_out(vty
, "%% Bad repetition operator\n");
2344 vty_out(vty
, "%% Regex syntax error\n");
2347 vty_out(vty
, "%% Invalid collating element\n");
2350 vty_out(vty
, "%% Invalid character class name\n");
2354 "%% Regex ended with escape character (\\)\n");
2358 "%% Invalid number in \\digit construction\n");
2361 vty_out(vty
, "%% Unbalanced square brackets\n");
2364 vty_out(vty
, "%% Unbalanced parentheses\n");
2367 vty_out(vty
, "%% Unbalanced braces\n");
2371 "%% Invalid endpoint in range expression\n");
2374 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2382 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2383 node
= vector_slot(cmdvec
, i
);
2386 clis
= node
->cmd_vector
;
2387 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2388 cli
= vector_slot(clis
, j
);
2390 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2391 vty_out(vty
, " (%s) ", node
->name
);
2392 print_cmd(vty
, cli
->string
);
2405 "Find CLI command matching a regular expression\n"
2406 "Search pattern (POSIX regex)\n")
2408 return cmd_find_cmds(vty
, argv
, argc
);
2411 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2412 DEFUN(script
, script_cmd
, "script SCRIPT FUNCTION",
2413 "Test command - execute a function in a script\n"
2414 "Script name (same as filename in /etc/frr/scripts/)\n"
2415 "Function name (in the script)\n")
2419 (void)str2prefix("1.2.3.4/24", &p
);
2420 struct frrscript
*fs
= frrscript_new(argv
[1]->arg
);
2422 if (frrscript_load(fs
, argv
[2]->arg
, NULL
)) {
2424 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2425 argv
[1]->arg
, argv
[2]->arg
);
2428 int ret
= frrscript_call(fs
, argv
[2]->arg
, ("p", &p
));
2430 prefix2str(&p
, buf
, sizeof(buf
));
2431 vty_out(vty
, "p: %s\n", buf
);
2432 vty_out(vty
, "Script result: %d\n", ret
);
2434 frrscript_delete(fs
);
2440 /* Set config filename. Called from vty.c */
2441 void host_config_set(const char *filename
)
2443 XFREE(MTYPE_HOST
, host
.config
);
2444 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2447 const char *host_config_get(void)
2452 void cmd_show_lib_debugs(struct vty
*vty
)
2454 route_map_show_debug(vty
);
2457 void install_default(enum node_type node
)
2459 _install_element(node
, &config_exit_cmd
);
2460 _install_element(node
, &config_quit_cmd
);
2461 _install_element(node
, &config_end_cmd
);
2462 _install_element(node
, &config_help_cmd
);
2463 _install_element(node
, &config_list_cmd
);
2464 _install_element(node
, &show_cli_graph_cmd
);
2465 _install_element(node
, &find_cmd
);
2467 _install_element(node
, &config_write_cmd
);
2468 _install_element(node
, &show_running_config_cmd
);
2470 _install_element(node
, &autocomplete_cmd
);
2472 nb_cli_install_default(node
);
2475 /* Initialize command interface. Install basic nodes and commands.
2477 * terminal = 0 -- vtysh / no logging, no config control
2478 * terminal = 1 -- normal daemon
2479 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2480 void cmd_init(int terminal
)
2482 struct utsname names
;
2487 /* register command preprocessors */
2488 hook_register(cmd_execute
, handle_pipe_action
);
2489 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2491 varhandlers
= list_new();
2493 /* Allocate initial top vector of commands. */
2494 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2496 /* Default host value settings. */
2497 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2498 host
.system
= XSTRDUP(MTYPE_HOST
, names
.sysname
);
2499 host
.release
= XSTRDUP(MTYPE_HOST
, names
.release
);
2500 host
.version
= XSTRDUP(MTYPE_HOST
, names
.version
);
2502 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2503 if ((strcmp(names
.domainname
, "(none)") == 0))
2504 host
.domainname
= NULL
;
2506 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2508 host
.domainname
= NULL
;
2510 host
.password
= NULL
;
2513 host
.noconfig
= (terminal
< 0);
2515 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2516 host
.motdfile
= NULL
;
2517 host
.allow_reserved_ranges
= false;
2519 /* Install top nodes. */
2520 install_node(&view_node
);
2521 install_node(&enable_node
);
2522 install_node(&auth_node
);
2523 install_node(&auth_enable_node
);
2524 install_node(&config_node
);
2526 /* Each node's basic commands. */
2527 install_element(VIEW_NODE
, &show_version_cmd
);
2528 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2531 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2533 install_element(VIEW_NODE
, &config_list_cmd
);
2534 install_element(VIEW_NODE
, &config_exit_cmd
);
2535 install_element(VIEW_NODE
, &config_quit_cmd
);
2536 install_element(VIEW_NODE
, &config_help_cmd
);
2537 install_element(VIEW_NODE
, &config_enable_cmd
);
2538 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2539 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2540 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2541 install_element(VIEW_NODE
, &echo_cmd
);
2542 install_element(VIEW_NODE
, &autocomplete_cmd
);
2543 install_element(VIEW_NODE
, &find_cmd
);
2544 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2545 install_element(VIEW_NODE
, &script_cmd
);
2549 install_element(ENABLE_NODE
, &config_end_cmd
);
2550 install_element(ENABLE_NODE
, &config_disable_cmd
);
2551 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2552 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2553 install_element(ENABLE_NODE
, &config_write_cmd
);
2554 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2555 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2557 install_default(CONFIG_NODE
);
2560 workqueue_cmd_init();
2564 install_element(CONFIG_NODE
, &hostname_cmd
);
2565 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2566 install_element(CONFIG_NODE
, &domainname_cmd
);
2567 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2572 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2574 install_element(CONFIG_NODE
, &password_cmd
);
2575 install_element(CONFIG_NODE
, &no_password_cmd
);
2576 install_element(CONFIG_NODE
, &enable_password_cmd
);
2577 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2579 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2580 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2581 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2582 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2583 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2584 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2585 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2586 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2587 install_element(CONFIG_NODE
, &allow_reserved_ranges_cmd
);
2588 install_element(CONFIG_NODE
, &no_allow_reserved_ranges_cmd
);
2591 vrf_install_commands();
2595 grammar_sandbox_init();
2599 void cmd_terminate(void)
2601 struct cmd_node
*cmd_node
;
2603 hook_unregister(cmd_execute
, handle_pipe_action
);
2604 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2607 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2608 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2609 // deleting the graph delets the cmd_element as
2611 graph_delete_graph(cmd_node
->cmdgraph
);
2612 vector_free(cmd_node
->cmd_vector
);
2613 hash_clean(cmd_node
->cmd_hash
, NULL
);
2614 hash_free(cmd_node
->cmd_hash
);
2615 cmd_node
->cmd_hash
= NULL
;
2618 vector_free(cmdvec
);
2622 XFREE(MTYPE_HOST
, host
.name
);
2623 XFREE(MTYPE_HOST
, host
.system
);
2624 XFREE(MTYPE_HOST
, host
.release
);
2625 XFREE(MTYPE_HOST
, host
.version
);
2626 XFREE(MTYPE_HOST
, host
.domainname
);
2627 XFREE(MTYPE_HOST
, host
.password
);
2628 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2629 XFREE(MTYPE_HOST
, host
.enable
);
2630 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2631 XFREE(MTYPE_HOST
, host
.motdfile
);
2632 XFREE(MTYPE_HOST
, host
.config
);
2633 XFREE(MTYPE_HOST
, host
.motd
);
2635 list_delete(&varhandlers
);