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"
51 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config")
52 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item")
59 /* clang-format off */
60 const struct message tokennames
[] = {
65 item(IPV4_PREFIX_TKN
),
67 item(IPV6_PREFIX_TKN
),
78 /* Command vector which includes some level of command lists. Normally
79 each daemon maintains each own cmdvec. */
82 /* Host information structure. */
86 * Returns host.name if any, otherwise
87 * it returns the system hostname.
89 const char *cmd_hostname_get(void)
95 * Returns unix domainname
97 const char *cmd_domainname_get(void)
99 return host
.domainname
;
102 /* Standard command node structures. */
103 static struct cmd_node auth_node
= {
106 .prompt
= "Password: ",
109 static struct cmd_node view_node
= {
115 static struct cmd_node auth_enable_node
= {
116 .name
= "auth enable",
117 .node
= AUTH_ENABLE_NODE
,
118 .prompt
= "Password: ",
121 static struct cmd_node enable_node
= {
127 static int config_write_host(struct vty
*vty
);
128 static struct cmd_node config_node
= {
131 .parent_node
= ENABLE_NODE
,
132 .prompt
= "%s(config)# ",
133 .config_write
= config_write_host
,
136 static const struct facility_map
{
140 } syslog_facilities
[] = {
141 {LOG_KERN
, "kern", 1},
142 {LOG_USER
, "user", 2},
143 {LOG_MAIL
, "mail", 1},
144 {LOG_DAEMON
, "daemon", 1},
145 {LOG_AUTH
, "auth", 1},
146 {LOG_SYSLOG
, "syslog", 1},
148 {LOG_NEWS
, "news", 1},
149 {LOG_UUCP
, "uucp", 2},
150 {LOG_CRON
, "cron", 1},
154 {LOG_LOCAL0
, "local0", 6},
155 {LOG_LOCAL1
, "local1", 6},
156 {LOG_LOCAL2
, "local2", 6},
157 {LOG_LOCAL3
, "local3", 6},
158 {LOG_LOCAL4
, "local4", 6},
159 {LOG_LOCAL5
, "local5", 6},
160 {LOG_LOCAL6
, "local6", 6},
161 {LOG_LOCAL7
, "local7", 6},
165 static const char *facility_name(int facility
)
167 const struct facility_map
*fm
;
169 for (fm
= syslog_facilities
; fm
->name
; fm
++)
170 if (fm
->facility
== facility
)
175 static int facility_match(const char *str
)
177 const struct facility_map
*fm
;
179 for (fm
= syslog_facilities
; fm
->name
; fm
++)
180 if (!strncmp(str
, fm
->name
, fm
->match
))
185 static int level_match(const char *s
)
189 for (level
= 0; zlog_priority
[level
] != NULL
; level
++)
190 if (!strncmp(s
, zlog_priority
[level
], 2))
192 return ZLOG_DISABLED
;
195 /* This is called from main when a daemon is invoked with -v or --version. */
196 void print_version(const char *progname
)
198 printf("%s version %s\n", progname
, FRR_VERSION
);
199 printf("%s\n", FRR_COPYRIGHT
);
200 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
203 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
205 int cnt
= MAX(argc
- shift
, 0);
206 const char *argstr
[cnt
+ 1];
211 for (int i
= 0; i
< cnt
; i
++)
212 argstr
[i
] = argv
[i
+ shift
]->arg
;
214 return frrstr_join(argstr
, cnt
, " ");
217 vector
cmd_make_strvec(const char *string
)
222 const char *copy
= string
;
224 /* skip leading whitespace */
225 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
228 /* if the entire string was whitespace or a comment, return */
229 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
232 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
234 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
235 if (strlen(vector_slot(result
, i
)) == 0) {
236 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
237 vector_unset(result
, i
);
241 vector_compact(result
);
246 void cmd_free_strvec(vector v
)
248 frrstr_strvec_free(v
);
252 * Convenience function for accessing argv data.
256 * @param text definition snippet of the desired token
257 * @param index the starting index, and where to store the
258 * index of the found token if it exists
259 * @return 1 if found, 0 otherwise
261 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
264 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
265 if ((found
= strmatch(text
, argv
[i
]->text
)))
270 static unsigned int cmd_hash_key(const void *p
)
272 int size
= sizeof(p
);
274 return jhash(p
, size
, 0);
277 static bool cmd_hash_cmp(const void *a
, const void *b
)
282 /* Install top node of command vector. */
283 void install_node(struct cmd_node
*node
)
285 vector_set_index(cmdvec
, node
->node
, node
);
286 node
->cmdgraph
= graph_new();
287 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
289 struct cmd_token
*token
=
290 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
291 graph_new_node(node
->cmdgraph
, token
,
292 (void (*)(void *)) & cmd_token_del
);
293 node
->cmd_hash
= hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
,
297 /* Return prompt character of specified node. */
298 const char *cmd_prompt(enum node_type node
)
300 struct cmd_node
*cnode
;
302 cnode
= vector_slot(cmdvec
, node
);
303 return cnode
->prompt
;
306 /* Install a command into a node. */
307 void install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
309 struct cmd_node
*cnode
;
311 /* cmd_init hasn't been called */
313 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
318 cnode
= vector_lookup(cmdvec
, ntype
);
323 "\tnode %d does not exist.\n"
324 "\tplease call install_node() before install_element()\n",
325 cmd
->name
, cmd
->string
, ntype
);
329 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
332 "\tnode %d (%s) already has this command installed.\n"
333 "\tduplicate install_element call?\n",
334 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
338 assert(hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
));
340 struct graph
*graph
= graph_new();
341 struct cmd_token
*token
=
342 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
343 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
345 cmd_graph_parse(graph
, cmd
);
346 cmd_graph_names(graph
);
347 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
348 graph_delete_graph(graph
);
350 vector_set(cnode
->cmd_vector
, (void *)cmd
);
352 if (ntype
== VIEW_NODE
)
353 install_element(ENABLE_NODE
, cmd
);
356 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
358 struct cmd_node
*cnode
;
360 /* cmd_init hasn't been called */
362 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
367 cnode
= vector_lookup(cmdvec
, ntype
);
372 "\tnode %d does not exist.\n"
373 "\tplease call install_node() before uninstall_element()\n",
374 cmd
->name
, cmd
->string
, ntype
);
378 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
381 "\tnode %d (%s) does not have this command installed.\n"
382 "\tduplicate uninstall_element call?\n",
383 cmd
->name
, cmd
->string
, ntype
, cnode
->name
);
387 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
389 struct graph
*graph
= graph_new();
390 struct cmd_token
*token
=
391 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
392 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
394 cmd_graph_parse(graph
, cmd
);
395 cmd_graph_names(graph
);
396 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
397 graph_delete_graph(graph
);
399 if (ntype
== VIEW_NODE
)
400 uninstall_element(ENABLE_NODE
, cmd
);
404 static const unsigned char itoa64
[] =
405 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
407 static void to64(char *s
, long v
, int n
)
410 *s
++ = itoa64
[v
& 0x3f];
415 static char *zencrypt(const char *passwd
)
419 char *crypt(const char *, const char *);
421 gettimeofday(&tv
, 0);
423 to64(&salt
[0], random(), 3);
424 to64(&salt
[3], tv
.tv_usec
, 3);
427 return crypt(passwd
, salt
);
430 /* This function write configuration of this host. */
431 static int config_write_host(struct vty
*vty
)
433 if (cmd_hostname_get())
434 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
436 if (cmd_domainname_get())
437 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
439 /* The following are all configuration commands that are not sent to
440 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
441 * we would always display 'log syslog informational' in the config
442 * which would cause other daemons to then switch to syslog when they
445 if (strcmp(zlog_default
->protoname
, "WATCHFRR")) {
447 if (host
.password_encrypt
)
448 vty_out(vty
, "password 8 %s\n",
449 host
.password_encrypt
);
450 if (host
.enable_encrypt
)
451 vty_out(vty
, "enable password 8 %s\n",
452 host
.enable_encrypt
);
455 vty_out(vty
, "password %s\n", host
.password
);
457 vty_out(vty
, "enable password %s\n",
462 && (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
464 vty_out(vty
, "log file %s", host
.logfile
);
465 if (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
466 != zlog_default
->default_lvl
)
469 [zlog_default
->maxlvl
474 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
] != ZLOG_DISABLED
) {
475 vty_out(vty
, "log stdout");
476 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
]
477 != zlog_default
->default_lvl
)
480 [zlog_default
->maxlvl
481 [ZLOG_DEST_STDOUT
]]);
485 if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
486 vty_out(vty
, "no log monitor\n");
487 else if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]
488 != zlog_default
->default_lvl
)
489 vty_out(vty
, "log monitor %s\n",
490 zlog_priority
[zlog_default
->maxlvl
491 [ZLOG_DEST_MONITOR
]]);
493 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
) {
494 vty_out(vty
, "log syslog");
495 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
]
496 != zlog_default
->default_lvl
)
498 zlog_priority
[zlog_default
->maxlvl
499 [ZLOG_DEST_SYSLOG
]]);
503 if (zlog_default
->facility
!= LOG_DAEMON
)
504 vty_out(vty
, "log facility %s\n",
505 facility_name(zlog_default
->facility
));
507 if (zlog_default
->record_priority
== 1)
508 vty_out(vty
, "log record-priority\n");
510 if (zlog_default
->timestamp_precision
> 0)
511 vty_out(vty
, "log timestamp precision %d\n",
512 zlog_default
->timestamp_precision
);
515 vty_out(vty
, "service advanced-vty\n");
518 vty_out(vty
, "service password-encryption\n");
521 vty_out(vty
, "service terminal-length %d\n",
525 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
527 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
529 vty_out(vty
, "banner motd line %s\n", host
.motd
);
531 vty_out(vty
, "no banner motd\n");
534 if (debug_memstats_at_exit
)
535 vty_out(vty
, "!\ndebug memstats-at-exit\n");
540 /* Utility function for getting command graph. */
541 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
543 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
544 return cnode
->cmdgraph
;
547 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
549 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
550 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
556 * Compare function for cmd_token.
557 * Used with qsort to sort command completions.
559 static int compare_completions(const void *fst
, const void *snd
)
561 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
562 *secnd
= *(const struct cmd_token
* const *)snd
;
563 return strcmp(first
->text
, secnd
->text
);
567 * Takes a list of completions returned by command_complete,
568 * dedeuplicates them based on both text and description,
569 * sorts them, and returns them as a vector.
571 * @param completions linked list of cmd_token
572 * @return deduplicated and sorted vector with
574 vector
completions_to_vec(struct list
*completions
)
576 vector comps
= vector_init(VECTOR_MIN_SIZE
);
579 struct cmd_token
*token
, *cr
= NULL
;
580 unsigned int i
, exists
;
581 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
582 if (token
->type
== END_TKN
&& (cr
= token
))
585 // linear search for token in completions vector
587 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
588 struct cmd_token
*curr
= vector_slot(comps
, i
);
590 exists
= !strcmp(curr
->text
, token
->text
)
591 && !strcmp(curr
->desc
, token
->desc
);
593 exists
= !strcmp(curr
->text
, token
->text
);
594 #endif /* VTYSH_DEBUG */
598 vector_set(comps
, token
);
602 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
603 &compare_completions
);
605 // make <cr> the first element, if it is present
607 vector_set_index(comps
, vector_active(comps
), NULL
);
608 memmove(comps
->index
+ 1, comps
->index
,
609 (comps
->alloced
- 1) * sizeof(void *));
610 vector_set_index(comps
, 0, cr
);
616 * Generates a vector of cmd_token representing possible completions
617 * on the current input.
619 * @param vline the vectorized input line
620 * @param vty the vty with the node to match on
621 * @param status pointer to matcher status code
622 * @return vector of struct cmd_token * with possible completions
624 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
627 struct list
*completions
;
628 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
630 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
632 if (MATCHER_ERROR(rv
)) {
633 *status
= CMD_ERR_NO_MATCH
;
637 vector comps
= completions_to_vec(completions
);
638 list_delete(&completions
);
640 // set status code appropriately
641 switch (vector_active(comps
)) {
643 *status
= CMD_ERR_NO_MATCH
;
646 *status
= CMD_COMPLETE_FULL_MATCH
;
649 *status
= CMD_COMPLETE_LIST_MATCH
;
655 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
659 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
660 enum node_type onode
;
661 int orig_xpath_index
;
662 vector shifted_vline
;
666 orig_xpath_index
= vty
->xpath_index
;
667 vty
->node
= ENABLE_NODE
;
668 vty
->xpath_index
= 0;
669 /* We can try it on enable node, cos' the vty is authenticated
672 shifted_vline
= vector_init(vector_count(vline
));
674 for (index
= 1; index
< vector_active(vline
); index
++) {
675 vector_set_index(shifted_vline
, index
- 1,
676 vector_lookup(vline
, index
));
679 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
681 vector_free(shifted_vline
);
683 vty
->xpath_index
= orig_xpath_index
;
687 return cmd_complete_command_real(vline
, vty
, status
);
690 static struct list
*varhandlers
= NULL
;
692 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
696 const struct cmd_variable_handler
*cvh
;
700 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
702 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
703 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
705 if (cvh
->varname
&& (!token
->varname
706 || strcmp(cvh
->varname
, token
->varname
)))
708 cvh
->completions(tmpcomps
, token
);
716 for (i
= vector_active(tmpcomps
); i
; i
--) {
717 char *item
= vector_slot(tmpcomps
, i
- 1);
718 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
719 vector_set(comps
, item
);
721 XFREE(MTYPE_COMPLETION
, item
);
723 vector_free(tmpcomps
);
726 #define AUTOCOMP_INDENT 5
728 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
731 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
732 int lc
= AUTOCOMP_INDENT
;
733 size_t cs
= AUTOCOMP_INDENT
;
735 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
736 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
737 char *item
= vector_slot(comps
, j
);
738 itemlen
= strlen(item
);
740 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
741 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
743 if (lc
+ itemlen
+ 1 >= cols
) {
744 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
745 AUTOCOMP_INDENT
, "");
746 lc
= AUTOCOMP_INDENT
;
749 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
752 XFREE(MTYPE_COMPLETION
, item
);
753 vector_set_index(comps
, j
, NULL
);
758 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
763 for (; cvh
->completions
; cvh
++)
764 listnode_add(varhandlers
, (void *)cvh
);
767 DEFUN_HIDDEN (autocomplete
,
769 "autocomplete TYPE TEXT VARNAME",
770 "Autocompletion handler (internal, for vtysh)\n"
773 "cmd_token->varname\n")
775 struct cmd_token tok
;
776 vector comps
= vector_init(32);
779 memset(&tok
, 0, sizeof(tok
));
780 tok
.type
= atoi(argv
[1]->arg
);
781 tok
.text
= argv
[2]->arg
;
782 tok
.varname
= argv
[3]->arg
;
783 if (!strcmp(tok
.varname
, "-"))
786 cmd_variable_complete(&tok
, NULL
, comps
);
788 for (i
= 0; i
< vector_active(comps
); i
++) {
789 char *text
= vector_slot(comps
, i
);
790 vty_out(vty
, "%s\n", text
);
791 XFREE(MTYPE_COMPLETION
, text
);
799 * Generate possible tab-completions for the given input. This function only
800 * returns results that would result in a valid command if used as Readline
801 * completions (as is the case in vtysh). For instance, if the passed vline ends
802 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
804 * @param vline vectorized input line
806 * @param status location to store matcher status code in
807 * @return set of valid strings for use with Readline as tab-completions.
810 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
813 int original_node
= vty
->node
;
814 vector input_line
= vector_init(vector_count(vline
));
816 // if the first token is 'do' we'll want to execute the command in the
818 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
819 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
821 // construct the input line we'll be matching on
822 unsigned int offset
= (do_shortcut
) ? 1 : 0;
823 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
824 vector_set_index(input_line
, index
,
825 vector_lookup(vline
, index
+ offset
));
827 // get token completions -- this is a copying operation
828 vector comps
= NULL
, initial_comps
;
829 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
831 if (!MATCHER_ERROR(*status
)) {
832 assert(initial_comps
);
833 // filter out everything that is not suitable for a
835 comps
= vector_init(VECTOR_MIN_SIZE
);
836 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
838 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
839 if (token
->type
== WORD_TKN
)
840 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
842 else if (IS_VARYING_TOKEN(token
->type
)) {
843 const char *ref
= vector_lookup(
844 vline
, vector_active(vline
) - 1);
845 cmd_variable_complete(token
, ref
, comps
);
848 vector_free(initial_comps
);
850 // since we filtered results, we need to re-set status code
851 switch (vector_active(comps
)) {
853 *status
= CMD_ERR_NO_MATCH
;
856 *status
= CMD_COMPLETE_FULL_MATCH
;
859 *status
= CMD_COMPLETE_LIST_MATCH
;
862 // copy completions text into an array of char*
863 ret
= XMALLOC(MTYPE_TMP
,
864 (vector_active(comps
) + 1) * sizeof(char *));
866 for (i
= 0; i
< vector_active(comps
); i
++) {
867 ret
[i
] = vector_slot(comps
, i
);
869 // set the last element to NULL, because this array is used in
870 // a Readline completion_generator function which expects NULL
871 // as a sentinel value
875 } else if (initial_comps
)
876 vector_free(initial_comps
);
878 // comps should always be null here
881 // free the adjusted input line
882 vector_free(input_line
);
884 // reset vty->node to its original value
885 vty
->node
= original_node
;
890 /* return parent node */
891 /* MUST eventually converge on CONFIG_NODE */
892 enum node_type
node_parent(enum node_type node
)
896 assert(node
> CONFIG_NODE
);
901 case BGP_FLOWSPECV4_NODE
:
902 case BGP_FLOWSPECV6_NODE
:
903 case BGP_VRF_POLICY_NODE
:
904 case BGP_VNC_DEFAULTS_NODE
:
905 case BGP_VNC_NVE_GROUP_NODE
:
906 case BGP_VNC_L2_GROUP_NODE
:
917 case BGP_EVPN_VNI_NODE
:
920 case KEYCHAIN_KEY_NODE
:
923 case LINK_PARAMS_NODE
:
924 ret
= INTERFACE_NODE
;
930 case LDP_IPV4_IFACE_NODE
:
933 case LDP_IPV6_IFACE_NODE
:
936 case LDP_PSEUDOWIRE_NODE
:
937 ret
= LDP_L2VPN_NODE
;
950 /* Execute command by argument vline vector. */
951 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
953 const struct cmd_element
**cmd
)
955 struct list
*argv_list
;
956 enum matcher_rv status
;
957 const struct cmd_element
*matched_element
= NULL
;
959 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
960 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
963 *cmd
= matched_element
;
965 // if matcher error, return corresponding CMD_ERR
966 if (MATCHER_ERROR(status
)) {
968 list_delete(&argv_list
);
970 case MATCHER_INCOMPLETE
:
971 return CMD_ERR_INCOMPLETE
;
972 case MATCHER_AMBIGUOUS
:
973 return CMD_ERR_AMBIGUOUS
;
975 return CMD_ERR_NO_MATCH
;
979 // build argv array from argv list
980 struct cmd_token
**argv
= XMALLOC(
981 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
983 struct cmd_token
*token
;
985 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
988 int argc
= argv_list
->count
;
991 if (matched_element
->daemon
)
992 ret
= CMD_SUCCESS_DAEMON
;
995 /* Clear array of enqueued configuration changes. */
996 vty
->num_cfg_changes
= 0;
997 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
999 /* Regenerate candidate configuration if necessary. */
1000 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
1001 && running_config
->version
1002 > vty
->candidate_config
->version
)
1003 nb_config_replace(vty
->candidate_config
,
1004 running_config
, true);
1007 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
1010 // delete list and cmd_token's in it
1011 list_delete(&argv_list
);
1012 XFREE(MTYPE_TMP
, argv
);
1018 * Execute a given command, handling things like "do ..." and checking
1019 * whether the given command might apply at a parent node if doesn't
1020 * apply for the current node.
1022 * @param vline Command line input, vector of char* where each element is
1024 * @param vty The vty context in which the command should be executed.
1025 * @param cmd Pointer where the struct cmd_element of the matched command
1026 * will be stored, if any. May be set to NULL if this info is
1028 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1029 * @return The status of the command that has been executed or an error code
1030 * as to why no command could be executed.
1032 int cmd_execute_command(vector vline
, struct vty
*vty
,
1033 const struct cmd_element
**cmd
, int vtysh
)
1035 int ret
, saved_ret
= 0;
1036 enum node_type onode
, try_node
;
1037 int orig_xpath_index
;
1039 onode
= try_node
= vty
->node
;
1040 orig_xpath_index
= vty
->xpath_index
;
1042 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1043 vector shifted_vline
;
1046 vty
->node
= ENABLE_NODE
;
1047 vty
->xpath_index
= 0;
1048 /* We can try it on enable node, cos' the vty is authenticated
1051 shifted_vline
= vector_init(vector_count(vline
));
1053 for (index
= 1; index
< vector_active(vline
); index
++)
1054 vector_set_index(shifted_vline
, index
- 1,
1055 vector_lookup(vline
, index
));
1057 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1060 vector_free(shifted_vline
);
1062 vty
->xpath_index
= orig_xpath_index
;
1067 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
1072 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1073 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1074 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1075 /* This assumes all nodes above CONFIG_NODE are childs of
1077 while (vty
->node
> CONFIG_NODE
) {
1078 try_node
= node_parent(try_node
);
1079 vty
->node
= try_node
;
1080 if (vty
->xpath_index
> 0)
1082 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1084 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1085 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1086 || ret
== CMD_NOT_MY_INSTANCE
1087 || ret
== CMD_WARNING_CONFIG_FAILED
)
1090 /* no command succeeded, reset the vty to the original node */
1092 vty
->xpath_index
= orig_xpath_index
;
1095 /* return command status for original node */
1100 * Execute a given command, matching it strictly against the current node.
1101 * This mode is used when reading config files.
1103 * @param vline Command line input, vector of char* where each element is
1105 * @param vty The vty context in which the command should be executed.
1106 * @param cmd Pointer where the struct cmd_element* of the matched command
1107 * will be stored, if any. May be set to NULL if this info is
1109 * @return The status of the command that has been executed or an error code
1110 * as to why no command could be executed.
1112 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1113 const struct cmd_element
**cmd
)
1115 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1119 * Hook for preprocessing command string before executing.
1121 * All subscribers are called with the raw command string that is to be
1122 * executed. If any changes are to be made, a new string should be allocated
1123 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1124 * is then responsible for freeing this string.
1126 * All processing functions must be mutually exclusive in their action, i.e. if
1127 * one subscriber decides to modify the command, all others must not modify it
1128 * when called. Feeding the output of one processing command into a subsequent
1129 * one is not supported.
1131 * This hook is intentionally internal to the command processing system.
1134 * The raw command string.
1137 * The result of any processing.
1139 DECLARE_HOOK(cmd_execute
,
1140 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1141 (vty
, cmd_in
, cmd_out
));
1142 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1143 (vty
, cmd_in
, cmd_out
));
1145 /* Hook executed after a CLI command. */
1146 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1148 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1152 * cmd_execute hook subscriber to handle `|` actions.
1154 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1158 char *orig
, *working
, *token
, *u
;
1159 char *pipe
= strstr(cmd_in
, "| ");
1164 /* duplicate string for processing purposes, not including pipe */
1165 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1167 /* retrieve action */
1168 token
= strsep(&working
, " ");
1171 /* match result to known actions */
1172 if (strmatch(token
, "include")) {
1173 /* the remaining text should be a regexp */
1174 char *regexp
= working
;
1177 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
);
1187 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1191 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1196 XFREE(MTYPE_TMP
, orig
);
1200 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1203 vty_set_include(vty
, NULL
);
1208 int cmd_execute(struct vty
*vty
, const char *cmd
,
1209 const struct cmd_element
**matched
, int vtysh
)
1212 char *cmd_out
= NULL
;
1213 const char *cmd_exec
;
1216 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
);
1228 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1230 XFREE(MTYPE_TMP
, cmd_out
);
1237 * Parse one line of config, walking up the parse tree attempting to find a
1240 * @param vty The vty context in which the command should be executed.
1241 * @param cmd Pointer where the struct cmd_element* of the match command
1242 * will be stored, if any. May be set to NULL if this info is
1244 * @param use_daemon Boolean to control whether or not we match on
1245 * CMD_SUCCESS_DAEMON
1247 * @return The status of the command that has been executed or an error code
1248 * as to why no command could be executed.
1250 int command_config_read_one_line(struct vty
*vty
,
1251 const struct cmd_element
**cmd
,
1252 uint32_t line_num
, int use_daemon
)
1257 vline
= cmd_make_strvec(vty
->buf
);
1259 /* In case of comment line */
1263 /* Execute configuration command : this is strict match */
1264 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1266 // Climb the tree and try the command again at each node
1267 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1268 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1269 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1270 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1271 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1272 && vty
->node
!= CONFIG_NODE
) {
1273 int saved_node
= vty
->node
;
1274 int saved_xpath_index
= vty
->xpath_index
;
1276 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1277 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1278 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1279 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1280 && vty
->node
> CONFIG_NODE
) {
1281 vty
->node
= node_parent(vty
->node
);
1282 if (vty
->xpath_index
> 0)
1284 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1287 // If climbing the tree did not work then ignore the command and
1288 // stay at the same node
1289 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1290 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1291 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1292 vty
->node
= saved_node
;
1293 vty
->xpath_index
= saved_xpath_index
;
1297 if (ret
!= CMD_SUCCESS
&&
1298 ret
!= CMD_WARNING
&&
1299 ret
!= CMD_SUCCESS_DAEMON
) {
1300 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1302 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1303 ve
->line_num
= line_num
;
1305 vty
->error
= list_new();
1307 listnode_add(vty
->error
, ve
);
1310 cmd_free_strvec(vline
);
1315 /* Configuration make from file. */
1316 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1318 int ret
, error_ret
= 0;
1321 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1324 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1326 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1327 && ret
!= CMD_ERR_NOTHING_TODO
)
1338 /* Configuration from terminal */
1339 DEFUN (config_terminal
,
1340 config_terminal_cmd
,
1341 "configure [terminal]",
1342 "Configuration from vty interface\n"
1343 "Configuration terminal\n")
1345 return vty_config_enter(vty
, false, false);
1348 /* Enable command */
1352 "Turn on privileged mode command\n")
1354 /* If enable password is NULL, change to ENABLE_NODE */
1355 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1356 || vty
->type
== VTY_SHELL_SERV
)
1357 vty
->node
= ENABLE_NODE
;
1359 vty
->node
= AUTH_ENABLE_NODE
;
1364 /* Disable command */
1368 "Turn off privileged mode command\n")
1370 if (vty
->node
== ENABLE_NODE
)
1371 vty
->node
= VIEW_NODE
;
1375 /* Down vty node level. */
1379 "Exit current mode and down to previous mode\n")
1385 void cmd_exit(struct vty
*vty
)
1387 struct cmd_node
*cnode
= vector_lookup(cmdvec
, vty
->node
);
1389 switch (vty
->node
) {
1395 vty
->status
= VTY_CLOSE
;
1398 vty
->node
= ENABLE_NODE
;
1399 vty_config_exit(vty
);
1402 if (cnode
->parent_node
)
1403 vty
->node
= cnode
->parent_node
;
1407 if (vty
->xpath_index
> 0)
1415 "Exit current mode and down to previous mode\n")
1417 return config_exit(self
, vty
, argc
, argv
);
1421 /* End of configuration. */
1425 "End current mode and change to enable mode.\n")
1428 vty_config_exit(vty
);
1429 vty
->node
= ENABLE_NODE
;
1435 DEFUN (show_version
,
1439 "Displays zebra version\n")
1441 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1442 cmd_hostname_get() ? cmd_hostname_get() : "");
1443 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1444 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1449 /* Help display function for all node. */
1453 "Description of the interactive help system\n")
1456 "Quagga VTY provides advanced help feature. When you need help,\n\
1457 anytime at the command line please press '?'.\n\
1459 If nothing matches, the help list will be empty and you must backup\n\
1460 until entering a '?' shows the available options.\n\
1461 Two styles of help are provided:\n\
1462 1. Full help is available when you are ready to enter a\n\
1463 command argument (e.g. 'show ?') and describes each possible\n\
1465 2. Partial help is provided when an abbreviated argument is entered\n\
1466 and you want to know what arguments match the input\n\
1467 (e.g. 'show me?'.)\n\n");
1471 static void permute(struct graph_node
*start
, struct vty
*vty
)
1473 static struct list
*position
= NULL
;
1475 position
= list_new();
1477 struct cmd_token
*stok
= start
->data
;
1478 struct graph_node
*gnn
;
1479 struct listnode
*ln
;
1482 listnode_add(position
, start
);
1483 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1484 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1485 struct cmd_token
*tok
= gn
->data
;
1486 if (tok
->attr
== CMD_ATTR_HIDDEN
1487 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1489 else if (tok
->type
== END_TKN
|| gn
== start
) {
1491 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1492 struct cmd_token
*tt
= gnn
->data
;
1493 if (tt
->type
< SPECIAL_TKN
)
1494 vty_out(vty
, " %s", tt
->text
);
1497 vty_out(vty
, "...");
1501 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1502 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1511 list_delete_node(position
, listtail(position
));
1514 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1516 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1519 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1521 /* loop over all commands at this node */
1522 const struct cmd_element
*element
= NULL
;
1523 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1525 if ((element
= vector_slot(node
->cmd_vector
, i
))
1526 && element
->attr
!= CMD_ATTR_DEPRECATED
1527 && element
->attr
!= CMD_ATTR_HIDDEN
)
1528 vty_out(vty
, " %s\n", element
->string
);
1533 /* Help display function for all node. */
1536 "list [permutations]",
1537 "Print command list\n"
1538 "Print all possible command permutations\n")
1540 return cmd_list_cmds(vty
, argc
== 2);
1543 DEFUN (show_commandtree
,
1544 show_commandtree_cmd
,
1545 "show commandtree [permutations]",
1547 "Show command tree\n"
1548 "Permutations that we are interested in\n")
1550 return cmd_list_cmds(vty
, argc
== 3);
1553 DEFUN_HIDDEN(show_cli_graph
,
1558 "Dump current command space as DOT graph\n")
1560 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1561 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1563 vty_out(vty
, "%s\n", dot
);
1564 XFREE(MTYPE_TMP
, dot
);
1568 static int vty_write_config(struct vty
*vty
)
1571 struct cmd_node
*node
;
1576 nb_cli_show_config_prepare(running_config
, false);
1578 if (vty
->type
== VTY_TERM
) {
1579 vty_out(vty
, "\nCurrent configuration:\n");
1580 vty_out(vty
, "!\n");
1583 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1584 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1585 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1586 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1587 vty_out(vty
, "!\n");
1589 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1590 if ((node
= vector_slot(cmdvec
, i
)) && node
->config_write
) {
1591 if ((*node
->config_write
)(vty
))
1592 vty_out(vty
, "!\n");
1595 if (vty
->type
== VTY_TERM
) {
1596 vty_out(vty
, "end\n");
1602 static int file_write_config(struct vty
*vty
)
1605 char *config_file
, *slash
;
1606 char *config_file_tmp
= NULL
;
1607 char *config_file_sav
= NULL
;
1608 int ret
= CMD_WARNING
;
1609 struct vty
*file_vty
;
1610 struct stat conf_stat
;
1615 /* Check and see if we are operating under vtysh configuration */
1616 if (host
.config
== NULL
) {
1618 "Can't save to configuration file, using vtysh.\n");
1623 config_file
= host
.config
;
1626 #define O_DIRECTORY 0
1628 slash
= strrchr(config_file
, '/');
1630 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1631 config_dir
[slash
- config_file
] = '\0';
1632 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1633 XFREE(MTYPE_TMP
, config_dir
);
1635 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1636 /* if dirfd is invalid, directory sync fails, but we're still OK */
1638 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1639 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1640 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1641 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1644 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1645 sprintf(config_file_tmp
, "%s.XXXXXX", config_file
);
1647 /* Open file to configuration write. */
1648 fd
= mkstemp(config_file_tmp
);
1650 vty_out(vty
, "Can't open configuration file %s.\n",
1654 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1655 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1656 config_file_tmp
, safe_strerror(errno
), errno
);
1660 /* Make vty for configuration file. */
1661 file_vty
= vty_new();
1663 file_vty
->type
= VTY_FILE
;
1665 /* Config file header print. */
1666 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1667 vty_time_print(file_vty
, 1);
1668 vty_out(file_vty
, "!\n");
1669 vty_write_config(file_vty
);
1670 vty_close(file_vty
);
1672 if (stat(config_file
, &conf_stat
) >= 0) {
1673 if (unlink(config_file_sav
) != 0)
1674 if (errno
!= ENOENT
) {
1676 "Can't unlink backup configuration file %s.\n",
1680 if (link(config_file
, config_file_sav
) != 0) {
1682 "Can't backup old configuration file %s.\n",
1689 if (rename(config_file_tmp
, config_file
) != 0) {
1690 vty_out(vty
, "Can't save configuration file %s.\n",
1697 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1701 if (ret
!= CMD_SUCCESS
)
1702 unlink(config_file_tmp
);
1705 XFREE(MTYPE_TMP
, config_file_tmp
);
1706 XFREE(MTYPE_TMP
, config_file_sav
);
1710 /* Write current configuration into file. */
1712 DEFUN (config_write
,
1714 "write [<file|memory|terminal>]",
1715 "Write running configuration to memory, network, or terminal\n"
1716 "Write to configuration file\n"
1717 "Write configuration currently in memory\n"
1718 "Write configuration to terminal\n")
1720 const int idx_type
= 1;
1722 // if command was 'write terminal' or 'write memory'
1723 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1724 return vty_write_config(vty
);
1727 return file_write_config(vty
);
1730 /* ALIAS_FIXME for 'write <terminal|memory>' */
1731 DEFUN (show_running_config
,
1732 show_running_config_cmd
,
1733 "show running-config",
1735 "running configuration (same as write terminal)\n")
1737 return vty_write_config(vty
);
1740 /* ALIAS_FIXME for 'write file' */
1741 DEFUN (copy_runningconf_startupconf
,
1742 copy_runningconf_startupconf_cmd
,
1743 "copy running-config startup-config",
1744 "Copy configuration\n"
1745 "Copy running config to... \n"
1746 "Copy running config to startup config (same as write file/memory)\n")
1748 return file_write_config(vty
);
1752 /* Write startup configuration into the terminal. */
1753 DEFUN (show_startup_config
,
1754 show_startup_config_cmd
,
1755 "show startup-config",
1757 "Contents of startup configuration\n")
1764 if (host
.config
== NULL
)
1767 confp
= fopen(host
.config
, "r");
1768 if (confp
== NULL
) {
1769 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1770 host
.config
, safe_strerror(errno
));
1774 while (fgets(buf
, BUFSIZ
, confp
)) {
1777 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1781 vty_out(vty
, "%s\n", buf
);
1789 int cmd_domainname_set(const char *domainname
)
1791 XFREE(MTYPE_HOST
, host
.domainname
);
1792 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1796 /* Hostname configuration */
1797 DEFUN(config_domainname
,
1800 "Set system's domain name\n"
1801 "This system's domain name\n")
1803 struct cmd_token
*word
= argv
[1];
1805 if (!isalpha((unsigned char)word
->arg
[0])) {
1806 vty_out(vty
, "Please specify string starting with alphabet\n");
1807 return CMD_WARNING_CONFIG_FAILED
;
1810 return cmd_domainname_set(word
->arg
);
1813 DEFUN(config_no_domainname
,
1815 "no domainname [DOMAINNAME]",
1817 "Reset system's domain name\n"
1818 "domain name of this router\n")
1820 return cmd_domainname_set(NULL
);
1823 int cmd_hostname_set(const char *hostname
)
1825 XFREE(MTYPE_HOST
, host
.name
);
1826 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1830 /* Hostname configuration */
1831 DEFUN (config_hostname
,
1834 "Set system's network name\n"
1835 "This system's network name\n")
1837 struct cmd_token
*word
= argv
[1];
1839 if (!isalnum((unsigned char)word
->arg
[0])) {
1841 "Please specify string starting with alphabet or number\n");
1842 return CMD_WARNING_CONFIG_FAILED
;
1845 /* With reference to RFC 1123 Section 2.1 */
1846 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1847 vty_out(vty
, "Hostname length should be less than %d chars\n",
1849 return CMD_WARNING_CONFIG_FAILED
;
1852 return cmd_hostname_set(word
->arg
);
1855 DEFUN (config_no_hostname
,
1857 "no hostname [HOSTNAME]",
1859 "Reset system's network name\n"
1860 "Host name of this router\n")
1862 return cmd_hostname_set(NULL
);
1865 /* VTY interface password set. */
1866 DEFUN (config_password
,
1868 "password [(8-8)] WORD",
1869 "Modify the terminal connection password\n"
1870 "Specifies a HIDDEN password will follow\n"
1871 "The password string\n")
1875 if (argc
== 3) // '8' was specified
1878 XFREE(MTYPE_HOST
, host
.password
);
1879 host
.password
= NULL
;
1880 if (host
.password_encrypt
)
1881 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1882 host
.password_encrypt
=
1883 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1887 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1889 "Please specify string starting with alphanumeric\n");
1890 return CMD_WARNING_CONFIG_FAILED
;
1894 XFREE(MTYPE_HOST
, host
.password
);
1895 host
.password
= NULL
;
1898 if (host
.password_encrypt
)
1899 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1900 host
.password_encrypt
=
1901 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1903 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1908 /* VTY interface password delete. */
1909 DEFUN (no_config_password
,
1913 "Modify the terminal connection password\n")
1915 bool warned
= false;
1917 if (host
.password
) {
1918 if (!vty_shell_serv(vty
)) {
1919 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1922 XFREE(MTYPE_HOST
, host
.password
);
1924 host
.password
= NULL
;
1926 if (host
.password_encrypt
) {
1927 if (!warned
&& !vty_shell_serv(vty
))
1928 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1929 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1931 host
.password_encrypt
= NULL
;
1936 /* VTY enable password set. */
1937 DEFUN (config_enable_password
,
1938 enable_password_cmd
,
1939 "enable password [(8-8)] WORD",
1940 "Modify enable password parameters\n"
1941 "Assign the privileged level password\n"
1942 "Specifies a HIDDEN password will follow\n"
1943 "The HIDDEN 'enable' password string\n")
1948 /* Crypt type is specified. */
1950 if (argv
[idx_8
]->arg
[0] == '8') {
1952 XFREE(MTYPE_HOST
, host
.enable
);
1955 if (host
.enable_encrypt
)
1956 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1957 host
.enable_encrypt
=
1958 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1962 vty_out(vty
, "Unknown encryption type.\n");
1963 return CMD_WARNING_CONFIG_FAILED
;
1967 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
1969 "Please specify string starting with alphanumeric\n");
1970 return CMD_WARNING_CONFIG_FAILED
;
1974 XFREE(MTYPE_HOST
, host
.enable
);
1977 /* Plain password input. */
1979 if (host
.enable_encrypt
)
1980 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1981 host
.enable_encrypt
=
1982 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1984 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1989 /* VTY enable password delete. */
1990 DEFUN (no_config_enable_password
,
1991 no_enable_password_cmd
,
1992 "no enable password",
1994 "Modify enable password parameters\n"
1995 "Assign the privileged level password\n")
1997 bool warned
= false;
2000 if (!vty_shell_serv(vty
)) {
2001 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2004 XFREE(MTYPE_HOST
, host
.enable
);
2008 if (host
.enable_encrypt
) {
2009 if (!warned
&& !vty_shell_serv(vty
))
2010 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2011 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2013 host
.enable_encrypt
= NULL
;
2018 DEFUN (service_password_encrypt
,
2019 service_password_encrypt_cmd
,
2020 "service password-encryption",
2021 "Set up miscellaneous service\n"
2022 "Enable encrypted passwords\n")
2029 if (host
.password
) {
2030 if (host
.password_encrypt
)
2031 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2032 host
.password_encrypt
=
2033 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2036 if (host
.enable_encrypt
)
2037 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2038 host
.enable_encrypt
=
2039 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2045 DEFUN (no_service_password_encrypt
,
2046 no_service_password_encrypt_cmd
,
2047 "no service password-encryption",
2049 "Set up miscellaneous service\n"
2050 "Enable encrypted passwords\n")
2057 if (host
.password_encrypt
)
2058 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2059 host
.password_encrypt
= NULL
;
2061 if (host
.enable_encrypt
)
2062 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2063 host
.enable_encrypt
= NULL
;
2068 DEFUN (config_terminal_length
,
2069 config_terminal_length_cmd
,
2070 "terminal length (0-512)",
2071 "Set terminal line parameters\n"
2072 "Set number of lines on a screen\n"
2073 "Number of lines on screen (0 for no pausing)\n")
2077 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2082 DEFUN (config_terminal_no_length
,
2083 config_terminal_no_length_cmd
,
2084 "terminal no length",
2085 "Set terminal line parameters\n"
2087 "Set number of lines on a screen\n")
2093 DEFUN (service_terminal_length
,
2094 service_terminal_length_cmd
,
2095 "service terminal-length (0-512)",
2096 "Set up miscellaneous service\n"
2097 "System wide terminal length configuration\n"
2098 "Number of lines of VTY (0 means no line control)\n")
2102 host
.lines
= atoi(argv
[idx_number
]->arg
);
2107 DEFUN (no_service_terminal_length
,
2108 no_service_terminal_length_cmd
,
2109 "no service terminal-length [(0-512)]",
2111 "Set up miscellaneous service\n"
2112 "System wide terminal length configuration\n"
2113 "Number of lines of VTY (0 means no line control)\n")
2119 DEFUN_HIDDEN (do_echo
,
2122 "Echo a message back to the vty\n"
2123 "The message to echo\n")
2127 vty_out(vty
, "%s\n",
2128 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2130 XFREE(MTYPE_TMP
, message
);
2134 DEFUN (config_logmsg
,
2136 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2137 "Send a message to enabled logging destinations\n"
2139 "The message to send\n")
2141 int idx_log_level
= 1;
2142 int idx_message
= 2;
2146 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2147 return CMD_ERR_NO_MATCH
;
2150 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2152 XFREE(MTYPE_TMP
, message
);
2157 DEFUN (show_logging
,
2161 "Show current logging configuration\n")
2163 struct zlog
*zl
= zlog_default
;
2165 vty_out(vty
, "Syslog logging: ");
2166 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
2167 vty_out(vty
, "disabled");
2169 vty_out(vty
, "level %s, facility %s, ident %s",
2170 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
2171 facility_name(zl
->facility
), zl
->ident
);
2174 vty_out(vty
, "Stdout logging: ");
2175 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
2176 vty_out(vty
, "disabled");
2178 vty_out(vty
, "level %s",
2179 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
2182 vty_out(vty
, "Monitor logging: ");
2183 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
2184 vty_out(vty
, "disabled");
2186 vty_out(vty
, "level %s",
2187 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
2190 vty_out(vty
, "File logging: ");
2191 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
2192 vty_out(vty
, "disabled");
2194 vty_out(vty
, "level %s, filename %s",
2195 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
2199 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
2200 vty_out(vty
, "Record priority: %s\n",
2201 (zl
->record_priority
? "enabled" : "disabled"));
2202 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2207 DEFUN (config_log_stdout
,
2208 config_log_stdout_cmd
,
2209 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2211 "Set stdout logging level\n"
2214 int idx_log_level
= 2;
2216 if (argc
== idx_log_level
) {
2217 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2222 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2223 return CMD_ERR_NO_MATCH
;
2224 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2228 DEFUN (no_config_log_stdout
,
2229 no_config_log_stdout_cmd
,
2230 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2233 "Cancel logging to stdout\n"
2236 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2240 DEFUN (config_log_monitor
,
2241 config_log_monitor_cmd
,
2242 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2244 "Set terminal line (monitor) logging level\n"
2247 int idx_log_level
= 2;
2249 if (argc
== idx_log_level
) {
2250 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2255 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2256 return CMD_ERR_NO_MATCH
;
2257 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2261 DEFUN (no_config_log_monitor
,
2262 no_config_log_monitor_cmd
,
2263 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2266 "Disable terminal line (monitor) logging\n"
2269 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2273 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2277 const char *fullpath
;
2279 /* Path detection. */
2280 if (!IS_DIRECTORY_SEP(*fname
)) {
2281 char cwd
[MAXPATHLEN
+ 1];
2282 cwd
[MAXPATHLEN
] = '\0';
2284 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2285 flog_err_sys(EC_LIB_SYSTEM_CALL
,
2286 "config_log_file: Unable to alloc mem!");
2287 return CMD_WARNING_CONFIG_FAILED
;
2290 p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2);
2291 sprintf(p
, "%s/%s", cwd
, fname
);
2296 ret
= zlog_set_file(fullpath
, loglevel
);
2298 XFREE(MTYPE_TMP
, p
);
2302 vty_out(vty
, "can't open logfile %s\n", fname
);
2303 return CMD_WARNING_CONFIG_FAILED
;
2306 XFREE(MTYPE_HOST
, host
.logfile
);
2308 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2310 #if defined(HAVE_CUMULUS)
2311 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2312 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2317 void command_setup_early_logging(const char *dest
, const char *level
)
2322 int nlevel
= level_match(level
);
2324 if (nlevel
!= ZLOG_DISABLED
)
2325 zlog_default
->default_lvl
= nlevel
;
2331 if (strcmp(dest
, "stdout") == 0) {
2332 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2336 if (strcmp(dest
, "syslog") == 0) {
2337 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2341 token
= strstr(dest
, ":");
2347 set_log_file(NULL
, token
, zlog_default
->default_lvl
);
2350 DEFUN (config_log_file
,
2351 config_log_file_cmd
,
2352 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2355 "Logging filename\n"
2358 int idx_filename
= 2;
2359 int idx_log_levels
= 3;
2362 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2364 return CMD_ERR_NO_MATCH
;
2365 return set_log_file(vty
, argv
[idx_filename
]->arg
, level
);
2367 return set_log_file(vty
, argv
[idx_filename
]->arg
,
2368 zlog_default
->default_lvl
);
2371 static void disable_log_file(void)
2375 XFREE(MTYPE_HOST
, host
.logfile
);
2378 DEFUN (no_config_log_file
,
2379 no_config_log_file_cmd
,
2380 "no log file [FILENAME [LEVEL]]",
2383 "Cancel logging to file\n"
2384 "Logging file name\n"
2391 DEFUN (config_log_syslog
,
2392 config_log_syslog_cmd
,
2393 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2395 "Set syslog logging level\n"
2398 int idx_log_levels
= 2;
2402 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2404 return CMD_ERR_NO_MATCH
;
2405 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2408 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2413 DEFUN (no_config_log_syslog
,
2414 no_config_log_syslog_cmd
,
2415 "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2418 "Cancel logging to syslog\n"
2422 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2426 DEFUN (config_log_facility
,
2427 config_log_facility_cmd
,
2428 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2430 "Facility parameter for syslog messages\n"
2434 int facility
= facility_match(argv
[idx_target
]->arg
);
2436 zlog_default
->facility
= facility
;
2440 DEFUN (no_config_log_facility
,
2441 no_config_log_facility_cmd
,
2442 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2445 "Reset syslog facility to default (daemon)\n"
2448 zlog_default
->facility
= LOG_DAEMON
;
2452 DEFUN (config_log_record_priority
,
2453 config_log_record_priority_cmd
,
2454 "log record-priority",
2456 "Log the priority of the message within the message\n")
2458 zlog_default
->record_priority
= 1;
2462 DEFUN (no_config_log_record_priority
,
2463 no_config_log_record_priority_cmd
,
2464 "no log record-priority",
2467 "Do not log the priority of the message within the message\n")
2469 zlog_default
->record_priority
= 0;
2473 DEFUN (config_log_timestamp_precision
,
2474 config_log_timestamp_precision_cmd
,
2475 "log timestamp precision (0-6)",
2477 "Timestamp configuration\n"
2478 "Set the timestamp precision\n"
2479 "Number of subsecond digits\n")
2482 zlog_default
->timestamp_precision
=
2483 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2487 DEFUN (no_config_log_timestamp_precision
,
2488 no_config_log_timestamp_precision_cmd
,
2489 "no log timestamp precision",
2492 "Timestamp configuration\n"
2493 "Reset the timestamp precision to the default value of 0\n")
2495 zlog_default
->timestamp_precision
= 0;
2499 DEFUN (debug_memstats
,
2501 "[no] debug memstats-at-exit",
2504 "Print memory type statistics at exit\n")
2506 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2510 int cmd_banner_motd_file(const char *file
)
2512 int success
= CMD_SUCCESS
;
2517 rpath
= realpath(file
, p
);
2519 return CMD_ERR_NO_FILE
;
2520 in
= strstr(rpath
, SYSCONFDIR
);
2522 XFREE(MTYPE_HOST
, host
.motdfile
);
2523 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2525 success
= CMD_WARNING_CONFIG_FAILED
;
2530 void cmd_banner_motd_line(const char *line
)
2532 XFREE(MTYPE_HOST
, host
.motd
);
2533 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2536 DEFUN (banner_motd_file
,
2537 banner_motd_file_cmd
,
2538 "banner motd file FILE",
2541 "Banner from a file\n"
2545 const char *filename
= argv
[idx_file
]->arg
;
2546 int cmd
= cmd_banner_motd_file(filename
);
2548 if (cmd
== CMD_ERR_NO_FILE
)
2549 vty_out(vty
, "%s does not exist", filename
);
2550 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2551 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2556 DEFUN (banner_motd_line
,
2557 banner_motd_line_cmd
,
2558 "banner motd line LINE...",
2561 "Banner from an input\n"
2567 argv_find(argv
, argc
, "LINE", &idx
);
2568 motd
= argv_concat(argv
, argc
, idx
);
2570 cmd_banner_motd_line(motd
);
2571 XFREE(MTYPE_TMP
, motd
);
2576 DEFUN (banner_motd_default
,
2577 banner_motd_default_cmd
,
2578 "banner motd default",
2579 "Set banner string\n"
2580 "Strings for motd\n"
2583 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2587 DEFUN (no_banner_motd
,
2591 "Set banner string\n"
2592 "Strings for motd\n")
2596 XFREE(MTYPE_HOST
, host
.motdfile
);
2597 host
.motdfile
= NULL
;
2604 "Find CLI command matching a regular expression\n"
2605 "Search pattern (POSIX regex)\n")
2607 char *pattern
= argv
[1]->arg
;
2608 const struct cmd_node
*node
;
2609 const struct cmd_element
*cli
;
2614 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2619 vty_out(vty
, "%% Invalid {...} expression\n");
2622 vty_out(vty
, "%% Bad repetition operator\n");
2625 vty_out(vty
, "%% Regex syntax error\n");
2628 vty_out(vty
, "%% Invalid collating element\n");
2631 vty_out(vty
, "%% Invalid character class name\n");
2635 "%% Regex ended with escape character (\\)\n");
2639 "%% Invalid number in \\digit construction\n");
2642 vty_out(vty
, "%% Unbalanced square brackets\n");
2645 vty_out(vty
, "%% Unbalanced parentheses\n");
2648 vty_out(vty
, "%% Unbalanced braces\n");
2652 "%% Invalid endpoint in range expression\n");
2655 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2663 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2664 node
= vector_slot(cmdvec
, i
);
2667 clis
= node
->cmd_vector
;
2668 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2669 cli
= vector_slot(clis
, j
);
2671 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0)
2672 vty_out(vty
, " (%s) %s\n",
2673 node
->name
, cli
->string
);
2682 /* Set config filename. Called from vty.c */
2683 void host_config_set(const char *filename
)
2685 XFREE(MTYPE_HOST
, host
.config
);
2686 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2689 const char *host_config_get(void)
2694 void install_default(enum node_type node
)
2696 install_element(node
, &config_exit_cmd
);
2697 install_element(node
, &config_quit_cmd
);
2698 install_element(node
, &config_end_cmd
);
2699 install_element(node
, &config_help_cmd
);
2700 install_element(node
, &config_list_cmd
);
2701 install_element(node
, &show_cli_graph_cmd
);
2702 install_element(node
, &find_cmd
);
2704 install_element(node
, &config_write_cmd
);
2705 install_element(node
, &show_running_config_cmd
);
2707 install_element(node
, &autocomplete_cmd
);
2709 nb_cli_install_default(node
);
2712 /* Initialize command interface. Install basic nodes and commands.
2714 * terminal = 0 -- vtysh / no logging, no config control
2715 * terminal = 1 -- normal daemon
2716 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2717 void cmd_init(int terminal
)
2719 struct utsname names
;
2724 /* register command preprocessors */
2725 hook_register(cmd_execute
, handle_pipe_action
);
2726 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2728 varhandlers
= list_new();
2730 /* Allocate initial top vector of commands. */
2731 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2733 /* Default host value settings. */
2734 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2735 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2736 if ((strcmp(names
.domainname
, "(none)") == 0))
2737 host
.domainname
= NULL
;
2739 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2741 host
.domainname
= NULL
;
2743 host
.password
= NULL
;
2745 host
.logfile
= NULL
;
2747 host
.noconfig
= (terminal
< 0);
2749 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2750 host
.motdfile
= NULL
;
2752 /* Install top nodes. */
2753 install_node(&view_node
);
2754 install_node(&enable_node
);
2755 install_node(&auth_node
);
2756 install_node(&auth_enable_node
);
2757 install_node(&config_node
);
2759 /* Each node's basic commands. */
2760 install_element(VIEW_NODE
, &show_version_cmd
);
2761 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2764 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2766 install_element(VIEW_NODE
, &config_list_cmd
);
2767 install_element(VIEW_NODE
, &config_exit_cmd
);
2768 install_element(VIEW_NODE
, &config_quit_cmd
);
2769 install_element(VIEW_NODE
, &config_help_cmd
);
2770 install_element(VIEW_NODE
, &config_enable_cmd
);
2771 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2772 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2773 install_element(VIEW_NODE
, &show_logging_cmd
);
2774 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2775 install_element(VIEW_NODE
, &echo_cmd
);
2776 install_element(VIEW_NODE
, &autocomplete_cmd
);
2777 install_element(VIEW_NODE
, &find_cmd
);
2779 install_element(ENABLE_NODE
, &config_end_cmd
);
2780 install_element(ENABLE_NODE
, &config_disable_cmd
);
2781 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2782 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2783 install_element(ENABLE_NODE
, &config_write_cmd
);
2784 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2785 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2787 install_default(CONFIG_NODE
);
2790 workqueue_cmd_init();
2794 install_element(CONFIG_NODE
, &hostname_cmd
);
2795 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2796 install_element(CONFIG_NODE
, &domainname_cmd
);
2797 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2800 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2802 install_element(CONFIG_NODE
, &password_cmd
);
2803 install_element(CONFIG_NODE
, &no_password_cmd
);
2804 install_element(CONFIG_NODE
, &enable_password_cmd
);
2805 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2807 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2808 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2809 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2810 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2811 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2812 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2813 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2814 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2815 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2816 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2817 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2818 install_element(CONFIG_NODE
,
2819 &no_config_log_record_priority_cmd
);
2820 install_element(CONFIG_NODE
,
2821 &config_log_timestamp_precision_cmd
);
2822 install_element(CONFIG_NODE
,
2823 &no_config_log_timestamp_precision_cmd
);
2824 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2825 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2826 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2827 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2828 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2829 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2830 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2831 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2833 vrf_install_commands();
2837 grammar_sandbox_init();
2841 void cmd_terminate(void)
2843 struct cmd_node
*cmd_node
;
2845 hook_unregister(cmd_execute
, handle_pipe_action
);
2846 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2849 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2850 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2851 // deleting the graph delets the cmd_element as
2853 graph_delete_graph(cmd_node
->cmdgraph
);
2854 vector_free(cmd_node
->cmd_vector
);
2855 hash_clean(cmd_node
->cmd_hash
, NULL
);
2856 hash_free(cmd_node
->cmd_hash
);
2857 cmd_node
->cmd_hash
= NULL
;
2860 vector_free(cmdvec
);
2864 XFREE(MTYPE_HOST
, host
.name
);
2865 XFREE(MTYPE_HOST
, host
.domainname
);
2866 XFREE(MTYPE_HOST
, host
.password
);
2867 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2868 XFREE(MTYPE_HOST
, host
.enable
);
2869 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2870 XFREE(MTYPE_HOST
, host
.logfile
);
2871 XFREE(MTYPE_HOST
, host
.motdfile
);
2872 XFREE(MTYPE_HOST
, host
.config
);
2873 XFREE(MTYPE_HOST
, host
.motd
);
2875 list_delete(&varhandlers
);