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,
88 "static ip", // IP_NODE,
90 "interface", // INTERFACE_NODE,
91 "nexthop-group", // NH_GROUP_NODE,
92 "zebra", // ZEBRA_NODE,
93 "table", // TABLE_NODE,
95 "ripng", // RIPNG_NODE,
96 "babel", // BABEL_NODE,
97 "eigrp", // EIGRP_NODE,
99 "bgp vpnv4", // BGP_VPNV4_NODE,
100 "bgp vpnv6", // BGP_VPNV6_NODE,
101 "bgp ipv4 unicast", // BGP_IPV4_NODE,
102 "bgp ipv4 multicast", // BGP_IPV4M_NODE,
103 "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE,
104 "bgp ipv6", // BGP_IPV6_NODE,
105 "bgp ipv6 multicast", // BGP_IPV6M_NODE,
106 "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE,
107 "bgp vrf policy", // BGP_VRF_POLICY_NODE,
108 "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE,
109 "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE,
110 "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE,
111 "rfp defaults", // RFP_DEFAULTS_NODE,
112 "bgp evpn", // BGP_EVPN_NODE,
113 "ospf", // OSPF_NODE,
114 "ospf6", // OSPF6_NODE,
116 "ldp ipv4", // LDP_IPV4_NODE,
117 "ldp ipv6", // LDP_IPV6_NODE,
118 "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE,
119 "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE,
120 "ldp l2vpn", // LDP_L2VPN_NODE,
121 "ldp", // LDP_PSEUDOWIRE_NODE,
122 "isis", // ISIS_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());
532 /* The following are all configuration commands that are not sent to
533 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
534 * we would always display 'log syslog informational' in the config
535 * which would cause other daemons to then switch to syslog when they
538 if (strcmp(zlog_default
->protoname
, "WATCHFRR")) {
540 if (host
.password_encrypt
)
541 vty_out(vty
, "password 8 %s\n",
542 host
.password_encrypt
);
543 if (host
.enable_encrypt
)
544 vty_out(vty
, "enable password 8 %s\n",
545 host
.enable_encrypt
);
548 vty_out(vty
, "password %s\n", host
.password
);
550 vty_out(vty
, "enable password %s\n",
554 if (zlog_default
->default_lvl
!= LOG_DEBUG
) {
556 "! N.B. The 'log trap' command is deprecated.\n");
557 vty_out(vty
, "log trap %s\n",
558 zlog_priority
[zlog_default
->default_lvl
]);
562 && (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
564 vty_out(vty
, "log file %s", host
.logfile
);
565 if (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
566 != zlog_default
->default_lvl
)
569 [zlog_default
->maxlvl
574 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
] != ZLOG_DISABLED
) {
575 vty_out(vty
, "log stdout");
576 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
]
577 != zlog_default
->default_lvl
)
580 [zlog_default
->maxlvl
581 [ZLOG_DEST_STDOUT
]]);
585 if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
586 vty_out(vty
, "no log monitor\n");
587 else if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]
588 != zlog_default
->default_lvl
)
589 vty_out(vty
, "log monitor %s\n",
590 zlog_priority
[zlog_default
->maxlvl
591 [ZLOG_DEST_MONITOR
]]);
593 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
) {
594 vty_out(vty
, "log syslog");
595 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
]
596 != zlog_default
->default_lvl
)
598 zlog_priority
[zlog_default
->maxlvl
599 [ZLOG_DEST_SYSLOG
]]);
603 if (zlog_default
->facility
!= LOG_DAEMON
)
604 vty_out(vty
, "log facility %s\n",
605 facility_name(zlog_default
->facility
));
607 if (zlog_default
->record_priority
== 1)
608 vty_out(vty
, "log record-priority\n");
610 if (zlog_default
->timestamp_precision
> 0)
611 vty_out(vty
, "log timestamp precision %d\n",
612 zlog_default
->timestamp_precision
);
615 vty_out(vty
, "service advanced-vty\n");
618 vty_out(vty
, "service password-encryption\n");
621 vty_out(vty
, "service terminal-length %d\n",
625 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
627 vty_out(vty
, "no banner motd\n");
630 if (debug_memstats_at_exit
)
631 vty_out(vty
, "!\ndebug memstats-at-exit\n");
636 /* Utility function for getting command graph. */
637 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
639 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
640 return cnode
->cmdgraph
;
643 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
645 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
646 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
652 * Compare function for cmd_token.
653 * Used with qsort to sort command completions.
655 static int compare_completions(const void *fst
, const void *snd
)
657 struct cmd_token
*first
= *(struct cmd_token
**)fst
,
658 *secnd
= *(struct cmd_token
**)snd
;
659 return strcmp(first
->text
, secnd
->text
);
663 * Takes a list of completions returned by command_complete,
664 * dedeuplicates them based on both text and description,
665 * sorts them, and returns them as a vector.
667 * @param completions linked list of cmd_token
668 * @return deduplicated and sorted vector with
670 vector
completions_to_vec(struct list
*completions
)
672 vector comps
= vector_init(VECTOR_MIN_SIZE
);
675 struct cmd_token
*token
, *cr
= NULL
;
676 unsigned int i
, exists
;
677 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
678 if (token
->type
== END_TKN
&& (cr
= token
))
681 // linear search for token in completions vector
683 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
684 struct cmd_token
*curr
= vector_slot(comps
, i
);
686 exists
= !strcmp(curr
->text
, token
->text
)
687 && !strcmp(curr
->desc
, token
->desc
);
689 exists
= !strcmp(curr
->text
, token
->text
);
690 #endif /* VTYSH_DEBUG */
694 vector_set(comps
, token
);
698 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
699 &compare_completions
);
701 // make <cr> the first element, if it is present
703 vector_set_index(comps
, vector_active(comps
), NULL
);
704 memmove(comps
->index
+ 1, comps
->index
,
705 (comps
->alloced
- 1) * sizeof(void *));
706 vector_set_index(comps
, 0, cr
);
712 * Generates a vector of cmd_token representing possible completions
713 * on the current input.
715 * @param vline the vectorized input line
716 * @param vty the vty with the node to match on
717 * @param status pointer to matcher status code
718 * @return vector of struct cmd_token * with possible completions
720 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
723 struct list
*completions
;
724 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
726 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
728 if (MATCHER_ERROR(rv
)) {
729 *status
= CMD_ERR_NO_MATCH
;
733 vector comps
= completions_to_vec(completions
);
734 list_delete_and_null(&completions
);
736 // set status code appropriately
737 switch (vector_active(comps
)) {
739 *status
= CMD_ERR_NO_MATCH
;
742 *status
= CMD_COMPLETE_FULL_MATCH
;
745 *status
= CMD_COMPLETE_LIST_MATCH
;
751 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
755 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
756 enum node_type onode
;
757 vector shifted_vline
;
761 vty
->node
= ENABLE_NODE
;
762 /* We can try it on enable node, cos' the vty is authenticated
765 shifted_vline
= vector_init(vector_count(vline
));
767 for (index
= 1; index
< vector_active(vline
); index
++) {
768 vector_set_index(shifted_vline
, index
- 1,
769 vector_lookup(vline
, index
));
772 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
774 vector_free(shifted_vline
);
779 return cmd_complete_command_real(vline
, vty
, status
);
782 static struct list
*varhandlers
= NULL
;
784 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
788 const struct cmd_variable_handler
*cvh
;
792 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
794 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
795 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
797 if (cvh
->varname
&& (!token
->varname
798 || strcmp(cvh
->varname
, token
->varname
)))
800 cvh
->completions(tmpcomps
, token
);
808 for (i
= vector_active(tmpcomps
); i
; i
--) {
809 char *item
= vector_slot(tmpcomps
, i
- 1);
810 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
811 vector_set(comps
, item
);
813 XFREE(MTYPE_COMPLETION
, item
);
815 vector_free(tmpcomps
);
818 #define AUTOCOMP_INDENT 5
820 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
823 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
824 int lc
= AUTOCOMP_INDENT
;
825 size_t cs
= AUTOCOMP_INDENT
;
827 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
828 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
829 char *item
= vector_slot(comps
, j
);
830 itemlen
= strlen(item
);
832 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
833 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
835 if (lc
+ itemlen
+ 1 >= cols
) {
836 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
837 AUTOCOMP_INDENT
, "");
838 lc
= AUTOCOMP_INDENT
;
841 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
844 XFREE(MTYPE_COMPLETION
, item
);
845 vector_set_index(comps
, j
, NULL
);
850 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
855 for (; cvh
->completions
; cvh
++)
856 listnode_add(varhandlers
, (void *)cvh
);
859 DEFUN_HIDDEN (autocomplete
,
861 "autocomplete TYPE TEXT VARNAME",
862 "Autocompletion handler (internal, for vtysh)\n"
865 "cmd_token->varname\n")
867 struct cmd_token tok
;
868 vector comps
= vector_init(32);
871 memset(&tok
, 0, sizeof(tok
));
872 tok
.type
= atoi(argv
[1]->arg
);
873 tok
.text
= argv
[2]->arg
;
874 tok
.varname
= argv
[3]->arg
;
875 if (!strcmp(tok
.varname
, "-"))
878 cmd_variable_complete(&tok
, NULL
, comps
);
880 for (i
= 0; i
< vector_active(comps
); i
++) {
881 char *text
= vector_slot(comps
, i
);
882 vty_out(vty
, "%s\n", text
);
883 XFREE(MTYPE_COMPLETION
, text
);
891 * Generate possible tab-completions for the given input. This function only
892 * returns results that would result in a valid command if used as Readline
893 * completions (as is the case in vtysh). For instance, if the passed vline ends
894 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
896 * @param vline vectorized input line
898 * @param status location to store matcher status code in
899 * @return set of valid strings for use with Readline as tab-completions.
902 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
905 int original_node
= vty
->node
;
906 vector input_line
= vector_init(vector_count(vline
));
908 // if the first token is 'do' we'll want to execute the command in the
910 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
911 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
913 // construct the input line we'll be matching on
914 unsigned int offset
= (do_shortcut
) ? 1 : 0;
915 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
916 vector_set_index(input_line
, index
,
917 vector_lookup(vline
, index
+ offset
));
919 // get token completions -- this is a copying operation
920 vector comps
= NULL
, initial_comps
;
921 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
923 if (!MATCHER_ERROR(*status
)) {
924 assert(initial_comps
);
925 // filter out everything that is not suitable for a
927 comps
= vector_init(VECTOR_MIN_SIZE
);
928 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
930 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
931 if (token
->type
== WORD_TKN
)
932 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
934 else if (IS_VARYING_TOKEN(token
->type
)) {
935 const char *ref
= vector_lookup(
936 vline
, vector_active(vline
) - 1);
937 cmd_variable_complete(token
, ref
, comps
);
940 vector_free(initial_comps
);
942 // since we filtered results, we need to re-set status code
943 switch (vector_active(comps
)) {
945 *status
= CMD_ERR_NO_MATCH
;
948 *status
= CMD_COMPLETE_FULL_MATCH
;
951 *status
= CMD_COMPLETE_LIST_MATCH
;
954 // copy completions text into an array of char*
955 ret
= XMALLOC(MTYPE_TMP
,
956 (vector_active(comps
) + 1) * sizeof(char *));
958 for (i
= 0; i
< vector_active(comps
); i
++) {
959 ret
[i
] = vector_slot(comps
, i
);
961 // set the last element to NULL, because this array is used in
962 // a Readline completion_generator function which expects NULL
963 // as a sentinel value
967 } else if (initial_comps
)
968 vector_free(initial_comps
);
970 // comps should always be null here
973 // free the adjusted input line
974 vector_free(input_line
);
976 // reset vty->node to its original value
977 vty
->node
= original_node
;
982 /* return parent node */
983 /* MUST eventually converge on CONFIG_NODE */
984 enum node_type
node_parent(enum node_type node
)
988 assert(node
> CONFIG_NODE
);
993 case BGP_FLOWSPECV4_NODE
:
994 case BGP_FLOWSPECV6_NODE
:
995 case BGP_VRF_POLICY_NODE
:
996 case BGP_VNC_DEFAULTS_NODE
:
997 case BGP_VNC_NVE_GROUP_NODE
:
998 case BGP_VNC_L2_GROUP_NODE
:
1000 case BGP_IPV4M_NODE
:
1001 case BGP_IPV4L_NODE
:
1003 case BGP_IPV6M_NODE
:
1005 case BGP_IPV6L_NODE
:
1008 case BGP_EVPN_VNI_NODE
:
1009 ret
= BGP_EVPN_NODE
;
1011 case KEYCHAIN_KEY_NODE
:
1012 ret
= KEYCHAIN_NODE
;
1014 case LINK_PARAMS_NODE
:
1015 ret
= INTERFACE_NODE
;
1021 case LDP_IPV4_IFACE_NODE
:
1022 ret
= LDP_IPV4_NODE
;
1024 case LDP_IPV6_IFACE_NODE
:
1025 ret
= LDP_IPV6_NODE
;
1027 case LDP_PSEUDOWIRE_NODE
:
1028 ret
= LDP_L2VPN_NODE
;
1038 /* Execute command by argument vline vector. */
1039 static int cmd_execute_command_real(vector vline
, enum filter_type filter
,
1041 const struct cmd_element
**cmd
)
1043 struct list
*argv_list
;
1044 enum matcher_rv status
;
1045 const struct cmd_element
*matched_element
= NULL
;
1047 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
1048 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
1051 *cmd
= matched_element
;
1053 // if matcher error, return corresponding CMD_ERR
1054 if (MATCHER_ERROR(status
)) {
1056 list_delete_and_null(&argv_list
);
1058 case MATCHER_INCOMPLETE
:
1059 return CMD_ERR_INCOMPLETE
;
1060 case MATCHER_AMBIGUOUS
:
1061 return CMD_ERR_AMBIGUOUS
;
1063 return CMD_ERR_NO_MATCH
;
1067 // build argv array from argv list
1068 struct cmd_token
**argv
= XMALLOC(
1069 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
1070 struct listnode
*ln
;
1071 struct cmd_token
*token
;
1073 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
1076 int argc
= argv_list
->count
;
1079 if (matched_element
->daemon
)
1080 ret
= CMD_SUCCESS_DAEMON
;
1082 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
1084 // delete list and cmd_token's in it
1085 list_delete_and_null(&argv_list
);
1086 XFREE(MTYPE_TMP
, argv
);
1092 * Execute a given command, handling things like "do ..." and checking
1093 * whether the given command might apply at a parent node if doesn't
1094 * apply for the current node.
1096 * @param vline Command line input, vector of char* where each element is
1098 * @param vty The vty context in which the command should be executed.
1099 * @param cmd Pointer where the struct cmd_element of the matched command
1100 * will be stored, if any. May be set to NULL if this info is
1102 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1103 * @return The status of the command that has been executed or an error code
1104 * as to why no command could be executed.
1106 int cmd_execute_command(vector vline
, struct vty
*vty
,
1107 const struct cmd_element
**cmd
, int vtysh
)
1109 int ret
, saved_ret
= 0;
1110 enum node_type onode
, try_node
;
1112 onode
= try_node
= vty
->node
;
1114 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1115 vector shifted_vline
;
1118 vty
->node
= ENABLE_NODE
;
1119 /* We can try it on enable node, cos' the vty is authenticated
1122 shifted_vline
= vector_init(vector_count(vline
));
1124 for (index
= 1; index
< vector_active(vline
); index
++)
1125 vector_set_index(shifted_vline
, index
- 1,
1126 vector_lookup(vline
, index
));
1128 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1131 vector_free(shifted_vline
);
1137 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
1142 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1143 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1144 /* This assumes all nodes above CONFIG_NODE are childs of
1146 while (vty
->node
> CONFIG_NODE
) {
1147 try_node
= node_parent(try_node
);
1148 vty
->node
= try_node
;
1149 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1151 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1152 || ret
== CMD_NOT_MY_INSTANCE
1153 || ret
== CMD_WARNING_CONFIG_FAILED
)
1156 /* no command succeeded, reset the vty to the original node */
1160 /* return command status for original node */
1165 * Execute a given command, matching it strictly against the current node.
1166 * This mode is used when reading config files.
1168 * @param vline Command line input, vector of char* where each element is
1170 * @param vty The vty context in which the command should be executed.
1171 * @param cmd Pointer where the struct cmd_element* of the matched command
1172 * will be stored, if any. May be set to NULL if this info is
1174 * @return The status of the command that has been executed or an error code
1175 * as to why no command could be executed.
1177 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1178 const struct cmd_element
**cmd
)
1180 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1184 * Parse one line of config, walking up the parse tree attempting to find a
1187 * @param vty The vty context in which the command should be executed.
1188 * @param cmd Pointer where the struct cmd_element* of the match command
1189 * will be stored, if any. May be set to NULL if this info is
1191 * @param use_daemon Boolean to control whether or not we match on
1192 * CMD_SUCCESS_DAEMON
1194 * @return The status of the command that has been executed or an error code
1195 * as to why no command could be executed.
1197 int command_config_read_one_line(struct vty
*vty
,
1198 const struct cmd_element
**cmd
, int use_daemon
)
1204 vline
= cmd_make_strvec(vty
->buf
);
1206 /* In case of comment line */
1210 /* Execute configuration command : this is strict match */
1211 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1213 // Climb the tree and try the command again at each node
1214 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1215 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1216 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1217 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1218 && vty
->node
!= CONFIG_NODE
) {
1220 saved_node
= vty
->node
;
1222 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1223 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1224 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1225 && vty
->node
> CONFIG_NODE
) {
1226 vty
->node
= node_parent(vty
->node
);
1227 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1230 // If climbing the tree did not work then ignore the command and
1231 // stay at the same node
1232 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1233 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1234 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1235 vty
->node
= saved_node
;
1239 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
)
1240 memcpy(vty
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1242 cmd_free_strvec(vline
);
1247 /* Configuration make from file. */
1248 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1250 int ret
, error_ret
= 0;
1253 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1257 ret
= command_config_read_one_line(vty
, NULL
, 0);
1259 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1260 && ret
!= CMD_ERR_NOTHING_TODO
)
1271 /* Configuration from terminal */
1272 DEFUN (config_terminal
,
1273 config_terminal_cmd
,
1274 "configure terminal",
1275 "Configuration from vty interface\n"
1276 "Configuration terminal\n")
1278 if (vty_config_lock(vty
))
1279 vty
->node
= CONFIG_NODE
;
1281 vty_out(vty
, "VTY configuration is locked by other VTY\n");
1282 return CMD_WARNING_CONFIG_FAILED
;
1287 /* Enable command */
1291 "Turn on privileged mode command\n")
1293 /* If enable password is NULL, change to ENABLE_NODE */
1294 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1295 || vty
->type
== VTY_SHELL_SERV
)
1296 vty
->node
= ENABLE_NODE
;
1298 vty
->node
= AUTH_ENABLE_NODE
;
1303 /* Disable command */
1307 "Turn off privileged mode command\n")
1309 if (vty
->node
== ENABLE_NODE
)
1310 vty
->node
= VIEW_NODE
;
1314 /* Down vty node level. */
1318 "Exit current mode and down to previous mode\n")
1324 void cmd_exit(struct vty
*vty
)
1326 switch (vty
->node
) {
1332 vty
->status
= VTY_CLOSE
;
1335 vty
->node
= ENABLE_NODE
;
1336 vty_config_unlock(vty
);
1338 case INTERFACE_NODE
:
1340 case LOGICALROUTER_NODE
:
1352 case LDP_L2VPN_NODE
:
1358 vty
->node
= CONFIG_NODE
;
1361 case BGP_IPV4M_NODE
:
1362 case BGP_IPV4L_NODE
:
1363 case BGP_VPNV4_NODE
:
1364 case BGP_VPNV6_NODE
:
1365 case BGP_FLOWSPECV4_NODE
:
1366 case BGP_FLOWSPECV6_NODE
:
1367 case BGP_VRF_POLICY_NODE
:
1368 case BGP_VNC_DEFAULTS_NODE
:
1369 case BGP_VNC_NVE_GROUP_NODE
:
1370 case BGP_VNC_L2_GROUP_NODE
:
1372 case BGP_IPV6M_NODE
:
1374 case BGP_IPV6L_NODE
:
1375 vty
->node
= BGP_NODE
;
1377 case BGP_EVPN_VNI_NODE
:
1378 vty
->node
= BGP_EVPN_NODE
;
1382 vty
->node
= LDP_NODE
;
1384 case LDP_IPV4_IFACE_NODE
:
1385 vty
->node
= LDP_IPV4_NODE
;
1387 case LDP_IPV6_IFACE_NODE
:
1388 vty
->node
= LDP_IPV6_NODE
;
1390 case LDP_PSEUDOWIRE_NODE
:
1391 vty
->node
= LDP_L2VPN_NODE
;
1393 case KEYCHAIN_KEY_NODE
:
1394 vty
->node
= KEYCHAIN_NODE
;
1396 case LINK_PARAMS_NODE
:
1397 vty
->node
= INTERFACE_NODE
;
1408 "Exit current mode and down to previous mode\n")
1410 return config_exit(self
, vty
, argc
, argv
);
1414 /* End of configuration. */
1418 "End current mode and change to enable mode.\n")
1420 switch (vty
->node
) {
1423 /* Nothing to do. */
1426 case INTERFACE_NODE
:
1428 case LOGICALROUTER_NODE
:
1437 case BGP_VRF_POLICY_NODE
:
1438 case BGP_VNC_DEFAULTS_NODE
:
1439 case BGP_VNC_NVE_GROUP_NODE
:
1440 case BGP_VNC_L2_GROUP_NODE
:
1441 case BGP_VPNV4_NODE
:
1442 case BGP_VPNV6_NODE
:
1443 case BGP_FLOWSPECV4_NODE
:
1444 case BGP_FLOWSPECV6_NODE
:
1446 case BGP_IPV4M_NODE
:
1447 case BGP_IPV4L_NODE
:
1449 case BGP_IPV6M_NODE
:
1451 case BGP_EVPN_VNI_NODE
:
1452 case BGP_IPV6L_NODE
:
1460 case LDP_IPV4_IFACE_NODE
:
1461 case LDP_IPV6_IFACE_NODE
:
1462 case LDP_L2VPN_NODE
:
1463 case LDP_PSEUDOWIRE_NODE
:
1466 case KEYCHAIN_KEY_NODE
:
1468 case LINK_PARAMS_NODE
:
1469 vty_config_unlock(vty
);
1470 vty
->node
= ENABLE_NODE
;
1479 DEFUN (show_version
,
1483 "Displays zebra version\n")
1485 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1486 cmd_hostname_get() ? cmd_hostname_get() : "");
1487 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1488 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1493 /* "Set" version ... ignore version tags */
1494 DEFUN (frr_version_defaults
,
1495 frr_version_defaults_cmd
,
1496 "frr <version|defaults> LINE...",
1497 "FRRouting global parameters\n"
1498 "version configuration was written by\n"
1499 "set of configuration defaults used\n"
1505 /* Help display function for all node. */
1509 "Description of the interactive help system\n")
1512 "Quagga VTY provides advanced help feature. When you need help,\n\
1513 anytime at the command line please press '?'.\n\
1515 If nothing matches, the help list will be empty and you must backup\n\
1516 until entering a '?' shows the available options.\n\
1517 Two styles of help are provided:\n\
1518 1. Full help is available when you are ready to enter a\n\
1519 command argument (e.g. 'show ?') and describes each possible\n\
1521 2. Partial help is provided when an abbreviated argument is entered\n\
1522 and you want to know what arguments match the input\n\
1523 (e.g. 'show me?'.)\n\n");
1527 static void permute(struct graph_node
*start
, struct vty
*vty
)
1529 static struct list
*position
= NULL
;
1531 position
= list_new();
1533 struct cmd_token
*stok
= start
->data
;
1534 struct graph_node
*gnn
;
1535 struct listnode
*ln
;
1538 listnode_add(position
, start
);
1539 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1540 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1541 struct cmd_token
*tok
= gn
->data
;
1542 if (tok
->attr
== CMD_ATTR_HIDDEN
1543 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1545 else if (tok
->type
== END_TKN
|| gn
== start
) {
1547 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1548 struct cmd_token
*tt
= gnn
->data
;
1549 if (tt
->type
< SPECIAL_TKN
)
1550 vty_out(vty
, " %s", tt
->text
);
1553 vty_out(vty
, "...");
1557 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1558 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1567 list_delete_node(position
, listtail(position
));
1570 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1572 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1575 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1577 /* loop over all commands at this node */
1578 struct cmd_element
*element
= NULL
;
1579 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1581 if ((element
= vector_slot(node
->cmd_vector
, i
))
1582 && element
->attr
!= CMD_ATTR_DEPRECATED
1583 && element
->attr
!= CMD_ATTR_HIDDEN
)
1584 vty_out(vty
, " %s\n", element
->string
);
1589 /* Help display function for all node. */
1592 "list [permutations]",
1593 "Print command list\n"
1594 "Print all possible command permutations\n")
1596 return cmd_list_cmds(vty
, argc
== 2);
1599 DEFUN (show_commandtree
,
1600 show_commandtree_cmd
,
1601 "show commandtree [permutations]",
1603 "Show command tree\n"
1604 "Permutations that we are interested in\n")
1606 return cmd_list_cmds(vty
, argc
== 3);
1609 DEFUN_HIDDEN(show_cli_graph
,
1614 "Dump current command space as DOT graph\n")
1616 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1617 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1619 vty_out(vty
, "%s\n", dot
);
1620 XFREE(MTYPE_TMP
, dot
);
1624 static int vty_write_config(struct vty
*vty
)
1627 struct cmd_node
*node
;
1632 if (vty
->type
== VTY_TERM
) {
1633 vty_out(vty
, "\nCurrent configuration:\n");
1634 vty_out(vty
, "!\n");
1637 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1638 vty_out(vty
, "frr defaults %s\n", DFLT_NAME
);
1639 vty_out(vty
, "!\n");
1641 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1642 if ((node
= vector_slot(cmdvec
, i
)) && node
->func
1643 && (node
->vtysh
|| vty
->type
!= VTY_SHELL
)) {
1644 if ((*node
->func
)(vty
))
1645 vty_out(vty
, "!\n");
1648 if (vty
->type
== VTY_TERM
) {
1649 vty_out(vty
, "end\n");
1655 static int file_write_config(struct vty
*vty
)
1658 char *config_file
, *slash
;
1659 char *config_file_tmp
= NULL
;
1660 char *config_file_sav
= NULL
;
1661 int ret
= CMD_WARNING
;
1662 struct vty
*file_vty
;
1663 struct stat conf_stat
;
1668 /* Check and see if we are operating under vtysh configuration */
1669 if (host
.config
== NULL
) {
1671 "Can't save to configuration file, using vtysh.\n");
1676 config_file
= host
.config
;
1679 #define O_DIRECTORY 0
1681 slash
= strrchr(config_file
, '/');
1683 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1684 config_dir
[slash
- config_file
] = '\0';
1685 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1686 XFREE(MTYPE_TMP
, config_dir
);
1688 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1689 /* if dirfd is invalid, directory sync fails, but we're still OK */
1691 config_file_sav
= XMALLOC(
1692 MTYPE_TMP
, strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1);
1693 strcpy(config_file_sav
, config_file
);
1694 strcat(config_file_sav
, CONF_BACKUP_EXT
);
1697 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1698 sprintf(config_file_tmp
, "%s.XXXXXX", config_file
);
1700 /* Open file to configuration write. */
1701 fd
= mkstemp(config_file_tmp
);
1703 vty_out(vty
, "Can't open configuration file %s.\n",
1707 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1708 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1709 config_file_tmp
, safe_strerror(errno
), errno
);
1713 /* Make vty for configuration file. */
1714 file_vty
= vty_new();
1716 file_vty
->type
= VTY_FILE
;
1718 /* Config file header print. */
1719 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1720 vty_time_print(file_vty
, 1);
1721 vty_out(file_vty
, "!\n");
1722 vty_write_config(file_vty
);
1723 vty_close(file_vty
);
1725 if (stat(config_file
, &conf_stat
) >= 0) {
1726 if (unlink(config_file_sav
) != 0)
1727 if (errno
!= ENOENT
) {
1729 "Can't unlink backup configuration file %s.\n",
1733 if (link(config_file
, config_file_sav
) != 0) {
1735 "Can't backup old configuration file %s.\n",
1742 if (rename(config_file_tmp
, config_file
) != 0) {
1743 vty_out(vty
, "Can't save configuration file %s.\n",
1750 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1754 if (ret
!= CMD_SUCCESS
)
1755 unlink(config_file_tmp
);
1758 XFREE(MTYPE_TMP
, config_file_tmp
);
1759 XFREE(MTYPE_TMP
, config_file_sav
);
1763 /* Write current configuration into file. */
1765 DEFUN (config_write
,
1767 "write [<file|memory|terminal>]",
1768 "Write running configuration to memory, network, or terminal\n"
1769 "Write to configuration file\n"
1770 "Write configuration currently in memory\n"
1771 "Write configuration to terminal\n")
1773 const int idx_type
= 1;
1775 // if command was 'write terminal' or 'write memory'
1776 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1777 return vty_write_config(vty
);
1780 return file_write_config(vty
);
1783 /* ALIAS_FIXME for 'write <terminal|memory>' */
1784 DEFUN (show_running_config
,
1785 show_running_config_cmd
,
1786 "show running-config",
1788 "running configuration (same as write terminal)\n")
1790 return vty_write_config(vty
);
1793 /* ALIAS_FIXME for 'write file' */
1794 DEFUN (copy_runningconf_startupconf
,
1795 copy_runningconf_startupconf_cmd
,
1796 "copy running-config startup-config",
1797 "Copy configuration\n"
1798 "Copy running config to... \n"
1799 "Copy running config to startup config (same as write file/memory)\n")
1801 return file_write_config(vty
);
1805 /* Write startup configuration into the terminal. */
1806 DEFUN (show_startup_config
,
1807 show_startup_config_cmd
,
1808 "show startup-config",
1810 "Contents of startup configuration\n")
1817 if (host
.config
== NULL
)
1820 confp
= fopen(host
.config
, "r");
1821 if (confp
== NULL
) {
1822 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1823 host
.config
, safe_strerror(errno
));
1827 while (fgets(buf
, BUFSIZ
, confp
)) {
1830 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1834 vty_out(vty
, "%s\n", buf
);
1842 int cmd_domainname_set(const char *domainname
)
1844 XFREE(MTYPE_HOST
, host
.domainname
);
1845 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1849 /* Hostname configuration */
1850 DEFUN(config_domainname
,
1853 "Set system's domain name\n"
1854 "This system's domain name\n")
1856 struct cmd_token
*word
= argv
[1];
1858 if (!isalpha((int)word
->arg
[0])) {
1859 vty_out(vty
, "Please specify string starting with alphabet\n");
1860 return CMD_WARNING_CONFIG_FAILED
;
1863 return cmd_domainname_set(word
->arg
);
1866 DEFUN(config_no_domainname
,
1868 "no domainname [DOMAINNAME]",
1870 "Reset system's domain name\n"
1871 "domain name of this router\n")
1873 return cmd_domainname_set(NULL
);
1876 int cmd_hostname_set(const char *hostname
)
1878 XFREE(MTYPE_HOST
, host
.name
);
1879 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1883 /* Hostname configuration */
1884 DEFUN (config_hostname
,
1887 "Set system's network name\n"
1888 "This system's network name\n")
1890 struct cmd_token
*word
= argv
[1];
1892 if (!isalnum((int)word
->arg
[0])) {
1893 vty_out(vty
, "Please specify string starting with alphabet\n");
1894 return CMD_WARNING_CONFIG_FAILED
;
1897 return cmd_hostname_set(word
->arg
);
1900 DEFUN (config_no_hostname
,
1902 "no hostname [HOSTNAME]",
1904 "Reset system's network name\n"
1905 "Host name of this router\n")
1907 return cmd_hostname_set(NULL
);
1910 /* VTY interface password set. */
1911 DEFUN (config_password
,
1913 "password [(8-8)] WORD",
1914 "Modify the terminal connection password\n"
1915 "Specifies a HIDDEN password will follow\n"
1916 "The password string\n")
1920 if (argc
== 3) // '8' was specified
1923 XFREE(MTYPE_HOST
, host
.password
);
1924 host
.password
= NULL
;
1925 if (host
.password_encrypt
)
1926 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1927 host
.password_encrypt
=
1928 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1932 if (!isalnum((int)argv
[idx_8
]->arg
[0])) {
1934 "Please specify string starting with alphanumeric\n");
1935 return CMD_WARNING_CONFIG_FAILED
;
1939 XFREE(MTYPE_HOST
, host
.password
);
1940 host
.password
= NULL
;
1943 if (host
.password_encrypt
)
1944 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1945 host
.password_encrypt
=
1946 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1948 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1953 /* VTY interface password delete. */
1954 DEFUN (no_config_password
,
1958 "Modify the terminal connection password\n")
1960 bool warned
= false;
1962 if (host
.password
) {
1963 if (!vty_shell_serv(vty
)) {
1965 "Please be aware that removing the password is "
1966 "a security risk and you should think twice "
1967 "about this command\n");
1970 XFREE(MTYPE_HOST
, host
.password
);
1972 host
.password
= NULL
;
1974 if (host
.password_encrypt
) {
1975 if (!warned
&& !vty_shell_serv(vty
))
1977 "Please be aware that removing the password is "
1978 "a security risk and you should think twice "
1979 "about this command\n");
1980 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1982 host
.password_encrypt
= NULL
;
1987 /* VTY enable password set. */
1988 DEFUN (config_enable_password
,
1989 enable_password_cmd
,
1990 "enable password [(8-8)] WORD",
1991 "Modify enable password parameters\n"
1992 "Assign the privileged level password\n"
1993 "Specifies a HIDDEN password will follow\n"
1994 "The HIDDEN 'enable' password string\n")
1999 /* Crypt type is specified. */
2001 if (argv
[idx_8
]->arg
[0] == '8') {
2003 XFREE(MTYPE_HOST
, host
.enable
);
2006 if (host
.enable_encrypt
)
2007 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2008 host
.enable_encrypt
=
2009 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2013 vty_out(vty
, "Unknown encryption type.\n");
2014 return CMD_WARNING_CONFIG_FAILED
;
2018 if (!isalnum((int)argv
[idx_8
]->arg
[0])) {
2020 "Please specify string starting with alphanumeric\n");
2021 return CMD_WARNING_CONFIG_FAILED
;
2025 XFREE(MTYPE_HOST
, host
.enable
);
2028 /* Plain password input. */
2030 if (host
.enable_encrypt
)
2031 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2032 host
.enable_encrypt
=
2033 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2035 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2040 /* VTY enable password delete. */
2041 DEFUN (no_config_enable_password
,
2042 no_enable_password_cmd
,
2043 "no enable password",
2045 "Modify enable password parameters\n"
2046 "Assign the privileged level password\n")
2048 bool warned
= false;
2051 if (!vty_shell_serv(vty
)) {
2053 "Please be aware that removing the password is "
2054 "a security risk and you should think twice "
2055 "about this command\n");
2058 XFREE(MTYPE_HOST
, host
.enable
);
2062 if (host
.enable_encrypt
) {
2063 if (!warned
&& !vty_shell_serv(vty
))
2065 "Please be aware that removing the password is "
2066 "a security risk and you should think twice "
2067 "about this command\n");
2068 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2070 host
.enable_encrypt
= NULL
;
2075 DEFUN (service_password_encrypt
,
2076 service_password_encrypt_cmd
,
2077 "service password-encryption",
2078 "Set up miscellaneous service\n"
2079 "Enable encrypted passwords\n")
2086 if (host
.password
) {
2087 if (host
.password_encrypt
)
2088 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2089 host
.password_encrypt
=
2090 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2093 if (host
.enable_encrypt
)
2094 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2095 host
.enable_encrypt
=
2096 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2102 DEFUN (no_service_password_encrypt
,
2103 no_service_password_encrypt_cmd
,
2104 "no service password-encryption",
2106 "Set up miscellaneous service\n"
2107 "Enable encrypted passwords\n")
2114 if (host
.password_encrypt
)
2115 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2116 host
.password_encrypt
= NULL
;
2118 if (host
.enable_encrypt
)
2119 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2120 host
.enable_encrypt
= NULL
;
2125 DEFUN (config_terminal_length
,
2126 config_terminal_length_cmd
,
2127 "terminal length (0-512)",
2128 "Set terminal line parameters\n"
2129 "Set number of lines on a screen\n"
2130 "Number of lines on screen (0 for no pausing)\n")
2134 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2139 DEFUN (config_terminal_no_length
,
2140 config_terminal_no_length_cmd
,
2141 "terminal no length",
2142 "Set terminal line parameters\n"
2144 "Set number of lines on a screen\n")
2150 DEFUN (service_terminal_length
,
2151 service_terminal_length_cmd
,
2152 "service terminal-length (0-512)",
2153 "Set up miscellaneous service\n"
2154 "System wide terminal length configuration\n"
2155 "Number of lines of VTY (0 means no line control)\n")
2159 host
.lines
= atoi(argv
[idx_number
]->arg
);
2164 DEFUN (no_service_terminal_length
,
2165 no_service_terminal_length_cmd
,
2166 "no service terminal-length [(0-512)]",
2168 "Set up miscellaneous service\n"
2169 "System wide terminal length configuration\n"
2170 "Number of lines of VTY (0 means no line control)\n")
2176 DEFUN_HIDDEN (do_echo
,
2179 "Echo a message back to the vty\n"
2180 "The message to echo\n")
2184 vty_out(vty
, "%s\n",
2185 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2187 XFREE(MTYPE_TMP
, message
);
2191 DEFUN (config_logmsg
,
2193 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2194 "Send a message to enabled logging destinations\n"
2196 "The message to send\n")
2198 int idx_log_level
= 1;
2199 int idx_message
= 2;
2203 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2204 return CMD_ERR_NO_MATCH
;
2207 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2209 XFREE(MTYPE_TMP
, message
);
2214 DEFUN (show_logging
,
2218 "Show current logging configuration\n")
2220 struct zlog
*zl
= zlog_default
;
2222 vty_out(vty
, "Syslog logging: ");
2223 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
2224 vty_out(vty
, "disabled");
2226 vty_out(vty
, "level %s, facility %s, ident %s",
2227 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
2228 facility_name(zl
->facility
), zl
->ident
);
2231 vty_out(vty
, "Stdout logging: ");
2232 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
2233 vty_out(vty
, "disabled");
2235 vty_out(vty
, "level %s",
2236 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
2239 vty_out(vty
, "Monitor logging: ");
2240 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
2241 vty_out(vty
, "disabled");
2243 vty_out(vty
, "level %s",
2244 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
2247 vty_out(vty
, "File logging: ");
2248 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
2249 vty_out(vty
, "disabled");
2251 vty_out(vty
, "level %s, filename %s",
2252 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
2256 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
2257 vty_out(vty
, "Record priority: %s\n",
2258 (zl
->record_priority
? "enabled" : "disabled"));
2259 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2264 DEFUN (config_log_stdout
,
2265 config_log_stdout_cmd
,
2266 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2268 "Set stdout logging level\n"
2271 int idx_log_level
= 2;
2273 if (argc
== idx_log_level
) {
2274 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2279 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2280 return CMD_ERR_NO_MATCH
;
2281 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2285 DEFUN (no_config_log_stdout
,
2286 no_config_log_stdout_cmd
,
2287 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2290 "Cancel logging to stdout\n"
2293 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2297 DEFUN (config_log_monitor
,
2298 config_log_monitor_cmd
,
2299 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2301 "Set terminal line (monitor) logging level\n"
2304 int idx_log_level
= 2;
2306 if (argc
== idx_log_level
) {
2307 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2312 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2313 return CMD_ERR_NO_MATCH
;
2314 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2318 DEFUN (no_config_log_monitor
,
2319 no_config_log_monitor_cmd
,
2320 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2323 "Disable terminal line (monitor) logging\n"
2326 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2330 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2334 const char *fullpath
;
2336 /* Path detection. */
2337 if (!IS_DIRECTORY_SEP(*fname
)) {
2338 char cwd
[MAXPATHLEN
+ 1];
2339 cwd
[MAXPATHLEN
] = '\0';
2341 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2342 zlog_err("config_log_file: Unable to alloc mem!");
2343 return CMD_WARNING_CONFIG_FAILED
;
2346 if ((p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2))
2348 zlog_err("config_log_file: Unable to alloc mem!");
2349 return CMD_WARNING_CONFIG_FAILED
;
2351 sprintf(p
, "%s/%s", cwd
, fname
);
2356 ret
= zlog_set_file(fullpath
, loglevel
);
2359 XFREE(MTYPE_TMP
, p
);
2362 vty_out(vty
, "can't open logfile %s\n", fname
);
2363 return CMD_WARNING_CONFIG_FAILED
;
2367 XFREE(MTYPE_HOST
, host
.logfile
);
2369 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2371 #if defined(HAVE_CUMULUS)
2372 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2373 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2378 DEFUN (config_log_file
,
2379 config_log_file_cmd
,
2380 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2383 "Logging filename\n"
2386 int idx_filename
= 2;
2387 int idx_log_levels
= 3;
2390 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2392 return CMD_ERR_NO_MATCH
;
2393 return set_log_file(vty
, argv
[idx_filename
]->arg
, level
);
2395 return set_log_file(vty
, argv
[idx_filename
]->arg
,
2396 zlog_default
->default_lvl
);
2399 static void disable_log_file(void)
2404 XFREE(MTYPE_HOST
, host
.logfile
);
2406 host
.logfile
= NULL
;
2409 DEFUN (no_config_log_file
,
2410 no_config_log_file_cmd
,
2411 "no log file [FILENAME [LEVEL]]",
2414 "Cancel logging to file\n"
2415 "Logging file name\n"
2422 DEFUN (config_log_syslog
,
2423 config_log_syslog_cmd
,
2424 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2426 "Set syslog logging level\n"
2429 int idx_log_levels
= 2;
2435 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2437 return CMD_ERR_NO_MATCH
;
2438 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2441 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2446 DEFUN (no_config_log_syslog
,
2447 no_config_log_syslog_cmd
,
2448 "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>]",
2451 "Cancel logging to syslog\n"
2455 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2459 DEFUN (config_log_facility
,
2460 config_log_facility_cmd
,
2461 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2463 "Facility parameter for syslog messages\n"
2467 int facility
= facility_match(argv
[idx_target
]->arg
);
2469 zlog_default
->facility
= facility
;
2473 DEFUN (no_config_log_facility
,
2474 no_config_log_facility_cmd
,
2475 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2478 "Reset syslog facility to default (daemon)\n"
2481 zlog_default
->facility
= LOG_DAEMON
;
2486 config_log_trap
, config_log_trap_cmd
,
2487 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2489 "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC
)
2494 if ((new_level
= level_match(argv
[2]->arg
)) == ZLOG_DISABLED
)
2495 return CMD_ERR_NO_MATCH
;
2497 zlog_default
->default_lvl
= new_level
;
2498 for (i
= 0; i
< ZLOG_NUM_DESTS
; i
++)
2499 if (zlog_default
->maxlvl
[i
] != ZLOG_DISABLED
)
2500 zlog_default
->maxlvl
[i
] = new_level
;
2505 no_config_log_trap
, no_config_log_trap_cmd
,
2506 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2509 "Permit all logging information\n" LOG_LEVEL_DESC
)
2511 zlog_default
->default_lvl
= LOG_DEBUG
;
2515 DEFUN (config_log_record_priority
,
2516 config_log_record_priority_cmd
,
2517 "log record-priority",
2519 "Log the priority of the message within the message\n")
2521 zlog_default
->record_priority
= 1;
2525 DEFUN (no_config_log_record_priority
,
2526 no_config_log_record_priority_cmd
,
2527 "no log record-priority",
2530 "Do not log the priority of the message within the message\n")
2532 zlog_default
->record_priority
= 0;
2536 DEFUN (config_log_timestamp_precision
,
2537 config_log_timestamp_precision_cmd
,
2538 "log timestamp precision (0-6)",
2540 "Timestamp configuration\n"
2541 "Set the timestamp precision\n"
2542 "Number of subsecond digits\n")
2545 zlog_default
->timestamp_precision
=
2546 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2550 DEFUN (no_config_log_timestamp_precision
,
2551 no_config_log_timestamp_precision_cmd
,
2552 "no log timestamp precision",
2555 "Timestamp configuration\n"
2556 "Reset the timestamp precision to the default value of 0\n")
2558 zlog_default
->timestamp_precision
= 0;
2562 DEFUN (debug_memstats
,
2564 "[no] debug memstats-at-exit",
2567 "Print memory type statistics at exit\n")
2569 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2573 int cmd_banner_motd_file(const char *file
)
2575 int success
= CMD_SUCCESS
;
2580 rpath
= realpath(file
, p
);
2582 return CMD_ERR_NO_FILE
;
2583 in
= strstr(rpath
, SYSCONFDIR
);
2586 XFREE(MTYPE_HOST
, host
.motdfile
);
2587 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2589 success
= CMD_WARNING_CONFIG_FAILED
;
2594 DEFUN (banner_motd_file
,
2595 banner_motd_file_cmd
,
2596 "banner motd file FILE",
2599 "Banner from a file\n"
2603 const char *filename
= argv
[idx_file
]->arg
;
2604 int cmd
= cmd_banner_motd_file(filename
);
2606 if (cmd
== CMD_ERR_NO_FILE
)
2607 vty_out(vty
, "%s does not exist", filename
);
2608 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2609 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2614 DEFUN (banner_motd_default
,
2615 banner_motd_default_cmd
,
2616 "banner motd default",
2617 "Set banner string\n"
2618 "Strings for motd\n"
2621 host
.motd
= default_motd
;
2625 DEFUN (no_banner_motd
,
2629 "Set banner string\n"
2630 "Strings for motd\n")
2634 XFREE(MTYPE_HOST
, host
.motdfile
);
2635 host
.motdfile
= NULL
;
2642 "Find CLI command containing text\n"
2643 "Text to search for\n")
2645 char *text
= argv_concat(argv
, argc
, 1);
2646 const struct cmd_node
*node
;
2647 const struct cmd_element
*cli
;
2650 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2651 node
= vector_slot(cmdvec
, i
);
2654 clis
= node
->cmd_vector
;
2655 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2656 cli
= vector_slot(clis
, j
);
2657 if (strcasestr(cli
->string
, text
))
2658 vty_out(vty
, " (%s) %s\n",
2659 node_names
[node
->node
], cli
->string
);
2663 XFREE(MTYPE_TMP
, text
);
2668 /* Set config filename. Called from vty.c */
2669 void host_config_set(const char *filename
)
2672 XFREE(MTYPE_HOST
, host
.config
);
2673 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2676 const char *host_config_get(void)
2681 void install_default(enum node_type node
)
2683 install_element(node
, &config_exit_cmd
);
2684 install_element(node
, &config_quit_cmd
);
2685 install_element(node
, &config_end_cmd
);
2686 install_element(node
, &config_help_cmd
);
2687 install_element(node
, &config_list_cmd
);
2688 install_element(node
, &show_cli_graph_cmd
);
2689 install_element(node
, &find_cmd
);
2691 install_element(node
, &config_write_cmd
);
2692 install_element(node
, &show_running_config_cmd
);
2694 install_element(node
, &autocomplete_cmd
);
2697 /* Initialize command interface. Install basic nodes and commands.
2699 * terminal = 0 -- vtysh / no logging, no config control
2700 * terminal = 1 -- normal daemon
2701 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2702 void cmd_init(int terminal
)
2704 struct utsname names
;
2706 if (array_size(node_names
) != NODE_TYPE_MAX
)
2707 assert(!"Update the CLI node description array!");
2712 varhandlers
= list_new();
2714 /* Allocate initial top vector of commands. */
2715 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2717 /* Default host value settings. */
2718 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2719 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2720 if ((strcmp(names
.domainname
, "(none)") == 0))
2721 host
.domainname
= NULL
;
2723 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2725 host
.domainname
= NULL
;
2727 host
.password
= NULL
;
2729 host
.logfile
= NULL
;
2731 host
.noconfig
= (terminal
< 0);
2733 host
.motd
= default_motd
;
2734 host
.motdfile
= NULL
;
2736 /* Install top nodes. */
2737 install_node(&view_node
, NULL
);
2738 install_node(&enable_node
, NULL
);
2739 install_node(&auth_node
, NULL
);
2740 install_node(&auth_enable_node
, NULL
);
2741 install_node(&config_node
, config_write_host
);
2743 /* Each node's basic commands. */
2744 install_element(VIEW_NODE
, &show_version_cmd
);
2745 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2746 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2749 install_element(VIEW_NODE
, &config_list_cmd
);
2750 install_element(VIEW_NODE
, &config_exit_cmd
);
2751 install_element(VIEW_NODE
, &config_quit_cmd
);
2752 install_element(VIEW_NODE
, &config_help_cmd
);
2753 install_element(VIEW_NODE
, &config_enable_cmd
);
2754 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2755 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2756 install_element(VIEW_NODE
, &show_logging_cmd
);
2757 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2758 install_element(VIEW_NODE
, &echo_cmd
);
2759 install_element(VIEW_NODE
, &autocomplete_cmd
);
2760 install_element(VIEW_NODE
, &find_cmd
);
2762 install_element(ENABLE_NODE
, &config_end_cmd
);
2763 install_element(ENABLE_NODE
, &config_disable_cmd
);
2764 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2765 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2766 install_element(ENABLE_NODE
, &config_write_cmd
);
2767 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2768 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2770 install_default(CONFIG_NODE
);
2773 workqueue_cmd_init();
2777 install_element(CONFIG_NODE
, &hostname_cmd
);
2778 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2779 install_element(CONFIG_NODE
, &domainname_cmd
);
2780 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2781 install_element(CONFIG_NODE
, &frr_version_defaults_cmd
);
2782 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2785 install_element(CONFIG_NODE
, &password_cmd
);
2786 install_element(CONFIG_NODE
, &no_password_cmd
);
2787 install_element(CONFIG_NODE
, &enable_password_cmd
);
2788 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2790 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2791 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2792 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2793 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2794 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2795 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2796 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2797 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2798 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2799 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2800 install_element(CONFIG_NODE
, &config_log_trap_cmd
);
2801 install_element(CONFIG_NODE
, &no_config_log_trap_cmd
);
2802 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2803 install_element(CONFIG_NODE
,
2804 &no_config_log_record_priority_cmd
);
2805 install_element(CONFIG_NODE
,
2806 &config_log_timestamp_precision_cmd
);
2807 install_element(CONFIG_NODE
,
2808 &no_config_log_timestamp_precision_cmd
);
2809 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2810 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2811 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2812 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2813 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2814 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2815 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2817 vrf_install_commands();
2821 grammar_sandbox_init();
2825 void cmd_terminate()
2827 struct cmd_node
*cmd_node
;
2830 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2831 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2832 // deleting the graph delets the cmd_element as
2834 graph_delete_graph(cmd_node
->cmdgraph
);
2835 vector_free(cmd_node
->cmd_vector
);
2836 hash_clean(cmd_node
->cmd_hash
, NULL
);
2837 hash_free(cmd_node
->cmd_hash
);
2838 cmd_node
->cmd_hash
= NULL
;
2841 vector_free(cmdvec
);
2846 XFREE(MTYPE_HOST
, host
.name
);
2847 if (host
.domainname
)
2848 XFREE(MTYPE_HOST
, host
.domainname
);
2850 XFREE(MTYPE_HOST
, host
.password
);
2851 if (host
.password_encrypt
)
2852 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2854 XFREE(MTYPE_HOST
, host
.enable
);
2855 if (host
.enable_encrypt
)
2856 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2858 XFREE(MTYPE_HOST
, host
.logfile
);
2860 XFREE(MTYPE_HOST
, host
.motdfile
);
2862 XFREE(MTYPE_HOST
, host
.config
);
2864 list_delete_and_null(&varhandlers
);