2 * CLI backend interface.
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
10 * This file is part of GNU Zebra.
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
49 DEFINE_MTYPE(LIB
, HOST
, "Host config")
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
146 "bfd", /* BFD_NODE */
147 "bfd peer", /* BFD_PEER_NODE */
149 /* clang-format on */
151 /* Command vector which includes some level of command lists. Normally
152 each daemon maintains each own cmdvec. */
153 vector cmdvec
= NULL
;
155 /* Host information structure. */
159 * Returns host.name if any, otherwise
160 * it returns the system hostname.
162 const char *cmd_hostname_get(void)
168 * Returns unix domainname
170 const char *cmd_domainname_get(void)
172 return host
.domainname
;
175 /* Standard command node structures. */
176 static struct cmd_node auth_node
= {
177 AUTH_NODE
, "Password: ",
180 static struct cmd_node view_node
= {
184 static struct cmd_node auth_enable_node
= {
185 AUTH_ENABLE_NODE
, "Password: ",
188 static struct cmd_node enable_node
= {
192 static struct cmd_node config_node
= {CONFIG_NODE
, "%s(config)# ", 1};
194 /* Default motd string. */
195 static const char *default_motd
= FRR_DEFAULT_MOTD
;
197 static const struct facility_map
{
201 } syslog_facilities
[] = {
202 {LOG_KERN
, "kern", 1},
203 {LOG_USER
, "user", 2},
204 {LOG_MAIL
, "mail", 1},
205 {LOG_DAEMON
, "daemon", 1},
206 {LOG_AUTH
, "auth", 1},
207 {LOG_SYSLOG
, "syslog", 1},
209 {LOG_NEWS
, "news", 1},
210 {LOG_UUCP
, "uucp", 2},
211 {LOG_CRON
, "cron", 1},
215 {LOG_LOCAL0
, "local0", 6},
216 {LOG_LOCAL1
, "local1", 6},
217 {LOG_LOCAL2
, "local2", 6},
218 {LOG_LOCAL3
, "local3", 6},
219 {LOG_LOCAL4
, "local4", 6},
220 {LOG_LOCAL5
, "local5", 6},
221 {LOG_LOCAL6
, "local6", 6},
222 {LOG_LOCAL7
, "local7", 6},
226 static const char *facility_name(int facility
)
228 const struct facility_map
*fm
;
230 for (fm
= syslog_facilities
; fm
->name
; fm
++)
231 if (fm
->facility
== facility
)
236 static int facility_match(const char *str
)
238 const struct facility_map
*fm
;
240 for (fm
= syslog_facilities
; fm
->name
; fm
++)
241 if (!strncmp(str
, fm
->name
, fm
->match
))
246 static int level_match(const char *s
)
250 for (level
= 0; zlog_priority
[level
] != NULL
; level
++)
251 if (!strncmp(s
, zlog_priority
[level
], 2))
253 return ZLOG_DISABLED
;
256 /* This is called from main when a daemon is invoked with -v or --version. */
257 void print_version(const char *progname
)
259 printf("%s version %s\n", progname
, FRR_VERSION
);
260 printf("%s\n", FRR_COPYRIGHT
);
261 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
264 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
266 int cnt
= MAX(argc
- shift
, 0);
267 const char *argstr
[cnt
+ 1];
272 for (int i
= 0; i
< cnt
; i
++)
273 argstr
[i
] = argv
[i
+ shift
]->arg
;
275 return frrstr_join(argstr
, cnt
, " ");
278 vector
cmd_make_strvec(const char *string
)
283 const char *copy
= string
;
285 /* skip leading whitespace */
286 while (isspace((int)*copy
) && *copy
!= '\0')
289 /* if the entire string was whitespace or a comment, return */
290 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
293 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
295 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
296 if (strlen(vector_slot(result
, i
)) == 0) {
297 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
298 vector_unset(result
, i
);
302 vector_compact(result
);
307 void cmd_free_strvec(vector v
)
309 frrstr_strvec_free(v
);
313 * Convenience function for accessing argv data.
317 * @param text definition snippet of the desired token
318 * @param index the starting index, and where to store the
319 * index of the found token if it exists
320 * @return 1 if found, 0 otherwise
322 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
325 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
326 if ((found
= strmatch(text
, argv
[i
]->text
)))
331 static unsigned int cmd_hash_key(void *p
)
333 int size
= sizeof(p
);
335 return jhash(p
, size
, 0);
338 static int cmd_hash_cmp(const void *a
, const void *b
)
343 /* Install top node of command vector. */
344 void install_node(struct cmd_node
*node
, int (*func
)(struct vty
*))
346 vector_set_index(cmdvec
, node
->node
, node
);
348 node
->cmdgraph
= graph_new();
349 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
351 struct cmd_token
*token
=
352 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
353 graph_new_node(node
->cmdgraph
, token
,
354 (void (*)(void *)) & cmd_token_del
);
355 node
->cmd_hash
= hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
,
359 /* Return prompt character of specified node. */
360 const char *cmd_prompt(enum node_type node
)
362 struct cmd_node
*cnode
;
364 cnode
= vector_slot(cmdvec
, node
);
365 return cnode
->prompt
;
368 /* Install a command into a node. */
369 void install_element(enum node_type ntype
, struct cmd_element
*cmd
)
371 struct cmd_node
*cnode
;
373 /* cmd_init hasn't been called */
375 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
380 cnode
= vector_lookup(cmdvec
, ntype
);
385 "\tnode %d (%s) does not exist.\n"
386 "\tplease call install_node() before install_element()\n",
387 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
391 if (hash_lookup(cnode
->cmd_hash
, cmd
) != NULL
) {
394 "\tnode %d (%s) already has this command installed.\n"
395 "\tduplicate install_element call?\n",
396 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
400 assert(hash_get(cnode
->cmd_hash
, cmd
, hash_alloc_intern
));
402 struct graph
*graph
= graph_new();
403 struct cmd_token
*token
=
404 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
405 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
407 cmd_graph_parse(graph
, cmd
);
408 cmd_graph_names(graph
);
409 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
410 graph_delete_graph(graph
);
412 vector_set(cnode
->cmd_vector
, cmd
);
414 if (ntype
== VIEW_NODE
)
415 install_element(ENABLE_NODE
, cmd
);
418 void uninstall_element(enum node_type ntype
, struct cmd_element
*cmd
)
420 struct cmd_node
*cnode
;
422 /* cmd_init hasn't been called */
424 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
429 cnode
= vector_lookup(cmdvec
, ntype
);
434 "\tnode %d (%s) does not exist.\n"
435 "\tplease call install_node() before uninstall_element()\n",
436 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
440 if (hash_release(cnode
->cmd_hash
, cmd
) == NULL
) {
443 "\tnode %d (%s) does not have this command installed.\n"
444 "\tduplicate uninstall_element call?\n",
445 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
449 vector_unset_value(cnode
->cmd_vector
, cmd
);
451 struct graph
*graph
= graph_new();
452 struct cmd_token
*token
=
453 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
454 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
456 cmd_graph_parse(graph
, cmd
);
457 cmd_graph_names(graph
);
458 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
459 graph_delete_graph(graph
);
461 if (ntype
== VIEW_NODE
)
462 uninstall_element(ENABLE_NODE
, cmd
);
466 static const unsigned char itoa64
[] =
467 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
469 static void to64(char *s
, long v
, int n
)
472 *s
++ = itoa64
[v
& 0x3f];
477 static char *zencrypt(const char *passwd
)
481 char *crypt(const char *, const char *);
483 gettimeofday(&tv
, 0);
485 to64(&salt
[0], random(), 3);
486 to64(&salt
[3], tv
.tv_usec
, 3);
489 return crypt(passwd
, salt
);
492 /* This function write configuration of this host. */
493 static int config_write_host(struct vty
*vty
)
495 if (cmd_hostname_get())
496 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
498 if (cmd_domainname_get())
499 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
501 /* The following are all configuration commands that are not sent to
502 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
503 * we would always display 'log syslog informational' in the config
504 * which would cause other daemons to then switch to syslog when they
507 if (strcmp(zlog_default
->protoname
, "WATCHFRR")) {
509 if (host
.password_encrypt
)
510 vty_out(vty
, "password 8 %s\n",
511 host
.password_encrypt
);
512 if (host
.enable_encrypt
)
513 vty_out(vty
, "enable password 8 %s\n",
514 host
.enable_encrypt
);
517 vty_out(vty
, "password %s\n", host
.password
);
519 vty_out(vty
, "enable password %s\n",
524 && (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
526 vty_out(vty
, "log file %s", host
.logfile
);
527 if (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
528 != zlog_default
->default_lvl
)
531 [zlog_default
->maxlvl
536 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
] != ZLOG_DISABLED
) {
537 vty_out(vty
, "log stdout");
538 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
]
539 != zlog_default
->default_lvl
)
542 [zlog_default
->maxlvl
543 [ZLOG_DEST_STDOUT
]]);
547 if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
548 vty_out(vty
, "no log monitor\n");
549 else if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]
550 != zlog_default
->default_lvl
)
551 vty_out(vty
, "log monitor %s\n",
552 zlog_priority
[zlog_default
->maxlvl
553 [ZLOG_DEST_MONITOR
]]);
555 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
) {
556 vty_out(vty
, "log syslog");
557 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
]
558 != zlog_default
->default_lvl
)
560 zlog_priority
[zlog_default
->maxlvl
561 [ZLOG_DEST_SYSLOG
]]);
565 if (zlog_default
->facility
!= LOG_DAEMON
)
566 vty_out(vty
, "log facility %s\n",
567 facility_name(zlog_default
->facility
));
569 if (zlog_default
->record_priority
== 1)
570 vty_out(vty
, "log record-priority\n");
572 if (zlog_default
->timestamp_precision
> 0)
573 vty_out(vty
, "log timestamp precision %d\n",
574 zlog_default
->timestamp_precision
);
577 vty_out(vty
, "service advanced-vty\n");
580 vty_out(vty
, "service password-encryption\n");
583 vty_out(vty
, "service terminal-length %d\n",
587 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
589 vty_out(vty
, "no banner motd\n");
592 if (debug_memstats_at_exit
)
593 vty_out(vty
, "!\ndebug memstats-at-exit\n");
598 /* Utility function for getting command graph. */
599 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
601 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
602 return cnode
->cmdgraph
;
605 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
607 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
608 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
614 * Compare function for cmd_token.
615 * Used with qsort to sort command completions.
617 static int compare_completions(const void *fst
, const void *snd
)
619 struct cmd_token
*first
= *(struct cmd_token
**)fst
,
620 *secnd
= *(struct cmd_token
**)snd
;
621 return strcmp(first
->text
, secnd
->text
);
625 * Takes a list of completions returned by command_complete,
626 * dedeuplicates them based on both text and description,
627 * sorts them, and returns them as a vector.
629 * @param completions linked list of cmd_token
630 * @return deduplicated and sorted vector with
632 vector
completions_to_vec(struct list
*completions
)
634 vector comps
= vector_init(VECTOR_MIN_SIZE
);
637 struct cmd_token
*token
, *cr
= NULL
;
638 unsigned int i
, exists
;
639 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
640 if (token
->type
== END_TKN
&& (cr
= token
))
643 // linear search for token in completions vector
645 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
646 struct cmd_token
*curr
= vector_slot(comps
, i
);
648 exists
= !strcmp(curr
->text
, token
->text
)
649 && !strcmp(curr
->desc
, token
->desc
);
651 exists
= !strcmp(curr
->text
, token
->text
);
652 #endif /* VTYSH_DEBUG */
656 vector_set(comps
, token
);
660 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
661 &compare_completions
);
663 // make <cr> the first element, if it is present
665 vector_set_index(comps
, vector_active(comps
), NULL
);
666 memmove(comps
->index
+ 1, comps
->index
,
667 (comps
->alloced
- 1) * sizeof(void *));
668 vector_set_index(comps
, 0, cr
);
674 * Generates a vector of cmd_token representing possible completions
675 * on the current input.
677 * @param vline the vectorized input line
678 * @param vty the vty with the node to match on
679 * @param status pointer to matcher status code
680 * @return vector of struct cmd_token * with possible completions
682 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
685 struct list
*completions
;
686 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
688 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
690 if (MATCHER_ERROR(rv
)) {
691 *status
= CMD_ERR_NO_MATCH
;
695 vector comps
= completions_to_vec(completions
);
696 list_delete_and_null(&completions
);
698 // set status code appropriately
699 switch (vector_active(comps
)) {
701 *status
= CMD_ERR_NO_MATCH
;
704 *status
= CMD_COMPLETE_FULL_MATCH
;
707 *status
= CMD_COMPLETE_LIST_MATCH
;
713 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
717 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
718 enum node_type onode
;
719 vector shifted_vline
;
723 vty
->node
= ENABLE_NODE
;
724 /* We can try it on enable node, cos' the vty is authenticated
727 shifted_vline
= vector_init(vector_count(vline
));
729 for (index
= 1; index
< vector_active(vline
); index
++) {
730 vector_set_index(shifted_vline
, index
- 1,
731 vector_lookup(vline
, index
));
734 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
736 vector_free(shifted_vline
);
741 return cmd_complete_command_real(vline
, vty
, status
);
744 static struct list
*varhandlers
= NULL
;
746 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
750 const struct cmd_variable_handler
*cvh
;
754 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
756 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
757 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
759 if (cvh
->varname
&& (!token
->varname
760 || strcmp(cvh
->varname
, token
->varname
)))
762 cvh
->completions(tmpcomps
, token
);
770 for (i
= vector_active(tmpcomps
); i
; i
--) {
771 char *item
= vector_slot(tmpcomps
, i
- 1);
772 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
773 vector_set(comps
, item
);
775 XFREE(MTYPE_COMPLETION
, item
);
777 vector_free(tmpcomps
);
780 #define AUTOCOMP_INDENT 5
782 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
785 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
786 int lc
= AUTOCOMP_INDENT
;
787 size_t cs
= AUTOCOMP_INDENT
;
789 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
790 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
791 char *item
= vector_slot(comps
, j
);
792 itemlen
= strlen(item
);
794 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
795 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
797 if (lc
+ itemlen
+ 1 >= cols
) {
798 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
799 AUTOCOMP_INDENT
, "");
800 lc
= AUTOCOMP_INDENT
;
803 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
806 XFREE(MTYPE_COMPLETION
, item
);
807 vector_set_index(comps
, j
, NULL
);
812 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
817 for (; cvh
->completions
; cvh
++)
818 listnode_add(varhandlers
, (void *)cvh
);
821 DEFUN_HIDDEN (autocomplete
,
823 "autocomplete TYPE TEXT VARNAME",
824 "Autocompletion handler (internal, for vtysh)\n"
827 "cmd_token->varname\n")
829 struct cmd_token tok
;
830 vector comps
= vector_init(32);
833 memset(&tok
, 0, sizeof(tok
));
834 tok
.type
= atoi(argv
[1]->arg
);
835 tok
.text
= argv
[2]->arg
;
836 tok
.varname
= argv
[3]->arg
;
837 if (!strcmp(tok
.varname
, "-"))
840 cmd_variable_complete(&tok
, NULL
, comps
);
842 for (i
= 0; i
< vector_active(comps
); i
++) {
843 char *text
= vector_slot(comps
, i
);
844 vty_out(vty
, "%s\n", text
);
845 XFREE(MTYPE_COMPLETION
, text
);
853 * Generate possible tab-completions for the given input. This function only
854 * returns results that would result in a valid command if used as Readline
855 * completions (as is the case in vtysh). For instance, if the passed vline ends
856 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
858 * @param vline vectorized input line
860 * @param status location to store matcher status code in
861 * @return set of valid strings for use with Readline as tab-completions.
864 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
867 int original_node
= vty
->node
;
868 vector input_line
= vector_init(vector_count(vline
));
870 // if the first token is 'do' we'll want to execute the command in the
872 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
873 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
875 // construct the input line we'll be matching on
876 unsigned int offset
= (do_shortcut
) ? 1 : 0;
877 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
878 vector_set_index(input_line
, index
,
879 vector_lookup(vline
, index
+ offset
));
881 // get token completions -- this is a copying operation
882 vector comps
= NULL
, initial_comps
;
883 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
885 if (!MATCHER_ERROR(*status
)) {
886 assert(initial_comps
);
887 // filter out everything that is not suitable for a
889 comps
= vector_init(VECTOR_MIN_SIZE
);
890 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
892 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
893 if (token
->type
== WORD_TKN
)
894 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
896 else if (IS_VARYING_TOKEN(token
->type
)) {
897 const char *ref
= vector_lookup(
898 vline
, vector_active(vline
) - 1);
899 cmd_variable_complete(token
, ref
, comps
);
902 vector_free(initial_comps
);
904 // since we filtered results, we need to re-set status code
905 switch (vector_active(comps
)) {
907 *status
= CMD_ERR_NO_MATCH
;
910 *status
= CMD_COMPLETE_FULL_MATCH
;
913 *status
= CMD_COMPLETE_LIST_MATCH
;
916 // copy completions text into an array of char*
917 ret
= XMALLOC(MTYPE_TMP
,
918 (vector_active(comps
) + 1) * sizeof(char *));
920 for (i
= 0; i
< vector_active(comps
); i
++) {
921 ret
[i
] = vector_slot(comps
, i
);
923 // set the last element to NULL, because this array is used in
924 // a Readline completion_generator function which expects NULL
925 // as a sentinel value
929 } else if (initial_comps
)
930 vector_free(initial_comps
);
932 // comps should always be null here
935 // free the adjusted input line
936 vector_free(input_line
);
938 // reset vty->node to its original value
939 vty
->node
= original_node
;
944 /* return parent node */
945 /* MUST eventually converge on CONFIG_NODE */
946 enum node_type
node_parent(enum node_type node
)
950 assert(node
> CONFIG_NODE
);
955 case BGP_FLOWSPECV4_NODE
:
956 case BGP_FLOWSPECV6_NODE
:
957 case BGP_VRF_POLICY_NODE
:
958 case BGP_VNC_DEFAULTS_NODE
:
959 case BGP_VNC_NVE_GROUP_NODE
:
960 case BGP_VNC_L2_GROUP_NODE
:
970 case BGP_EVPN_VNI_NODE
:
973 case KEYCHAIN_KEY_NODE
:
976 case LINK_PARAMS_NODE
:
977 ret
= INTERFACE_NODE
;
983 case LDP_IPV4_IFACE_NODE
:
986 case LDP_IPV6_IFACE_NODE
:
989 case LDP_PSEUDOWIRE_NODE
:
990 ret
= LDP_L2VPN_NODE
;
1003 /* Execute command by argument vline vector. */
1004 static int cmd_execute_command_real(vector vline
, enum filter_type filter
,
1006 const struct cmd_element
**cmd
)
1008 struct list
*argv_list
;
1009 enum matcher_rv status
;
1010 const struct cmd_element
*matched_element
= NULL
;
1012 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
1013 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
1016 *cmd
= matched_element
;
1018 // if matcher error, return corresponding CMD_ERR
1019 if (MATCHER_ERROR(status
)) {
1021 list_delete_and_null(&argv_list
);
1023 case MATCHER_INCOMPLETE
:
1024 return CMD_ERR_INCOMPLETE
;
1025 case MATCHER_AMBIGUOUS
:
1026 return CMD_ERR_AMBIGUOUS
;
1028 return CMD_ERR_NO_MATCH
;
1032 // build argv array from argv list
1033 struct cmd_token
**argv
= XMALLOC(
1034 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
1035 struct listnode
*ln
;
1036 struct cmd_token
*token
;
1038 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
1041 int argc
= argv_list
->count
;
1044 if (matched_element
->daemon
)
1045 ret
= CMD_SUCCESS_DAEMON
;
1047 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
1049 // delete list and cmd_token's in it
1050 list_delete_and_null(&argv_list
);
1051 XFREE(MTYPE_TMP
, argv
);
1057 * Execute a given command, handling things like "do ..." and checking
1058 * whether the given command might apply at a parent node if doesn't
1059 * apply for the current node.
1061 * @param vline Command line input, vector of char* where each element is
1063 * @param vty The vty context in which the command should be executed.
1064 * @param cmd Pointer where the struct cmd_element of the matched command
1065 * will be stored, if any. May be set to NULL if this info is
1067 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1068 * @return The status of the command that has been executed or an error code
1069 * as to why no command could be executed.
1071 int cmd_execute_command(vector vline
, struct vty
*vty
,
1072 const struct cmd_element
**cmd
, int vtysh
)
1074 int ret
, saved_ret
= 0;
1075 enum node_type onode
, try_node
;
1077 onode
= try_node
= vty
->node
;
1079 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1080 vector shifted_vline
;
1083 vty
->node
= ENABLE_NODE
;
1084 /* We can try it on enable node, cos' the vty is authenticated
1087 shifted_vline
= vector_init(vector_count(vline
));
1089 for (index
= 1; index
< vector_active(vline
); index
++)
1090 vector_set_index(shifted_vline
, index
- 1,
1091 vector_lookup(vline
, index
));
1093 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1096 vector_free(shifted_vline
);
1102 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
1107 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1108 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1109 /* This assumes all nodes above CONFIG_NODE are childs of
1111 while (vty
->node
> CONFIG_NODE
) {
1112 try_node
= node_parent(try_node
);
1113 vty
->node
= try_node
;
1114 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1116 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1117 || ret
== CMD_NOT_MY_INSTANCE
1118 || ret
== CMD_WARNING_CONFIG_FAILED
)
1121 /* no command succeeded, reset the vty to the original node */
1125 /* return command status for original node */
1130 * Execute a given command, matching it strictly against the current node.
1131 * This mode is used when reading config files.
1133 * @param vline Command line input, vector of char* where each element is
1135 * @param vty The vty context in which the command should be executed.
1136 * @param cmd Pointer where the struct cmd_element* of the matched command
1137 * will be stored, if any. May be set to NULL if this info is
1139 * @return The status of the command that has been executed or an error code
1140 * as to why no command could be executed.
1142 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1143 const struct cmd_element
**cmd
)
1145 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1149 * Hook for preprocessing command string before executing.
1151 * All subscribers are called with the raw command string that is to be
1152 * executed. If any changes are to be made, a new string should be allocated
1153 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1154 * is then responsible for freeing this string.
1156 * All processing functions must be mutually exclusive in their action, i.e. if
1157 * one subscriber decides to modify the command, all others must not modify it
1158 * when called. Feeding the output of one processing command into a subsequent
1159 * one is not supported.
1161 * This hook is intentionally internal to the command processing system.
1164 * The raw command string.
1167 * The result of any processing.
1169 DECLARE_HOOK(cmd_execute
,
1170 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1171 (vty
, cmd_in
, cmd_out
));
1172 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1173 (vty
, cmd_in
, cmd_out
));
1175 /* Hook executed after a CLI command. */
1176 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1178 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1182 * cmd_execute hook subscriber to handle `|` actions.
1184 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1188 char *orig
, *working
, *token
, *u
;
1189 char *pipe
= strstr(cmd_in
, "| ");
1194 /* duplicate string for processing purposes, not including pipe */
1195 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1197 /* retrieve action */
1198 token
= strsep(&working
, " ");
1200 /* match result to known actions */
1201 if (strmatch(token
, "include")) {
1202 /* the remaining text should be a regexp */
1203 char *regexp
= working
;
1206 vty_out(vty
, "%% Need a regexp to filter with\n");
1210 bool succ
= vty_set_include(vty
, regexp
);
1213 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1216 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1220 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1225 XFREE(MTYPE_TMP
, orig
);
1229 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1232 vty_set_include(vty
, NULL
);
1237 int cmd_execute(struct vty
*vty
, const char *cmd
,
1238 const struct cmd_element
**matched
, int vtysh
)
1241 char *cmd_out
= NULL
;
1242 const char *cmd_exec
;
1245 hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1246 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1248 vline
= cmd_make_strvec(cmd_exec
);
1251 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1252 cmd_free_strvec(vline
);
1257 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1260 XFREE(MTYPE_TMP
, cmd_out
);
1267 * Parse one line of config, walking up the parse tree attempting to find a
1270 * @param vty The vty context in which the command should be executed.
1271 * @param cmd Pointer where the struct cmd_element* of the match command
1272 * will be stored, if any. May be set to NULL if this info is
1274 * @param use_daemon Boolean to control whether or not we match on
1275 * CMD_SUCCESS_DAEMON
1277 * @return The status of the command that has been executed or an error code
1278 * as to why no command could be executed.
1280 int command_config_read_one_line(struct vty
*vty
,
1281 const struct cmd_element
**cmd
, int use_daemon
)
1287 vline
= cmd_make_strvec(vty
->buf
);
1289 /* In case of comment line */
1293 /* Execute configuration command : this is strict match */
1294 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1296 // Climb the tree and try the command again at each node
1297 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1298 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1299 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1300 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1301 && vty
->node
!= CONFIG_NODE
) {
1303 saved_node
= vty
->node
;
1305 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1306 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1307 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1308 && vty
->node
> CONFIG_NODE
) {
1309 vty
->node
= node_parent(vty
->node
);
1310 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1313 // If climbing the tree did not work then ignore the command and
1314 // stay at the same node
1315 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1316 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1317 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1318 vty
->node
= saved_node
;
1322 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
)
1323 memcpy(vty
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1325 cmd_free_strvec(vline
);
1330 /* Configuration make from file. */
1331 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1333 int ret
, error_ret
= 0;
1336 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1340 ret
= command_config_read_one_line(vty
, NULL
, 0);
1342 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1343 && ret
!= CMD_ERR_NOTHING_TODO
)
1354 /* Configuration from terminal */
1355 DEFUN (config_terminal
,
1356 config_terminal_cmd
,
1357 "configure terminal",
1358 "Configuration from vty interface\n"
1359 "Configuration terminal\n")
1361 if (vty_config_lock(vty
))
1362 vty
->node
= CONFIG_NODE
;
1364 vty_out(vty
, "VTY configuration is locked by other VTY\n");
1365 return CMD_WARNING_CONFIG_FAILED
;
1370 /* Enable command */
1374 "Turn on privileged mode command\n")
1376 /* If enable password is NULL, change to ENABLE_NODE */
1377 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1378 || vty
->type
== VTY_SHELL_SERV
)
1379 vty
->node
= ENABLE_NODE
;
1381 vty
->node
= AUTH_ENABLE_NODE
;
1386 /* Disable command */
1390 "Turn off privileged mode command\n")
1392 if (vty
->node
== ENABLE_NODE
)
1393 vty
->node
= VIEW_NODE
;
1397 /* Down vty node level. */
1401 "Exit current mode and down to previous mode\n")
1407 void cmd_exit(struct vty
*vty
)
1409 switch (vty
->node
) {
1415 vty
->status
= VTY_CLOSE
;
1418 vty
->node
= ENABLE_NODE
;
1419 vty_config_unlock(vty
);
1421 case INTERFACE_NODE
:
1423 case LOGICALROUTER_NODE
:
1435 case LDP_L2VPN_NODE
:
1442 vty
->node
= CONFIG_NODE
;
1445 case BGP_IPV4M_NODE
:
1446 case BGP_IPV4L_NODE
:
1447 case BGP_VPNV4_NODE
:
1448 case BGP_VPNV6_NODE
:
1449 case BGP_FLOWSPECV4_NODE
:
1450 case BGP_FLOWSPECV6_NODE
:
1451 case BGP_VRF_POLICY_NODE
:
1452 case BGP_VNC_DEFAULTS_NODE
:
1453 case BGP_VNC_NVE_GROUP_NODE
:
1454 case BGP_VNC_L2_GROUP_NODE
:
1456 case BGP_IPV6M_NODE
:
1458 case BGP_IPV6L_NODE
:
1459 vty
->node
= BGP_NODE
;
1461 case BGP_EVPN_VNI_NODE
:
1462 vty
->node
= BGP_EVPN_NODE
;
1466 vty
->node
= LDP_NODE
;
1468 case LDP_IPV4_IFACE_NODE
:
1469 vty
->node
= LDP_IPV4_NODE
;
1471 case LDP_IPV6_IFACE_NODE
:
1472 vty
->node
= LDP_IPV6_NODE
;
1474 case LDP_PSEUDOWIRE_NODE
:
1475 vty
->node
= LDP_L2VPN_NODE
;
1477 case KEYCHAIN_KEY_NODE
:
1478 vty
->node
= KEYCHAIN_NODE
;
1480 case LINK_PARAMS_NODE
:
1481 vty
->node
= INTERFACE_NODE
;
1484 vty
->node
= BFD_NODE
;
1495 "Exit current mode and down to previous mode\n")
1497 return config_exit(self
, vty
, argc
, argv
);
1501 /* End of configuration. */
1505 "End current mode and change to enable mode.\n")
1507 switch (vty
->node
) {
1510 /* Nothing to do. */
1513 case INTERFACE_NODE
:
1515 case LOGICALROUTER_NODE
:
1524 case BGP_VRF_POLICY_NODE
:
1525 case BGP_VNC_DEFAULTS_NODE
:
1526 case BGP_VNC_NVE_GROUP_NODE
:
1527 case BGP_VNC_L2_GROUP_NODE
:
1528 case BGP_VPNV4_NODE
:
1529 case BGP_VPNV6_NODE
:
1530 case BGP_FLOWSPECV4_NODE
:
1531 case BGP_FLOWSPECV6_NODE
:
1533 case BGP_IPV4M_NODE
:
1534 case BGP_IPV4L_NODE
:
1536 case BGP_IPV6M_NODE
:
1538 case BGP_EVPN_VNI_NODE
:
1539 case BGP_IPV6L_NODE
:
1547 case LDP_IPV4_IFACE_NODE
:
1548 case LDP_IPV6_IFACE_NODE
:
1549 case LDP_L2VPN_NODE
:
1550 case LDP_PSEUDOWIRE_NODE
:
1553 case KEYCHAIN_KEY_NODE
:
1555 case LINK_PARAMS_NODE
:
1558 vty_config_unlock(vty
);
1559 vty
->node
= ENABLE_NODE
;
1568 DEFUN (show_version
,
1572 "Displays zebra version\n")
1574 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1575 cmd_hostname_get() ? cmd_hostname_get() : "");
1576 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1577 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1582 /* "Set" version ... ignore version tags */
1583 DEFUN (frr_version_defaults
,
1584 frr_version_defaults_cmd
,
1585 "frr <version|defaults> LINE...",
1586 "FRRouting global parameters\n"
1587 "version configuration was written by\n"
1588 "set of configuration defaults used\n"
1594 /* Help display function for all node. */
1598 "Description of the interactive help system\n")
1601 "Quagga VTY provides advanced help feature. When you need help,\n\
1602 anytime at the command line please press '?'.\n\
1604 If nothing matches, the help list will be empty and you must backup\n\
1605 until entering a '?' shows the available options.\n\
1606 Two styles of help are provided:\n\
1607 1. Full help is available when you are ready to enter a\n\
1608 command argument (e.g. 'show ?') and describes each possible\n\
1610 2. Partial help is provided when an abbreviated argument is entered\n\
1611 and you want to know what arguments match the input\n\
1612 (e.g. 'show me?'.)\n\n");
1616 static void permute(struct graph_node
*start
, struct vty
*vty
)
1618 static struct list
*position
= NULL
;
1620 position
= list_new();
1622 struct cmd_token
*stok
= start
->data
;
1623 struct graph_node
*gnn
;
1624 struct listnode
*ln
;
1627 listnode_add(position
, start
);
1628 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1629 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1630 struct cmd_token
*tok
= gn
->data
;
1631 if (tok
->attr
== CMD_ATTR_HIDDEN
1632 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1634 else if (tok
->type
== END_TKN
|| gn
== start
) {
1636 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1637 struct cmd_token
*tt
= gnn
->data
;
1638 if (tt
->type
< SPECIAL_TKN
)
1639 vty_out(vty
, " %s", tt
->text
);
1642 vty_out(vty
, "...");
1646 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1647 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1656 list_delete_node(position
, listtail(position
));
1659 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1661 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1664 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1666 /* loop over all commands at this node */
1667 struct cmd_element
*element
= NULL
;
1668 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1670 if ((element
= vector_slot(node
->cmd_vector
, i
))
1671 && element
->attr
!= CMD_ATTR_DEPRECATED
1672 && element
->attr
!= CMD_ATTR_HIDDEN
)
1673 vty_out(vty
, " %s\n", element
->string
);
1678 /* Help display function for all node. */
1681 "list [permutations]",
1682 "Print command list\n"
1683 "Print all possible command permutations\n")
1685 return cmd_list_cmds(vty
, argc
== 2);
1688 DEFUN (show_commandtree
,
1689 show_commandtree_cmd
,
1690 "show commandtree [permutations]",
1692 "Show command tree\n"
1693 "Permutations that we are interested in\n")
1695 return cmd_list_cmds(vty
, argc
== 3);
1698 DEFUN_HIDDEN(show_cli_graph
,
1703 "Dump current command space as DOT graph\n")
1705 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1706 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1708 vty_out(vty
, "%s\n", dot
);
1709 XFREE(MTYPE_TMP
, dot
);
1713 static int vty_write_config(struct vty
*vty
)
1716 struct cmd_node
*node
;
1721 if (vty
->type
== VTY_TERM
) {
1722 vty_out(vty
, "\nCurrent configuration:\n");
1723 vty_out(vty
, "!\n");
1726 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1727 vty_out(vty
, "frr defaults %s\n", DFLT_NAME
);
1728 vty_out(vty
, "!\n");
1730 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1731 if ((node
= vector_slot(cmdvec
, i
)) && node
->func
1732 && (node
->vtysh
|| vty
->type
!= VTY_SHELL
)) {
1733 if ((*node
->func
)(vty
))
1734 vty_out(vty
, "!\n");
1737 if (vty
->type
== VTY_TERM
) {
1738 vty_out(vty
, "end\n");
1744 static int file_write_config(struct vty
*vty
)
1747 char *config_file
, *slash
;
1748 char *config_file_tmp
= NULL
;
1749 char *config_file_sav
= NULL
;
1750 int ret
= CMD_WARNING
;
1751 struct vty
*file_vty
;
1752 struct stat conf_stat
;
1757 /* Check and see if we are operating under vtysh configuration */
1758 if (host
.config
== NULL
) {
1760 "Can't save to configuration file, using vtysh.\n");
1765 config_file
= host
.config
;
1768 #define O_DIRECTORY 0
1770 slash
= strrchr(config_file
, '/');
1772 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1773 config_dir
[slash
- config_file
] = '\0';
1774 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1775 XFREE(MTYPE_TMP
, config_dir
);
1777 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1778 /* if dirfd is invalid, directory sync fails, but we're still OK */
1780 config_file_sav
= XMALLOC(
1781 MTYPE_TMP
, strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1);
1782 strcpy(config_file_sav
, config_file
);
1783 strcat(config_file_sav
, CONF_BACKUP_EXT
);
1786 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1787 sprintf(config_file_tmp
, "%s.XXXXXX", config_file
);
1789 /* Open file to configuration write. */
1790 fd
= mkstemp(config_file_tmp
);
1792 vty_out(vty
, "Can't open configuration file %s.\n",
1796 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1797 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1798 config_file_tmp
, safe_strerror(errno
), errno
);
1802 /* Make vty for configuration file. */
1803 file_vty
= vty_new();
1805 file_vty
->type
= VTY_FILE
;
1807 /* Config file header print. */
1808 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1809 vty_time_print(file_vty
, 1);
1810 vty_out(file_vty
, "!\n");
1811 vty_write_config(file_vty
);
1812 vty_close(file_vty
);
1814 if (stat(config_file
, &conf_stat
) >= 0) {
1815 if (unlink(config_file_sav
) != 0)
1816 if (errno
!= ENOENT
) {
1818 "Can't unlink backup configuration file %s.\n",
1822 if (link(config_file
, config_file_sav
) != 0) {
1824 "Can't backup old configuration file %s.\n",
1831 if (rename(config_file_tmp
, config_file
) != 0) {
1832 vty_out(vty
, "Can't save configuration file %s.\n",
1839 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1843 if (ret
!= CMD_SUCCESS
)
1844 unlink(config_file_tmp
);
1847 XFREE(MTYPE_TMP
, config_file_tmp
);
1848 XFREE(MTYPE_TMP
, config_file_sav
);
1852 /* Write current configuration into file. */
1854 DEFUN (config_write
,
1856 "write [<file|memory|terminal>]",
1857 "Write running configuration to memory, network, or terminal\n"
1858 "Write to configuration file\n"
1859 "Write configuration currently in memory\n"
1860 "Write configuration to terminal\n")
1862 const int idx_type
= 1;
1864 // if command was 'write terminal' or 'write memory'
1865 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1866 return vty_write_config(vty
);
1869 return file_write_config(vty
);
1872 /* ALIAS_FIXME for 'write <terminal|memory>' */
1873 DEFUN (show_running_config
,
1874 show_running_config_cmd
,
1875 "show running-config",
1877 "running configuration (same as write terminal)\n")
1879 return vty_write_config(vty
);
1882 /* ALIAS_FIXME for 'write file' */
1883 DEFUN (copy_runningconf_startupconf
,
1884 copy_runningconf_startupconf_cmd
,
1885 "copy running-config startup-config",
1886 "Copy configuration\n"
1887 "Copy running config to... \n"
1888 "Copy running config to startup config (same as write file/memory)\n")
1890 return file_write_config(vty
);
1894 /* Write startup configuration into the terminal. */
1895 DEFUN (show_startup_config
,
1896 show_startup_config_cmd
,
1897 "show startup-config",
1899 "Contents of startup configuration\n")
1906 if (host
.config
== NULL
)
1909 confp
= fopen(host
.config
, "r");
1910 if (confp
== NULL
) {
1911 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1912 host
.config
, safe_strerror(errno
));
1916 while (fgets(buf
, BUFSIZ
, confp
)) {
1919 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1923 vty_out(vty
, "%s\n", buf
);
1931 int cmd_domainname_set(const char *domainname
)
1933 XFREE(MTYPE_HOST
, host
.domainname
);
1934 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1938 /* Hostname configuration */
1939 DEFUN(config_domainname
,
1942 "Set system's domain name\n"
1943 "This system's domain name\n")
1945 struct cmd_token
*word
= argv
[1];
1947 if (!isalpha((int)word
->arg
[0])) {
1948 vty_out(vty
, "Please specify string starting with alphabet\n");
1949 return CMD_WARNING_CONFIG_FAILED
;
1952 return cmd_domainname_set(word
->arg
);
1955 DEFUN(config_no_domainname
,
1957 "no domainname [DOMAINNAME]",
1959 "Reset system's domain name\n"
1960 "domain name of this router\n")
1962 return cmd_domainname_set(NULL
);
1965 int cmd_hostname_set(const char *hostname
)
1967 XFREE(MTYPE_HOST
, host
.name
);
1968 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1972 /* Hostname configuration */
1973 DEFUN (config_hostname
,
1976 "Set system's network name\n"
1977 "This system's network name\n")
1979 struct cmd_token
*word
= argv
[1];
1981 if (!isalnum((int)word
->arg
[0])) {
1982 vty_out(vty
, "Please specify string starting with alphabet\n");
1983 return CMD_WARNING_CONFIG_FAILED
;
1986 return cmd_hostname_set(word
->arg
);
1989 DEFUN (config_no_hostname
,
1991 "no hostname [HOSTNAME]",
1993 "Reset system's network name\n"
1994 "Host name of this router\n")
1996 return cmd_hostname_set(NULL
);
1999 /* VTY interface password set. */
2000 DEFUN (config_password
,
2002 "password [(8-8)] WORD",
2003 "Modify the terminal connection password\n"
2004 "Specifies a HIDDEN password will follow\n"
2005 "The password string\n")
2009 if (argc
== 3) // '8' was specified
2012 XFREE(MTYPE_HOST
, host
.password
);
2013 host
.password
= NULL
;
2014 if (host
.password_encrypt
)
2015 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2016 host
.password_encrypt
=
2017 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2021 if (!isalnum((int)argv
[idx_8
]->arg
[0])) {
2023 "Please specify string starting with alphanumeric\n");
2024 return CMD_WARNING_CONFIG_FAILED
;
2028 XFREE(MTYPE_HOST
, host
.password
);
2029 host
.password
= NULL
;
2032 if (host
.password_encrypt
)
2033 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2034 host
.password_encrypt
=
2035 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2037 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2042 /* VTY interface password delete. */
2043 DEFUN (no_config_password
,
2047 "Modify the terminal connection password\n")
2049 bool warned
= false;
2051 if (host
.password
) {
2052 if (!vty_shell_serv(vty
)) {
2053 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2056 XFREE(MTYPE_HOST
, host
.password
);
2058 host
.password
= NULL
;
2060 if (host
.password_encrypt
) {
2061 if (!warned
&& !vty_shell_serv(vty
))
2062 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2063 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2065 host
.password_encrypt
= NULL
;
2070 /* VTY enable password set. */
2071 DEFUN (config_enable_password
,
2072 enable_password_cmd
,
2073 "enable password [(8-8)] WORD",
2074 "Modify enable password parameters\n"
2075 "Assign the privileged level password\n"
2076 "Specifies a HIDDEN password will follow\n"
2077 "The HIDDEN 'enable' password string\n")
2082 /* Crypt type is specified. */
2084 if (argv
[idx_8
]->arg
[0] == '8') {
2086 XFREE(MTYPE_HOST
, host
.enable
);
2089 if (host
.enable_encrypt
)
2090 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2091 host
.enable_encrypt
=
2092 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2096 vty_out(vty
, "Unknown encryption type.\n");
2097 return CMD_WARNING_CONFIG_FAILED
;
2101 if (!isalnum((int)argv
[idx_8
]->arg
[0])) {
2103 "Please specify string starting with alphanumeric\n");
2104 return CMD_WARNING_CONFIG_FAILED
;
2108 XFREE(MTYPE_HOST
, host
.enable
);
2111 /* Plain password input. */
2113 if (host
.enable_encrypt
)
2114 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2115 host
.enable_encrypt
=
2116 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2118 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2123 /* VTY enable password delete. */
2124 DEFUN (no_config_enable_password
,
2125 no_enable_password_cmd
,
2126 "no enable password",
2128 "Modify enable password parameters\n"
2129 "Assign the privileged level password\n")
2131 bool warned
= false;
2134 if (!vty_shell_serv(vty
)) {
2135 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2138 XFREE(MTYPE_HOST
, host
.enable
);
2142 if (host
.enable_encrypt
) {
2143 if (!warned
&& !vty_shell_serv(vty
))
2144 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2145 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2147 host
.enable_encrypt
= NULL
;
2152 DEFUN (service_password_encrypt
,
2153 service_password_encrypt_cmd
,
2154 "service password-encryption",
2155 "Set up miscellaneous service\n"
2156 "Enable encrypted passwords\n")
2163 if (host
.password
) {
2164 if (host
.password_encrypt
)
2165 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2166 host
.password_encrypt
=
2167 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2170 if (host
.enable_encrypt
)
2171 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2172 host
.enable_encrypt
=
2173 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2179 DEFUN (no_service_password_encrypt
,
2180 no_service_password_encrypt_cmd
,
2181 "no service password-encryption",
2183 "Set up miscellaneous service\n"
2184 "Enable encrypted passwords\n")
2191 if (host
.password_encrypt
)
2192 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2193 host
.password_encrypt
= NULL
;
2195 if (host
.enable_encrypt
)
2196 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2197 host
.enable_encrypt
= NULL
;
2202 DEFUN (config_terminal_length
,
2203 config_terminal_length_cmd
,
2204 "terminal length (0-512)",
2205 "Set terminal line parameters\n"
2206 "Set number of lines on a screen\n"
2207 "Number of lines on screen (0 for no pausing)\n")
2211 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2216 DEFUN (config_terminal_no_length
,
2217 config_terminal_no_length_cmd
,
2218 "terminal no length",
2219 "Set terminal line parameters\n"
2221 "Set number of lines on a screen\n")
2227 DEFUN (service_terminal_length
,
2228 service_terminal_length_cmd
,
2229 "service terminal-length (0-512)",
2230 "Set up miscellaneous service\n"
2231 "System wide terminal length configuration\n"
2232 "Number of lines of VTY (0 means no line control)\n")
2236 host
.lines
= atoi(argv
[idx_number
]->arg
);
2241 DEFUN (no_service_terminal_length
,
2242 no_service_terminal_length_cmd
,
2243 "no service terminal-length [(0-512)]",
2245 "Set up miscellaneous service\n"
2246 "System wide terminal length configuration\n"
2247 "Number of lines of VTY (0 means no line control)\n")
2253 DEFUN_HIDDEN (do_echo
,
2256 "Echo a message back to the vty\n"
2257 "The message to echo\n")
2261 vty_out(vty
, "%s\n",
2262 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2264 XFREE(MTYPE_TMP
, message
);
2268 DEFUN (config_logmsg
,
2270 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2271 "Send a message to enabled logging destinations\n"
2273 "The message to send\n")
2275 int idx_log_level
= 1;
2276 int idx_message
= 2;
2280 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2281 return CMD_ERR_NO_MATCH
;
2284 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2286 XFREE(MTYPE_TMP
, message
);
2291 DEFUN (show_logging
,
2295 "Show current logging configuration\n")
2297 struct zlog
*zl
= zlog_default
;
2299 vty_out(vty
, "Syslog logging: ");
2300 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
2301 vty_out(vty
, "disabled");
2303 vty_out(vty
, "level %s, facility %s, ident %s",
2304 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
2305 facility_name(zl
->facility
), zl
->ident
);
2308 vty_out(vty
, "Stdout logging: ");
2309 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
2310 vty_out(vty
, "disabled");
2312 vty_out(vty
, "level %s",
2313 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
2316 vty_out(vty
, "Monitor logging: ");
2317 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
2318 vty_out(vty
, "disabled");
2320 vty_out(vty
, "level %s",
2321 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
2324 vty_out(vty
, "File logging: ");
2325 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
2326 vty_out(vty
, "disabled");
2328 vty_out(vty
, "level %s, filename %s",
2329 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
2333 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
2334 vty_out(vty
, "Record priority: %s\n",
2335 (zl
->record_priority
? "enabled" : "disabled"));
2336 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2341 DEFUN (config_log_stdout
,
2342 config_log_stdout_cmd
,
2343 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2345 "Set stdout logging level\n"
2348 int idx_log_level
= 2;
2350 if (argc
== idx_log_level
) {
2351 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2356 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2357 return CMD_ERR_NO_MATCH
;
2358 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2362 DEFUN (no_config_log_stdout
,
2363 no_config_log_stdout_cmd
,
2364 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2367 "Cancel logging to stdout\n"
2370 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2374 DEFUN (config_log_monitor
,
2375 config_log_monitor_cmd
,
2376 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2378 "Set terminal line (monitor) logging level\n"
2381 int idx_log_level
= 2;
2383 if (argc
== idx_log_level
) {
2384 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2389 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2390 return CMD_ERR_NO_MATCH
;
2391 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2395 DEFUN (no_config_log_monitor
,
2396 no_config_log_monitor_cmd
,
2397 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2400 "Disable terminal line (monitor) logging\n"
2403 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2407 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2411 const char *fullpath
;
2413 /* Path detection. */
2414 if (!IS_DIRECTORY_SEP(*fname
)) {
2415 char cwd
[MAXPATHLEN
+ 1];
2416 cwd
[MAXPATHLEN
] = '\0';
2418 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2419 zlog_err("config_log_file: Unable to alloc mem!");
2420 return CMD_WARNING_CONFIG_FAILED
;
2423 if ((p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2))
2425 zlog_err("config_log_file: Unable to alloc mem!");
2426 return CMD_WARNING_CONFIG_FAILED
;
2428 sprintf(p
, "%s/%s", cwd
, fname
);
2433 ret
= zlog_set_file(fullpath
, loglevel
);
2436 XFREE(MTYPE_TMP
, p
);
2440 vty_out(vty
, "can't open logfile %s\n", fname
);
2441 return CMD_WARNING_CONFIG_FAILED
;
2445 XFREE(MTYPE_HOST
, host
.logfile
);
2447 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2449 #if defined(HAVE_CUMULUS)
2450 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2451 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2456 void command_setup_early_logging(const char *dest
, const char *level
)
2461 int nlevel
= level_match(level
);
2463 if (nlevel
!= ZLOG_DISABLED
)
2464 zlog_default
->default_lvl
= nlevel
;
2470 if (strcmp(dest
, "stdout") == 0) {
2471 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2475 if (strcmp(dest
, "syslog") == 0) {
2476 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2480 token
= strstr(dest
, ":");
2486 set_log_file(NULL
, token
, zlog_default
->default_lvl
);
2489 DEFUN (config_log_file
,
2490 config_log_file_cmd
,
2491 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2494 "Logging filename\n"
2497 int idx_filename
= 2;
2498 int idx_log_levels
= 3;
2501 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2503 return CMD_ERR_NO_MATCH
;
2504 return set_log_file(vty
, argv
[idx_filename
]->arg
, level
);
2506 return set_log_file(vty
, argv
[idx_filename
]->arg
,
2507 zlog_default
->default_lvl
);
2510 static void disable_log_file(void)
2515 XFREE(MTYPE_HOST
, host
.logfile
);
2517 host
.logfile
= NULL
;
2520 DEFUN (no_config_log_file
,
2521 no_config_log_file_cmd
,
2522 "no log file [FILENAME [LEVEL]]",
2525 "Cancel logging to file\n"
2526 "Logging file name\n"
2533 DEFUN (config_log_syslog
,
2534 config_log_syslog_cmd
,
2535 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2537 "Set syslog logging level\n"
2540 int idx_log_levels
= 2;
2546 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2548 return CMD_ERR_NO_MATCH
;
2549 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2552 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2557 DEFUN (no_config_log_syslog
,
2558 no_config_log_syslog_cmd
,
2559 "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>]",
2562 "Cancel logging to syslog\n"
2566 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2570 DEFUN (config_log_facility
,
2571 config_log_facility_cmd
,
2572 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2574 "Facility parameter for syslog messages\n"
2578 int facility
= facility_match(argv
[idx_target
]->arg
);
2580 zlog_default
->facility
= facility
;
2584 DEFUN (no_config_log_facility
,
2585 no_config_log_facility_cmd
,
2586 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2589 "Reset syslog facility to default (daemon)\n"
2592 zlog_default
->facility
= LOG_DAEMON
;
2596 DEFUN (config_log_record_priority
,
2597 config_log_record_priority_cmd
,
2598 "log record-priority",
2600 "Log the priority of the message within the message\n")
2602 zlog_default
->record_priority
= 1;
2606 DEFUN (no_config_log_record_priority
,
2607 no_config_log_record_priority_cmd
,
2608 "no log record-priority",
2611 "Do not log the priority of the message within the message\n")
2613 zlog_default
->record_priority
= 0;
2617 DEFUN (config_log_timestamp_precision
,
2618 config_log_timestamp_precision_cmd
,
2619 "log timestamp precision (0-6)",
2621 "Timestamp configuration\n"
2622 "Set the timestamp precision\n"
2623 "Number of subsecond digits\n")
2626 zlog_default
->timestamp_precision
=
2627 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2631 DEFUN (no_config_log_timestamp_precision
,
2632 no_config_log_timestamp_precision_cmd
,
2633 "no log timestamp precision",
2636 "Timestamp configuration\n"
2637 "Reset the timestamp precision to the default value of 0\n")
2639 zlog_default
->timestamp_precision
= 0;
2643 DEFUN (debug_memstats
,
2645 "[no] debug memstats-at-exit",
2648 "Print memory type statistics at exit\n")
2650 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2654 int cmd_banner_motd_file(const char *file
)
2656 int success
= CMD_SUCCESS
;
2661 rpath
= realpath(file
, p
);
2663 return CMD_ERR_NO_FILE
;
2664 in
= strstr(rpath
, SYSCONFDIR
);
2667 XFREE(MTYPE_HOST
, host
.motdfile
);
2668 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2670 success
= CMD_WARNING_CONFIG_FAILED
;
2675 DEFUN (banner_motd_file
,
2676 banner_motd_file_cmd
,
2677 "banner motd file FILE",
2680 "Banner from a file\n"
2684 const char *filename
= argv
[idx_file
]->arg
;
2685 int cmd
= cmd_banner_motd_file(filename
);
2687 if (cmd
== CMD_ERR_NO_FILE
)
2688 vty_out(vty
, "%s does not exist", filename
);
2689 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2690 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2695 DEFUN (banner_motd_default
,
2696 banner_motd_default_cmd
,
2697 "banner motd default",
2698 "Set banner string\n"
2699 "Strings for motd\n"
2702 host
.motd
= default_motd
;
2706 DEFUN (no_banner_motd
,
2710 "Set banner string\n"
2711 "Strings for motd\n")
2715 XFREE(MTYPE_HOST
, host
.motdfile
);
2716 host
.motdfile
= NULL
;
2723 "Find CLI command containing text\n"
2724 "Text to search for\n")
2726 char *text
= argv_concat(argv
, argc
, 1);
2727 const struct cmd_node
*node
;
2728 const struct cmd_element
*cli
;
2731 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2732 node
= vector_slot(cmdvec
, i
);
2735 clis
= node
->cmd_vector
;
2736 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2737 cli
= vector_slot(clis
, j
);
2738 if (strcasestr(cli
->string
, text
))
2739 vty_out(vty
, " (%s) %s\n",
2740 node_names
[node
->node
], cli
->string
);
2744 XFREE(MTYPE_TMP
, text
);
2749 /* Set config filename. Called from vty.c */
2750 void host_config_set(const char *filename
)
2753 XFREE(MTYPE_HOST
, host
.config
);
2754 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2757 const char *host_config_get(void)
2762 void install_default(enum node_type node
)
2764 install_element(node
, &config_exit_cmd
);
2765 install_element(node
, &config_quit_cmd
);
2766 install_element(node
, &config_end_cmd
);
2767 install_element(node
, &config_help_cmd
);
2768 install_element(node
, &config_list_cmd
);
2769 install_element(node
, &show_cli_graph_cmd
);
2770 install_element(node
, &find_cmd
);
2772 install_element(node
, &config_write_cmd
);
2773 install_element(node
, &show_running_config_cmd
);
2775 install_element(node
, &autocomplete_cmd
);
2778 /* Initialize command interface. Install basic nodes and commands.
2780 * terminal = 0 -- vtysh / no logging, no config control
2781 * terminal = 1 -- normal daemon
2782 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2783 void cmd_init(int terminal
)
2785 struct utsname names
;
2787 if (array_size(node_names
) != NODE_TYPE_MAX
)
2788 assert(!"Update the CLI node description array!");
2793 /* register command preprocessors */
2794 hook_register(cmd_execute
, handle_pipe_action
);
2795 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2797 varhandlers
= list_new();
2799 /* Allocate initial top vector of commands. */
2800 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2802 /* Default host value settings. */
2803 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2804 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2805 if ((strcmp(names
.domainname
, "(none)") == 0))
2806 host
.domainname
= NULL
;
2808 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2810 host
.domainname
= NULL
;
2812 host
.password
= NULL
;
2814 host
.logfile
= NULL
;
2816 host
.noconfig
= (terminal
< 0);
2818 host
.motd
= default_motd
;
2819 host
.motdfile
= NULL
;
2821 /* Install top nodes. */
2822 install_node(&view_node
, NULL
);
2823 install_node(&enable_node
, NULL
);
2824 install_node(&auth_node
, NULL
);
2825 install_node(&auth_enable_node
, NULL
);
2826 install_node(&config_node
, config_write_host
);
2828 /* Each node's basic commands. */
2829 install_element(VIEW_NODE
, &show_version_cmd
);
2830 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2831 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2834 install_element(VIEW_NODE
, &config_list_cmd
);
2835 install_element(VIEW_NODE
, &config_exit_cmd
);
2836 install_element(VIEW_NODE
, &config_quit_cmd
);
2837 install_element(VIEW_NODE
, &config_help_cmd
);
2838 install_element(VIEW_NODE
, &config_enable_cmd
);
2839 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2840 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2841 install_element(VIEW_NODE
, &show_logging_cmd
);
2842 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2843 install_element(VIEW_NODE
, &echo_cmd
);
2844 install_element(VIEW_NODE
, &autocomplete_cmd
);
2845 install_element(VIEW_NODE
, &find_cmd
);
2847 install_element(ENABLE_NODE
, &config_end_cmd
);
2848 install_element(ENABLE_NODE
, &config_disable_cmd
);
2849 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2850 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2851 install_element(ENABLE_NODE
, &config_write_cmd
);
2852 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2853 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2855 install_default(CONFIG_NODE
);
2858 workqueue_cmd_init();
2862 install_element(CONFIG_NODE
, &hostname_cmd
);
2863 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2864 install_element(CONFIG_NODE
, &domainname_cmd
);
2865 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2866 install_element(CONFIG_NODE
, &frr_version_defaults_cmd
);
2867 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2870 install_element(CONFIG_NODE
, &password_cmd
);
2871 install_element(CONFIG_NODE
, &no_password_cmd
);
2872 install_element(CONFIG_NODE
, &enable_password_cmd
);
2873 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2875 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2876 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2877 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2878 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2879 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2880 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2881 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2882 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2883 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2884 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2885 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2886 install_element(CONFIG_NODE
,
2887 &no_config_log_record_priority_cmd
);
2888 install_element(CONFIG_NODE
,
2889 &config_log_timestamp_precision_cmd
);
2890 install_element(CONFIG_NODE
,
2891 &no_config_log_timestamp_precision_cmd
);
2892 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2893 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2894 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2895 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2896 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2897 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2898 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2900 vrf_install_commands();
2904 grammar_sandbox_init();
2908 void cmd_terminate()
2910 struct cmd_node
*cmd_node
;
2912 hook_unregister(cmd_execute
, handle_pipe_action
);
2913 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2916 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2917 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2918 // deleting the graph delets the cmd_element as
2920 graph_delete_graph(cmd_node
->cmdgraph
);
2921 vector_free(cmd_node
->cmd_vector
);
2922 hash_clean(cmd_node
->cmd_hash
, NULL
);
2923 hash_free(cmd_node
->cmd_hash
);
2924 cmd_node
->cmd_hash
= NULL
;
2927 vector_free(cmdvec
);
2932 XFREE(MTYPE_HOST
, host
.name
);
2933 if (host
.domainname
)
2934 XFREE(MTYPE_HOST
, host
.domainname
);
2936 XFREE(MTYPE_HOST
, host
.password
);
2937 if (host
.password_encrypt
)
2938 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2940 XFREE(MTYPE_HOST
, host
.enable
);
2941 if (host
.enable_encrypt
)
2942 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2944 XFREE(MTYPE_HOST
, host
.logfile
);
2946 XFREE(MTYPE_HOST
, host
.motdfile
);
2948 XFREE(MTYPE_HOST
, host
.config
);
2950 list_delete_and_null(&varhandlers
);