2 * CLI backend interface.
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
10 * This file is part of GNU Zebra.
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
48 #include "lib_errors.h"
49 #include "northbound_cli.h"
51 DEFINE_MTYPE_STATIC(LIB
, HOST
, "Host config")
52 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item")
59 /* clang-format off */
60 const struct message tokennames
[] = {
65 item(IPV4_PREFIX_TKN
),
67 item(IPV6_PREFIX_TKN
),
77 const char *const node_names
[] = {
80 "auth enable", // AUTH_ENABLE_NODE,
81 "enable", // ENABLE_NODE,
82 "config", // CONFIG_NODE,
83 "debug", // DEBUG_NODE,
84 "vrf debug", // VRF_DEBUG_NODE,
85 "northbound debug", // NORTHBOUND_DEBUG_NODE,
86 "vnc debug", // DEBUG_VNC_NODE,
87 "route-map debug", /* RMAP_DEBUG_NODE */
88 "resolver debug", /* RESOLVER_DEBUG_NODE */
90 "keychain", // KEYCHAIN_NODE,
91 "keychain key", // KEYCHAIN_KEY_NODE,
92 "static ip", // IP_NODE,
94 "interface", // INTERFACE_NODE,
95 "nexthop-group", // NH_GROUP_NODE,
96 "zebra", // ZEBRA_NODE,
97 "table", // TABLE_NODE,
99 "ripng", // RIPNG_NODE,
100 "babel", // BABEL_NODE,
101 "eigrp", // EIGRP_NODE,
103 "bgp vpnv4", // BGP_VPNV4_NODE,
104 "bgp vpnv6", // BGP_VPNV6_NODE,
105 "bgp ipv4 unicast", // BGP_IPV4_NODE,
106 "bgp ipv4 multicast", // BGP_IPV4M_NODE,
107 "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE,
108 "bgp ipv6", // BGP_IPV6_NODE,
109 "bgp ipv6 multicast", // BGP_IPV6M_NODE,
110 "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE,
111 "bgp vrf policy", // BGP_VRF_POLICY_NODE,
112 "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE,
113 "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE,
114 "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE,
115 "rfp defaults", // RFP_DEFAULTS_NODE,
116 "bgp evpn", // BGP_EVPN_NODE,
117 "ospf", // OSPF_NODE,
118 "ospf6", // OSPF6_NODE,
120 "ldp ipv4", // LDP_IPV4_NODE,
121 "ldp ipv6", // LDP_IPV6_NODE,
122 "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE,
123 "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE,
124 "ldp l2vpn", // LDP_L2VPN_NODE,
125 "ldp", // LDP_PSEUDOWIRE_NODE,
126 "isis", // ISIS_NODE,
127 "ipv4 access list", // ACCESS_NODE,
128 "ipv4 prefix list", // PREFIX_NODE,
129 "ipv6 access list", // ACCESS_IPV6_NODE,
130 "MAC access list", // ACCESS_MAC_NODE,
131 "ipv6 prefix list", // PREFIX_IPV6_NODE,
132 "as list", // AS_LIST_NODE,
133 "community list", // COMMUNITY_LIST_NODE,
134 "routemap", // RMAP_NODE,
135 "pbr-map", // PBRMAP_NODE,
136 "smux", // SMUX_NODE,
137 "dump", // DUMP_NODE,
138 "forwarding", // FORWARDING_NODE,
139 "protocol", // PROTOCOL_NODE,
140 "mpls", // MPLS_NODE,
143 "link-params", // LINK_PARAMS_NODE,
144 "bgp evpn vni", // BGP_EVPN_VNI_NODE,
146 "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE
148 "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
150 "bfd", /* BFD_NODE */
151 "bfd peer", /* BFD_PEER_NODE */
152 "openfabric", // OPENFABRIC_NODE
153 "vrrp", /* VRRP_NODE */
154 "bmp", /* BMP_NODE */
156 /* clang-format on */
158 /* Command vector which includes some level of command lists. Normally
159 each daemon maintains each own cmdvec. */
160 vector cmdvec
= NULL
;
162 /* Host information structure. */
166 * Returns host.name if any, otherwise
167 * it returns the system hostname.
169 const char *cmd_hostname_get(void)
175 * Returns unix domainname
177 const char *cmd_domainname_get(void)
179 return host
.domainname
;
182 /* Standard command node structures. */
183 static struct cmd_node auth_node
= {
184 AUTH_NODE
, "Password: ",
187 static struct cmd_node view_node
= {
191 static struct cmd_node auth_enable_node
= {
192 AUTH_ENABLE_NODE
, "Password: ",
195 static struct cmd_node enable_node
= {
199 static struct cmd_node config_node
= {CONFIG_NODE
, "%s(config)# ", 1};
201 static const struct facility_map
{
205 } syslog_facilities
[] = {
206 {LOG_KERN
, "kern", 1},
207 {LOG_USER
, "user", 2},
208 {LOG_MAIL
, "mail", 1},
209 {LOG_DAEMON
, "daemon", 1},
210 {LOG_AUTH
, "auth", 1},
211 {LOG_SYSLOG
, "syslog", 1},
213 {LOG_NEWS
, "news", 1},
214 {LOG_UUCP
, "uucp", 2},
215 {LOG_CRON
, "cron", 1},
219 {LOG_LOCAL0
, "local0", 6},
220 {LOG_LOCAL1
, "local1", 6},
221 {LOG_LOCAL2
, "local2", 6},
222 {LOG_LOCAL3
, "local3", 6},
223 {LOG_LOCAL4
, "local4", 6},
224 {LOG_LOCAL5
, "local5", 6},
225 {LOG_LOCAL6
, "local6", 6},
226 {LOG_LOCAL7
, "local7", 6},
230 static const char *facility_name(int facility
)
232 const struct facility_map
*fm
;
234 for (fm
= syslog_facilities
; fm
->name
; fm
++)
235 if (fm
->facility
== facility
)
240 static int facility_match(const char *str
)
242 const struct facility_map
*fm
;
244 for (fm
= syslog_facilities
; fm
->name
; fm
++)
245 if (!strncmp(str
, fm
->name
, fm
->match
))
250 static int level_match(const char *s
)
254 for (level
= 0; zlog_priority
[level
] != NULL
; level
++)
255 if (!strncmp(s
, zlog_priority
[level
], 2))
257 return ZLOG_DISABLED
;
260 /* This is called from main when a daemon is invoked with -v or --version. */
261 void print_version(const char *progname
)
263 printf("%s version %s\n", progname
, FRR_VERSION
);
264 printf("%s\n", FRR_COPYRIGHT
);
265 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
268 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
270 int cnt
= MAX(argc
- shift
, 0);
271 const char *argstr
[cnt
+ 1];
276 for (int i
= 0; i
< cnt
; i
++)
277 argstr
[i
] = argv
[i
+ shift
]->arg
;
279 return frrstr_join(argstr
, cnt
, " ");
282 vector
cmd_make_strvec(const char *string
)
287 const char *copy
= string
;
289 /* skip leading whitespace */
290 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
293 /* if the entire string was whitespace or a comment, return */
294 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
297 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
299 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
300 if (strlen(vector_slot(result
, i
)) == 0) {
301 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
302 vector_unset(result
, i
);
306 vector_compact(result
);
311 void cmd_free_strvec(vector v
)
313 frrstr_strvec_free(v
);
317 * Convenience function for accessing argv data.
321 * @param text definition snippet of the desired token
322 * @param index the starting index, and where to store the
323 * index of the found token if it exists
324 * @return 1 if found, 0 otherwise
326 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
329 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
330 if ((found
= strmatch(text
, argv
[i
]->text
)))
335 static unsigned int cmd_hash_key(const void *p
)
337 int size
= sizeof(p
);
339 return jhash(p
, size
, 0);
342 static bool cmd_hash_cmp(const void *a
, const void *b
)
347 /* Install top node of command vector. */
348 void install_node(struct cmd_node
*node
, int (*func
)(struct vty
*))
350 vector_set_index(cmdvec
, node
->node
, node
);
352 node
->cmdgraph
= graph_new();
353 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
355 struct cmd_token
*token
=
356 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
357 graph_new_node(node
->cmdgraph
, token
,
358 (void (*)(void *)) & cmd_token_del
);
359 node
->cmd_hash
= hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
,
363 /* Return prompt character of specified node. */
364 const char *cmd_prompt(enum node_type node
)
366 struct cmd_node
*cnode
;
368 cnode
= vector_slot(cmdvec
, node
);
369 return cnode
->prompt
;
372 /* Install a command into a node. */
373 void install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
375 struct cmd_node
*cnode
;
377 /* cmd_init hasn't been called */
379 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
384 cnode
= vector_lookup(cmdvec
, ntype
);
389 "\tnode %d (%s) does not exist.\n"
390 "\tplease call install_node() before install_element()\n",
391 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
395 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
398 "\tnode %d (%s) already has this command installed.\n"
399 "\tduplicate install_element call?\n",
400 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
404 assert(hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
));
406 struct graph
*graph
= graph_new();
407 struct cmd_token
*token
=
408 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
409 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
411 cmd_graph_parse(graph
, cmd
);
412 cmd_graph_names(graph
);
413 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
414 graph_delete_graph(graph
);
416 vector_set(cnode
->cmd_vector
, (void *)cmd
);
418 if (ntype
== VIEW_NODE
)
419 install_element(ENABLE_NODE
, cmd
);
422 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
424 struct cmd_node
*cnode
;
426 /* cmd_init hasn't been called */
428 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
433 cnode
= vector_lookup(cmdvec
, ntype
);
438 "\tnode %d (%s) does not exist.\n"
439 "\tplease call install_node() before uninstall_element()\n",
440 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
444 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
447 "\tnode %d (%s) does not have this command installed.\n"
448 "\tduplicate uninstall_element call?\n",
449 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
453 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
455 struct graph
*graph
= graph_new();
456 struct cmd_token
*token
=
457 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
458 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
460 cmd_graph_parse(graph
, cmd
);
461 cmd_graph_names(graph
);
462 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
463 graph_delete_graph(graph
);
465 if (ntype
== VIEW_NODE
)
466 uninstall_element(ENABLE_NODE
, cmd
);
470 static const unsigned char itoa64
[] =
471 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
473 static void to64(char *s
, long v
, int n
)
476 *s
++ = itoa64
[v
& 0x3f];
481 static char *zencrypt(const char *passwd
)
485 char *crypt(const char *, const char *);
487 gettimeofday(&tv
, 0);
489 to64(&salt
[0], random(), 3);
490 to64(&salt
[3], tv
.tv_usec
, 3);
493 return crypt(passwd
, salt
);
496 /* This function write configuration of this host. */
497 static int config_write_host(struct vty
*vty
)
499 if (cmd_hostname_get())
500 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
502 if (cmd_domainname_get())
503 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
505 /* The following are all configuration commands that are not sent to
506 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
507 * we would always display 'log syslog informational' in the config
508 * which would cause other daemons to then switch to syslog when they
511 if (strcmp(zlog_default
->protoname
, "WATCHFRR")) {
513 if (host
.password_encrypt
)
514 vty_out(vty
, "password 8 %s\n",
515 host
.password_encrypt
);
516 if (host
.enable_encrypt
)
517 vty_out(vty
, "enable password 8 %s\n",
518 host
.enable_encrypt
);
521 vty_out(vty
, "password %s\n", host
.password
);
523 vty_out(vty
, "enable password %s\n",
528 && (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
530 vty_out(vty
, "log file %s", host
.logfile
);
531 if (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
532 != zlog_default
->default_lvl
)
535 [zlog_default
->maxlvl
540 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
] != ZLOG_DISABLED
) {
541 vty_out(vty
, "log stdout");
542 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
]
543 != zlog_default
->default_lvl
)
546 [zlog_default
->maxlvl
547 [ZLOG_DEST_STDOUT
]]);
551 if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
552 vty_out(vty
, "no log monitor\n");
553 else if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]
554 != zlog_default
->default_lvl
)
555 vty_out(vty
, "log monitor %s\n",
556 zlog_priority
[zlog_default
->maxlvl
557 [ZLOG_DEST_MONITOR
]]);
559 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
) {
560 vty_out(vty
, "log syslog");
561 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
]
562 != zlog_default
->default_lvl
)
564 zlog_priority
[zlog_default
->maxlvl
565 [ZLOG_DEST_SYSLOG
]]);
569 if (zlog_default
->facility
!= LOG_DAEMON
)
570 vty_out(vty
, "log facility %s\n",
571 facility_name(zlog_default
->facility
));
573 if (zlog_default
->record_priority
== 1)
574 vty_out(vty
, "log record-priority\n");
576 if (zlog_default
->timestamp_precision
> 0)
577 vty_out(vty
, "log timestamp precision %d\n",
578 zlog_default
->timestamp_precision
);
581 vty_out(vty
, "service advanced-vty\n");
584 vty_out(vty
, "service password-encryption\n");
587 vty_out(vty
, "service terminal-length %d\n",
591 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
593 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
595 vty_out(vty
, "banner motd line %s\n", host
.motd
);
597 vty_out(vty
, "no banner motd\n");
600 if (debug_memstats_at_exit
)
601 vty_out(vty
, "!\ndebug memstats-at-exit\n");
606 /* Utility function for getting command graph. */
607 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
609 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
610 return cnode
->cmdgraph
;
613 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
615 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
616 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
622 * Compare function for cmd_token.
623 * Used with qsort to sort command completions.
625 static int compare_completions(const void *fst
, const void *snd
)
627 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
628 *secnd
= *(const struct cmd_token
* const *)snd
;
629 return strcmp(first
->text
, secnd
->text
);
633 * Takes a list of completions returned by command_complete,
634 * dedeuplicates them based on both text and description,
635 * sorts them, and returns them as a vector.
637 * @param completions linked list of cmd_token
638 * @return deduplicated and sorted vector with
640 vector
completions_to_vec(struct list
*completions
)
642 vector comps
= vector_init(VECTOR_MIN_SIZE
);
645 struct cmd_token
*token
, *cr
= NULL
;
646 unsigned int i
, exists
;
647 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
648 if (token
->type
== END_TKN
&& (cr
= token
))
651 // linear search for token in completions vector
653 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
654 struct cmd_token
*curr
= vector_slot(comps
, i
);
656 exists
= !strcmp(curr
->text
, token
->text
)
657 && !strcmp(curr
->desc
, token
->desc
);
659 exists
= !strcmp(curr
->text
, token
->text
);
660 #endif /* VTYSH_DEBUG */
664 vector_set(comps
, token
);
668 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
669 &compare_completions
);
671 // make <cr> the first element, if it is present
673 vector_set_index(comps
, vector_active(comps
), NULL
);
674 memmove(comps
->index
+ 1, comps
->index
,
675 (comps
->alloced
- 1) * sizeof(void *));
676 vector_set_index(comps
, 0, cr
);
682 * Generates a vector of cmd_token representing possible completions
683 * on the current input.
685 * @param vline the vectorized input line
686 * @param vty the vty with the node to match on
687 * @param status pointer to matcher status code
688 * @return vector of struct cmd_token * with possible completions
690 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
693 struct list
*completions
;
694 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
696 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
698 if (MATCHER_ERROR(rv
)) {
699 *status
= CMD_ERR_NO_MATCH
;
703 vector comps
= completions_to_vec(completions
);
704 list_delete(&completions
);
706 // set status code appropriately
707 switch (vector_active(comps
)) {
709 *status
= CMD_ERR_NO_MATCH
;
712 *status
= CMD_COMPLETE_FULL_MATCH
;
715 *status
= CMD_COMPLETE_LIST_MATCH
;
721 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
725 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
726 enum node_type onode
;
727 int orig_xpath_index
;
728 vector shifted_vline
;
732 orig_xpath_index
= vty
->xpath_index
;
733 vty
->node
= ENABLE_NODE
;
734 vty
->xpath_index
= 0;
735 /* We can try it on enable node, cos' the vty is authenticated
738 shifted_vline
= vector_init(vector_count(vline
));
740 for (index
= 1; index
< vector_active(vline
); index
++) {
741 vector_set_index(shifted_vline
, index
- 1,
742 vector_lookup(vline
, index
));
745 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
747 vector_free(shifted_vline
);
749 vty
->xpath_index
= orig_xpath_index
;
753 return cmd_complete_command_real(vline
, vty
, status
);
756 static struct list
*varhandlers
= NULL
;
758 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
762 const struct cmd_variable_handler
*cvh
;
766 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
768 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
769 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
771 if (cvh
->varname
&& (!token
->varname
772 || strcmp(cvh
->varname
, token
->varname
)))
774 cvh
->completions(tmpcomps
, token
);
782 for (i
= vector_active(tmpcomps
); i
; i
--) {
783 char *item
= vector_slot(tmpcomps
, i
- 1);
784 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
785 vector_set(comps
, item
);
787 XFREE(MTYPE_COMPLETION
, item
);
789 vector_free(tmpcomps
);
792 #define AUTOCOMP_INDENT 5
794 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
797 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
798 int lc
= AUTOCOMP_INDENT
;
799 size_t cs
= AUTOCOMP_INDENT
;
801 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
802 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
803 char *item
= vector_slot(comps
, j
);
804 itemlen
= strlen(item
);
806 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
807 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
809 if (lc
+ itemlen
+ 1 >= cols
) {
810 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
811 AUTOCOMP_INDENT
, "");
812 lc
= AUTOCOMP_INDENT
;
815 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
818 XFREE(MTYPE_COMPLETION
, item
);
819 vector_set_index(comps
, j
, NULL
);
824 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
829 for (; cvh
->completions
; cvh
++)
830 listnode_add(varhandlers
, (void *)cvh
);
833 DEFUN_HIDDEN (autocomplete
,
835 "autocomplete TYPE TEXT VARNAME",
836 "Autocompletion handler (internal, for vtysh)\n"
839 "cmd_token->varname\n")
841 struct cmd_token tok
;
842 vector comps
= vector_init(32);
845 memset(&tok
, 0, sizeof(tok
));
846 tok
.type
= atoi(argv
[1]->arg
);
847 tok
.text
= argv
[2]->arg
;
848 tok
.varname
= argv
[3]->arg
;
849 if (!strcmp(tok
.varname
, "-"))
852 cmd_variable_complete(&tok
, NULL
, comps
);
854 for (i
= 0; i
< vector_active(comps
); i
++) {
855 char *text
= vector_slot(comps
, i
);
856 vty_out(vty
, "%s\n", text
);
857 XFREE(MTYPE_COMPLETION
, text
);
865 * Generate possible tab-completions for the given input. This function only
866 * returns results that would result in a valid command if used as Readline
867 * completions (as is the case in vtysh). For instance, if the passed vline ends
868 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
870 * @param vline vectorized input line
872 * @param status location to store matcher status code in
873 * @return set of valid strings for use with Readline as tab-completions.
876 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
879 int original_node
= vty
->node
;
880 vector input_line
= vector_init(vector_count(vline
));
882 // if the first token is 'do' we'll want to execute the command in the
884 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
885 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
887 // construct the input line we'll be matching on
888 unsigned int offset
= (do_shortcut
) ? 1 : 0;
889 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
890 vector_set_index(input_line
, index
,
891 vector_lookup(vline
, index
+ offset
));
893 // get token completions -- this is a copying operation
894 vector comps
= NULL
, initial_comps
;
895 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
897 if (!MATCHER_ERROR(*status
)) {
898 assert(initial_comps
);
899 // filter out everything that is not suitable for a
901 comps
= vector_init(VECTOR_MIN_SIZE
);
902 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
904 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
905 if (token
->type
== WORD_TKN
)
906 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
908 else if (IS_VARYING_TOKEN(token
->type
)) {
909 const char *ref
= vector_lookup(
910 vline
, vector_active(vline
) - 1);
911 cmd_variable_complete(token
, ref
, comps
);
914 vector_free(initial_comps
);
916 // since we filtered results, we need to re-set status code
917 switch (vector_active(comps
)) {
919 *status
= CMD_ERR_NO_MATCH
;
922 *status
= CMD_COMPLETE_FULL_MATCH
;
925 *status
= CMD_COMPLETE_LIST_MATCH
;
928 // copy completions text into an array of char*
929 ret
= XMALLOC(MTYPE_TMP
,
930 (vector_active(comps
) + 1) * sizeof(char *));
932 for (i
= 0; i
< vector_active(comps
); i
++) {
933 ret
[i
] = vector_slot(comps
, i
);
935 // set the last element to NULL, because this array is used in
936 // a Readline completion_generator function which expects NULL
937 // as a sentinel value
941 } else if (initial_comps
)
942 vector_free(initial_comps
);
944 // comps should always be null here
947 // free the adjusted input line
948 vector_free(input_line
);
950 // reset vty->node to its original value
951 vty
->node
= original_node
;
956 /* return parent node */
957 /* MUST eventually converge on CONFIG_NODE */
958 enum node_type
node_parent(enum node_type node
)
962 assert(node
> CONFIG_NODE
);
967 case BGP_FLOWSPECV4_NODE
:
968 case BGP_FLOWSPECV6_NODE
:
969 case BGP_VRF_POLICY_NODE
:
970 case BGP_VNC_DEFAULTS_NODE
:
971 case BGP_VNC_NVE_GROUP_NODE
:
972 case BGP_VNC_L2_GROUP_NODE
:
983 case BGP_EVPN_VNI_NODE
:
986 case KEYCHAIN_KEY_NODE
:
989 case LINK_PARAMS_NODE
:
990 ret
= INTERFACE_NODE
;
996 case LDP_IPV4_IFACE_NODE
:
999 case LDP_IPV6_IFACE_NODE
:
1000 ret
= LDP_IPV6_NODE
;
1002 case LDP_PSEUDOWIRE_NODE
:
1003 ret
= LDP_L2VPN_NODE
;
1016 /* Execute command by argument vline vector. */
1017 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
1019 const struct cmd_element
**cmd
)
1021 struct list
*argv_list
;
1022 enum matcher_rv status
;
1023 const struct cmd_element
*matched_element
= NULL
;
1025 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
1026 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
1029 *cmd
= matched_element
;
1031 // if matcher error, return corresponding CMD_ERR
1032 if (MATCHER_ERROR(status
)) {
1034 list_delete(&argv_list
);
1036 case MATCHER_INCOMPLETE
:
1037 return CMD_ERR_INCOMPLETE
;
1038 case MATCHER_AMBIGUOUS
:
1039 return CMD_ERR_AMBIGUOUS
;
1041 return CMD_ERR_NO_MATCH
;
1045 // build argv array from argv list
1046 struct cmd_token
**argv
= XMALLOC(
1047 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
1048 struct listnode
*ln
;
1049 struct cmd_token
*token
;
1051 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
1054 int argc
= argv_list
->count
;
1057 if (matched_element
->daemon
)
1058 ret
= CMD_SUCCESS_DAEMON
;
1061 /* Clear array of enqueued configuration changes. */
1062 vty
->num_cfg_changes
= 0;
1063 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
1065 /* Regenerate candidate configuration if necessary. */
1066 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
1067 && running_config
->version
1068 > vty
->candidate_config
->version
)
1069 nb_config_replace(vty
->candidate_config
,
1070 running_config
, true);
1073 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
1076 // delete list and cmd_token's in it
1077 list_delete(&argv_list
);
1078 XFREE(MTYPE_TMP
, argv
);
1084 * Execute a given command, handling things like "do ..." and checking
1085 * whether the given command might apply at a parent node if doesn't
1086 * apply for the current node.
1088 * @param vline Command line input, vector of char* where each element is
1090 * @param vty The vty context in which the command should be executed.
1091 * @param cmd Pointer where the struct cmd_element of the matched command
1092 * will be stored, if any. May be set to NULL if this info is
1094 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1095 * @return The status of the command that has been executed or an error code
1096 * as to why no command could be executed.
1098 int cmd_execute_command(vector vline
, struct vty
*vty
,
1099 const struct cmd_element
**cmd
, int vtysh
)
1101 int ret
, saved_ret
= 0;
1102 enum node_type onode
, try_node
;
1103 int orig_xpath_index
;
1105 onode
= try_node
= vty
->node
;
1106 orig_xpath_index
= vty
->xpath_index
;
1108 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1109 vector shifted_vline
;
1112 vty
->node
= ENABLE_NODE
;
1113 vty
->xpath_index
= 0;
1114 /* We can try it on enable node, cos' the vty is authenticated
1117 shifted_vline
= vector_init(vector_count(vline
));
1119 for (index
= 1; index
< vector_active(vline
); index
++)
1120 vector_set_index(shifted_vline
, index
- 1,
1121 vector_lookup(vline
, index
));
1123 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1126 vector_free(shifted_vline
);
1128 vty
->xpath_index
= orig_xpath_index
;
1133 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
1138 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1139 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1140 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1141 /* This assumes all nodes above CONFIG_NODE are childs of
1143 while (vty
->node
> CONFIG_NODE
) {
1144 try_node
= node_parent(try_node
);
1145 vty
->node
= try_node
;
1146 if (vty
->xpath_index
> 0)
1148 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1150 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1151 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1152 || ret
== CMD_NOT_MY_INSTANCE
1153 || ret
== CMD_WARNING_CONFIG_FAILED
)
1156 /* no command succeeded, reset the vty to the original node */
1158 vty
->xpath_index
= orig_xpath_index
;
1161 /* return command status for original node */
1166 * Execute a given command, matching it strictly against the current node.
1167 * This mode is used when reading config files.
1169 * @param vline Command line input, vector of char* where each element is
1171 * @param vty The vty context in which the command should be executed.
1172 * @param cmd Pointer where the struct cmd_element* of the matched command
1173 * will be stored, if any. May be set to NULL if this info is
1175 * @return The status of the command that has been executed or an error code
1176 * as to why no command could be executed.
1178 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1179 const struct cmd_element
**cmd
)
1181 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1185 * Hook for preprocessing command string before executing.
1187 * All subscribers are called with the raw command string that is to be
1188 * executed. If any changes are to be made, a new string should be allocated
1189 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1190 * is then responsible for freeing this string.
1192 * All processing functions must be mutually exclusive in their action, i.e. if
1193 * one subscriber decides to modify the command, all others must not modify it
1194 * when called. Feeding the output of one processing command into a subsequent
1195 * one is not supported.
1197 * This hook is intentionally internal to the command processing system.
1200 * The raw command string.
1203 * The result of any processing.
1205 DECLARE_HOOK(cmd_execute
,
1206 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1207 (vty
, cmd_in
, cmd_out
));
1208 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1209 (vty
, cmd_in
, cmd_out
));
1211 /* Hook executed after a CLI command. */
1212 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1214 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1218 * cmd_execute hook subscriber to handle `|` actions.
1220 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1224 char *orig
, *working
, *token
, *u
;
1225 char *pipe
= strstr(cmd_in
, "| ");
1230 /* duplicate string for processing purposes, not including pipe */
1231 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1233 /* retrieve action */
1234 token
= strsep(&working
, " ");
1237 /* match result to known actions */
1238 if (strmatch(token
, "include")) {
1239 /* the remaining text should be a regexp */
1240 char *regexp
= working
;
1243 vty_out(vty
, "%% Need a regexp to filter with\n");
1247 bool succ
= vty_set_include(vty
, regexp
);
1250 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1253 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1257 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1262 XFREE(MTYPE_TMP
, orig
);
1266 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1269 vty_set_include(vty
, NULL
);
1274 int cmd_execute(struct vty
*vty
, const char *cmd
,
1275 const struct cmd_element
**matched
, int vtysh
)
1278 char *cmd_out
= NULL
;
1279 const char *cmd_exec
;
1282 hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1283 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1285 vline
= cmd_make_strvec(cmd_exec
);
1288 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1289 cmd_free_strvec(vline
);
1294 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1296 XFREE(MTYPE_TMP
, cmd_out
);
1303 * Parse one line of config, walking up the parse tree attempting to find a
1306 * @param vty The vty context in which the command should be executed.
1307 * @param cmd Pointer where the struct cmd_element* of the match command
1308 * will be stored, if any. May be set to NULL if this info is
1310 * @param use_daemon Boolean to control whether or not we match on
1311 * CMD_SUCCESS_DAEMON
1313 * @return The status of the command that has been executed or an error code
1314 * as to why no command could be executed.
1316 int command_config_read_one_line(struct vty
*vty
,
1317 const struct cmd_element
**cmd
,
1318 uint32_t line_num
, int use_daemon
)
1323 vline
= cmd_make_strvec(vty
->buf
);
1325 /* In case of comment line */
1329 /* Execute configuration command : this is strict match */
1330 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1332 // Climb the tree and try the command again at each node
1333 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1334 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1335 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1336 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1337 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1338 && vty
->node
!= CONFIG_NODE
) {
1339 int saved_node
= vty
->node
;
1340 int saved_xpath_index
= vty
->xpath_index
;
1342 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1343 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1344 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1345 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1346 && vty
->node
> CONFIG_NODE
) {
1347 vty
->node
= node_parent(vty
->node
);
1348 if (vty
->xpath_index
> 0)
1350 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1353 // If climbing the tree did not work then ignore the command and
1354 // stay at the same node
1355 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1356 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1357 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1358 vty
->node
= saved_node
;
1359 vty
->xpath_index
= saved_xpath_index
;
1363 if (ret
!= CMD_SUCCESS
&&
1364 ret
!= CMD_WARNING
&&
1365 ret
!= CMD_SUCCESS_DAEMON
) {
1366 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1368 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1369 ve
->line_num
= line_num
;
1371 vty
->error
= list_new();
1373 listnode_add(vty
->error
, ve
);
1376 cmd_free_strvec(vline
);
1381 /* Configuration make from file. */
1382 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1384 int ret
, error_ret
= 0;
1387 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1390 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1392 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1393 && ret
!= CMD_ERR_NOTHING_TODO
)
1404 /* Configuration from terminal */
1405 DEFUN (config_terminal
,
1406 config_terminal_cmd
,
1407 "configure [terminal]",
1408 "Configuration from vty interface\n"
1409 "Configuration terminal\n")
1411 return vty_config_enter(vty
, false, false);
1414 /* Enable command */
1418 "Turn on privileged mode command\n")
1420 /* If enable password is NULL, change to ENABLE_NODE */
1421 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1422 || vty
->type
== VTY_SHELL_SERV
)
1423 vty
->node
= ENABLE_NODE
;
1425 vty
->node
= AUTH_ENABLE_NODE
;
1430 /* Disable command */
1434 "Turn off privileged mode command\n")
1436 if (vty
->node
== ENABLE_NODE
)
1437 vty
->node
= VIEW_NODE
;
1441 /* Down vty node level. */
1445 "Exit current mode and down to previous mode\n")
1451 void cmd_exit(struct vty
*vty
)
1453 switch (vty
->node
) {
1459 vty
->status
= VTY_CLOSE
;
1462 vty
->node
= ENABLE_NODE
;
1463 vty_config_exit(vty
);
1465 case INTERFACE_NODE
:
1478 case LDP_L2VPN_NODE
:
1480 case OPENFABRIC_NODE
:
1486 vty
->node
= CONFIG_NODE
;
1489 case BGP_IPV4M_NODE
:
1490 case BGP_IPV4L_NODE
:
1491 case BGP_VPNV4_NODE
:
1492 case BGP_VPNV6_NODE
:
1493 case BGP_FLOWSPECV4_NODE
:
1494 case BGP_FLOWSPECV6_NODE
:
1495 case BGP_VRF_POLICY_NODE
:
1496 case BGP_VNC_DEFAULTS_NODE
:
1497 case BGP_VNC_NVE_GROUP_NODE
:
1498 case BGP_VNC_L2_GROUP_NODE
:
1500 case BGP_IPV6M_NODE
:
1502 case BGP_IPV6L_NODE
:
1504 vty
->node
= BGP_NODE
;
1506 case BGP_EVPN_VNI_NODE
:
1507 vty
->node
= BGP_EVPN_NODE
;
1511 vty
->node
= LDP_NODE
;
1513 case LDP_IPV4_IFACE_NODE
:
1514 vty
->node
= LDP_IPV4_NODE
;
1516 case LDP_IPV6_IFACE_NODE
:
1517 vty
->node
= LDP_IPV6_NODE
;
1519 case LDP_PSEUDOWIRE_NODE
:
1520 vty
->node
= LDP_L2VPN_NODE
;
1522 case KEYCHAIN_KEY_NODE
:
1523 vty
->node
= KEYCHAIN_NODE
;
1525 case LINK_PARAMS_NODE
:
1526 vty
->node
= INTERFACE_NODE
;
1529 vty
->node
= BFD_NODE
;
1535 if (vty
->xpath_index
> 0)
1543 "Exit current mode and down to previous mode\n")
1545 return config_exit(self
, vty
, argc
, argv
);
1549 /* End of configuration. */
1553 "End current mode and change to enable mode.\n")
1556 vty_config_exit(vty
);
1557 vty
->node
= ENABLE_NODE
;
1564 DEFUN (show_version
,
1568 "Displays zebra version\n")
1570 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1571 cmd_hostname_get() ? cmd_hostname_get() : "");
1572 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1573 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1578 /* Help display function for all node. */
1582 "Description of the interactive help system\n")
1585 "Quagga VTY provides advanced help feature. When you need help,\n\
1586 anytime at the command line please press '?'.\n\
1588 If nothing matches, the help list will be empty and you must backup\n\
1589 until entering a '?' shows the available options.\n\
1590 Two styles of help are provided:\n\
1591 1. Full help is available when you are ready to enter a\n\
1592 command argument (e.g. 'show ?') and describes each possible\n\
1594 2. Partial help is provided when an abbreviated argument is entered\n\
1595 and you want to know what arguments match the input\n\
1596 (e.g. 'show me?'.)\n\n");
1600 static void permute(struct graph_node
*start
, struct vty
*vty
)
1602 static struct list
*position
= NULL
;
1604 position
= list_new();
1606 struct cmd_token
*stok
= start
->data
;
1607 struct graph_node
*gnn
;
1608 struct listnode
*ln
;
1611 listnode_add(position
, start
);
1612 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1613 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1614 struct cmd_token
*tok
= gn
->data
;
1615 if (tok
->attr
== CMD_ATTR_HIDDEN
1616 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1618 else if (tok
->type
== END_TKN
|| gn
== start
) {
1620 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1621 struct cmd_token
*tt
= gnn
->data
;
1622 if (tt
->type
< SPECIAL_TKN
)
1623 vty_out(vty
, " %s", tt
->text
);
1626 vty_out(vty
, "...");
1630 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1631 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1640 list_delete_node(position
, listtail(position
));
1643 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1645 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1648 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1650 /* loop over all commands at this node */
1651 const struct cmd_element
*element
= NULL
;
1652 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1654 if ((element
= vector_slot(node
->cmd_vector
, i
))
1655 && element
->attr
!= CMD_ATTR_DEPRECATED
1656 && element
->attr
!= CMD_ATTR_HIDDEN
)
1657 vty_out(vty
, " %s\n", element
->string
);
1662 /* Help display function for all node. */
1665 "list [permutations]",
1666 "Print command list\n"
1667 "Print all possible command permutations\n")
1669 return cmd_list_cmds(vty
, argc
== 2);
1672 DEFUN (show_commandtree
,
1673 show_commandtree_cmd
,
1674 "show commandtree [permutations]",
1676 "Show command tree\n"
1677 "Permutations that we are interested in\n")
1679 return cmd_list_cmds(vty
, argc
== 3);
1682 DEFUN_HIDDEN(show_cli_graph
,
1687 "Dump current command space as DOT graph\n")
1689 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1690 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1692 vty_out(vty
, "%s\n", dot
);
1693 XFREE(MTYPE_TMP
, dot
);
1697 static int vty_write_config(struct vty
*vty
)
1700 struct cmd_node
*node
;
1705 nb_cli_show_config_prepare(running_config
, false);
1707 if (vty
->type
== VTY_TERM
) {
1708 vty_out(vty
, "\nCurrent configuration:\n");
1709 vty_out(vty
, "!\n");
1712 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1713 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1714 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1715 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1716 vty_out(vty
, "!\n");
1718 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1719 if ((node
= vector_slot(cmdvec
, i
)) && node
->func
1720 && (node
->vtysh
|| vty
->type
!= VTY_SHELL
)) {
1721 if ((*node
->func
)(vty
))
1722 vty_out(vty
, "!\n");
1725 if (vty
->type
== VTY_TERM
) {
1726 vty_out(vty
, "end\n");
1732 static int file_write_config(struct vty
*vty
)
1735 char *config_file
, *slash
;
1736 char *config_file_tmp
= NULL
;
1737 char *config_file_sav
= NULL
;
1738 int ret
= CMD_WARNING
;
1739 struct vty
*file_vty
;
1740 struct stat conf_stat
;
1745 /* Check and see if we are operating under vtysh configuration */
1746 if (host
.config
== NULL
) {
1748 "Can't save to configuration file, using vtysh.\n");
1753 config_file
= host
.config
;
1756 #define O_DIRECTORY 0
1758 slash
= strrchr(config_file
, '/');
1760 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1761 config_dir
[slash
- config_file
] = '\0';
1762 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1763 XFREE(MTYPE_TMP
, config_dir
);
1765 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1766 /* if dirfd is invalid, directory sync fails, but we're still OK */
1768 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1769 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1770 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1771 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1774 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1775 sprintf(config_file_tmp
, "%s.XXXXXX", config_file
);
1777 /* Open file to configuration write. */
1778 fd
= mkstemp(config_file_tmp
);
1780 vty_out(vty
, "Can't open configuration file %s.\n",
1784 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1785 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1786 config_file_tmp
, safe_strerror(errno
), errno
);
1790 /* Make vty for configuration file. */
1791 file_vty
= vty_new();
1793 file_vty
->type
= VTY_FILE
;
1795 /* Config file header print. */
1796 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1797 vty_time_print(file_vty
, 1);
1798 vty_out(file_vty
, "!\n");
1799 vty_write_config(file_vty
);
1800 vty_close(file_vty
);
1802 if (stat(config_file
, &conf_stat
) >= 0) {
1803 if (unlink(config_file_sav
) != 0)
1804 if (errno
!= ENOENT
) {
1806 "Can't unlink backup configuration file %s.\n",
1810 if (link(config_file
, config_file_sav
) != 0) {
1812 "Can't backup old configuration file %s.\n",
1819 if (rename(config_file_tmp
, config_file
) != 0) {
1820 vty_out(vty
, "Can't save configuration file %s.\n",
1827 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1831 if (ret
!= CMD_SUCCESS
)
1832 unlink(config_file_tmp
);
1835 XFREE(MTYPE_TMP
, config_file_tmp
);
1836 XFREE(MTYPE_TMP
, config_file_sav
);
1840 /* Write current configuration into file. */
1842 DEFUN (config_write
,
1844 "write [<file|memory|terminal>]",
1845 "Write running configuration to memory, network, or terminal\n"
1846 "Write to configuration file\n"
1847 "Write configuration currently in memory\n"
1848 "Write configuration to terminal\n")
1850 const int idx_type
= 1;
1852 // if command was 'write terminal' or 'write memory'
1853 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1854 return vty_write_config(vty
);
1857 return file_write_config(vty
);
1860 /* ALIAS_FIXME for 'write <terminal|memory>' */
1861 DEFUN (show_running_config
,
1862 show_running_config_cmd
,
1863 "show running-config",
1865 "running configuration (same as write terminal)\n")
1867 return vty_write_config(vty
);
1870 /* ALIAS_FIXME for 'write file' */
1871 DEFUN (copy_runningconf_startupconf
,
1872 copy_runningconf_startupconf_cmd
,
1873 "copy running-config startup-config",
1874 "Copy configuration\n"
1875 "Copy running config to... \n"
1876 "Copy running config to startup config (same as write file/memory)\n")
1878 return file_write_config(vty
);
1882 /* Write startup configuration into the terminal. */
1883 DEFUN (show_startup_config
,
1884 show_startup_config_cmd
,
1885 "show startup-config",
1887 "Contents of startup configuration\n")
1894 if (host
.config
== NULL
)
1897 confp
= fopen(host
.config
, "r");
1898 if (confp
== NULL
) {
1899 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1900 host
.config
, safe_strerror(errno
));
1904 while (fgets(buf
, BUFSIZ
, confp
)) {
1907 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1911 vty_out(vty
, "%s\n", buf
);
1919 int cmd_domainname_set(const char *domainname
)
1921 XFREE(MTYPE_HOST
, host
.domainname
);
1922 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1926 /* Hostname configuration */
1927 DEFUN(config_domainname
,
1930 "Set system's domain name\n"
1931 "This system's domain name\n")
1933 struct cmd_token
*word
= argv
[1];
1935 if (!isalpha((unsigned char)word
->arg
[0])) {
1936 vty_out(vty
, "Please specify string starting with alphabet\n");
1937 return CMD_WARNING_CONFIG_FAILED
;
1940 return cmd_domainname_set(word
->arg
);
1943 DEFUN(config_no_domainname
,
1945 "no domainname [DOMAINNAME]",
1947 "Reset system's domain name\n"
1948 "domain name of this router\n")
1950 return cmd_domainname_set(NULL
);
1953 int cmd_hostname_set(const char *hostname
)
1955 XFREE(MTYPE_HOST
, host
.name
);
1956 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1960 /* Hostname configuration */
1961 DEFUN (config_hostname
,
1964 "Set system's network name\n"
1965 "This system's network name\n")
1967 struct cmd_token
*word
= argv
[1];
1969 if (!isalnum((unsigned char)word
->arg
[0])) {
1971 "Please specify string starting with alphabet or number\n");
1972 return CMD_WARNING_CONFIG_FAILED
;
1975 /* With reference to RFC 1123 Section 2.1 */
1976 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1977 vty_out(vty
, "Hostname length should be less than %d chars\n",
1979 return CMD_WARNING_CONFIG_FAILED
;
1982 return cmd_hostname_set(word
->arg
);
1985 DEFUN (config_no_hostname
,
1987 "no hostname [HOSTNAME]",
1989 "Reset system's network name\n"
1990 "Host name of this router\n")
1992 return cmd_hostname_set(NULL
);
1995 /* VTY interface password set. */
1996 DEFUN (config_password
,
1998 "password [(8-8)] WORD",
1999 "Modify the terminal connection password\n"
2000 "Specifies a HIDDEN password will follow\n"
2001 "The password string\n")
2005 if (argc
== 3) // '8' was specified
2008 XFREE(MTYPE_HOST
, host
.password
);
2009 host
.password
= NULL
;
2010 if (host
.password_encrypt
)
2011 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2012 host
.password_encrypt
=
2013 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2017 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2019 "Please specify string starting with alphanumeric\n");
2020 return CMD_WARNING_CONFIG_FAILED
;
2024 XFREE(MTYPE_HOST
, host
.password
);
2025 host
.password
= NULL
;
2028 if (host
.password_encrypt
)
2029 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2030 host
.password_encrypt
=
2031 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2033 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2038 /* VTY interface password delete. */
2039 DEFUN (no_config_password
,
2043 "Modify the terminal connection password\n")
2045 bool warned
= false;
2047 if (host
.password
) {
2048 if (!vty_shell_serv(vty
)) {
2049 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2052 XFREE(MTYPE_HOST
, host
.password
);
2054 host
.password
= NULL
;
2056 if (host
.password_encrypt
) {
2057 if (!warned
&& !vty_shell_serv(vty
))
2058 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2059 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2061 host
.password_encrypt
= NULL
;
2066 /* VTY enable password set. */
2067 DEFUN (config_enable_password
,
2068 enable_password_cmd
,
2069 "enable password [(8-8)] WORD",
2070 "Modify enable password parameters\n"
2071 "Assign the privileged level password\n"
2072 "Specifies a HIDDEN password will follow\n"
2073 "The HIDDEN 'enable' password string\n")
2078 /* Crypt type is specified. */
2080 if (argv
[idx_8
]->arg
[0] == '8') {
2082 XFREE(MTYPE_HOST
, host
.enable
);
2085 if (host
.enable_encrypt
)
2086 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2087 host
.enable_encrypt
=
2088 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2092 vty_out(vty
, "Unknown encryption type.\n");
2093 return CMD_WARNING_CONFIG_FAILED
;
2097 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2099 "Please specify string starting with alphanumeric\n");
2100 return CMD_WARNING_CONFIG_FAILED
;
2104 XFREE(MTYPE_HOST
, host
.enable
);
2107 /* Plain password input. */
2109 if (host
.enable_encrypt
)
2110 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2111 host
.enable_encrypt
=
2112 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2114 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2119 /* VTY enable password delete. */
2120 DEFUN (no_config_enable_password
,
2121 no_enable_password_cmd
,
2122 "no enable password",
2124 "Modify enable password parameters\n"
2125 "Assign the privileged level password\n")
2127 bool warned
= false;
2130 if (!vty_shell_serv(vty
)) {
2131 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2134 XFREE(MTYPE_HOST
, host
.enable
);
2138 if (host
.enable_encrypt
) {
2139 if (!warned
&& !vty_shell_serv(vty
))
2140 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2141 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2143 host
.enable_encrypt
= NULL
;
2148 DEFUN (service_password_encrypt
,
2149 service_password_encrypt_cmd
,
2150 "service password-encryption",
2151 "Set up miscellaneous service\n"
2152 "Enable encrypted passwords\n")
2159 if (host
.password
) {
2160 if (host
.password_encrypt
)
2161 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2162 host
.password_encrypt
=
2163 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2166 if (host
.enable_encrypt
)
2167 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2168 host
.enable_encrypt
=
2169 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2175 DEFUN (no_service_password_encrypt
,
2176 no_service_password_encrypt_cmd
,
2177 "no service password-encryption",
2179 "Set up miscellaneous service\n"
2180 "Enable encrypted passwords\n")
2187 if (host
.password_encrypt
)
2188 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2189 host
.password_encrypt
= NULL
;
2191 if (host
.enable_encrypt
)
2192 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2193 host
.enable_encrypt
= NULL
;
2198 DEFUN (config_terminal_length
,
2199 config_terminal_length_cmd
,
2200 "terminal length (0-512)",
2201 "Set terminal line parameters\n"
2202 "Set number of lines on a screen\n"
2203 "Number of lines on screen (0 for no pausing)\n")
2207 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2212 DEFUN (config_terminal_no_length
,
2213 config_terminal_no_length_cmd
,
2214 "terminal no length",
2215 "Set terminal line parameters\n"
2217 "Set number of lines on a screen\n")
2223 DEFUN (service_terminal_length
,
2224 service_terminal_length_cmd
,
2225 "service terminal-length (0-512)",
2226 "Set up miscellaneous service\n"
2227 "System wide terminal length configuration\n"
2228 "Number of lines of VTY (0 means no line control)\n")
2232 host
.lines
= atoi(argv
[idx_number
]->arg
);
2237 DEFUN (no_service_terminal_length
,
2238 no_service_terminal_length_cmd
,
2239 "no service terminal-length [(0-512)]",
2241 "Set up miscellaneous service\n"
2242 "System wide terminal length configuration\n"
2243 "Number of lines of VTY (0 means no line control)\n")
2249 DEFUN_HIDDEN (do_echo
,
2252 "Echo a message back to the vty\n"
2253 "The message to echo\n")
2257 vty_out(vty
, "%s\n",
2258 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2260 XFREE(MTYPE_TMP
, message
);
2264 DEFUN (config_logmsg
,
2266 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2267 "Send a message to enabled logging destinations\n"
2269 "The message to send\n")
2271 int idx_log_level
= 1;
2272 int idx_message
= 2;
2276 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2277 return CMD_ERR_NO_MATCH
;
2280 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2282 XFREE(MTYPE_TMP
, message
);
2287 DEFUN (show_logging
,
2291 "Show current logging configuration\n")
2293 struct zlog
*zl
= zlog_default
;
2295 vty_out(vty
, "Syslog logging: ");
2296 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
2297 vty_out(vty
, "disabled");
2299 vty_out(vty
, "level %s, facility %s, ident %s",
2300 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
2301 facility_name(zl
->facility
), zl
->ident
);
2304 vty_out(vty
, "Stdout logging: ");
2305 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
2306 vty_out(vty
, "disabled");
2308 vty_out(vty
, "level %s",
2309 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
2312 vty_out(vty
, "Monitor logging: ");
2313 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
2314 vty_out(vty
, "disabled");
2316 vty_out(vty
, "level %s",
2317 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
2320 vty_out(vty
, "File logging: ");
2321 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
2322 vty_out(vty
, "disabled");
2324 vty_out(vty
, "level %s, filename %s",
2325 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
2329 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
2330 vty_out(vty
, "Record priority: %s\n",
2331 (zl
->record_priority
? "enabled" : "disabled"));
2332 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2337 DEFUN (config_log_stdout
,
2338 config_log_stdout_cmd
,
2339 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2341 "Set stdout logging level\n"
2344 int idx_log_level
= 2;
2346 if (argc
== idx_log_level
) {
2347 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2352 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2353 return CMD_ERR_NO_MATCH
;
2354 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2358 DEFUN (no_config_log_stdout
,
2359 no_config_log_stdout_cmd
,
2360 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2363 "Cancel logging to stdout\n"
2366 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2370 DEFUN (config_log_monitor
,
2371 config_log_monitor_cmd
,
2372 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2374 "Set terminal line (monitor) logging level\n"
2377 int idx_log_level
= 2;
2379 if (argc
== idx_log_level
) {
2380 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2385 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2386 return CMD_ERR_NO_MATCH
;
2387 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2391 DEFUN (no_config_log_monitor
,
2392 no_config_log_monitor_cmd
,
2393 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2396 "Disable terminal line (monitor) logging\n"
2399 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2403 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2407 const char *fullpath
;
2409 /* Path detection. */
2410 if (!IS_DIRECTORY_SEP(*fname
)) {
2411 char cwd
[MAXPATHLEN
+ 1];
2412 cwd
[MAXPATHLEN
] = '\0';
2414 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2415 flog_err_sys(EC_LIB_SYSTEM_CALL
,
2416 "config_log_file: Unable to alloc mem!");
2417 return CMD_WARNING_CONFIG_FAILED
;
2420 p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2);
2421 sprintf(p
, "%s/%s", cwd
, fname
);
2426 ret
= zlog_set_file(fullpath
, loglevel
);
2428 XFREE(MTYPE_TMP
, p
);
2432 vty_out(vty
, "can't open logfile %s\n", fname
);
2433 return CMD_WARNING_CONFIG_FAILED
;
2436 XFREE(MTYPE_HOST
, host
.logfile
);
2438 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2440 #if defined(HAVE_CUMULUS)
2441 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2442 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2447 void command_setup_early_logging(const char *dest
, const char *level
)
2452 int nlevel
= level_match(level
);
2454 if (nlevel
!= ZLOG_DISABLED
)
2455 zlog_default
->default_lvl
= nlevel
;
2461 if (strcmp(dest
, "stdout") == 0) {
2462 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2466 if (strcmp(dest
, "syslog") == 0) {
2467 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2471 token
= strstr(dest
, ":");
2477 set_log_file(NULL
, token
, zlog_default
->default_lvl
);
2480 DEFUN (config_log_file
,
2481 config_log_file_cmd
,
2482 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2485 "Logging filename\n"
2488 int idx_filename
= 2;
2489 int idx_log_levels
= 3;
2492 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2494 return CMD_ERR_NO_MATCH
;
2495 return set_log_file(vty
, argv
[idx_filename
]->arg
, level
);
2497 return set_log_file(vty
, argv
[idx_filename
]->arg
,
2498 zlog_default
->default_lvl
);
2501 static void disable_log_file(void)
2505 XFREE(MTYPE_HOST
, host
.logfile
);
2507 host
.logfile
= NULL
;
2510 DEFUN (no_config_log_file
,
2511 no_config_log_file_cmd
,
2512 "no log file [FILENAME [LEVEL]]",
2515 "Cancel logging to file\n"
2516 "Logging file name\n"
2523 DEFUN (config_log_syslog
,
2524 config_log_syslog_cmd
,
2525 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2527 "Set syslog logging level\n"
2530 int idx_log_levels
= 2;
2534 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2536 return CMD_ERR_NO_MATCH
;
2537 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2540 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2545 DEFUN (no_config_log_syslog
,
2546 no_config_log_syslog_cmd
,
2547 "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>]",
2550 "Cancel logging to syslog\n"
2554 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2558 DEFUN (config_log_facility
,
2559 config_log_facility_cmd
,
2560 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2562 "Facility parameter for syslog messages\n"
2566 int facility
= facility_match(argv
[idx_target
]->arg
);
2568 zlog_default
->facility
= facility
;
2572 DEFUN (no_config_log_facility
,
2573 no_config_log_facility_cmd
,
2574 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2577 "Reset syslog facility to default (daemon)\n"
2580 zlog_default
->facility
= LOG_DAEMON
;
2584 DEFUN (config_log_record_priority
,
2585 config_log_record_priority_cmd
,
2586 "log record-priority",
2588 "Log the priority of the message within the message\n")
2590 zlog_default
->record_priority
= 1;
2594 DEFUN (no_config_log_record_priority
,
2595 no_config_log_record_priority_cmd
,
2596 "no log record-priority",
2599 "Do not log the priority of the message within the message\n")
2601 zlog_default
->record_priority
= 0;
2605 DEFUN (config_log_timestamp_precision
,
2606 config_log_timestamp_precision_cmd
,
2607 "log timestamp precision (0-6)",
2609 "Timestamp configuration\n"
2610 "Set the timestamp precision\n"
2611 "Number of subsecond digits\n")
2614 zlog_default
->timestamp_precision
=
2615 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2619 DEFUN (no_config_log_timestamp_precision
,
2620 no_config_log_timestamp_precision_cmd
,
2621 "no log timestamp precision",
2624 "Timestamp configuration\n"
2625 "Reset the timestamp precision to the default value of 0\n")
2627 zlog_default
->timestamp_precision
= 0;
2631 DEFUN (debug_memstats
,
2633 "[no] debug memstats-at-exit",
2636 "Print memory type statistics at exit\n")
2638 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2642 int cmd_banner_motd_file(const char *file
)
2644 int success
= CMD_SUCCESS
;
2649 rpath
= realpath(file
, p
);
2651 return CMD_ERR_NO_FILE
;
2652 in
= strstr(rpath
, SYSCONFDIR
);
2654 XFREE(MTYPE_HOST
, host
.motdfile
);
2655 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2657 success
= CMD_WARNING_CONFIG_FAILED
;
2662 void cmd_banner_motd_line(const char *line
)
2665 XFREE(MTYPE_HOST
, host
.motd
);
2666 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2669 DEFUN (banner_motd_file
,
2670 banner_motd_file_cmd
,
2671 "banner motd file FILE",
2674 "Banner from a file\n"
2678 const char *filename
= argv
[idx_file
]->arg
;
2679 int cmd
= cmd_banner_motd_file(filename
);
2681 if (cmd
== CMD_ERR_NO_FILE
)
2682 vty_out(vty
, "%s does not exist", filename
);
2683 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2684 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2689 DEFUN (banner_motd_line
,
2690 banner_motd_line_cmd
,
2691 "banner motd line LINE...",
2694 "Banner from an input\n"
2700 argv_find(argv
, argc
, "LINE", &idx
);
2701 motd
= argv_concat(argv
, argc
, idx
);
2703 cmd_banner_motd_line(motd
);
2704 XFREE(MTYPE_TMP
, motd
);
2709 DEFUN (banner_motd_default
,
2710 banner_motd_default_cmd
,
2711 "banner motd default",
2712 "Set banner string\n"
2713 "Strings for motd\n"
2716 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2720 DEFUN (no_banner_motd
,
2724 "Set banner string\n"
2725 "Strings for motd\n")
2729 XFREE(MTYPE_HOST
, host
.motdfile
);
2730 host
.motdfile
= NULL
;
2737 "Find CLI command matching a regular expression\n"
2738 "Search pattern (POSIX regex)\n")
2740 char *pattern
= argv
[1]->arg
;
2741 const struct cmd_node
*node
;
2742 const struct cmd_element
*cli
;
2747 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2752 vty_out(vty
, "%% Invalid {...} expression\n");
2755 vty_out(vty
, "%% Bad repetition operator\n");
2758 vty_out(vty
, "%% Regex syntax error\n");
2761 vty_out(vty
, "%% Invalid collating element\n");
2764 vty_out(vty
, "%% Invalid character class name\n");
2768 "%% Regex ended with escape character (\\)\n");
2772 "%% Invalid number in \\digit construction\n");
2775 vty_out(vty
, "%% Unbalanced square brackets\n");
2778 vty_out(vty
, "%% Unbalanced parentheses\n");
2781 vty_out(vty
, "%% Unbalanced braces\n");
2785 "%% Invalid endpoint in range expression\n");
2788 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2796 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2797 node
= vector_slot(cmdvec
, i
);
2800 clis
= node
->cmd_vector
;
2801 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2802 cli
= vector_slot(clis
, j
);
2804 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0)
2805 vty_out(vty
, " (%s) %s\n",
2806 node_names
[node
->node
], cli
->string
);
2815 /* Set config filename. Called from vty.c */
2816 void host_config_set(const char *filename
)
2818 XFREE(MTYPE_HOST
, host
.config
);
2819 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2822 const char *host_config_get(void)
2827 void install_default(enum node_type node
)
2829 install_element(node
, &config_exit_cmd
);
2830 install_element(node
, &config_quit_cmd
);
2831 install_element(node
, &config_end_cmd
);
2832 install_element(node
, &config_help_cmd
);
2833 install_element(node
, &config_list_cmd
);
2834 install_element(node
, &show_cli_graph_cmd
);
2835 install_element(node
, &find_cmd
);
2837 install_element(node
, &config_write_cmd
);
2838 install_element(node
, &show_running_config_cmd
);
2840 install_element(node
, &autocomplete_cmd
);
2842 nb_cli_install_default(node
);
2845 /* Initialize command interface. Install basic nodes and commands.
2847 * terminal = 0 -- vtysh / no logging, no config control
2848 * terminal = 1 -- normal daemon
2849 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2850 void cmd_init(int terminal
)
2852 struct utsname names
;
2854 if (array_size(node_names
) != NODE_TYPE_MAX
)
2855 assert(!"Update the CLI node description array!");
2860 /* register command preprocessors */
2861 hook_register(cmd_execute
, handle_pipe_action
);
2862 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2864 varhandlers
= list_new();
2866 /* Allocate initial top vector of commands. */
2867 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2869 /* Default host value settings. */
2870 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2871 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2872 if ((strcmp(names
.domainname
, "(none)") == 0))
2873 host
.domainname
= NULL
;
2875 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2877 host
.domainname
= NULL
;
2879 host
.password
= NULL
;
2881 host
.logfile
= NULL
;
2883 host
.noconfig
= (terminal
< 0);
2885 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2886 host
.motdfile
= NULL
;
2888 /* Install top nodes. */
2889 install_node(&view_node
, NULL
);
2890 install_node(&enable_node
, NULL
);
2891 install_node(&auth_node
, NULL
);
2892 install_node(&auth_enable_node
, NULL
);
2893 install_node(&config_node
, config_write_host
);
2895 /* Each node's basic commands. */
2896 install_element(VIEW_NODE
, &show_version_cmd
);
2897 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2900 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2902 install_element(VIEW_NODE
, &config_list_cmd
);
2903 install_element(VIEW_NODE
, &config_exit_cmd
);
2904 install_element(VIEW_NODE
, &config_quit_cmd
);
2905 install_element(VIEW_NODE
, &config_help_cmd
);
2906 install_element(VIEW_NODE
, &config_enable_cmd
);
2907 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2908 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2909 install_element(VIEW_NODE
, &show_logging_cmd
);
2910 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2911 install_element(VIEW_NODE
, &echo_cmd
);
2912 install_element(VIEW_NODE
, &autocomplete_cmd
);
2913 install_element(VIEW_NODE
, &find_cmd
);
2915 install_element(ENABLE_NODE
, &config_end_cmd
);
2916 install_element(ENABLE_NODE
, &config_disable_cmd
);
2917 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2918 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2919 install_element(ENABLE_NODE
, &config_write_cmd
);
2920 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2921 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2923 install_default(CONFIG_NODE
);
2926 workqueue_cmd_init();
2930 install_element(CONFIG_NODE
, &hostname_cmd
);
2931 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2932 install_element(CONFIG_NODE
, &domainname_cmd
);
2933 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2936 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2938 install_element(CONFIG_NODE
, &password_cmd
);
2939 install_element(CONFIG_NODE
, &no_password_cmd
);
2940 install_element(CONFIG_NODE
, &enable_password_cmd
);
2941 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2943 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2944 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2945 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2946 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2947 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2948 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2949 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2950 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2951 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2952 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2953 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2954 install_element(CONFIG_NODE
,
2955 &no_config_log_record_priority_cmd
);
2956 install_element(CONFIG_NODE
,
2957 &config_log_timestamp_precision_cmd
);
2958 install_element(CONFIG_NODE
,
2959 &no_config_log_timestamp_precision_cmd
);
2960 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2961 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2962 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2963 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2964 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2965 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2966 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2967 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2969 vrf_install_commands();
2973 grammar_sandbox_init();
2977 void cmd_terminate(void)
2979 struct cmd_node
*cmd_node
;
2981 hook_unregister(cmd_execute
, handle_pipe_action
);
2982 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2985 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2986 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2987 // deleting the graph delets the cmd_element as
2989 graph_delete_graph(cmd_node
->cmdgraph
);
2990 vector_free(cmd_node
->cmd_vector
);
2991 hash_clean(cmd_node
->cmd_hash
, NULL
);
2992 hash_free(cmd_node
->cmd_hash
);
2993 cmd_node
->cmd_hash
= NULL
;
2996 vector_free(cmdvec
);
3000 XFREE(MTYPE_HOST
, host
.name
);
3001 XFREE(MTYPE_HOST
, host
.domainname
);
3002 XFREE(MTYPE_HOST
, host
.password
);
3003 XFREE(MTYPE_HOST
, host
.password_encrypt
);
3004 XFREE(MTYPE_HOST
, host
.enable
);
3005 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
3006 XFREE(MTYPE_HOST
, host
.logfile
);
3007 XFREE(MTYPE_HOST
, host
.motdfile
);
3008 XFREE(MTYPE_HOST
, host
.config
);
3009 XFREE(MTYPE_HOST
, host
.motd
);
3011 list_delete(&varhandlers
);