2 * CLI backend interface.
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
10 * This file is part of GNU Zebra.
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
48 #include "lib_errors.h"
49 #include "northbound_cli.h"
52 #include "frrscript.h"
54 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config");
55 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item");
62 /* clang-format off */
63 const struct message tokennames
[] = {
68 item(IPV4_PREFIX_TKN
),
70 item(IPV6_PREFIX_TKN
),
82 /* Command vector which includes some level of command lists. Normally
83 each daemon maintains each own cmdvec. */
86 /* Host information structure. */
89 /* for vtysh, put together CLI trees only when switching into node */
90 static bool defer_cli_tree
;
93 * Returns host.name if any, otherwise
94 * it returns the system hostname.
96 const char *cmd_hostname_get(void)
102 * Returns unix domainname
104 const char *cmd_domainname_get(void)
106 return host
.domainname
;
109 const char *cmd_system_get(void)
114 const char *cmd_release_get(void)
119 const char *cmd_version_get(void)
124 bool cmd_allow_reserved_ranges_get(void)
126 return host
.allow_reserved_ranges
;
129 static int root_on_exit(struct vty
*vty
);
131 /* Standard command node structures. */
132 static struct cmd_node auth_node
= {
135 .prompt
= "Password: ",
138 static struct cmd_node view_node
= {
142 .node_exit
= root_on_exit
,
145 static struct cmd_node auth_enable_node
= {
146 .name
= "auth enable",
147 .node
= AUTH_ENABLE_NODE
,
148 .prompt
= "Password: ",
151 static struct cmd_node enable_node
= {
155 .node_exit
= root_on_exit
,
158 static int config_write_host(struct vty
*vty
);
159 static struct cmd_node config_node
= {
162 .parent_node
= ENABLE_NODE
,
163 .prompt
= "%s(config)# ",
164 .config_write
= config_write_host
,
165 .node_exit
= vty_config_node_exit
,
168 /* This is called from main when a daemon is invoked with -v or --version. */
169 void print_version(const char *progname
)
171 printf("%s version %s\n", progname
, FRR_VERSION
);
172 printf("%s\n", FRR_COPYRIGHT
);
173 #ifdef ENABLE_VERSION_BUILD_CONFIG
174 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
178 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
180 int cnt
= MAX(argc
- shift
, 0);
181 const char *argstr
[cnt
+ 1];
186 for (int i
= 0; i
< cnt
; i
++)
187 argstr
[i
] = argv
[i
+ shift
]->arg
;
189 return frrstr_join(argstr
, cnt
, " ");
192 vector
cmd_make_strvec(const char *string
)
197 const char *copy
= string
;
199 /* skip leading whitespace */
200 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
203 /* if the entire string was whitespace or a comment, return */
204 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
207 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
209 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
210 if (strlen(vector_slot(result
, i
)) == 0) {
211 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
212 vector_unset(result
, i
);
216 vector_compact(result
);
221 void cmd_free_strvec(vector v
)
223 frrstr_strvec_free(v
);
227 * Convenience function for accessing argv data.
231 * @param text definition snippet of the desired token
232 * @param index the starting index, and where to store the
233 * index of the found token if it exists
234 * @return 1 if found, 0 otherwise
236 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
239 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
240 if ((found
= strmatch(text
, argv
[i
]->text
)))
245 static unsigned int cmd_hash_key(const void *p
)
247 int size
= sizeof(p
);
249 return jhash(p
, size
, 0);
252 static bool cmd_hash_cmp(const void *a
, const void *b
)
257 /* Install top node of command vector. */
258 void install_node(struct cmd_node
*node
)
260 #define CMD_HASH_STR_SIZE 256
261 char hash_name
[CMD_HASH_STR_SIZE
];
263 vector_set_index(cmdvec
, node
->node
, node
);
264 node
->cmdgraph
= graph_new();
265 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
267 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
268 graph_new_node(node
->cmdgraph
, token
,
269 (void (*)(void *)) & cmd_token_del
);
271 snprintf(hash_name
, sizeof(hash_name
), "Command Hash: %s", node
->name
);
273 hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
, hash_name
);
276 /* Return prompt character of specified node. */
277 const char *cmd_prompt(enum node_type node
)
279 struct cmd_node
*cnode
;
281 cnode
= vector_slot(cmdvec
, node
);
282 return cnode
->prompt
;
285 void cmd_defer_tree(bool val
)
287 defer_cli_tree
= val
;
290 /* Install a command into a node. */
291 void _install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
293 struct cmd_node
*cnode
;
295 /* cmd_init hasn't been called */
297 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
302 cnode
= vector_lookup(cmdvec
, ntype
);
307 "\tnode %d does not exist.\n"
308 "\tplease call install_node() before install_element()\n",
309 cmd
->name
, cmd
->string
, ntype
);
313 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
316 "\tnode %d (%s) already has this command installed.\n"
317 "\tduplicate install_element call?\n",
318 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
322 (void)hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
);
324 if (cnode
->graph_built
|| !defer_cli_tree
) {
325 struct graph
*graph
= graph_new();
326 struct cmd_token
*token
=
327 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
328 graph_new_node(graph
, token
,
329 (void (*)(void *)) & cmd_token_del
);
331 cmd_graph_parse(graph
, cmd
);
332 cmd_graph_names(graph
);
333 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
334 graph_delete_graph(graph
);
336 cnode
->graph_built
= true;
339 vector_set(cnode
->cmd_vector
, (void *)cmd
);
341 if (ntype
== VIEW_NODE
)
342 _install_element(ENABLE_NODE
, cmd
);
345 static void cmd_finalize_iter(struct hash_bucket
*hb
, void *arg
)
347 struct cmd_node
*cnode
= arg
;
348 const struct cmd_element
*cmd
= hb
->data
;
349 struct graph
*graph
= graph_new();
350 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
352 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
354 cmd_graph_parse(graph
, cmd
);
355 cmd_graph_names(graph
);
356 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
357 graph_delete_graph(graph
);
360 void cmd_finalize_node(struct cmd_node
*cnode
)
362 if (cnode
->graph_built
)
365 hash_iterate(cnode
->cmd_hash
, cmd_finalize_iter
, cnode
);
366 cnode
->graph_built
= true;
369 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
371 struct cmd_node
*cnode
;
373 /* cmd_init hasn't been called */
375 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
380 cnode
= vector_lookup(cmdvec
, ntype
);
385 "\tnode %d does not exist.\n"
386 "\tplease call install_node() before uninstall_element()\n",
387 cmd
->name
, cmd
->string
, ntype
);
391 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
394 "\tnode %d (%s) does not have this command installed.\n"
395 "\tduplicate uninstall_element call?\n",
396 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
400 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
402 if (cnode
->graph_built
) {
403 struct graph
*graph
= graph_new();
404 struct cmd_token
*token
=
405 cmd_token_new(START_TKN
, 0, NULL
, NULL
);
406 graph_new_node(graph
, token
,
407 (void (*)(void *)) & cmd_token_del
);
409 cmd_graph_parse(graph
, cmd
);
410 cmd_graph_names(graph
);
411 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
412 graph_delete_graph(graph
);
415 if (ntype
== VIEW_NODE
)
416 uninstall_element(ENABLE_NODE
, cmd
);
420 static const unsigned char itoa64
[] =
421 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
423 static void to64(char *s
, long v
, int n
)
426 *s
++ = itoa64
[v
& 0x3f];
431 static char *zencrypt(const char *passwd
)
436 gettimeofday(&tv
, 0);
438 to64(&salt
[0], frr_weak_random(), 3);
439 to64(&salt
[3], tv
.tv_usec
, 3);
442 return crypt(passwd
, salt
);
445 static bool full_cli
;
447 /* This function write configuration of this host. */
448 static int config_write_host(struct vty
*vty
)
452 name
= cmd_hostname_get();
453 if (name
&& name
[0] != '\0')
454 vty_out(vty
, "hostname %s\n", name
);
456 name
= cmd_domainname_get();
457 if (name
&& name
[0] != '\0')
458 vty_out(vty
, "domainname %s\n", name
);
460 if (cmd_allow_reserved_ranges_get())
461 vty_out(vty
, "allow-reserved-ranges\n");
463 /* The following are all configuration commands that are not sent to
464 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
465 * we would always display 'log syslog informational' in the config
466 * which would cause other daemons to then switch to syslog when they
471 if (host
.password_encrypt
)
472 vty_out(vty
, "password 8 %s\n",
473 host
.password_encrypt
);
474 if (host
.enable_encrypt
)
475 vty_out(vty
, "enable password 8 %s\n",
476 host
.enable_encrypt
);
479 vty_out(vty
, "password %s\n", host
.password
);
481 vty_out(vty
, "enable password %s\n",
484 log_config_write(vty
);
486 /* print disable always, but enable only if default is flipped
487 * => prep for future removal of compile-time knob
489 if (!cputime_enabled
)
490 vty_out(vty
, "no service cputime-stats\n");
491 #ifdef EXCLUDE_CPU_TIME
493 vty_out(vty
, "service cputime-stats\n");
496 if (!cputime_threshold
)
497 vty_out(vty
, "no service cputime-warning\n");
498 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
499 else /* again, always print non-default */
501 else if (cputime_threshold
!= 5000000)
503 vty_out(vty
, "service cputime-warning %lu\n",
506 if (!walltime_threshold
)
507 vty_out(vty
, "no service walltime-warning\n");
508 #if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
509 else /* again, always print non-default */
511 else if (walltime_threshold
!= 5000000)
513 vty_out(vty
, "service walltime-warning %lu\n",
517 vty_out(vty
, "service advanced-vty\n");
520 vty_out(vty
, "service password-encryption\n");
523 vty_out(vty
, "service terminal-length %d\n",
527 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
529 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
531 vty_out(vty
, "banner motd line %s\n", host
.motd
);
533 vty_out(vty
, "no banner motd\n");
536 if (debug_memstats_at_exit
)
537 vty_out(vty
, "!\ndebug memstats-at-exit\n");
542 /* Utility function for getting command graph. */
543 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
545 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
547 cmd_finalize_node(cnode
);
548 return cnode
->cmdgraph
;
551 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
553 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
554 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
560 * Compare function for cmd_token.
561 * Used with qsort to sort command completions.
563 static int compare_completions(const void *fst
, const void *snd
)
565 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
566 *secnd
= *(const struct cmd_token
* const *)snd
;
567 return strcmp(first
->text
, secnd
->text
);
571 * Takes a list of completions returned by command_complete,
572 * dedeuplicates them based on both text and description,
573 * sorts them, and returns them as a vector.
575 * @param completions linked list of cmd_token
576 * @return deduplicated and sorted vector with
578 vector
completions_to_vec(struct list
*completions
)
580 vector comps
= vector_init(VECTOR_MIN_SIZE
);
583 struct cmd_token
*token
, *cr
= NULL
;
584 unsigned int i
, exists
;
585 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
586 if (token
->type
== END_TKN
&& (cr
= token
))
589 // linear search for token in completions vector
591 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
592 struct cmd_token
*curr
= vector_slot(comps
, i
);
594 exists
= !strcmp(curr
->text
, token
->text
)
595 && !strcmp(curr
->desc
, token
->desc
);
597 exists
= !strcmp(curr
->text
, token
->text
);
598 #endif /* VTYSH_DEBUG */
602 vector_set(comps
, token
);
606 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
607 &compare_completions
);
609 // make <cr> the first element, if it is present
611 vector_set_index(comps
, vector_active(comps
), NULL
);
612 memmove(comps
->index
+ 1, comps
->index
,
613 (comps
->alloced
- 1) * sizeof(void *));
614 vector_set_index(comps
, 0, cr
);
620 * Generates a vector of cmd_token representing possible completions
621 * on the current input.
623 * @param vline the vectorized input line
624 * @param vty the vty with the node to match on
625 * @param status pointer to matcher status code
626 * @return vector of struct cmd_token * with possible completions
628 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
631 struct list
*completions
;
632 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
634 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
636 if (MATCHER_ERROR(rv
)) {
637 *status
= CMD_ERR_NO_MATCH
;
641 vector comps
= completions_to_vec(completions
);
642 list_delete(&completions
);
644 // set status code appropriately
645 switch (vector_active(comps
)) {
647 *status
= CMD_ERR_NO_MATCH
;
650 *status
= CMD_COMPLETE_FULL_MATCH
;
653 *status
= CMD_COMPLETE_LIST_MATCH
;
659 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
663 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
664 enum node_type onode
;
665 int orig_xpath_index
;
666 vector shifted_vline
;
670 orig_xpath_index
= vty
->xpath_index
;
671 vty
->node
= ENABLE_NODE
;
672 vty
->xpath_index
= 0;
673 /* We can try it on enable node, cos' the vty is authenticated
676 shifted_vline
= vector_init(vector_count(vline
));
678 for (index
= 1; index
< vector_active(vline
); index
++) {
679 vector_set_index(shifted_vline
, index
- 1,
680 vector_lookup(vline
, index
));
683 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
685 vector_free(shifted_vline
);
687 vty
->xpath_index
= orig_xpath_index
;
691 return cmd_complete_command_real(vline
, vty
, status
);
694 static struct list
*varhandlers
= NULL
;
696 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
700 const struct cmd_variable_handler
*cvh
;
704 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
706 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
707 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
709 if (cvh
->varname
&& (!token
->varname
710 || strcmp(cvh
->varname
, token
->varname
)))
712 cvh
->completions(tmpcomps
, token
);
720 for (i
= vector_active(tmpcomps
); i
; i
--) {
721 char *item
= vector_slot(tmpcomps
, i
- 1);
722 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
723 vector_set(comps
, item
);
725 XFREE(MTYPE_COMPLETION
, item
);
727 vector_free(tmpcomps
);
730 #define AUTOCOMP_INDENT 5
732 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
735 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
736 int lc
= AUTOCOMP_INDENT
;
737 size_t cs
= AUTOCOMP_INDENT
;
739 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
740 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
741 char *item
= vector_slot(comps
, j
);
742 itemlen
= strlen(item
);
744 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
745 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
747 if (lc
+ itemlen
+ 1 >= cols
) {
748 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
749 AUTOCOMP_INDENT
, "");
750 lc
= AUTOCOMP_INDENT
;
753 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
756 XFREE(MTYPE_COMPLETION
, item
);
757 vector_set_index(comps
, j
, NULL
);
762 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
767 for (; cvh
->completions
; cvh
++)
768 listnode_add(varhandlers
, (void *)cvh
);
771 DEFUN_HIDDEN (autocomplete
,
773 "autocomplete TYPE TEXT VARNAME",
774 "Autocompletion handler (internal, for vtysh)\n"
777 "cmd_token->varname\n")
779 struct cmd_token tok
;
780 vector comps
= vector_init(32);
783 memset(&tok
, 0, sizeof(tok
));
784 tok
.type
= atoi(argv
[1]->arg
);
785 tok
.text
= argv
[2]->arg
;
786 tok
.varname
= argv
[3]->arg
;
787 if (!strcmp(tok
.varname
, "-"))
790 cmd_variable_complete(&tok
, NULL
, comps
);
792 for (i
= 0; i
< vector_active(comps
); i
++) {
793 char *text
= vector_slot(comps
, i
);
794 vty_out(vty
, "%s\n", text
);
795 XFREE(MTYPE_COMPLETION
, text
);
803 * Generate possible tab-completions for the given input. This function only
804 * returns results that would result in a valid command if used as Readline
805 * completions (as is the case in vtysh). For instance, if the passed vline ends
806 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
808 * @param vline vectorized input line
810 * @param status location to store matcher status code in
811 * @return set of valid strings for use with Readline as tab-completions.
814 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
817 int original_node
= vty
->node
;
818 vector input_line
= vector_init(vector_count(vline
));
820 // if the first token is 'do' we'll want to execute the command in the
822 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
823 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
825 // construct the input line we'll be matching on
826 unsigned int offset
= (do_shortcut
) ? 1 : 0;
827 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
828 vector_set_index(input_line
, index
,
829 vector_lookup(vline
, index
+ offset
));
831 // get token completions -- this is a copying operation
832 vector comps
= NULL
, initial_comps
;
833 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
835 if (!MATCHER_ERROR(*status
)) {
836 assert(initial_comps
);
837 // filter out everything that is not suitable for a
839 comps
= vector_init(VECTOR_MIN_SIZE
);
840 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
842 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
843 if (token
->type
== WORD_TKN
)
844 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
846 else if (IS_VARYING_TOKEN(token
->type
)) {
847 const char *ref
= vector_lookup(
848 vline
, vector_active(vline
) - 1);
849 cmd_variable_complete(token
, ref
, comps
);
852 vector_free(initial_comps
);
854 // since we filtered results, we need to re-set status code
855 switch (vector_active(comps
)) {
857 *status
= CMD_ERR_NO_MATCH
;
860 *status
= CMD_COMPLETE_FULL_MATCH
;
863 *status
= CMD_COMPLETE_LIST_MATCH
;
866 // copy completions text into an array of char*
867 ret
= XMALLOC(MTYPE_TMP
,
868 (vector_active(comps
) + 1) * sizeof(char *));
870 for (i
= 0; i
< vector_active(comps
); i
++) {
871 ret
[i
] = vector_slot(comps
, i
);
873 // set the last element to NULL, because this array is used in
874 // a Readline completion_generator function which expects NULL
875 // as a sentinel value
879 } else if (initial_comps
)
880 vector_free(initial_comps
);
882 // comps should always be null here
885 // free the adjusted input line
886 vector_free(input_line
);
888 // reset vty->node to its original value
889 vty
->node
= original_node
;
894 /* return parent node */
895 /* MUST eventually converge on CONFIG_NODE */
896 enum node_type
node_parent(enum node_type node
)
898 struct cmd_node
*cnode
;
900 assert(node
> CONFIG_NODE
);
902 cnode
= vector_lookup(cmdvec
, node
);
904 return cnode
->parent_node
;
907 /* Execute command by argument vline vector. */
908 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
910 const struct cmd_element
**cmd
,
911 unsigned int up_level
)
913 struct list
*argv_list
;
914 enum matcher_rv status
;
915 const struct cmd_element
*matched_element
= NULL
;
917 int xpath_index
= vty
->xpath_index
;
918 int node
= vty
->node
;
920 /* only happens for legacy split config file load; need to check for
921 * a match before calling node_exit handlers below
923 for (i
= 0; i
< up_level
; i
++) {
924 struct cmd_node
*cnode
;
926 if (node
<= CONFIG_NODE
)
927 return CMD_NO_LEVEL_UP
;
929 cnode
= vector_slot(cmdvec
, node
);
930 node
= node_parent(node
);
932 if (xpath_index
> 0 && !cnode
->no_xpath
)
936 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, node
);
937 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
940 *cmd
= matched_element
;
942 // if matcher error, return corresponding CMD_ERR
943 if (MATCHER_ERROR(status
)) {
945 list_delete(&argv_list
);
947 case MATCHER_INCOMPLETE
:
948 return CMD_ERR_INCOMPLETE
;
949 case MATCHER_AMBIGUOUS
:
950 return CMD_ERR_AMBIGUOUS
;
952 return CMD_ERR_NO_MATCH
;
956 for (i
= 0; i
< up_level
; i
++)
959 // build argv array from argv list
960 struct cmd_token
**argv
= XMALLOC(
961 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
963 struct cmd_token
*token
;
966 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
969 int argc
= argv_list
->count
;
972 if (matched_element
->daemon
)
973 ret
= CMD_SUCCESS_DAEMON
;
976 /* Clear array of enqueued configuration changes. */
977 vty
->num_cfg_changes
= 0;
978 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
980 /* Regenerate candidate configuration if necessary. */
981 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
982 && running_config
->version
983 > vty
->candidate_config
->version
)
984 nb_config_replace(vty
->candidate_config
,
985 running_config
, true);
988 * Perform pending commit (if any) before executing
991 if (!(matched_element
->attr
& CMD_ATTR_YANG
))
992 (void)nb_cli_pending_commit_check(vty
);
995 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
998 // delete list and cmd_token's in it
999 list_delete(&argv_list
);
1000 XFREE(MTYPE_TMP
, argv
);
1006 * Execute a given command, handling things like "do ..." and checking
1007 * whether the given command might apply at a parent node if doesn't
1008 * apply for the current node.
1010 * @param vline Command line input, vector of char* where each element is
1012 * @param vty The vty context in which the command should be executed.
1013 * @param cmd Pointer where the struct cmd_element of the matched command
1014 * will be stored, if any. May be set to NULL if this info is
1016 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1017 * @return The status of the command that has been executed or an error code
1018 * as to why no command could be executed.
1020 int cmd_execute_command(vector vline
, struct vty
*vty
,
1021 const struct cmd_element
**cmd
, int vtysh
)
1023 int ret
, saved_ret
= 0;
1024 enum node_type onode
, try_node
;
1025 int orig_xpath_index
;
1027 onode
= try_node
= vty
->node
;
1028 orig_xpath_index
= vty
->xpath_index
;
1030 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1031 vector shifted_vline
;
1034 vty
->node
= ENABLE_NODE
;
1035 vty
->xpath_index
= 0;
1036 /* We can try it on enable node, cos' the vty is authenticated
1039 shifted_vline
= vector_init(vector_count(vline
));
1041 for (index
= 1; index
< vector_active(vline
); index
++)
1042 vector_set_index(shifted_vline
, index
- 1,
1043 vector_lookup(vline
, index
));
1045 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1048 vector_free(shifted_vline
);
1050 vty
->xpath_index
= orig_xpath_index
;
1055 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
, 0);
1060 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1061 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1062 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1063 /* This assumes all nodes above CONFIG_NODE are childs of
1065 while (vty
->node
> CONFIG_NODE
) {
1066 struct cmd_node
*cnode
= vector_slot(cmdvec
, try_node
);
1068 try_node
= node_parent(try_node
);
1069 vty
->node
= try_node
;
1070 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1073 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1075 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1076 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1077 || ret
== CMD_NOT_MY_INSTANCE
1078 || ret
== CMD_WARNING_CONFIG_FAILED
)
1081 /* no command succeeded, reset the vty to the original node */
1083 vty
->xpath_index
= orig_xpath_index
;
1086 /* return command status for original node */
1091 * Execute a given command, matching it strictly against the current node.
1092 * This mode is used when reading config files.
1094 * @param vline Command line input, vector of char* where each element is
1096 * @param vty The vty context in which the command should be executed.
1097 * @param cmd Pointer where the struct cmd_element* of the matched command
1098 * will be stored, if any. May be set to NULL if this info is
1100 * @return The status of the command that has been executed or an error code
1101 * as to why no command could be executed.
1103 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1104 const struct cmd_element
**cmd
)
1106 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
, 0);
1110 * Hook for preprocessing command string before executing.
1112 * All subscribers are called with the raw command string that is to be
1113 * executed. If any changes are to be made, a new string should be allocated
1114 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1115 * is then responsible for freeing this string.
1117 * All processing functions must be mutually exclusive in their action, i.e. if
1118 * one subscriber decides to modify the command, all others must not modify it
1119 * when called. Feeding the output of one processing command into a subsequent
1120 * one is not supported.
1122 * This hook is intentionally internal to the command processing system.
1125 * The raw command string.
1128 * The result of any processing.
1130 DECLARE_HOOK(cmd_execute
,
1131 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1132 (vty
, cmd_in
, cmd_out
));
1133 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1134 (vty
, cmd_in
, cmd_out
));
1136 /* Hook executed after a CLI command. */
1137 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1139 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1143 * cmd_execute hook subscriber to handle `|` actions.
1145 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1149 char *orig
, *working
, *token
, *u
;
1150 char *pipe
= strstr(cmd_in
, "| ");
1156 /* duplicate string for processing purposes, not including pipe */
1157 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1159 /* retrieve action */
1160 token
= strsep(&working
, " ");
1163 /* match result to known actions */
1164 if (strmatch(token
, "include")) {
1165 /* the remaining text should be a regexp */
1166 char *regexp
= working
;
1169 vty_out(vty
, "%% Need a regexp to filter with\n");
1174 bool succ
= vty_set_include(vty
, regexp
);
1177 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1181 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1185 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1191 XFREE(MTYPE_TMP
, orig
);
1195 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1198 vty_set_include(vty
, NULL
);
1203 int cmd_execute(struct vty
*vty
, const char *cmd
,
1204 const struct cmd_element
**matched
, int vtysh
)
1207 char *cmd_out
= NULL
;
1208 const char *cmd_exec
= NULL
;
1211 ret
= hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1217 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1219 vline
= cmd_make_strvec(cmd_exec
);
1222 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1223 cmd_free_strvec(vline
);
1229 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1231 XFREE(MTYPE_TMP
, cmd_out
);
1238 * Parse one line of config, walking up the parse tree attempting to find a
1241 * @param vty The vty context in which the command should be executed.
1242 * @param cmd Pointer where the struct cmd_element* of the match command
1243 * will be stored, if any. May be set to NULL if this info is
1245 * @param use_daemon Boolean to control whether or not we match on
1246 * CMD_SUCCESS_DAEMON
1248 * @return The status of the command that has been executed or an error code
1249 * as to why no command could be executed.
1251 int command_config_read_one_line(struct vty
*vty
,
1252 const struct cmd_element
**cmd
,
1253 uint32_t line_num
, int use_daemon
)
1257 unsigned up_level
= 0;
1259 vline
= cmd_make_strvec(vty
->buf
);
1261 /* In case of comment line */
1265 /* Execute configuration command : this is strict match */
1266 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1268 /* The logic for trying parent nodes is in cmd_execute_command_real()
1269 * since calling ->node_exit() correctly is a bit involved. This is
1270 * also the only reason CMD_NO_LEVEL_UP exists.
1272 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1273 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1274 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1275 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1276 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1277 && ret
!= CMD_NO_LEVEL_UP
)
1278 ret
= cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
,
1281 if (ret
== CMD_NO_LEVEL_UP
)
1282 ret
= CMD_ERR_NO_MATCH
;
1284 if (ret
!= CMD_SUCCESS
&&
1285 ret
!= CMD_WARNING
&&
1286 ret
!= CMD_SUCCESS_DAEMON
) {
1287 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1289 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1290 ve
->line_num
= line_num
;
1292 vty
->error
= list_new();
1294 listnode_add(vty
->error
, ve
);
1297 cmd_free_strvec(vline
);
1302 /* Configuration make from file. */
1303 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1305 int ret
, error_ret
= 0;
1308 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1311 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1313 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1314 && ret
!= CMD_ERR_NOTHING_TODO
)
1325 /* Configuration from terminal */
1326 DEFUN (config_terminal
,
1327 config_terminal_cmd
,
1328 "configure [terminal]",
1329 "Configuration from vty interface\n"
1330 "Configuration terminal\n")
1332 return vty_config_enter(vty
, false, false);
1335 /* Enable command */
1339 "Turn on privileged mode command\n")
1341 /* If enable password is NULL, change to ENABLE_NODE */
1342 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1343 || vty
->type
== VTY_SHELL_SERV
)
1344 vty
->node
= ENABLE_NODE
;
1346 vty
->node
= AUTH_ENABLE_NODE
;
1351 /* Disable command */
1355 "Turn off privileged mode command\n")
1357 if (vty
->node
== ENABLE_NODE
)
1358 vty
->node
= VIEW_NODE
;
1362 /* Down vty node level. */
1366 "Exit current mode and down to previous mode\n")
1372 static int root_on_exit(struct vty
*vty
)
1377 vty
->status
= VTY_CLOSE
;
1381 void cmd_exit(struct vty
*vty
)
1383 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1385 if (cnode
->node_exit
) {
1386 if (!cnode
->node_exit(vty
))
1389 if (cnode
->parent_node
)
1390 vty
->node
= cnode
->parent_node
;
1391 if (vty
->xpath_index
> 0 && !cnode
->no_xpath
)
1399 "Exit current mode and down to previous mode\n")
1401 return config_exit(self
, vty
, argc
, argv
);
1405 /* End of configuration. */
1409 "End current mode and change to enable mode.\n")
1412 vty_config_exit(vty
);
1413 vty
->node
= ENABLE_NODE
;
1419 DEFUN (show_version
,
1423 "Displays zebra version\n")
1425 vty_out(vty
, "%s %s (%s) on %s(%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1426 cmd_hostname_get() ? cmd_hostname_get() : "", cmd_system_get(),
1428 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1429 #ifdef ENABLE_VERSION_BUILD_CONFIG
1430 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1435 /* Help display function for all node. */
1439 "Description of the interactive help system\n")
1442 "Quagga VTY provides advanced help feature. When you need help,\n\
1443 anytime at the command line please press '?'.\n\
1445 If nothing matches, the help list will be empty and you must backup\n\
1446 until entering a '?' shows the available options.\n\
1447 Two styles of help are provided:\n\
1448 1. Full help is available when you are ready to enter a\n\
1449 command argument (e.g. 'show ?') and describes each possible\n\
1451 2. Partial help is provided when an abbreviated argument is entered\n\
1452 and you want to know what arguments match the input\n\
1453 (e.g. 'show me?'.)\n\n");
1457 static void permute(struct graph_node
*start
, struct vty
*vty
)
1459 static struct list
*position
= NULL
;
1461 position
= list_new();
1463 struct cmd_token
*stok
= start
->data
;
1464 struct graph_node
*gnn
;
1465 struct listnode
*ln
;
1468 listnode_add(position
, start
);
1469 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1470 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1471 struct cmd_token
*tok
= gn
->data
;
1472 if (tok
->attr
& CMD_ATTR_HIDDEN
)
1474 else if (tok
->type
== END_TKN
|| gn
== start
) {
1476 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1477 struct cmd_token
*tt
= gnn
->data
;
1478 if (tt
->type
< SPECIAL_TKN
)
1479 vty_out(vty
, " %s", tt
->text
);
1482 vty_out(vty
, "...");
1486 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1487 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1496 list_delete_node(position
, listtail(position
));
1499 static void print_cmd(struct vty
*vty
, const char *cmd
)
1501 int i
, j
, len
= strlen(cmd
);
1506 for (i
= 0; i
< len
; i
++) {
1510 else if (strchr(" ()<>[]{}|", cmd
[i
]))
1516 if (isspace(cmd
[i
])) {
1517 /* skip leading whitespace */
1520 /* skip trailing whitespace */
1523 /* skip all whitespace after opening brackets or pipe */
1524 if (strchr("(<[{|", cmd
[i
- 1])) {
1525 while (isspace(cmd
[i
+ 1]))
1529 /* skip repeated whitespace */
1530 if (isspace(cmd
[i
+ 1]))
1532 /* skip whitespace before closing brackets or pipe */
1533 if (strchr(")>]}|", cmd
[i
+ 1]))
1535 /* convert tabs to spaces */
1536 if (cmd
[i
] == '\t') {
1546 vty_out(vty
, "%s\n", buf
);
1549 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1551 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1554 cmd_finalize_node(node
);
1555 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1557 /* loop over all commands at this node */
1558 const struct cmd_element
*element
= NULL
;
1559 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1561 if ((element
= vector_slot(node
->cmd_vector
, i
)) &&
1562 !(element
->attr
& CMD_ATTR_HIDDEN
)) {
1564 print_cmd(vty
, element
->string
);
1570 /* Help display function for all node. */
1573 "list [permutations]",
1574 "Print command list\n"
1575 "Print all possible command permutations\n")
1577 return cmd_list_cmds(vty
, argc
== 2);
1580 DEFUN (show_commandtree
,
1581 show_commandtree_cmd
,
1582 "show commandtree [permutations]",
1584 "Show command tree\n"
1585 "Permutations that we are interested in\n")
1587 return cmd_list_cmds(vty
, argc
== 3);
1590 DEFUN_HIDDEN(show_cli_graph
,
1595 "Dump current command space as DOT graph\n")
1597 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1600 cmd_finalize_node(cn
);
1601 dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1603 vty_out(vty
, "%s\n", dot
);
1604 XFREE(MTYPE_TMP
, dot
);
1608 static int vty_write_config(struct vty
*vty
)
1611 struct cmd_node
*node
;
1616 nb_cli_show_config_prepare(running_config
, false);
1618 if (vty
->type
== VTY_TERM
) {
1619 vty_out(vty
, "\nCurrent configuration:\n");
1620 vty_out(vty
, "!\n");
1623 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1624 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1625 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1626 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1627 vty_out(vty
, "!\n");
1629 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1630 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1631 if ((*node
->config_write
)(vty
))
1632 vty_out(vty
, "!\n");
1635 if (vty
->type
== VTY_TERM
) {
1636 vty_out(vty
, "end\n");
1642 static int file_write_config(struct vty
*vty
)
1645 char *config_file
, *slash
;
1646 char *config_file_tmp
= NULL
;
1647 char *config_file_sav
= NULL
;
1648 int ret
= CMD_WARNING
;
1649 struct vty
*file_vty
;
1650 struct stat conf_stat
;
1655 /* Check and see if we are operating under vtysh configuration */
1656 if (host
.config
== NULL
) {
1658 "Can't save to configuration file, using vtysh.\n");
1663 config_file
= host
.config
;
1666 #define O_DIRECTORY 0
1668 slash
= strrchr(config_file
, '/');
1670 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1671 config_dir
[slash
- config_file
] = '\0';
1672 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1673 XFREE(MTYPE_TMP
, config_dir
);
1675 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1676 /* if dirfd is invalid, directory sync fails, but we're still OK */
1678 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1679 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1680 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1681 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1684 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1685 snprintf(config_file_tmp
, strlen(config_file
) + 8, "%s.XXXXXX",
1688 /* Open file to configuration write. */
1689 fd
= mkstemp(config_file_tmp
);
1691 vty_out(vty
, "Can't open configuration file %s.\n",
1695 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1696 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1697 config_file_tmp
, safe_strerror(errno
), errno
);
1701 /* Make vty for configuration file. */
1702 file_vty
= vty_new();
1704 file_vty
->type
= VTY_FILE
;
1706 /* Config file header print. */
1707 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1708 vty_time_print(file_vty
, 1);
1709 vty_out(file_vty
, "!\n");
1710 vty_write_config(file_vty
);
1711 vty_close(file_vty
);
1713 if (stat(config_file
, &conf_stat
) >= 0) {
1714 if (unlink(config_file_sav
) != 0)
1715 if (errno
!= ENOENT
) {
1717 "Can't unlink backup configuration file %s.\n",
1721 if (link(config_file
, config_file_sav
) != 0) {
1723 "Can't backup old configuration file %s.\n",
1730 if (rename(config_file_tmp
, config_file
) != 0) {
1731 vty_out(vty
, "Can't save configuration file %s.\n",
1738 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1742 if (ret
!= CMD_SUCCESS
)
1743 unlink(config_file_tmp
);
1746 XFREE(MTYPE_TMP
, config_file_tmp
);
1747 XFREE(MTYPE_TMP
, config_file_sav
);
1751 /* Write current configuration into file. */
1753 DEFUN (config_write
,
1755 "write [<file|memory|terminal>]",
1756 "Write running configuration to memory, network, or terminal\n"
1757 "Write to configuration file\n"
1758 "Write configuration currently in memory\n"
1759 "Write configuration to terminal\n")
1761 const int idx_type
= 1;
1763 // if command was 'write terminal' or 'write memory'
1764 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1765 return vty_write_config(vty
);
1768 return file_write_config(vty
);
1771 /* ALIAS_FIXME for 'write <terminal|memory>' */
1772 DEFUN (show_running_config
,
1773 show_running_config_cmd
,
1774 "show running-config",
1776 "running configuration (same as write terminal)\n")
1778 return vty_write_config(vty
);
1781 /* ALIAS_FIXME for 'write file' */
1782 DEFUN (copy_runningconf_startupconf
,
1783 copy_runningconf_startupconf_cmd
,
1784 "copy running-config startup-config",
1785 "Copy configuration\n"
1786 "Copy running config to... \n"
1787 "Copy running config to startup config (same as write file/memory)\n")
1789 return file_write_config(vty
);
1793 /* Write startup configuration into the terminal. */
1794 DEFUN (show_startup_config
,
1795 show_startup_config_cmd
,
1796 "show startup-config",
1798 "Contents of startup configuration\n")
1805 if (host
.config
== NULL
)
1808 confp
= fopen(host
.config
, "r");
1809 if (confp
== NULL
) {
1810 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1811 host
.config
, safe_strerror(errno
));
1815 while (fgets(buf
, BUFSIZ
, confp
)) {
1818 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1822 vty_out(vty
, "%s\n", buf
);
1830 int cmd_domainname_set(const char *domainname
)
1832 XFREE(MTYPE_HOST
, host
.domainname
);
1833 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1837 /* Hostname configuration */
1838 DEFUN(config_domainname
,
1841 "Set system's domain name\n"
1842 "This system's domain name\n")
1844 struct cmd_token
*word
= argv
[1];
1846 if (!isalpha((unsigned char)word
->arg
[0])) {
1847 vty_out(vty
, "Please specify string starting with alphabet\n");
1848 return CMD_WARNING_CONFIG_FAILED
;
1851 return cmd_domainname_set(word
->arg
);
1854 DEFUN(config_no_domainname
,
1856 "no domainname [DOMAINNAME]",
1858 "Reset system's domain name\n"
1859 "domain name of this router\n")
1861 return cmd_domainname_set(NULL
);
1864 int cmd_hostname_set(const char *hostname
)
1866 XFREE(MTYPE_HOST
, host
.name
);
1867 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1871 /* Hostname configuration */
1872 DEFUN (config_hostname
,
1875 "Set system's network name\n"
1876 "This system's network name\n")
1878 struct cmd_token
*word
= argv
[1];
1880 if (!isalnum((unsigned char)word
->arg
[0])) {
1882 "Please specify string starting with alphabet or number\n");
1883 return CMD_WARNING_CONFIG_FAILED
;
1886 /* With reference to RFC 1123 Section 2.1 */
1887 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1888 vty_out(vty
, "Hostname length should be less than %d chars\n",
1890 return CMD_WARNING_CONFIG_FAILED
;
1893 return cmd_hostname_set(word
->arg
);
1896 DEFUN (config_no_hostname
,
1898 "no hostname [HOSTNAME]",
1900 "Reset system's network name\n"
1901 "Host name of this router\n")
1903 return cmd_hostname_set(NULL
);
1906 /* VTY interface password set. */
1907 DEFUN (config_password
,
1909 "password [(8-8)] WORD",
1910 "Modify the terminal connection password\n"
1911 "Specifies a HIDDEN password will follow\n"
1912 "The password string\n")
1916 if (argc
== 3) // '8' was specified
1919 XFREE(MTYPE_HOST
, host
.password
);
1920 host
.password
= NULL
;
1921 if (host
.password_encrypt
)
1922 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1923 host
.password_encrypt
=
1924 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1928 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1930 "Please specify string starting with alphanumeric\n");
1931 return CMD_WARNING_CONFIG_FAILED
;
1935 XFREE(MTYPE_HOST
, host
.password
);
1936 host
.password
= NULL
;
1939 if (host
.password_encrypt
)
1940 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1941 host
.password_encrypt
=
1942 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1944 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1949 /* VTY interface password delete. */
1950 DEFUN (no_config_password
,
1954 "Modify the terminal connection password\n")
1956 bool warned
= false;
1958 if (host
.password
) {
1959 if (!vty_shell_serv(vty
)) {
1960 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1963 XFREE(MTYPE_HOST
, host
.password
);
1965 host
.password
= NULL
;
1967 if (host
.password_encrypt
) {
1968 if (!warned
&& !vty_shell_serv(vty
))
1969 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1970 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1972 host
.password_encrypt
= NULL
;
1977 /* VTY enable password set. */
1978 DEFUN (config_enable_password
,
1979 enable_password_cmd
,
1980 "enable password [(8-8)] WORD",
1981 "Modify enable password parameters\n"
1982 "Assign the privileged level password\n"
1983 "Specifies a HIDDEN password will follow\n"
1984 "The HIDDEN 'enable' password string\n")
1989 /* Crypt type is specified. */
1991 if (argv
[idx_8
]->arg
[0] == '8') {
1993 XFREE(MTYPE_HOST
, host
.enable
);
1996 if (host
.enable_encrypt
)
1997 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1998 host
.enable_encrypt
=
1999 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2003 vty_out(vty
, "Unknown encryption type.\n");
2004 return CMD_WARNING_CONFIG_FAILED
;
2008 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2010 "Please specify string starting with alphanumeric\n");
2011 return CMD_WARNING_CONFIG_FAILED
;
2015 XFREE(MTYPE_HOST
, host
.enable
);
2018 /* Plain password input. */
2020 if (host
.enable_encrypt
)
2021 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2022 host
.enable_encrypt
=
2023 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2025 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2030 /* VTY enable password delete. */
2031 DEFUN (no_config_enable_password
,
2032 no_enable_password_cmd
,
2033 "no enable password",
2035 "Modify enable password parameters\n"
2036 "Assign the privileged level password\n")
2038 bool warned
= false;
2041 if (!vty_shell_serv(vty
)) {
2042 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2045 XFREE(MTYPE_HOST
, host
.enable
);
2049 if (host
.enable_encrypt
) {
2050 if (!warned
&& !vty_shell_serv(vty
))
2051 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2052 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2054 host
.enable_encrypt
= NULL
;
2059 DEFUN (service_password_encrypt
,
2060 service_password_encrypt_cmd
,
2061 "service password-encryption",
2062 "Set up miscellaneous service\n"
2063 "Enable encrypted passwords\n")
2070 if (host
.password
) {
2071 if (host
.password_encrypt
)
2072 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2073 host
.password_encrypt
=
2074 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2077 if (host
.enable_encrypt
)
2078 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2079 host
.enable_encrypt
=
2080 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2086 DEFUN (no_service_password_encrypt
,
2087 no_service_password_encrypt_cmd
,
2088 "no service password-encryption",
2090 "Set up miscellaneous service\n"
2091 "Enable encrypted passwords\n")
2098 if (host
.password_encrypt
)
2099 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2100 host
.password_encrypt
= NULL
;
2102 if (host
.enable_encrypt
)
2103 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2104 host
.enable_encrypt
= NULL
;
2109 DEFUN (config_terminal_length
,
2110 config_terminal_length_cmd
,
2111 "terminal length (0-512)",
2112 "Set terminal line parameters\n"
2113 "Set number of lines on a screen\n"
2114 "Number of lines on screen (0 for no pausing)\n")
2118 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2123 DEFUN (config_terminal_no_length
,
2124 config_terminal_no_length_cmd
,
2125 "terminal no length",
2126 "Set terminal line parameters\n"
2128 "Set number of lines on a screen\n")
2134 DEFUN (service_terminal_length
,
2135 service_terminal_length_cmd
,
2136 "service terminal-length (0-512)",
2137 "Set up miscellaneous service\n"
2138 "System wide terminal length configuration\n"
2139 "Number of lines of VTY (0 means no line control)\n")
2143 host
.lines
= atoi(argv
[idx_number
]->arg
);
2148 DEFUN (no_service_terminal_length
,
2149 no_service_terminal_length_cmd
,
2150 "no service terminal-length [(0-512)]",
2152 "Set up miscellaneous service\n"
2153 "System wide terminal length configuration\n"
2154 "Number of lines of VTY (0 means no line control)\n")
2160 DEFUN_HIDDEN (do_echo
,
2163 "Echo a message back to the vty\n"
2164 "The message to echo\n")
2168 vty_out(vty
, "%s\n",
2169 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2171 XFREE(MTYPE_TMP
, message
);
2175 DEFUN (config_logmsg
,
2177 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2178 "Send a message to enabled logging destinations\n"
2180 "The message to send\n")
2182 int idx_log_level
= 1;
2183 int idx_message
= 2;
2187 level
= log_level_match(argv
[idx_log_level
]->arg
);
2188 if (level
== ZLOG_DISABLED
)
2189 return CMD_ERR_NO_MATCH
;
2192 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2194 XFREE(MTYPE_TMP
, message
);
2199 DEFUN (debug_memstats
,
2201 "[no] debug memstats-at-exit",
2204 "Print memory type statistics at exit\n")
2206 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2210 int cmd_banner_motd_file(const char *file
)
2212 int success
= CMD_SUCCESS
;
2217 rpath
= realpath(file
, p
);
2219 return CMD_ERR_NO_FILE
;
2220 in
= strstr(rpath
, SYSCONFDIR
);
2222 XFREE(MTYPE_HOST
, host
.motdfile
);
2223 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2225 success
= CMD_WARNING_CONFIG_FAILED
;
2230 void cmd_banner_motd_line(const char *line
)
2232 XFREE(MTYPE_HOST
, host
.motd
);
2233 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2236 DEFUN (banner_motd_file
,
2237 banner_motd_file_cmd
,
2238 "banner motd file FILE",
2241 "Banner from a file\n"
2245 const char *filename
= argv
[idx_file
]->arg
;
2246 int cmd
= cmd_banner_motd_file(filename
);
2248 if (cmd
== CMD_ERR_NO_FILE
)
2249 vty_out(vty
, "%s does not exist\n", filename
);
2250 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2251 vty_out(vty
, "%s must be in %s\n", filename
, SYSCONFDIR
);
2256 DEFUN (banner_motd_line
,
2257 banner_motd_line_cmd
,
2258 "banner motd line LINE...",
2261 "Banner from an input\n"
2267 argv_find(argv
, argc
, "LINE", &idx
);
2268 motd
= argv_concat(argv
, argc
, idx
);
2270 cmd_banner_motd_line(motd
);
2271 XFREE(MTYPE_TMP
, motd
);
2276 DEFUN (banner_motd_default
,
2277 banner_motd_default_cmd
,
2278 "banner motd default",
2279 "Set banner string\n"
2280 "Strings for motd\n"
2283 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2287 DEFUN (no_banner_motd
,
2291 "Set banner string\n"
2292 "Strings for motd\n")
2296 XFREE(MTYPE_HOST
, host
.motdfile
);
2297 host
.motdfile
= NULL
;
2301 DEFUN(allow_reserved_ranges
, allow_reserved_ranges_cmd
, "allow-reserved-ranges",
2302 "Allow using IPv4 (Class E) reserved IP space\n")
2304 host
.allow_reserved_ranges
= true;
2308 DEFUN(no_allow_reserved_ranges
, no_allow_reserved_ranges_cmd
,
2309 "no allow-reserved-ranges",
2310 NO_STR
"Allow using IPv4 (Class E) reserved IP space\n")
2312 host
.allow_reserved_ranges
= false;
2316 int cmd_find_cmds(struct vty
*vty
, struct cmd_token
**argv
, int argc
)
2318 const struct cmd_node
*node
;
2319 const struct cmd_element
*cli
;
2324 char *pattern
= argv_concat(argv
, argc
, 1);
2325 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2326 XFREE(MTYPE_TMP
, pattern
);
2331 vty_out(vty
, "%% Invalid {...} expression\n");
2334 vty_out(vty
, "%% Bad repetition operator\n");
2337 vty_out(vty
, "%% Regex syntax error\n");
2340 vty_out(vty
, "%% Invalid collating element\n");
2343 vty_out(vty
, "%% Invalid character class name\n");
2347 "%% Regex ended with escape character (\\)\n");
2351 "%% Invalid number in \\digit construction\n");
2354 vty_out(vty
, "%% Unbalanced square brackets\n");
2357 vty_out(vty
, "%% Unbalanced parentheses\n");
2360 vty_out(vty
, "%% Unbalanced braces\n");
2364 "%% Invalid endpoint in range expression\n");
2367 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2375 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2376 node
= vector_slot(cmdvec
, i
);
2379 clis
= node
->cmd_vector
;
2380 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2381 cli
= vector_slot(clis
, j
);
2383 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0) {
2384 vty_out(vty
, " (%s) ", node
->name
);
2385 print_cmd(vty
, cli
->string
);
2398 "Find CLI command matching a regular expression\n"
2399 "Search pattern (POSIX regex)\n")
2401 return cmd_find_cmds(vty
, argv
, argc
);
2404 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2405 DEFUN(script
, script_cmd
, "script SCRIPT FUNCTION",
2406 "Test command - execute a function in a script\n"
2407 "Script name (same as filename in /etc/frr/scripts/)\n"
2408 "Function name (in the script)\n")
2412 (void)str2prefix("1.2.3.4/24", &p
);
2413 struct frrscript
*fs
= frrscript_new(argv
[1]->arg
);
2415 if (frrscript_load(fs
, argv
[2]->arg
, NULL
)) {
2417 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2418 argv
[1]->arg
, argv
[2]->arg
);
2421 int ret
= frrscript_call(fs
, argv
[2]->arg
, ("p", &p
));
2423 prefix2str(&p
, buf
, sizeof(buf
));
2424 vty_out(vty
, "p: %s\n", buf
);
2425 vty_out(vty
, "Script result: %d\n", ret
);
2427 frrscript_delete(fs
);
2433 /* Set config filename. Called from vty.c */
2434 void host_config_set(const char *filename
)
2436 XFREE(MTYPE_HOST
, host
.config
);
2437 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2440 const char *host_config_get(void)
2445 void install_default(enum node_type node
)
2447 _install_element(node
, &config_exit_cmd
);
2448 _install_element(node
, &config_quit_cmd
);
2449 _install_element(node
, &config_end_cmd
);
2450 _install_element(node
, &config_help_cmd
);
2451 _install_element(node
, &config_list_cmd
);
2452 _install_element(node
, &show_cli_graph_cmd
);
2453 _install_element(node
, &find_cmd
);
2455 _install_element(node
, &config_write_cmd
);
2456 _install_element(node
, &show_running_config_cmd
);
2458 _install_element(node
, &autocomplete_cmd
);
2460 nb_cli_install_default(node
);
2463 /* Initialize command interface. Install basic nodes and commands.
2465 * terminal = 0 -- vtysh / no logging, no config control
2466 * terminal = 1 -- normal daemon
2467 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2468 void cmd_init(int terminal
)
2470 struct utsname names
;
2475 /* register command preprocessors */
2476 hook_register(cmd_execute
, handle_pipe_action
);
2477 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2479 varhandlers
= list_new();
2481 /* Allocate initial top vector of commands. */
2482 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2484 /* Default host value settings. */
2485 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2486 host
.system
= XSTRDUP(MTYPE_HOST
, names
.sysname
);
2487 host
.release
= XSTRDUP(MTYPE_HOST
, names
.release
);
2488 host
.version
= XSTRDUP(MTYPE_HOST
, names
.version
);
2490 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2491 if ((strcmp(names
.domainname
, "(none)") == 0))
2492 host
.domainname
= NULL
;
2494 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2496 host
.domainname
= NULL
;
2498 host
.password
= NULL
;
2501 host
.noconfig
= (terminal
< 0);
2503 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2504 host
.motdfile
= NULL
;
2505 host
.allow_reserved_ranges
= false;
2507 /* Install top nodes. */
2508 install_node(&view_node
);
2509 install_node(&enable_node
);
2510 install_node(&auth_node
);
2511 install_node(&auth_enable_node
);
2512 install_node(&config_node
);
2514 /* Each node's basic commands. */
2515 install_element(VIEW_NODE
, &show_version_cmd
);
2516 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2519 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2521 install_element(VIEW_NODE
, &config_list_cmd
);
2522 install_element(VIEW_NODE
, &config_exit_cmd
);
2523 install_element(VIEW_NODE
, &config_quit_cmd
);
2524 install_element(VIEW_NODE
, &config_help_cmd
);
2525 install_element(VIEW_NODE
, &config_enable_cmd
);
2526 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2527 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2528 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2529 install_element(VIEW_NODE
, &echo_cmd
);
2530 install_element(VIEW_NODE
, &autocomplete_cmd
);
2531 install_element(VIEW_NODE
, &find_cmd
);
2532 #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2533 install_element(VIEW_NODE
, &script_cmd
);
2537 install_element(ENABLE_NODE
, &config_end_cmd
);
2538 install_element(ENABLE_NODE
, &config_disable_cmd
);
2539 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2540 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2541 install_element(ENABLE_NODE
, &config_write_cmd
);
2542 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2543 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2545 install_default(CONFIG_NODE
);
2548 workqueue_cmd_init();
2552 install_element(CONFIG_NODE
, &hostname_cmd
);
2553 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2554 install_element(CONFIG_NODE
, &domainname_cmd
);
2555 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2560 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2562 install_element(CONFIG_NODE
, &password_cmd
);
2563 install_element(CONFIG_NODE
, &no_password_cmd
);
2564 install_element(CONFIG_NODE
, &enable_password_cmd
);
2565 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2567 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2568 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2569 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2570 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2571 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2572 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2573 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2574 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2575 install_element(CONFIG_NODE
, &allow_reserved_ranges_cmd
);
2576 install_element(CONFIG_NODE
, &no_allow_reserved_ranges_cmd
);
2579 vrf_install_commands();
2583 grammar_sandbox_init();
2587 void cmd_terminate(void)
2589 struct cmd_node
*cmd_node
;
2591 hook_unregister(cmd_execute
, handle_pipe_action
);
2592 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2595 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2596 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2597 // deleting the graph delets the cmd_element as
2599 graph_delete_graph(cmd_node
->cmdgraph
);
2600 vector_free(cmd_node
->cmd_vector
);
2601 hash_clean(cmd_node
->cmd_hash
, NULL
);
2602 hash_free(cmd_node
->cmd_hash
);
2603 cmd_node
->cmd_hash
= NULL
;
2606 vector_free(cmdvec
);
2610 XFREE(MTYPE_HOST
, host
.name
);
2611 XFREE(MTYPE_HOST
, host
.system
);
2612 XFREE(MTYPE_HOST
, host
.release
);
2613 XFREE(MTYPE_HOST
, host
.version
);
2614 XFREE(MTYPE_HOST
, host
.domainname
);
2615 XFREE(MTYPE_HOST
, host
.password
);
2616 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2617 XFREE(MTYPE_HOST
, host
.enable
);
2618 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2619 XFREE(MTYPE_HOST
, host
.motdfile
);
2620 XFREE(MTYPE_HOST
, host
.config
);
2621 XFREE(MTYPE_HOST
, host
.motd
);
2623 list_delete(&varhandlers
);