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
33 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
48 DEFINE_MTYPE(LIB
, HOST
, "Host config")
49 DEFINE_MTYPE(LIB
, STRVEC
, "String vector")
50 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item")
57 /* clang-format off */
58 const struct message tokennames
[] = {
63 item(IPV4_PREFIX_TKN
),
65 item(IPV6_PREFIX_TKN
),
75 const char *node_names
[] = {
78 "auth enable", // AUTH_ENABLE_NODE,
79 "enable", // ENABLE_NODE,
80 "config", // CONFIG_NODE,
81 "debug", // DEBUG_NODE,
82 "vrf debug", // VRF_DEBUG_NODE,
83 "vnc debug", // DEBUG_VNC_NODE,
85 "keychain", // KEYCHAIN_NODE,
86 "keychain key", // KEYCHAIN_KEY_NODE,
87 "logical-router", // LOGICALROUTER_NODE,
89 "interface", // INTERFACE_NODE,
90 "nexthop-group", // NH_GROUP_NODE,
91 "zebra", // ZEBRA_NODE,
92 "table", // TABLE_NODE,
94 "ripng", // RIPNG_NODE,
95 "babel", // BABEL_NODE,
96 "eigrp", // EIGRP_NODE,
98 "bgp vpnv4", // BGP_VPNV4_NODE,
99 "bgp vpnv6", // BGP_VPNV6_NODE,
100 "bgp ipv4 unicast", // BGP_IPV4_NODE,
101 "bgp ipv4 multicast", // BGP_IPV4M_NODE,
102 "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE,
103 "bgp ipv6", // BGP_IPV6_NODE,
104 "bgp ipv6 multicast", // BGP_IPV6M_NODE,
105 "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE,
106 "bgp vrf policy", // BGP_VRF_POLICY_NODE,
107 "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE,
108 "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE,
109 "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE,
110 "rfp defaults", // RFP_DEFAULTS_NODE,
111 "bgp evpn", // BGP_EVPN_NODE,
112 "ospf", // OSPF_NODE,
113 "ospf6", // OSPF6_NODE,
115 "ldp ipv4", // LDP_IPV4_NODE,
116 "ldp ipv6", // LDP_IPV6_NODE,
117 "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE,
118 "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE,
119 "ldp l2vpn", // LDP_L2VPN_NODE,
120 "ldp", // LDP_PSEUDOWIRE_NODE,
121 "isis", // ISIS_NODE,
122 "static ip", // IP_NODE,
123 "ipv4 access list", // ACCESS_NODE,
124 "ipv4 prefix list", // PREFIX_NODE,
125 "ipv6 access list", // ACCESS_IPV6_NODE,
126 "MAC access list", // ACCESS_MAC_NODE,
127 "ipv6 prefix list", // PREFIX_IPV6_NODE,
128 "as list", // AS_LIST_NODE,
129 "community list", // COMMUNITY_LIST_NODE,
130 "routemap", // RMAP_NODE,
131 "pbr-map", // PBRMAP_NODE,
132 "smux", // SMUX_NODE,
133 "dump", // DUMP_NODE,
134 "forwarding", // FORWARDING_NODE,
135 "protocol", // PROTOCOL_NODE,
136 "mpls", // MPLS_NODE,
139 "link-params", // LINK_PARAMS_NODE,
140 "bgp evpn vni", // BGP_EVPN_VNI_NODE,
142 "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE
144 "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
147 /* clang-format on */
149 /* Command vector which includes some level of command lists. Normally
150 each daemon maintains each own cmdvec. */
151 vector cmdvec
= NULL
;
153 /* Host information structure. */
157 * Returns host.name if any, otherwise
158 * it returns the system hostname.
160 const char *cmd_hostname_get(void)
166 * Returns unix domainname
168 const char *cmd_domainname_get(void)
170 return host
.domainname
;
173 /* Standard command node structures. */
174 static struct cmd_node auth_node
= {
175 AUTH_NODE
, "Password: ",
178 static struct cmd_node view_node
= {
182 static struct cmd_node auth_enable_node
= {
183 AUTH_ENABLE_NODE
, "Password: ",
186 static struct cmd_node enable_node
= {
190 static struct cmd_node config_node
= {CONFIG_NODE
, "%s(config)# ", 1};
192 /* Default motd string. */
193 static const char *default_motd
= FRR_DEFAULT_MOTD
;
195 static const struct facility_map
{
199 } syslog_facilities
[] = {
200 {LOG_KERN
, "kern", 1},
201 {LOG_USER
, "user", 2},
202 {LOG_MAIL
, "mail", 1},
203 {LOG_DAEMON
, "daemon", 1},
204 {LOG_AUTH
, "auth", 1},
205 {LOG_SYSLOG
, "syslog", 1},
207 {LOG_NEWS
, "news", 1},
208 {LOG_UUCP
, "uucp", 2},
209 {LOG_CRON
, "cron", 1},
213 {LOG_LOCAL0
, "local0", 6},
214 {LOG_LOCAL1
, "local1", 6},
215 {LOG_LOCAL2
, "local2", 6},
216 {LOG_LOCAL3
, "local3", 6},
217 {LOG_LOCAL4
, "local4", 6},
218 {LOG_LOCAL5
, "local5", 6},
219 {LOG_LOCAL6
, "local6", 6},
220 {LOG_LOCAL7
, "local7", 6},
224 static const char *facility_name(int facility
)
226 const struct facility_map
*fm
;
228 for (fm
= syslog_facilities
; fm
->name
; fm
++)
229 if (fm
->facility
== facility
)
234 static int facility_match(const char *str
)
236 const struct facility_map
*fm
;
238 for (fm
= syslog_facilities
; fm
->name
; fm
++)
239 if (!strncmp(str
, fm
->name
, fm
->match
))
244 static int level_match(const char *s
)
248 for (level
= 0; zlog_priority
[level
] != NULL
; level
++)
249 if (!strncmp(s
, zlog_priority
[level
], 2))
251 return ZLOG_DISABLED
;
254 /* This is called from main when a daemon is invoked with -v or --version. */
255 void print_version(const char *progname
)
257 printf("%s version %s\n", progname
, FRR_VERSION
);
258 printf("%s\n", FRR_COPYRIGHT
);
259 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
263 /* Utility function to concatenate argv argument into a single string
264 with inserting ' ' character between each argument. */
265 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
273 for (i
= shift
; i
< argc
; i
++)
274 len
+= strlen(argv
[i
]->arg
) + 1;
277 p
= str
= XMALLOC(MTYPE_TMP
, len
);
278 for (i
= shift
; i
< argc
; i
++) {
280 memcpy(p
, argv
[i
]->arg
, (arglen
= strlen(argv
[i
]->arg
)));
289 * Convenience function for accessing argv data.
293 * @param text definition snippet of the desired token
294 * @param index the starting index, and where to store the
295 * index of the found token if it exists
296 * @return 1 if found, 0 otherwise
298 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
301 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
302 if ((found
= strmatch(text
, argv
[i
]->text
)))
307 static unsigned int cmd_hash_key(void *p
)
309 int size
= sizeof(p
);
311 return jhash(p
, size
, 0);
314 static int cmd_hash_cmp(const void *a
, const void *b
)
319 /* Install top node of command vector. */
320 void install_node(struct cmd_node
*node
, int (*func
)(struct vty
*))
322 vector_set_index(cmdvec
, node
->node
, node
);
324 node
->cmdgraph
= graph_new();
325 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
327 struct cmd_token
*token
=
328 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
329 graph_new_node(node
->cmdgraph
, token
,
330 (void (*)(void *)) & cmd_token_del
);
331 node
->cmd_hash
= hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
,
336 * Tokenizes a string, storing tokens in a vector.
337 * Whitespace is ignored.
339 * Delimiter string = " \n\r\t".
341 * @param string to tokenize
342 * @return tokenized string
344 vector
cmd_make_strvec(const char *string
)
349 char *copy
, *copystart
;
350 copystart
= copy
= XSTRDUP(MTYPE_TMP
, string
);
352 // skip leading whitespace
353 while (isspace((int)*copy
) && *copy
!= '\0')
356 // if the entire string was whitespace or a comment, return
357 if (*copy
== '\0' || *copy
== '!' || *copy
== '#') {
358 XFREE(MTYPE_TMP
, copystart
);
362 vector strvec
= vector_init(VECTOR_MIN_SIZE
);
363 const char *delim
= " \n\r\t", *tok
= NULL
;
365 tok
= strsep(©
, delim
);
367 vector_set(strvec
, XSTRDUP(MTYPE_STRVEC
, tok
));
370 XFREE(MTYPE_TMP
, copystart
);
374 /* Free allocated string vector. */
375 void cmd_free_strvec(vector v
)
383 for (i
= 0; i
< vector_active(v
); i
++)
384 if ((cp
= vector_slot(v
, i
)) != NULL
)
385 XFREE(MTYPE_STRVEC
, cp
);
390 /* Return prompt character of specified node. */
391 const char *cmd_prompt(enum node_type node
)
393 struct cmd_node
*cnode
;
395 cnode
= vector_slot(cmdvec
, node
);
396 return cnode
->prompt
;
399 /* Install a command into a node. */
400 void install_element(enum node_type ntype
, struct cmd_element
*cmd
)
402 struct cmd_node
*cnode
;
404 /* cmd_init hasn't been called */
406 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
411 cnode
= vector_lookup(cmdvec
, ntype
);
416 "\tnode %d (%s) does not exist.\n"
417 "\tplease call install_node() before install_element()\n",
418 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
422 if (hash_lookup(cnode
->cmd_hash
, cmd
) != NULL
) {
425 "\tnode %d (%s) already has this command installed.\n"
426 "\tduplicate install_element call?\n",
427 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
431 assert(hash_get(cnode
->cmd_hash
, cmd
, hash_alloc_intern
));
433 struct graph
*graph
= graph_new();
434 struct cmd_token
*token
=
435 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
436 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
438 cmd_graph_parse(graph
, cmd
);
439 cmd_graph_names(graph
);
440 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
441 graph_delete_graph(graph
);
443 vector_set(cnode
->cmd_vector
, cmd
);
445 if (ntype
== VIEW_NODE
)
446 install_element(ENABLE_NODE
, cmd
);
449 void uninstall_element(enum node_type ntype
, struct cmd_element
*cmd
)
451 struct cmd_node
*cnode
;
453 /* cmd_init hasn't been called */
455 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
460 cnode
= vector_lookup(cmdvec
, ntype
);
465 "\tnode %d (%s) does not exist.\n"
466 "\tplease call install_node() before uninstall_element()\n",
467 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
471 if (hash_release(cnode
->cmd_hash
, cmd
) == NULL
) {
474 "\tnode %d (%s) does not have this command installed.\n"
475 "\tduplicate uninstall_element call?\n",
476 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
480 vector_unset_value(cnode
->cmd_vector
, cmd
);
482 struct graph
*graph
= graph_new();
483 struct cmd_token
*token
=
484 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
485 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
487 cmd_graph_parse(graph
, cmd
);
488 cmd_graph_names(graph
);
489 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
490 graph_delete_graph(graph
);
492 if (ntype
== VIEW_NODE
)
493 uninstall_element(ENABLE_NODE
, cmd
);
497 static const unsigned char itoa64
[] =
498 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
500 static void to64(char *s
, long v
, int n
)
503 *s
++ = itoa64
[v
& 0x3f];
508 static char *zencrypt(const char *passwd
)
512 char *crypt(const char *, const char *);
514 gettimeofday(&tv
, 0);
516 to64(&salt
[0], random(), 3);
517 to64(&salt
[3], tv
.tv_usec
, 3);
520 return crypt(passwd
, salt
);
523 /* This function write configuration of this host. */
524 static int config_write_host(struct vty
*vty
)
526 if (cmd_hostname_get())
527 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
529 if (cmd_domainname_get())
530 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
533 if (host
.password_encrypt
)
534 vty_out(vty
, "password 8 %s\n", host
.password_encrypt
);
535 if (host
.enable_encrypt
)
536 vty_out(vty
, "enable password 8 %s\n",
537 host
.enable_encrypt
);
540 vty_out(vty
, "password %s\n", host
.password
);
542 vty_out(vty
, "enable password %s\n", host
.enable
);
545 if (zlog_default
->default_lvl
!= LOG_DEBUG
) {
546 vty_out(vty
, "! N.B. The 'log trap' command is deprecated.\n");
547 vty_out(vty
, "log trap %s\n",
548 zlog_priority
[zlog_default
->default_lvl
]);
552 && (zlog_default
->maxlvl
[ZLOG_DEST_FILE
] != ZLOG_DISABLED
)) {
553 vty_out(vty
, "log file %s", host
.logfile
);
554 if (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
555 != zlog_default
->default_lvl
)
558 [zlog_default
->maxlvl
[ZLOG_DEST_FILE
]]);
562 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
] != ZLOG_DISABLED
) {
563 vty_out(vty
, "log stdout");
564 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
]
565 != zlog_default
->default_lvl
)
567 zlog_priority
[zlog_default
->maxlvl
568 [ZLOG_DEST_STDOUT
]]);
572 if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
573 vty_out(vty
, "no log monitor\n");
574 else if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]
575 != zlog_default
->default_lvl
)
576 vty_out(vty
, "log monitor %s\n",
577 zlog_priority
[zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]]);
579 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
) {
580 vty_out(vty
, "log syslog");
581 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
]
582 != zlog_default
->default_lvl
)
584 zlog_priority
[zlog_default
->maxlvl
585 [ZLOG_DEST_SYSLOG
]]);
589 if (zlog_default
->facility
!= LOG_DAEMON
)
590 vty_out(vty
, "log facility %s\n",
591 facility_name(zlog_default
->facility
));
593 if (zlog_default
->record_priority
== 1)
594 vty_out(vty
, "log record-priority\n");
596 if (zlog_default
->timestamp_precision
> 0)
597 vty_out(vty
, "log timestamp precision %d\n",
598 zlog_default
->timestamp_precision
);
601 vty_out(vty
, "service advanced-vty\n");
604 vty_out(vty
, "service password-encryption\n");
607 vty_out(vty
, "service terminal-length %d\n", host
.lines
);
610 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
612 vty_out(vty
, "no banner motd\n");
614 if (debug_memstats_at_exit
)
615 vty_out(vty
, "!\ndebug memstats-at-exit\n");
620 /* Utility function for getting command graph. */
621 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
623 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
624 return cnode
->cmdgraph
;
627 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
629 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
630 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
636 * Compare function for cmd_token.
637 * Used with qsort to sort command completions.
639 static int compare_completions(const void *fst
, const void *snd
)
641 struct cmd_token
*first
= *(struct cmd_token
**)fst
,
642 *secnd
= *(struct cmd_token
**)snd
;
643 return strcmp(first
->text
, secnd
->text
);
647 * Takes a list of completions returned by command_complete,
648 * dedeuplicates them based on both text and description,
649 * sorts them, and returns them as a vector.
651 * @param completions linked list of cmd_token
652 * @return deduplicated and sorted vector with
654 vector
completions_to_vec(struct list
*completions
)
656 vector comps
= vector_init(VECTOR_MIN_SIZE
);
659 struct cmd_token
*token
, *cr
= NULL
;
660 unsigned int i
, exists
;
661 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
662 if (token
->type
== END_TKN
&& (cr
= token
))
665 // linear search for token in completions vector
667 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
668 struct cmd_token
*curr
= vector_slot(comps
, i
);
670 exists
= !strcmp(curr
->text
, token
->text
)
671 && !strcmp(curr
->desc
, token
->desc
);
673 exists
= !strcmp(curr
->text
, token
->text
);
674 #endif /* VTYSH_DEBUG */
678 vector_set(comps
, token
);
682 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
683 &compare_completions
);
685 // make <cr> the first element, if it is present
687 vector_set_index(comps
, vector_active(comps
), NULL
);
688 memmove(comps
->index
+ 1, comps
->index
,
689 (comps
->alloced
- 1) * sizeof(void *));
690 vector_set_index(comps
, 0, cr
);
696 * Generates a vector of cmd_token representing possible completions
697 * on the current input.
699 * @param vline the vectorized input line
700 * @param vty the vty with the node to match on
701 * @param status pointer to matcher status code
702 * @return vector of struct cmd_token * with possible completions
704 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
707 struct list
*completions
;
708 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
710 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
712 if (MATCHER_ERROR(rv
)) {
713 *status
= CMD_ERR_NO_MATCH
;
717 vector comps
= completions_to_vec(completions
);
718 list_delete_and_null(&completions
);
720 // set status code appropriately
721 switch (vector_active(comps
)) {
723 *status
= CMD_ERR_NO_MATCH
;
726 *status
= CMD_COMPLETE_FULL_MATCH
;
729 *status
= CMD_COMPLETE_LIST_MATCH
;
735 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
739 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
740 enum node_type onode
;
741 vector shifted_vline
;
745 vty
->node
= ENABLE_NODE
;
746 /* We can try it on enable node, cos' the vty is authenticated
749 shifted_vline
= vector_init(vector_count(vline
));
751 for (index
= 1; index
< vector_active(vline
); index
++) {
752 vector_set_index(shifted_vline
, index
- 1,
753 vector_lookup(vline
, index
));
756 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
758 vector_free(shifted_vline
);
763 return cmd_complete_command_real(vline
, vty
, status
);
766 static struct list
*varhandlers
= NULL
;
768 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
772 const struct cmd_variable_handler
*cvh
;
776 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
778 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
779 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
781 if (cvh
->varname
&& (!token
->varname
782 || strcmp(cvh
->varname
, token
->varname
)))
784 cvh
->completions(tmpcomps
, token
);
792 for (i
= vector_active(tmpcomps
); i
; i
--) {
793 char *item
= vector_slot(tmpcomps
, i
- 1);
794 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
795 vector_set(comps
, item
);
797 XFREE(MTYPE_COMPLETION
, item
);
799 vector_free(tmpcomps
);
802 #define AUTOCOMP_INDENT 5
804 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
807 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
808 int lc
= AUTOCOMP_INDENT
;
809 size_t cs
= AUTOCOMP_INDENT
;
811 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
812 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
813 char *item
= vector_slot(comps
, j
);
814 itemlen
= strlen(item
);
816 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
817 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
819 if (lc
+ itemlen
+ 1 >= cols
) {
820 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
821 AUTOCOMP_INDENT
, "");
822 lc
= AUTOCOMP_INDENT
;
825 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
828 XFREE(MTYPE_COMPLETION
, item
);
829 vector_set_index(comps
, j
, NULL
);
834 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
839 for (; cvh
->completions
; cvh
++)
840 listnode_add(varhandlers
, (void *)cvh
);
843 DEFUN_HIDDEN (autocomplete
,
845 "autocomplete TYPE TEXT VARNAME",
846 "Autocompletion handler (internal, for vtysh)\n"
849 "cmd_token->varname\n")
851 struct cmd_token tok
;
852 vector comps
= vector_init(32);
855 memset(&tok
, 0, sizeof(tok
));
856 tok
.type
= atoi(argv
[1]->arg
);
857 tok
.text
= argv
[2]->arg
;
858 tok
.varname
= argv
[3]->arg
;
859 if (!strcmp(tok
.varname
, "-"))
862 cmd_variable_complete(&tok
, NULL
, comps
);
864 for (i
= 0; i
< vector_active(comps
); i
++) {
865 char *text
= vector_slot(comps
, i
);
866 vty_out(vty
, "%s\n", text
);
867 XFREE(MTYPE_COMPLETION
, text
);
875 * Generate possible tab-completions for the given input. This function only
876 * returns results that would result in a valid command if used as Readline
877 * completions (as is the case in vtysh). For instance, if the passed vline ends
878 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
880 * @param vline vectorized input line
882 * @param status location to store matcher status code in
883 * @return set of valid strings for use with Readline as tab-completions.
886 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
889 int original_node
= vty
->node
;
890 vector input_line
= vector_init(vector_count(vline
));
892 // if the first token is 'do' we'll want to execute the command in the
894 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
895 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
897 // construct the input line we'll be matching on
898 unsigned int offset
= (do_shortcut
) ? 1 : 0;
899 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
900 vector_set_index(input_line
, index
,
901 vector_lookup(vline
, index
+ offset
));
903 // get token completions -- this is a copying operation
904 vector comps
= NULL
, initial_comps
;
905 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
907 if (!MATCHER_ERROR(*status
)) {
908 assert(initial_comps
);
909 // filter out everything that is not suitable for a
911 comps
= vector_init(VECTOR_MIN_SIZE
);
912 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
914 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
915 if (token
->type
== WORD_TKN
)
916 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
918 else if (IS_VARYING_TOKEN(token
->type
)) {
919 const char *ref
= vector_lookup(
920 vline
, vector_active(vline
) - 1);
921 cmd_variable_complete(token
, ref
, comps
);
924 vector_free(initial_comps
);
926 // since we filtered results, we need to re-set status code
927 switch (vector_active(comps
)) {
929 *status
= CMD_ERR_NO_MATCH
;
932 *status
= CMD_COMPLETE_FULL_MATCH
;
935 *status
= CMD_COMPLETE_LIST_MATCH
;
938 // copy completions text into an array of char*
939 ret
= XMALLOC(MTYPE_TMP
,
940 (vector_active(comps
) + 1) * sizeof(char *));
942 for (i
= 0; i
< vector_active(comps
); i
++) {
943 ret
[i
] = vector_slot(comps
, i
);
945 // set the last element to NULL, because this array is used in
946 // a Readline completion_generator function which expects NULL
947 // as a sentinel value
951 } else if (initial_comps
)
952 vector_free(initial_comps
);
954 // comps should always be null here
957 // free the adjusted input line
958 vector_free(input_line
);
960 // reset vty->node to its original value
961 vty
->node
= original_node
;
966 /* return parent node */
967 /* MUST eventually converge on CONFIG_NODE */
968 enum node_type
node_parent(enum node_type node
)
972 assert(node
> CONFIG_NODE
);
977 case BGP_FLOWSPECV4_NODE
:
978 case BGP_FLOWSPECV6_NODE
:
979 case BGP_VRF_POLICY_NODE
:
980 case BGP_VNC_DEFAULTS_NODE
:
981 case BGP_VNC_NVE_GROUP_NODE
:
982 case BGP_VNC_L2_GROUP_NODE
:
992 case BGP_EVPN_VNI_NODE
:
995 case KEYCHAIN_KEY_NODE
:
998 case LINK_PARAMS_NODE
:
999 ret
= INTERFACE_NODE
;
1005 case LDP_IPV4_IFACE_NODE
:
1006 ret
= LDP_IPV4_NODE
;
1008 case LDP_IPV6_IFACE_NODE
:
1009 ret
= LDP_IPV6_NODE
;
1011 case LDP_PSEUDOWIRE_NODE
:
1012 ret
= LDP_L2VPN_NODE
;
1022 /* Execute command by argument vline vector. */
1023 static int cmd_execute_command_real(vector vline
, enum filter_type filter
,
1025 const struct cmd_element
**cmd
)
1027 struct list
*argv_list
;
1028 enum matcher_rv status
;
1029 const struct cmd_element
*matched_element
= NULL
;
1031 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
1032 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
1035 *cmd
= matched_element
;
1037 // if matcher error, return corresponding CMD_ERR
1038 if (MATCHER_ERROR(status
)) {
1040 list_delete_and_null(&argv_list
);
1042 case MATCHER_INCOMPLETE
:
1043 return CMD_ERR_INCOMPLETE
;
1044 case MATCHER_AMBIGUOUS
:
1045 return CMD_ERR_AMBIGUOUS
;
1047 return CMD_ERR_NO_MATCH
;
1051 // build argv array from argv list
1052 struct cmd_token
**argv
= XMALLOC(
1053 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
1054 struct listnode
*ln
;
1055 struct cmd_token
*token
;
1057 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
1060 int argc
= argv_list
->count
;
1063 if (matched_element
->daemon
)
1064 ret
= CMD_SUCCESS_DAEMON
;
1066 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
1068 // delete list and cmd_token's in it
1069 list_delete_and_null(&argv_list
);
1070 XFREE(MTYPE_TMP
, argv
);
1076 * Execute a given command, handling things like "do ..." and checking
1077 * whether the given command might apply at a parent node if doesn't
1078 * apply for the current node.
1080 * @param vline Command line input, vector of char* where each element is
1082 * @param vty The vty context in which the command should be executed.
1083 * @param cmd Pointer where the struct cmd_element of the matched command
1084 * will be stored, if any. May be set to NULL if this info is
1086 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1087 * @return The status of the command that has been executed or an error code
1088 * as to why no command could be executed.
1090 int cmd_execute_command(vector vline
, struct vty
*vty
,
1091 const struct cmd_element
**cmd
, int vtysh
)
1093 int ret
, saved_ret
= 0;
1094 enum node_type onode
, try_node
;
1096 onode
= try_node
= vty
->node
;
1098 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1099 vector shifted_vline
;
1102 vty
->node
= ENABLE_NODE
;
1103 /* We can try it on enable node, cos' the vty is authenticated
1106 shifted_vline
= vector_init(vector_count(vline
));
1108 for (index
= 1; index
< vector_active(vline
); index
++)
1109 vector_set_index(shifted_vline
, index
- 1,
1110 vector_lookup(vline
, index
));
1112 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1115 vector_free(shifted_vline
);
1121 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
1126 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1127 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1128 /* This assumes all nodes above CONFIG_NODE are childs of
1130 while (vty
->node
> CONFIG_NODE
) {
1131 try_node
= node_parent(try_node
);
1132 vty
->node
= try_node
;
1133 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1135 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1136 || ret
== CMD_NOT_MY_INSTANCE
1137 || ret
== CMD_WARNING_CONFIG_FAILED
)
1140 /* no command succeeded, reset the vty to the original node */
1144 /* return command status for original node */
1149 * Execute a given command, matching it strictly against the current node.
1150 * This mode is used when reading config files.
1152 * @param vline Command line input, vector of char* where each element is
1154 * @param vty The vty context in which the command should be executed.
1155 * @param cmd Pointer where the struct cmd_element* of the matched command
1156 * will be stored, if any. May be set to NULL if this info is
1158 * @return The status of the command that has been executed or an error code
1159 * as to why no command could be executed.
1161 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1162 const struct cmd_element
**cmd
)
1164 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1168 * Parse one line of config, walking up the parse tree attempting to find a
1171 * @param vty The vty context in which the command should be executed.
1172 * @param cmd Pointer where the struct cmd_element* of the match command
1173 * will be stored, if any. May be set to NULL if this info is
1175 * @param use_daemon Boolean to control whether or not we match on
1176 * CMD_SUCCESS_DAEMON
1178 * @return The status of the command that has been executed or an error code
1179 * as to why no command could be executed.
1181 int command_config_read_one_line(struct vty
*vty
,
1182 const struct cmd_element
**cmd
, int use_daemon
)
1188 vline
= cmd_make_strvec(vty
->buf
);
1190 /* In case of comment line */
1194 /* Execute configuration command : this is strict match */
1195 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1197 // Climb the tree and try the command again at each node
1198 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1199 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1200 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1201 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1202 && vty
->node
!= CONFIG_NODE
) {
1204 saved_node
= vty
->node
;
1206 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1207 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1208 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1209 && vty
->node
> CONFIG_NODE
) {
1210 vty
->node
= node_parent(vty
->node
);
1211 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1214 // If climbing the tree did not work then ignore the command and
1215 // stay at the same node
1216 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1217 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1218 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1219 vty
->node
= saved_node
;
1223 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
)
1224 memcpy(vty
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1226 cmd_free_strvec(vline
);
1231 /* Configuration make from file. */
1232 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1234 int ret
, error_ret
= 0;
1237 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1241 ret
= command_config_read_one_line(vty
, NULL
, 0);
1243 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1244 && ret
!= CMD_ERR_NOTHING_TODO
)
1255 /* Configuration from terminal */
1256 DEFUN (config_terminal
,
1257 config_terminal_cmd
,
1258 "configure terminal",
1259 "Configuration from vty interface\n"
1260 "Configuration terminal\n")
1262 if (vty_config_lock(vty
))
1263 vty
->node
= CONFIG_NODE
;
1265 vty_out(vty
, "VTY configuration is locked by other VTY\n");
1266 return CMD_WARNING_CONFIG_FAILED
;
1271 /* Enable command */
1275 "Turn on privileged mode command\n")
1277 /* If enable password is NULL, change to ENABLE_NODE */
1278 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1279 || vty
->type
== VTY_SHELL_SERV
)
1280 vty
->node
= ENABLE_NODE
;
1282 vty
->node
= AUTH_ENABLE_NODE
;
1287 /* Disable command */
1291 "Turn off privileged mode command\n")
1293 if (vty
->node
== ENABLE_NODE
)
1294 vty
->node
= VIEW_NODE
;
1298 /* Down vty node level. */
1302 "Exit current mode and down to previous mode\n")
1308 void cmd_exit(struct vty
*vty
)
1310 switch (vty
->node
) {
1316 vty
->status
= VTY_CLOSE
;
1319 vty
->node
= ENABLE_NODE
;
1320 vty_config_unlock(vty
);
1322 case INTERFACE_NODE
:
1324 case LOGICALROUTER_NODE
:
1336 case LDP_L2VPN_NODE
:
1342 vty
->node
= CONFIG_NODE
;
1345 case BGP_IPV4M_NODE
:
1346 case BGP_IPV4L_NODE
:
1347 case BGP_VPNV4_NODE
:
1348 case BGP_VPNV6_NODE
:
1349 case BGP_FLOWSPECV4_NODE
:
1350 case BGP_FLOWSPECV6_NODE
:
1351 case BGP_VRF_POLICY_NODE
:
1352 case BGP_VNC_DEFAULTS_NODE
:
1353 case BGP_VNC_NVE_GROUP_NODE
:
1354 case BGP_VNC_L2_GROUP_NODE
:
1356 case BGP_IPV6M_NODE
:
1358 case BGP_IPV6L_NODE
:
1359 vty
->node
= BGP_NODE
;
1361 case BGP_EVPN_VNI_NODE
:
1362 vty
->node
= BGP_EVPN_NODE
;
1366 vty
->node
= LDP_NODE
;
1368 case LDP_IPV4_IFACE_NODE
:
1369 vty
->node
= LDP_IPV4_NODE
;
1371 case LDP_IPV6_IFACE_NODE
:
1372 vty
->node
= LDP_IPV6_NODE
;
1374 case LDP_PSEUDOWIRE_NODE
:
1375 vty
->node
= LDP_L2VPN_NODE
;
1377 case KEYCHAIN_KEY_NODE
:
1378 vty
->node
= KEYCHAIN_NODE
;
1380 case LINK_PARAMS_NODE
:
1381 vty
->node
= INTERFACE_NODE
;
1392 "Exit current mode and down to previous mode\n")
1394 return config_exit(self
, vty
, argc
, argv
);
1398 /* End of configuration. */
1402 "End current mode and change to enable mode.\n")
1404 switch (vty
->node
) {
1407 /* Nothing to do. */
1410 case INTERFACE_NODE
:
1412 case LOGICALROUTER_NODE
:
1421 case BGP_VRF_POLICY_NODE
:
1422 case BGP_VNC_DEFAULTS_NODE
:
1423 case BGP_VNC_NVE_GROUP_NODE
:
1424 case BGP_VNC_L2_GROUP_NODE
:
1425 case BGP_VPNV4_NODE
:
1426 case BGP_VPNV6_NODE
:
1427 case BGP_FLOWSPECV4_NODE
:
1428 case BGP_FLOWSPECV6_NODE
:
1430 case BGP_IPV4M_NODE
:
1431 case BGP_IPV4L_NODE
:
1433 case BGP_IPV6M_NODE
:
1435 case BGP_EVPN_VNI_NODE
:
1436 case BGP_IPV6L_NODE
:
1444 case LDP_IPV4_IFACE_NODE
:
1445 case LDP_IPV6_IFACE_NODE
:
1446 case LDP_L2VPN_NODE
:
1447 case LDP_PSEUDOWIRE_NODE
:
1450 case KEYCHAIN_KEY_NODE
:
1452 case LINK_PARAMS_NODE
:
1453 vty_config_unlock(vty
);
1454 vty
->node
= ENABLE_NODE
;
1463 DEFUN (show_version
,
1467 "Displays zebra version\n")
1469 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1470 cmd_hostname_get() ? cmd_hostname_get() : "");
1471 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1472 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1477 /* "Set" version ... ignore version tags */
1478 DEFUN (frr_version_defaults
,
1479 frr_version_defaults_cmd
,
1480 "frr <version|defaults> LINE...",
1481 "FRRouting global parameters\n"
1482 "version configuration was written by\n"
1483 "set of configuration defaults used\n"
1489 /* Help display function for all node. */
1493 "Description of the interactive help system\n")
1496 "Quagga VTY provides advanced help feature. When you need help,\n\
1497 anytime at the command line please press '?'.\n\
1499 If nothing matches, the help list will be empty and you must backup\n\
1500 until entering a '?' shows the available options.\n\
1501 Two styles of help are provided:\n\
1502 1. Full help is available when you are ready to enter a\n\
1503 command argument (e.g. 'show ?') and describes each possible\n\
1505 2. Partial help is provided when an abbreviated argument is entered\n\
1506 and you want to know what arguments match the input\n\
1507 (e.g. 'show me?'.)\n\n");
1511 static void permute(struct graph_node
*start
, struct vty
*vty
)
1513 static struct list
*position
= NULL
;
1515 position
= list_new();
1517 struct cmd_token
*stok
= start
->data
;
1518 struct graph_node
*gnn
;
1519 struct listnode
*ln
;
1522 listnode_add(position
, start
);
1523 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1524 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1525 struct cmd_token
*tok
= gn
->data
;
1526 if (tok
->attr
== CMD_ATTR_HIDDEN
1527 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1529 else if (tok
->type
== END_TKN
|| gn
== start
) {
1531 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1532 struct cmd_token
*tt
= gnn
->data
;
1533 if (tt
->type
< SPECIAL_TKN
)
1534 vty_out(vty
, " %s", tt
->text
);
1537 vty_out(vty
, "...");
1541 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1542 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1551 list_delete_node(position
, listtail(position
));
1554 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1556 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1559 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1561 /* loop over all commands at this node */
1562 struct cmd_element
*element
= NULL
;
1563 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1565 if ((element
= vector_slot(node
->cmd_vector
, i
))
1566 && element
->attr
!= CMD_ATTR_DEPRECATED
1567 && element
->attr
!= CMD_ATTR_HIDDEN
)
1568 vty_out(vty
, " %s\n", element
->string
);
1573 /* Help display function for all node. */
1576 "list [permutations]",
1577 "Print command list\n"
1578 "Print all possible command permutations\n")
1580 return cmd_list_cmds(vty
, argc
== 2);
1583 DEFUN (show_commandtree
,
1584 show_commandtree_cmd
,
1585 "show commandtree [permutations]",
1587 "Show command tree\n"
1588 "Permutations that we are interested in\n")
1590 return cmd_list_cmds(vty
, argc
== 3);
1593 DEFUN_HIDDEN(show_cli_graph
,
1598 "Dump current command space as DOT graph\n")
1600 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1601 char *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 if (vty
->type
== VTY_TERM
) {
1617 vty_out(vty
, "\nCurrent configuration:\n");
1618 vty_out(vty
, "!\n");
1621 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1622 vty_out(vty
, "frr defaults %s\n", DFLT_NAME
);
1623 vty_out(vty
, "!\n");
1625 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1626 if ((node
= vector_slot(cmdvec
, i
)) && node
->func
1627 && (node
->vtysh
|| vty
->type
!= VTY_SHELL
)) {
1628 if ((*node
->func
)(vty
))
1629 vty_out(vty
, "!\n");
1632 if (vty
->type
== VTY_TERM
) {
1633 vty_out(vty
, "end\n");
1639 static int file_write_config(struct vty
*vty
)
1642 char *config_file
, *slash
;
1643 char *config_file_tmp
= NULL
;
1644 char *config_file_sav
= NULL
;
1645 int ret
= CMD_WARNING
;
1646 struct vty
*file_vty
;
1647 struct stat conf_stat
;
1652 /* Check and see if we are operating under vtysh configuration */
1653 if (host
.config
== NULL
) {
1655 "Can't save to configuration file, using vtysh.\n");
1660 config_file
= host
.config
;
1663 #define O_DIRECTORY 0
1665 slash
= strrchr(config_file
, '/');
1667 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1668 config_dir
[slash
- config_file
] = '\0';
1669 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1670 XFREE(MTYPE_TMP
, config_dir
);
1672 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1673 /* if dirfd is invalid, directory sync fails, but we're still OK */
1675 config_file_sav
= XMALLOC(
1676 MTYPE_TMP
, strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1);
1677 strcpy(config_file_sav
, config_file
);
1678 strcat(config_file_sav
, CONF_BACKUP_EXT
);
1681 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1682 sprintf(config_file_tmp
, "%s.XXXXXX", config_file
);
1684 /* Open file to configuration write. */
1685 fd
= mkstemp(config_file_tmp
);
1687 vty_out(vty
, "Can't open configuration file %s.\n",
1691 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1692 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1693 config_file_tmp
, safe_strerror(errno
), errno
);
1697 /* Make vty for configuration file. */
1698 file_vty
= vty_new();
1700 file_vty
->type
= VTY_FILE
;
1702 /* Config file header print. */
1703 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1704 vty_time_print(file_vty
, 1);
1705 vty_out(file_vty
, "!\n");
1706 vty_write_config(file_vty
);
1707 vty_close(file_vty
);
1709 if (stat(config_file
, &conf_stat
) >= 0) {
1710 if (unlink(config_file_sav
) != 0)
1711 if (errno
!= ENOENT
) {
1713 "Can't unlink backup configuration file %s.\n",
1717 if (link(config_file
, config_file_sav
) != 0) {
1719 "Can't backup old configuration file %s.\n",
1726 if (rename(config_file_tmp
, config_file
) != 0) {
1727 vty_out(vty
, "Can't save configuration file %s.\n",
1734 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1738 if (ret
!= CMD_SUCCESS
)
1739 unlink(config_file_tmp
);
1742 XFREE(MTYPE_TMP
, config_file_tmp
);
1743 XFREE(MTYPE_TMP
, config_file_sav
);
1747 /* Write current configuration into file. */
1749 DEFUN (config_write
,
1751 "write [<file|memory|terminal>]",
1752 "Write running configuration to memory, network, or terminal\n"
1753 "Write to configuration file\n"
1754 "Write configuration currently in memory\n"
1755 "Write configuration to terminal\n")
1757 const int idx_type
= 1;
1759 // if command was 'write terminal' or 'write memory'
1760 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1761 return vty_write_config(vty
);
1764 return file_write_config(vty
);
1767 /* ALIAS_FIXME for 'write <terminal|memory>' */
1768 DEFUN (show_running_config
,
1769 show_running_config_cmd
,
1770 "show running-config",
1772 "running configuration (same as write terminal)\n")
1774 return vty_write_config(vty
);
1777 /* ALIAS_FIXME for 'write file' */
1778 DEFUN (copy_runningconf_startupconf
,
1779 copy_runningconf_startupconf_cmd
,
1780 "copy running-config startup-config",
1781 "Copy configuration\n"
1782 "Copy running config to... \n"
1783 "Copy running config to startup config (same as write file/memory)\n")
1785 return file_write_config(vty
);
1789 /* Write startup configuration into the terminal. */
1790 DEFUN (show_startup_config
,
1791 show_startup_config_cmd
,
1792 "show startup-config",
1794 "Contents of startup configuration\n")
1801 if (host
.config
== NULL
)
1804 confp
= fopen(host
.config
, "r");
1805 if (confp
== NULL
) {
1806 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1807 host
.config
, safe_strerror(errno
));
1811 while (fgets(buf
, BUFSIZ
, confp
)) {
1814 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1818 vty_out(vty
, "%s\n", buf
);
1826 int cmd_domainname_set(const char *domainname
)
1828 XFREE(MTYPE_HOST
, host
.domainname
);
1829 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1833 /* Hostname configuration */
1834 DEFUN(config_domainname
,
1837 "Set system's domain name\n"
1838 "This system's domain name\n")
1840 struct cmd_token
*word
= argv
[1];
1842 if (!isalpha((int)word
->arg
[0])) {
1843 vty_out(vty
, "Please specify string starting with alphabet\n");
1844 return CMD_WARNING_CONFIG_FAILED
;
1847 return cmd_domainname_set(word
->arg
);
1850 DEFUN(config_no_domainname
,
1852 "no domainname [DOMAINNAME]",
1854 "Reset system's domain name\n"
1855 "domain name of this router\n")
1857 return cmd_domainname_set(NULL
);
1860 int cmd_hostname_set(const char *hostname
)
1862 XFREE(MTYPE_HOST
, host
.name
);
1863 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1867 /* Hostname configuration */
1868 DEFUN (config_hostname
,
1871 "Set system's network name\n"
1872 "This system's network name\n")
1874 struct cmd_token
*word
= argv
[1];
1876 if (!isalpha((int)word
->arg
[0])) {
1877 vty_out(vty
, "Please specify string starting with alphabet\n");
1878 return CMD_WARNING_CONFIG_FAILED
;
1881 return cmd_hostname_set(word
->arg
);
1884 DEFUN (config_no_hostname
,
1886 "no hostname [HOSTNAME]",
1888 "Reset system's network name\n"
1889 "Host name of this router\n")
1891 return cmd_hostname_set(NULL
);
1894 /* VTY interface password set. */
1895 DEFUN (config_password
,
1897 "password [(8-8)] WORD",
1898 "Assign the terminal connection password\n"
1899 "Specifies a HIDDEN password will follow\n"
1900 "The password string\n")
1904 if (argc
== 3) // '8' was specified
1907 XFREE(MTYPE_HOST
, host
.password
);
1908 host
.password
= NULL
;
1909 if (host
.password_encrypt
)
1910 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1911 host
.password_encrypt
=
1912 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1916 if (!isalnum((int)argv
[idx_8
]->arg
[0])) {
1918 "Please specify string starting with alphanumeric\n");
1919 return CMD_WARNING_CONFIG_FAILED
;
1923 XFREE(MTYPE_HOST
, host
.password
);
1924 host
.password
= NULL
;
1927 if (host
.password_encrypt
)
1928 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1929 host
.password_encrypt
=
1930 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1932 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1937 /* VTY enable password set. */
1938 DEFUN (config_enable_password
,
1939 enable_password_cmd
,
1940 "enable password [(8-8)] WORD",
1941 "Modify enable password parameters\n"
1942 "Assign the privileged level password\n"
1943 "Specifies a HIDDEN password will follow\n"
1944 "The HIDDEN 'enable' password string\n")
1949 /* Crypt type is specified. */
1951 if (argv
[idx_8
]->arg
[0] == '8') {
1953 XFREE(MTYPE_HOST
, host
.enable
);
1956 if (host
.enable_encrypt
)
1957 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1958 host
.enable_encrypt
=
1959 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1963 vty_out(vty
, "Unknown encryption type.\n");
1964 return CMD_WARNING_CONFIG_FAILED
;
1968 if (!isalnum((int)argv
[idx_8
]->arg
[0])) {
1970 "Please specify string starting with alphanumeric\n");
1971 return CMD_WARNING_CONFIG_FAILED
;
1975 XFREE(MTYPE_HOST
, host
.enable
);
1978 /* Plain password input. */
1980 if (host
.enable_encrypt
)
1981 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1982 host
.enable_encrypt
=
1983 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1985 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1990 /* VTY enable password delete. */
1991 DEFUN (no_config_enable_password
,
1992 no_enable_password_cmd
,
1993 "no enable password",
1995 "Modify enable password parameters\n"
1996 "Assign the privileged level password\n")
1999 XFREE(MTYPE_HOST
, host
.enable
);
2002 if (host
.enable_encrypt
)
2003 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2004 host
.enable_encrypt
= NULL
;
2009 DEFUN (service_password_encrypt
,
2010 service_password_encrypt_cmd
,
2011 "service password-encryption",
2012 "Set up miscellaneous service\n"
2013 "Enable encrypted passwords\n")
2020 if (host
.password
) {
2021 if (host
.password_encrypt
)
2022 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2023 host
.password_encrypt
=
2024 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2027 if (host
.enable_encrypt
)
2028 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2029 host
.enable_encrypt
=
2030 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2036 DEFUN (no_service_password_encrypt
,
2037 no_service_password_encrypt_cmd
,
2038 "no service password-encryption",
2040 "Set up miscellaneous service\n"
2041 "Enable encrypted passwords\n")
2048 if (host
.password_encrypt
)
2049 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2050 host
.password_encrypt
= NULL
;
2052 if (host
.enable_encrypt
)
2053 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2054 host
.enable_encrypt
= NULL
;
2059 DEFUN (config_terminal_length
,
2060 config_terminal_length_cmd
,
2061 "terminal length (0-512)",
2062 "Set terminal line parameters\n"
2063 "Set number of lines on a screen\n"
2064 "Number of lines on screen (0 for no pausing)\n")
2068 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2073 DEFUN (config_terminal_no_length
,
2074 config_terminal_no_length_cmd
,
2075 "terminal no length",
2076 "Set terminal line parameters\n"
2078 "Set number of lines on a screen\n")
2084 DEFUN (service_terminal_length
,
2085 service_terminal_length_cmd
,
2086 "service terminal-length (0-512)",
2087 "Set up miscellaneous service\n"
2088 "System wide terminal length configuration\n"
2089 "Number of lines of VTY (0 means no line control)\n")
2093 host
.lines
= atoi(argv
[idx_number
]->arg
);
2098 DEFUN (no_service_terminal_length
,
2099 no_service_terminal_length_cmd
,
2100 "no service terminal-length [(0-512)]",
2102 "Set up miscellaneous service\n"
2103 "System wide terminal length configuration\n"
2104 "Number of lines of VTY (0 means no line control)\n")
2110 DEFUN_HIDDEN (do_echo
,
2113 "Echo a message back to the vty\n"
2114 "The message to echo\n")
2118 vty_out(vty
, "%s\n",
2119 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2121 XFREE(MTYPE_TMP
, message
);
2125 DEFUN (config_logmsg
,
2127 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2128 "Send a message to enabled logging destinations\n"
2130 "The message to send\n")
2132 int idx_log_level
= 1;
2133 int idx_message
= 2;
2137 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2138 return CMD_ERR_NO_MATCH
;
2141 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2143 XFREE(MTYPE_TMP
, message
);
2148 DEFUN (show_logging
,
2152 "Show current logging configuration\n")
2154 struct zlog
*zl
= zlog_default
;
2156 vty_out(vty
, "Syslog logging: ");
2157 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
2158 vty_out(vty
, "disabled");
2160 vty_out(vty
, "level %s, facility %s, ident %s",
2161 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
2162 facility_name(zl
->facility
), zl
->ident
);
2165 vty_out(vty
, "Stdout logging: ");
2166 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
2167 vty_out(vty
, "disabled");
2169 vty_out(vty
, "level %s",
2170 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
2173 vty_out(vty
, "Monitor logging: ");
2174 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
2175 vty_out(vty
, "disabled");
2177 vty_out(vty
, "level %s",
2178 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
2181 vty_out(vty
, "File logging: ");
2182 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
2183 vty_out(vty
, "disabled");
2185 vty_out(vty
, "level %s, filename %s",
2186 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
2190 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
2191 vty_out(vty
, "Record priority: %s\n",
2192 (zl
->record_priority
? "enabled" : "disabled"));
2193 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2198 DEFUN (config_log_stdout
,
2199 config_log_stdout_cmd
,
2200 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2202 "Set stdout logging level\n"
2205 int idx_log_level
= 2;
2207 if (argc
== idx_log_level
) {
2208 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2213 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2214 return CMD_ERR_NO_MATCH
;
2215 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2219 DEFUN (no_config_log_stdout
,
2220 no_config_log_stdout_cmd
,
2221 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2224 "Cancel logging to stdout\n"
2227 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2231 DEFUN (config_log_monitor
,
2232 config_log_monitor_cmd
,
2233 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2235 "Set terminal line (monitor) logging level\n"
2238 int idx_log_level
= 2;
2240 if (argc
== idx_log_level
) {
2241 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2246 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2247 return CMD_ERR_NO_MATCH
;
2248 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2252 DEFUN (no_config_log_monitor
,
2253 no_config_log_monitor_cmd
,
2254 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2257 "Disable terminal line (monitor) logging\n"
2260 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2264 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2268 const char *fullpath
;
2270 /* Path detection. */
2271 if (!IS_DIRECTORY_SEP(*fname
)) {
2272 char cwd
[MAXPATHLEN
+ 1];
2273 cwd
[MAXPATHLEN
] = '\0';
2275 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2276 zlog_err("config_log_file: Unable to alloc mem!");
2277 return CMD_WARNING_CONFIG_FAILED
;
2280 if ((p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2))
2282 zlog_err("config_log_file: Unable to alloc mem!");
2283 return CMD_WARNING_CONFIG_FAILED
;
2285 sprintf(p
, "%s/%s", cwd
, fname
);
2290 ret
= zlog_set_file(fullpath
, loglevel
);
2293 XFREE(MTYPE_TMP
, p
);
2296 vty_out(vty
, "can't open logfile %s\n", fname
);
2297 return CMD_WARNING_CONFIG_FAILED
;
2301 XFREE(MTYPE_HOST
, host
.logfile
);
2303 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2305 #if defined(HAVE_CUMULUS)
2306 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2307 zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] = ZLOG_DISABLED
;
2312 DEFUN (config_log_file
,
2313 config_log_file_cmd
,
2314 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2317 "Logging filename\n"
2320 int idx_filename
= 2;
2321 int idx_log_levels
= 3;
2324 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2326 return CMD_ERR_NO_MATCH
;
2327 return set_log_file(vty
, argv
[idx_filename
]->arg
, level
);
2329 return set_log_file(vty
, argv
[idx_filename
]->arg
,
2330 zlog_default
->default_lvl
);
2333 DEFUN (no_config_log_file
,
2334 no_config_log_file_cmd
,
2335 "no log file [FILENAME [LEVEL]]",
2338 "Cancel logging to file\n"
2339 "Logging file name\n"
2345 XFREE(MTYPE_HOST
, host
.logfile
);
2347 host
.logfile
= NULL
;
2352 DEFUN (config_log_syslog
,
2353 config_log_syslog_cmd
,
2354 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2356 "Set syslog logging level\n"
2359 int idx_log_levels
= 2;
2362 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2364 return CMD_ERR_NO_MATCH
;
2365 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2368 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2373 DEFUN (no_config_log_syslog
,
2374 no_config_log_syslog_cmd
,
2375 "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>]",
2378 "Cancel logging to syslog\n"
2382 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2386 DEFUN (config_log_facility
,
2387 config_log_facility_cmd
,
2388 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2390 "Facility parameter for syslog messages\n"
2394 int facility
= facility_match(argv
[idx_target
]->arg
);
2396 zlog_default
->facility
= facility
;
2400 DEFUN (no_config_log_facility
,
2401 no_config_log_facility_cmd
,
2402 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2405 "Reset syslog facility to default (daemon)\n"
2408 zlog_default
->facility
= LOG_DAEMON
;
2413 config_log_trap
, config_log_trap_cmd
,
2414 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2416 "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC
)
2421 if ((new_level
= level_match(argv
[2]->arg
)) == ZLOG_DISABLED
)
2422 return CMD_ERR_NO_MATCH
;
2424 zlog_default
->default_lvl
= new_level
;
2425 for (i
= 0; i
< ZLOG_NUM_DESTS
; i
++)
2426 if (zlog_default
->maxlvl
[i
] != ZLOG_DISABLED
)
2427 zlog_default
->maxlvl
[i
] = new_level
;
2432 no_config_log_trap
, no_config_log_trap_cmd
,
2433 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2436 "Permit all logging information\n" LOG_LEVEL_DESC
)
2438 zlog_default
->default_lvl
= LOG_DEBUG
;
2442 DEFUN (config_log_record_priority
,
2443 config_log_record_priority_cmd
,
2444 "log record-priority",
2446 "Log the priority of the message within the message\n")
2448 zlog_default
->record_priority
= 1;
2452 DEFUN (no_config_log_record_priority
,
2453 no_config_log_record_priority_cmd
,
2454 "no log record-priority",
2457 "Do not log the priority of the message within the message\n")
2459 zlog_default
->record_priority
= 0;
2463 DEFUN (config_log_timestamp_precision
,
2464 config_log_timestamp_precision_cmd
,
2465 "log timestamp precision (0-6)",
2467 "Timestamp configuration\n"
2468 "Set the timestamp precision\n"
2469 "Number of subsecond digits\n")
2472 zlog_default
->timestamp_precision
=
2473 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2477 DEFUN (no_config_log_timestamp_precision
,
2478 no_config_log_timestamp_precision_cmd
,
2479 "no log timestamp precision",
2482 "Timestamp configuration\n"
2483 "Reset the timestamp precision to the default value of 0\n")
2485 zlog_default
->timestamp_precision
= 0;
2489 DEFUN (debug_memstats
,
2491 "[no] debug memstats-at-exit",
2494 "Print memory type statistics at exit\n")
2496 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2500 int cmd_banner_motd_file(const char *file
)
2502 int success
= CMD_SUCCESS
;
2507 rpath
= realpath(file
, p
);
2509 return CMD_ERR_NO_FILE
;
2510 in
= strstr(rpath
, SYSCONFDIR
);
2513 XFREE(MTYPE_HOST
, host
.motdfile
);
2514 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2516 success
= CMD_WARNING_CONFIG_FAILED
;
2521 DEFUN (banner_motd_file
,
2522 banner_motd_file_cmd
,
2523 "banner motd file FILE",
2526 "Banner from a file\n"
2530 const char *filename
= argv
[idx_file
]->arg
;
2531 int cmd
= cmd_banner_motd_file(filename
);
2533 if (cmd
== CMD_ERR_NO_FILE
)
2534 vty_out(vty
, "%s does not exist", filename
);
2535 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2536 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2541 DEFUN (banner_motd_default
,
2542 banner_motd_default_cmd
,
2543 "banner motd default",
2544 "Set banner string\n"
2545 "Strings for motd\n"
2548 host
.motd
= default_motd
;
2552 DEFUN (no_banner_motd
,
2556 "Set banner string\n"
2557 "Strings for motd\n")
2561 XFREE(MTYPE_HOST
, host
.motdfile
);
2562 host
.motdfile
= NULL
;
2569 "Find CLI command containing text\n"
2570 "Text to search for\n")
2572 char *text
= argv_concat(argv
, argc
, 1);
2573 const struct cmd_node
*node
;
2574 const struct cmd_element
*cli
;
2577 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2578 node
= vector_slot(cmdvec
, i
);
2581 clis
= node
->cmd_vector
;
2582 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2583 cli
= vector_slot(clis
, j
);
2584 if (strcasestr(cli
->string
, text
))
2585 vty_out(vty
, " (%s) %s\n",
2586 node_names
[node
->node
], cli
->string
);
2590 XFREE(MTYPE_TMP
, text
);
2595 /* Set config filename. Called from vty.c */
2596 void host_config_set(const char *filename
)
2599 XFREE(MTYPE_HOST
, host
.config
);
2600 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2603 const char *host_config_get(void)
2608 void install_default(enum node_type node
)
2610 install_element(node
, &config_exit_cmd
);
2611 install_element(node
, &config_quit_cmd
);
2612 install_element(node
, &config_end_cmd
);
2613 install_element(node
, &config_help_cmd
);
2614 install_element(node
, &config_list_cmd
);
2615 install_element(node
, &show_cli_graph_cmd
);
2616 install_element(node
, &find_cmd
);
2618 install_element(node
, &config_write_cmd
);
2619 install_element(node
, &show_running_config_cmd
);
2621 install_element(node
, &autocomplete_cmd
);
2624 /* Initialize command interface. Install basic nodes and commands.
2626 * terminal = 0 -- vtysh / no logging, no config control
2627 * terminal = 1 -- normal daemon
2628 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2629 void cmd_init(int terminal
)
2631 struct utsname names
;
2633 if (array_size(node_names
) != NODE_TYPE_MAX
)
2634 assert(!"Update the CLI node description array!");
2639 varhandlers
= list_new();
2641 /* Allocate initial top vector of commands. */
2642 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2644 /* Default host value settings. */
2645 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2646 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2647 if ((strcmp(names
.domainname
, "(none)") == 0))
2648 host
.domainname
= NULL
;
2650 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2652 host
.domainname
= NULL
;
2654 host
.password
= NULL
;
2656 host
.logfile
= NULL
;
2658 host
.noconfig
= (terminal
< 0);
2660 host
.motd
= default_motd
;
2661 host
.motdfile
= NULL
;
2663 /* Install top nodes. */
2664 install_node(&view_node
, NULL
);
2665 install_node(&enable_node
, NULL
);
2666 install_node(&auth_node
, NULL
);
2667 install_node(&auth_enable_node
, NULL
);
2668 install_node(&config_node
, config_write_host
);
2670 /* Each node's basic commands. */
2671 install_element(VIEW_NODE
, &show_version_cmd
);
2672 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2673 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2676 install_element(VIEW_NODE
, &config_list_cmd
);
2677 install_element(VIEW_NODE
, &config_exit_cmd
);
2678 install_element(VIEW_NODE
, &config_quit_cmd
);
2679 install_element(VIEW_NODE
, &config_help_cmd
);
2680 install_element(VIEW_NODE
, &config_enable_cmd
);
2681 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2682 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2683 install_element(VIEW_NODE
, &show_logging_cmd
);
2684 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2685 install_element(VIEW_NODE
, &echo_cmd
);
2686 install_element(VIEW_NODE
, &autocomplete_cmd
);
2687 install_element(VIEW_NODE
, &find_cmd
);
2689 install_element(ENABLE_NODE
, &config_end_cmd
);
2690 install_element(ENABLE_NODE
, &config_disable_cmd
);
2691 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2692 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2693 install_element(ENABLE_NODE
, &config_write_cmd
);
2694 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2695 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2697 install_default(CONFIG_NODE
);
2700 workqueue_cmd_init();
2704 install_element(CONFIG_NODE
, &hostname_cmd
);
2705 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2706 install_element(CONFIG_NODE
, &domainname_cmd
);
2707 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2708 install_element(CONFIG_NODE
, &frr_version_defaults_cmd
);
2709 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2712 install_element(CONFIG_NODE
, &password_cmd
);
2713 install_element(CONFIG_NODE
, &enable_password_cmd
);
2714 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2716 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2717 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2718 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2719 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2720 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2721 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2722 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2723 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2724 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2725 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2726 install_element(CONFIG_NODE
, &config_log_trap_cmd
);
2727 install_element(CONFIG_NODE
, &no_config_log_trap_cmd
);
2728 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2729 install_element(CONFIG_NODE
,
2730 &no_config_log_record_priority_cmd
);
2731 install_element(CONFIG_NODE
,
2732 &config_log_timestamp_precision_cmd
);
2733 install_element(CONFIG_NODE
,
2734 &no_config_log_timestamp_precision_cmd
);
2735 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2736 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2737 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2738 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2739 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2740 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2741 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2743 vrf_install_commands();
2747 grammar_sandbox_init();
2751 void cmd_terminate()
2753 struct cmd_node
*cmd_node
;
2756 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2757 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2758 // deleting the graph delets the cmd_element as
2760 graph_delete_graph(cmd_node
->cmdgraph
);
2761 vector_free(cmd_node
->cmd_vector
);
2762 hash_clean(cmd_node
->cmd_hash
, NULL
);
2763 hash_free(cmd_node
->cmd_hash
);
2764 cmd_node
->cmd_hash
= NULL
;
2767 vector_free(cmdvec
);
2772 XFREE(MTYPE_HOST
, host
.name
);
2773 if (host
.domainname
)
2774 XFREE(MTYPE_HOST
, host
.domainname
);
2776 XFREE(MTYPE_HOST
, host
.password
);
2777 if (host
.password_encrypt
)
2778 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2780 XFREE(MTYPE_HOST
, host
.enable
);
2781 if (host
.enable_encrypt
)
2782 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2784 XFREE(MTYPE_HOST
, host
.logfile
);
2786 XFREE(MTYPE_HOST
, host
.motdfile
);
2788 XFREE(MTYPE_HOST
, host
.config
);
2790 list_delete_and_null(&varhandlers
);