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 "Modify 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 interface password delete. */
1938 DEFUN (no_config_password
,
1942 "Modify the terminal connection password\n")
1944 bool warned
= false;
1946 if (host
.password
) {
1947 if (!vty_shell_serv(vty
)) {
1948 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1951 XFREE(MTYPE_HOST
, host
.password
);
1953 host
.password
= NULL
;
1955 if (host
.password_encrypt
) {
1956 if (!warned
&& !vty_shell_serv(vty
))
1957 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
1958 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1960 host
.password_encrypt
= NULL
;
1965 /* VTY enable password set. */
1966 DEFUN (config_enable_password
,
1967 enable_password_cmd
,
1968 "enable password [(8-8)] WORD",
1969 "Modify enable password parameters\n"
1970 "Assign the privileged level password\n"
1971 "Specifies a HIDDEN password will follow\n"
1972 "The HIDDEN 'enable' password string\n")
1977 /* Crypt type is specified. */
1979 if (argv
[idx_8
]->arg
[0] == '8') {
1981 XFREE(MTYPE_HOST
, host
.enable
);
1984 if (host
.enable_encrypt
)
1985 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1986 host
.enable_encrypt
=
1987 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1991 vty_out(vty
, "Unknown encryption type.\n");
1992 return CMD_WARNING_CONFIG_FAILED
;
1996 if (!isalnum((int)argv
[idx_8
]->arg
[0])) {
1998 "Please specify string starting with alphanumeric\n");
1999 return CMD_WARNING_CONFIG_FAILED
;
2003 XFREE(MTYPE_HOST
, host
.enable
);
2006 /* Plain password input. */
2008 if (host
.enable_encrypt
)
2009 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2010 host
.enable_encrypt
=
2011 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2013 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2018 /* VTY enable password delete. */
2019 DEFUN (no_config_enable_password
,
2020 no_enable_password_cmd
,
2021 "no enable password",
2023 "Modify enable password parameters\n"
2024 "Assign the privileged level password\n")
2026 bool warned
= false;
2029 if (!vty_shell_serv(vty
)) {
2030 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2033 XFREE(MTYPE_HOST
, host
.enable
);
2037 if (host
.enable_encrypt
) {
2038 if (!warned
&& !vty_shell_serv(vty
))
2039 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2040 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2042 host
.enable_encrypt
= NULL
;
2047 DEFUN (service_password_encrypt
,
2048 service_password_encrypt_cmd
,
2049 "service password-encryption",
2050 "Set up miscellaneous service\n"
2051 "Enable encrypted passwords\n")
2058 if (host
.password
) {
2059 if (host
.password_encrypt
)
2060 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2061 host
.password_encrypt
=
2062 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2065 if (host
.enable_encrypt
)
2066 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2067 host
.enable_encrypt
=
2068 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2074 DEFUN (no_service_password_encrypt
,
2075 no_service_password_encrypt_cmd
,
2076 "no service password-encryption",
2078 "Set up miscellaneous service\n"
2079 "Enable encrypted passwords\n")
2086 if (host
.password_encrypt
)
2087 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2088 host
.password_encrypt
= NULL
;
2090 if (host
.enable_encrypt
)
2091 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2092 host
.enable_encrypt
= NULL
;
2097 DEFUN (config_terminal_length
,
2098 config_terminal_length_cmd
,
2099 "terminal length (0-512)",
2100 "Set terminal line parameters\n"
2101 "Set number of lines on a screen\n"
2102 "Number of lines on screen (0 for no pausing)\n")
2106 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2111 DEFUN (config_terminal_no_length
,
2112 config_terminal_no_length_cmd
,
2113 "terminal no length",
2114 "Set terminal line parameters\n"
2116 "Set number of lines on a screen\n")
2122 DEFUN (service_terminal_length
,
2123 service_terminal_length_cmd
,
2124 "service terminal-length (0-512)",
2125 "Set up miscellaneous service\n"
2126 "System wide terminal length configuration\n"
2127 "Number of lines of VTY (0 means no line control)\n")
2131 host
.lines
= atoi(argv
[idx_number
]->arg
);
2136 DEFUN (no_service_terminal_length
,
2137 no_service_terminal_length_cmd
,
2138 "no service terminal-length [(0-512)]",
2140 "Set up miscellaneous service\n"
2141 "System wide terminal length configuration\n"
2142 "Number of lines of VTY (0 means no line control)\n")
2148 DEFUN_HIDDEN (do_echo
,
2151 "Echo a message back to the vty\n"
2152 "The message to echo\n")
2156 vty_out(vty
, "%s\n",
2157 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2159 XFREE(MTYPE_TMP
, message
);
2163 DEFUN (config_logmsg
,
2165 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2166 "Send a message to enabled logging destinations\n"
2168 "The message to send\n")
2170 int idx_log_level
= 1;
2171 int idx_message
= 2;
2175 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2176 return CMD_ERR_NO_MATCH
;
2179 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2181 XFREE(MTYPE_TMP
, message
);
2186 DEFUN (show_logging
,
2190 "Show current logging configuration\n")
2192 struct zlog
*zl
= zlog_default
;
2194 vty_out(vty
, "Syslog logging: ");
2195 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
2196 vty_out(vty
, "disabled");
2198 vty_out(vty
, "level %s, facility %s, ident %s",
2199 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
2200 facility_name(zl
->facility
), zl
->ident
);
2203 vty_out(vty
, "Stdout logging: ");
2204 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
2205 vty_out(vty
, "disabled");
2207 vty_out(vty
, "level %s",
2208 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
2211 vty_out(vty
, "Monitor logging: ");
2212 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
2213 vty_out(vty
, "disabled");
2215 vty_out(vty
, "level %s",
2216 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
2219 vty_out(vty
, "File logging: ");
2220 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
2221 vty_out(vty
, "disabled");
2223 vty_out(vty
, "level %s, filename %s",
2224 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
2228 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
2229 vty_out(vty
, "Record priority: %s\n",
2230 (zl
->record_priority
? "enabled" : "disabled"));
2231 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2236 DEFUN (config_log_stdout
,
2237 config_log_stdout_cmd
,
2238 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2240 "Set stdout logging level\n"
2243 int idx_log_level
= 2;
2245 if (argc
== idx_log_level
) {
2246 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2251 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2252 return CMD_ERR_NO_MATCH
;
2253 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2257 DEFUN (no_config_log_stdout
,
2258 no_config_log_stdout_cmd
,
2259 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2262 "Cancel logging to stdout\n"
2265 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2269 DEFUN (config_log_monitor
,
2270 config_log_monitor_cmd
,
2271 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2273 "Set terminal line (monitor) logging level\n"
2276 int idx_log_level
= 2;
2278 if (argc
== idx_log_level
) {
2279 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2284 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2285 return CMD_ERR_NO_MATCH
;
2286 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2290 DEFUN (no_config_log_monitor
,
2291 no_config_log_monitor_cmd
,
2292 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2295 "Disable terminal line (monitor) logging\n"
2298 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2302 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2306 const char *fullpath
;
2308 /* Path detection. */
2309 if (!IS_DIRECTORY_SEP(*fname
)) {
2310 char cwd
[MAXPATHLEN
+ 1];
2311 cwd
[MAXPATHLEN
] = '\0';
2313 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2314 zlog_err("config_log_file: Unable to alloc mem!");
2315 return CMD_WARNING_CONFIG_FAILED
;
2318 if ((p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2))
2320 zlog_err("config_log_file: Unable to alloc mem!");
2321 return CMD_WARNING_CONFIG_FAILED
;
2323 sprintf(p
, "%s/%s", cwd
, fname
);
2328 ret
= zlog_set_file(fullpath
, loglevel
);
2331 XFREE(MTYPE_TMP
, p
);
2334 vty_out(vty
, "can't open logfile %s\n", fname
);
2335 return CMD_WARNING_CONFIG_FAILED
;
2339 XFREE(MTYPE_HOST
, host
.logfile
);
2341 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2343 #if defined(HAVE_CUMULUS)
2344 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2345 zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] = ZLOG_DISABLED
;
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 DEFUN (no_config_log_file
,
2372 no_config_log_file_cmd
,
2373 "no log file [FILENAME [LEVEL]]",
2376 "Cancel logging to file\n"
2377 "Logging file name\n"
2383 XFREE(MTYPE_HOST
, host
.logfile
);
2385 host
.logfile
= NULL
;
2390 DEFUN (config_log_syslog
,
2391 config_log_syslog_cmd
,
2392 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2394 "Set syslog logging level\n"
2397 int idx_log_levels
= 2;
2400 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2402 return CMD_ERR_NO_MATCH
;
2403 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2406 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2411 DEFUN (no_config_log_syslog
,
2412 no_config_log_syslog_cmd
,
2413 "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>]",
2416 "Cancel logging to syslog\n"
2420 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2424 DEFUN (config_log_facility
,
2425 config_log_facility_cmd
,
2426 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2428 "Facility parameter for syslog messages\n"
2432 int facility
= facility_match(argv
[idx_target
]->arg
);
2434 zlog_default
->facility
= facility
;
2438 DEFUN (no_config_log_facility
,
2439 no_config_log_facility_cmd
,
2440 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2443 "Reset syslog facility to default (daemon)\n"
2446 zlog_default
->facility
= LOG_DAEMON
;
2451 config_log_trap
, config_log_trap_cmd
,
2452 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2454 "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC
)
2459 if ((new_level
= level_match(argv
[2]->arg
)) == ZLOG_DISABLED
)
2460 return CMD_ERR_NO_MATCH
;
2462 zlog_default
->default_lvl
= new_level
;
2463 for (i
= 0; i
< ZLOG_NUM_DESTS
; i
++)
2464 if (zlog_default
->maxlvl
[i
] != ZLOG_DISABLED
)
2465 zlog_default
->maxlvl
[i
] = new_level
;
2470 no_config_log_trap
, no_config_log_trap_cmd
,
2471 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2474 "Permit all logging information\n" LOG_LEVEL_DESC
)
2476 zlog_default
->default_lvl
= LOG_DEBUG
;
2480 DEFUN (config_log_record_priority
,
2481 config_log_record_priority_cmd
,
2482 "log record-priority",
2484 "Log the priority of the message within the message\n")
2486 zlog_default
->record_priority
= 1;
2490 DEFUN (no_config_log_record_priority
,
2491 no_config_log_record_priority_cmd
,
2492 "no log record-priority",
2495 "Do not log the priority of the message within the message\n")
2497 zlog_default
->record_priority
= 0;
2501 DEFUN (config_log_timestamp_precision
,
2502 config_log_timestamp_precision_cmd
,
2503 "log timestamp precision (0-6)",
2505 "Timestamp configuration\n"
2506 "Set the timestamp precision\n"
2507 "Number of subsecond digits\n")
2510 zlog_default
->timestamp_precision
=
2511 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2515 DEFUN (no_config_log_timestamp_precision
,
2516 no_config_log_timestamp_precision_cmd
,
2517 "no log timestamp precision",
2520 "Timestamp configuration\n"
2521 "Reset the timestamp precision to the default value of 0\n")
2523 zlog_default
->timestamp_precision
= 0;
2527 DEFUN (debug_memstats
,
2529 "[no] debug memstats-at-exit",
2532 "Print memory type statistics at exit\n")
2534 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2538 int cmd_banner_motd_file(const char *file
)
2540 int success
= CMD_SUCCESS
;
2545 rpath
= realpath(file
, p
);
2547 return CMD_ERR_NO_FILE
;
2548 in
= strstr(rpath
, SYSCONFDIR
);
2551 XFREE(MTYPE_HOST
, host
.motdfile
);
2552 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2554 success
= CMD_WARNING_CONFIG_FAILED
;
2559 DEFUN (banner_motd_file
,
2560 banner_motd_file_cmd
,
2561 "banner motd file FILE",
2564 "Banner from a file\n"
2568 const char *filename
= argv
[idx_file
]->arg
;
2569 int cmd
= cmd_banner_motd_file(filename
);
2571 if (cmd
== CMD_ERR_NO_FILE
)
2572 vty_out(vty
, "%s does not exist", filename
);
2573 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2574 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2579 DEFUN (banner_motd_default
,
2580 banner_motd_default_cmd
,
2581 "banner motd default",
2582 "Set banner string\n"
2583 "Strings for motd\n"
2586 host
.motd
= default_motd
;
2590 DEFUN (no_banner_motd
,
2594 "Set banner string\n"
2595 "Strings for motd\n")
2599 XFREE(MTYPE_HOST
, host
.motdfile
);
2600 host
.motdfile
= NULL
;
2607 "Find CLI command containing text\n"
2608 "Text to search for\n")
2610 char *text
= argv_concat(argv
, argc
, 1);
2611 const struct cmd_node
*node
;
2612 const struct cmd_element
*cli
;
2615 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2616 node
= vector_slot(cmdvec
, i
);
2619 clis
= node
->cmd_vector
;
2620 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2621 cli
= vector_slot(clis
, j
);
2622 if (strcasestr(cli
->string
, text
))
2623 vty_out(vty
, " (%s) %s\n",
2624 node_names
[node
->node
], cli
->string
);
2628 XFREE(MTYPE_TMP
, text
);
2633 /* Set config filename. Called from vty.c */
2634 void host_config_set(const char *filename
)
2637 XFREE(MTYPE_HOST
, host
.config
);
2638 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2641 const char *host_config_get(void)
2646 void install_default(enum node_type node
)
2648 install_element(node
, &config_exit_cmd
);
2649 install_element(node
, &config_quit_cmd
);
2650 install_element(node
, &config_end_cmd
);
2651 install_element(node
, &config_help_cmd
);
2652 install_element(node
, &config_list_cmd
);
2653 install_element(node
, &show_cli_graph_cmd
);
2654 install_element(node
, &find_cmd
);
2656 install_element(node
, &config_write_cmd
);
2657 install_element(node
, &show_running_config_cmd
);
2659 install_element(node
, &autocomplete_cmd
);
2662 /* Initialize command interface. Install basic nodes and commands.
2664 * terminal = 0 -- vtysh / no logging, no config control
2665 * terminal = 1 -- normal daemon
2666 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2667 void cmd_init(int terminal
)
2669 struct utsname names
;
2671 if (array_size(node_names
) != NODE_TYPE_MAX
)
2672 assert(!"Update the CLI node description array!");
2677 varhandlers
= list_new();
2679 /* Allocate initial top vector of commands. */
2680 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2682 /* Default host value settings. */
2683 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2684 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2685 if ((strcmp(names
.domainname
, "(none)") == 0))
2686 host
.domainname
= NULL
;
2688 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2690 host
.domainname
= NULL
;
2692 host
.password
= NULL
;
2694 host
.logfile
= NULL
;
2696 host
.noconfig
= (terminal
< 0);
2698 host
.motd
= default_motd
;
2699 host
.motdfile
= NULL
;
2701 /* Install top nodes. */
2702 install_node(&view_node
, NULL
);
2703 install_node(&enable_node
, NULL
);
2704 install_node(&auth_node
, NULL
);
2705 install_node(&auth_enable_node
, NULL
);
2706 install_node(&config_node
, config_write_host
);
2708 /* Each node's basic commands. */
2709 install_element(VIEW_NODE
, &show_version_cmd
);
2710 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2711 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2714 install_element(VIEW_NODE
, &config_list_cmd
);
2715 install_element(VIEW_NODE
, &config_exit_cmd
);
2716 install_element(VIEW_NODE
, &config_quit_cmd
);
2717 install_element(VIEW_NODE
, &config_help_cmd
);
2718 install_element(VIEW_NODE
, &config_enable_cmd
);
2719 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2720 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2721 install_element(VIEW_NODE
, &show_logging_cmd
);
2722 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2723 install_element(VIEW_NODE
, &echo_cmd
);
2724 install_element(VIEW_NODE
, &autocomplete_cmd
);
2725 install_element(VIEW_NODE
, &find_cmd
);
2727 install_element(ENABLE_NODE
, &config_end_cmd
);
2728 install_element(ENABLE_NODE
, &config_disable_cmd
);
2729 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2730 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2731 install_element(ENABLE_NODE
, &config_write_cmd
);
2732 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2733 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2735 install_default(CONFIG_NODE
);
2738 workqueue_cmd_init();
2742 install_element(CONFIG_NODE
, &hostname_cmd
);
2743 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2744 install_element(CONFIG_NODE
, &domainname_cmd
);
2745 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2746 install_element(CONFIG_NODE
, &frr_version_defaults_cmd
);
2747 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2750 install_element(CONFIG_NODE
, &password_cmd
);
2751 install_element(CONFIG_NODE
, &no_password_cmd
);
2752 install_element(CONFIG_NODE
, &enable_password_cmd
);
2753 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2755 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2756 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2757 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2758 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2759 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2760 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2761 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2762 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2763 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2764 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2765 install_element(CONFIG_NODE
, &config_log_trap_cmd
);
2766 install_element(CONFIG_NODE
, &no_config_log_trap_cmd
);
2767 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2768 install_element(CONFIG_NODE
,
2769 &no_config_log_record_priority_cmd
);
2770 install_element(CONFIG_NODE
,
2771 &config_log_timestamp_precision_cmd
);
2772 install_element(CONFIG_NODE
,
2773 &no_config_log_timestamp_precision_cmd
);
2774 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2775 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2776 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2777 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2778 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2779 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2780 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2782 vrf_install_commands();
2786 grammar_sandbox_init();
2790 void cmd_terminate()
2792 struct cmd_node
*cmd_node
;
2795 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2796 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2797 // deleting the graph delets the cmd_element as
2799 graph_delete_graph(cmd_node
->cmdgraph
);
2800 vector_free(cmd_node
->cmd_vector
);
2801 hash_clean(cmd_node
->cmd_hash
, NULL
);
2802 hash_free(cmd_node
->cmd_hash
);
2803 cmd_node
->cmd_hash
= NULL
;
2806 vector_free(cmdvec
);
2811 XFREE(MTYPE_HOST
, host
.name
);
2812 if (host
.domainname
)
2813 XFREE(MTYPE_HOST
, host
.domainname
);
2815 XFREE(MTYPE_HOST
, host
.password
);
2816 if (host
.password_encrypt
)
2817 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2819 XFREE(MTYPE_HOST
, host
.enable
);
2820 if (host
.enable_encrypt
)
2821 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2823 XFREE(MTYPE_HOST
, host
.logfile
);
2825 XFREE(MTYPE_HOST
, host
.motdfile
);
2827 XFREE(MTYPE_HOST
, host
.config
);
2829 list_delete_and_null(&varhandlers
);