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
= {
185 .prompt
= "Password: ",
188 static struct cmd_node view_node
= {
193 static struct cmd_node auth_enable_node
= {
194 .node
= AUTH_ENABLE_NODE
,
195 .prompt
= "Password: ",
198 static struct cmd_node enable_node
= {
203 static struct cmd_node config_node
= {
205 .prompt
= "%s(config)# ",
208 static const struct facility_map
{
212 } syslog_facilities
[] = {
213 {LOG_KERN
, "kern", 1},
214 {LOG_USER
, "user", 2},
215 {LOG_MAIL
, "mail", 1},
216 {LOG_DAEMON
, "daemon", 1},
217 {LOG_AUTH
, "auth", 1},
218 {LOG_SYSLOG
, "syslog", 1},
220 {LOG_NEWS
, "news", 1},
221 {LOG_UUCP
, "uucp", 2},
222 {LOG_CRON
, "cron", 1},
226 {LOG_LOCAL0
, "local0", 6},
227 {LOG_LOCAL1
, "local1", 6},
228 {LOG_LOCAL2
, "local2", 6},
229 {LOG_LOCAL3
, "local3", 6},
230 {LOG_LOCAL4
, "local4", 6},
231 {LOG_LOCAL5
, "local5", 6},
232 {LOG_LOCAL6
, "local6", 6},
233 {LOG_LOCAL7
, "local7", 6},
237 static const char *facility_name(int facility
)
239 const struct facility_map
*fm
;
241 for (fm
= syslog_facilities
; fm
->name
; fm
++)
242 if (fm
->facility
== facility
)
247 static int facility_match(const char *str
)
249 const struct facility_map
*fm
;
251 for (fm
= syslog_facilities
; fm
->name
; fm
++)
252 if (!strncmp(str
, fm
->name
, fm
->match
))
257 static int level_match(const char *s
)
261 for (level
= 0; zlog_priority
[level
] != NULL
; level
++)
262 if (!strncmp(s
, zlog_priority
[level
], 2))
264 return ZLOG_DISABLED
;
267 /* This is called from main when a daemon is invoked with -v or --version. */
268 void print_version(const char *progname
)
270 printf("%s version %s\n", progname
, FRR_VERSION
);
271 printf("%s\n", FRR_COPYRIGHT
);
272 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
275 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
277 int cnt
= MAX(argc
- shift
, 0);
278 const char *argstr
[cnt
+ 1];
283 for (int i
= 0; i
< cnt
; i
++)
284 argstr
[i
] = argv
[i
+ shift
]->arg
;
286 return frrstr_join(argstr
, cnt
, " ");
289 vector
cmd_make_strvec(const char *string
)
294 const char *copy
= string
;
296 /* skip leading whitespace */
297 while (isspace((unsigned char)*copy
) && *copy
!= '\0')
300 /* if the entire string was whitespace or a comment, return */
301 if (*copy
== '\0' || *copy
== '!' || *copy
== '#')
304 vector result
= frrstr_split_vec(copy
, "\n\r\t ");
306 for (unsigned int i
= 0; i
< vector_active(result
); i
++) {
307 if (strlen(vector_slot(result
, i
)) == 0) {
308 XFREE(MTYPE_TMP
, vector_slot(result
, i
));
309 vector_unset(result
, i
);
313 vector_compact(result
);
318 void cmd_free_strvec(vector v
)
320 frrstr_strvec_free(v
);
324 * Convenience function for accessing argv data.
328 * @param text definition snippet of the desired token
329 * @param index the starting index, and where to store the
330 * index of the found token if it exists
331 * @return 1 if found, 0 otherwise
333 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
336 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
337 if ((found
= strmatch(text
, argv
[i
]->text
)))
342 static unsigned int cmd_hash_key(const void *p
)
344 int size
= sizeof(p
);
346 return jhash(p
, size
, 0);
349 static bool cmd_hash_cmp(const void *a
, const void *b
)
354 /* Install top node of command vector. */
355 void install_node(struct cmd_node
*node
, int (*func
)(struct vty
*))
357 vector_set_index(cmdvec
, node
->node
, node
);
359 node
->cmdgraph
= graph_new();
360 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
362 struct cmd_token
*token
=
363 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
364 graph_new_node(node
->cmdgraph
, token
,
365 (void (*)(void *)) & cmd_token_del
);
366 node
->cmd_hash
= hash_create_size(16, cmd_hash_key
, cmd_hash_cmp
,
370 /* Return prompt character of specified node. */
371 const char *cmd_prompt(enum node_type node
)
373 struct cmd_node
*cnode
;
375 cnode
= vector_slot(cmdvec
, node
);
376 return cnode
->prompt
;
379 /* Install a command into a node. */
380 void install_element(enum node_type ntype
, const struct cmd_element
*cmd
)
382 struct cmd_node
*cnode
;
384 /* cmd_init hasn't been called */
386 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
391 cnode
= vector_lookup(cmdvec
, ntype
);
396 "\tnode %d (%s) does not exist.\n"
397 "\tplease call install_node() before install_element()\n",
398 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
402 if (hash_lookup(cnode
->cmd_hash
, (void *)cmd
) != NULL
) {
405 "\tnode %d (%s) already has this command installed.\n"
406 "\tduplicate install_element call?\n",
407 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
411 assert(hash_get(cnode
->cmd_hash
, (void *)cmd
, hash_alloc_intern
));
413 struct graph
*graph
= graph_new();
414 struct cmd_token
*token
=
415 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
416 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
418 cmd_graph_parse(graph
, cmd
);
419 cmd_graph_names(graph
);
420 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
421 graph_delete_graph(graph
);
423 vector_set(cnode
->cmd_vector
, (void *)cmd
);
425 if (ntype
== VIEW_NODE
)
426 install_element(ENABLE_NODE
, cmd
);
429 void uninstall_element(enum node_type ntype
, const struct cmd_element
*cmd
)
431 struct cmd_node
*cnode
;
433 /* cmd_init hasn't been called */
435 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
440 cnode
= vector_lookup(cmdvec
, ntype
);
445 "\tnode %d (%s) does not exist.\n"
446 "\tplease call install_node() before uninstall_element()\n",
447 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
451 if (hash_release(cnode
->cmd_hash
, (void *)cmd
) == NULL
) {
454 "\tnode %d (%s) does not have this command installed.\n"
455 "\tduplicate uninstall_element call?\n",
456 cmd
->name
, cmd
->string
, ntype
, node_names
[ntype
]);
460 vector_unset_value(cnode
->cmd_vector
, (void *)cmd
);
462 struct graph
*graph
= graph_new();
463 struct cmd_token
*token
=
464 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
465 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
467 cmd_graph_parse(graph
, cmd
);
468 cmd_graph_names(graph
);
469 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
470 graph_delete_graph(graph
);
472 if (ntype
== VIEW_NODE
)
473 uninstall_element(ENABLE_NODE
, cmd
);
477 static const unsigned char itoa64
[] =
478 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
480 static void to64(char *s
, long v
, int n
)
483 *s
++ = itoa64
[v
& 0x3f];
488 static char *zencrypt(const char *passwd
)
492 char *crypt(const char *, const char *);
494 gettimeofday(&tv
, 0);
496 to64(&salt
[0], random(), 3);
497 to64(&salt
[3], tv
.tv_usec
, 3);
500 return crypt(passwd
, salt
);
503 /* This function write configuration of this host. */
504 static int config_write_host(struct vty
*vty
)
506 if (cmd_hostname_get())
507 vty_out(vty
, "hostname %s\n", cmd_hostname_get());
509 if (cmd_domainname_get())
510 vty_out(vty
, "domainname %s\n", cmd_domainname_get());
512 /* The following are all configuration commands that are not sent to
513 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
514 * we would always display 'log syslog informational' in the config
515 * which would cause other daemons to then switch to syslog when they
518 if (strcmp(zlog_default
->protoname
, "WATCHFRR")) {
520 if (host
.password_encrypt
)
521 vty_out(vty
, "password 8 %s\n",
522 host
.password_encrypt
);
523 if (host
.enable_encrypt
)
524 vty_out(vty
, "enable password 8 %s\n",
525 host
.enable_encrypt
);
528 vty_out(vty
, "password %s\n", host
.password
);
530 vty_out(vty
, "enable password %s\n",
535 && (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
537 vty_out(vty
, "log file %s", host
.logfile
);
538 if (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
539 != zlog_default
->default_lvl
)
542 [zlog_default
->maxlvl
547 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
] != ZLOG_DISABLED
) {
548 vty_out(vty
, "log stdout");
549 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
]
550 != zlog_default
->default_lvl
)
553 [zlog_default
->maxlvl
554 [ZLOG_DEST_STDOUT
]]);
558 if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
559 vty_out(vty
, "no log monitor\n");
560 else if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]
561 != zlog_default
->default_lvl
)
562 vty_out(vty
, "log monitor %s\n",
563 zlog_priority
[zlog_default
->maxlvl
564 [ZLOG_DEST_MONITOR
]]);
566 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
) {
567 vty_out(vty
, "log syslog");
568 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
]
569 != zlog_default
->default_lvl
)
571 zlog_priority
[zlog_default
->maxlvl
572 [ZLOG_DEST_SYSLOG
]]);
576 if (zlog_default
->facility
!= LOG_DAEMON
)
577 vty_out(vty
, "log facility %s\n",
578 facility_name(zlog_default
->facility
));
580 if (zlog_default
->record_priority
== 1)
581 vty_out(vty
, "log record-priority\n");
583 if (zlog_default
->timestamp_precision
> 0)
584 vty_out(vty
, "log timestamp precision %d\n",
585 zlog_default
->timestamp_precision
);
588 vty_out(vty
, "service advanced-vty\n");
591 vty_out(vty
, "service password-encryption\n");
594 vty_out(vty
, "service terminal-length %d\n",
598 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
600 && strncmp(host
.motd
, FRR_DEFAULT_MOTD
,
602 vty_out(vty
, "banner motd line %s\n", host
.motd
);
604 vty_out(vty
, "no banner motd\n");
607 if (debug_memstats_at_exit
)
608 vty_out(vty
, "!\ndebug memstats-at-exit\n");
613 /* Utility function for getting command graph. */
614 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
616 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
617 return cnode
->cmdgraph
;
620 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
622 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
623 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
629 * Compare function for cmd_token.
630 * Used with qsort to sort command completions.
632 static int compare_completions(const void *fst
, const void *snd
)
634 const struct cmd_token
*first
= *(const struct cmd_token
* const *)fst
,
635 *secnd
= *(const struct cmd_token
* const *)snd
;
636 return strcmp(first
->text
, secnd
->text
);
640 * Takes a list of completions returned by command_complete,
641 * dedeuplicates them based on both text and description,
642 * sorts them, and returns them as a vector.
644 * @param completions linked list of cmd_token
645 * @return deduplicated and sorted vector with
647 vector
completions_to_vec(struct list
*completions
)
649 vector comps
= vector_init(VECTOR_MIN_SIZE
);
652 struct cmd_token
*token
, *cr
= NULL
;
653 unsigned int i
, exists
;
654 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
655 if (token
->type
== END_TKN
&& (cr
= token
))
658 // linear search for token in completions vector
660 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
661 struct cmd_token
*curr
= vector_slot(comps
, i
);
663 exists
= !strcmp(curr
->text
, token
->text
)
664 && !strcmp(curr
->desc
, token
->desc
);
666 exists
= !strcmp(curr
->text
, token
->text
);
667 #endif /* VTYSH_DEBUG */
671 vector_set(comps
, token
);
675 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
676 &compare_completions
);
678 // make <cr> the first element, if it is present
680 vector_set_index(comps
, vector_active(comps
), NULL
);
681 memmove(comps
->index
+ 1, comps
->index
,
682 (comps
->alloced
- 1) * sizeof(void *));
683 vector_set_index(comps
, 0, cr
);
689 * Generates a vector of cmd_token representing possible completions
690 * on the current input.
692 * @param vline the vectorized input line
693 * @param vty the vty with the node to match on
694 * @param status pointer to matcher status code
695 * @return vector of struct cmd_token * with possible completions
697 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
700 struct list
*completions
;
701 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
703 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
705 if (MATCHER_ERROR(rv
)) {
706 *status
= CMD_ERR_NO_MATCH
;
710 vector comps
= completions_to_vec(completions
);
711 list_delete(&completions
);
713 // set status code appropriately
714 switch (vector_active(comps
)) {
716 *status
= CMD_ERR_NO_MATCH
;
719 *status
= CMD_COMPLETE_FULL_MATCH
;
722 *status
= CMD_COMPLETE_LIST_MATCH
;
728 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
732 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
733 enum node_type onode
;
734 int orig_xpath_index
;
735 vector shifted_vline
;
739 orig_xpath_index
= vty
->xpath_index
;
740 vty
->node
= ENABLE_NODE
;
741 vty
->xpath_index
= 0;
742 /* We can try it on enable node, cos' the vty is authenticated
745 shifted_vline
= vector_init(vector_count(vline
));
747 for (index
= 1; index
< vector_active(vline
); index
++) {
748 vector_set_index(shifted_vline
, index
- 1,
749 vector_lookup(vline
, index
));
752 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
754 vector_free(shifted_vline
);
756 vty
->xpath_index
= orig_xpath_index
;
760 return cmd_complete_command_real(vline
, vty
, status
);
763 static struct list
*varhandlers
= NULL
;
765 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
769 const struct cmd_variable_handler
*cvh
;
773 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
775 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
776 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
778 if (cvh
->varname
&& (!token
->varname
779 || strcmp(cvh
->varname
, token
->varname
)))
781 cvh
->completions(tmpcomps
, token
);
789 for (i
= vector_active(tmpcomps
); i
; i
--) {
790 char *item
= vector_slot(tmpcomps
, i
- 1);
791 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
792 vector_set(comps
, item
);
794 XFREE(MTYPE_COMPLETION
, item
);
796 vector_free(tmpcomps
);
799 #define AUTOCOMP_INDENT 5
801 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
804 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
805 int lc
= AUTOCOMP_INDENT
;
806 size_t cs
= AUTOCOMP_INDENT
;
808 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
809 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
810 char *item
= vector_slot(comps
, j
);
811 itemlen
= strlen(item
);
813 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
814 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
816 if (lc
+ itemlen
+ 1 >= cols
) {
817 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
818 AUTOCOMP_INDENT
, "");
819 lc
= AUTOCOMP_INDENT
;
822 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
825 XFREE(MTYPE_COMPLETION
, item
);
826 vector_set_index(comps
, j
, NULL
);
831 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
836 for (; cvh
->completions
; cvh
++)
837 listnode_add(varhandlers
, (void *)cvh
);
840 DEFUN_HIDDEN (autocomplete
,
842 "autocomplete TYPE TEXT VARNAME",
843 "Autocompletion handler (internal, for vtysh)\n"
846 "cmd_token->varname\n")
848 struct cmd_token tok
;
849 vector comps
= vector_init(32);
852 memset(&tok
, 0, sizeof(tok
));
853 tok
.type
= atoi(argv
[1]->arg
);
854 tok
.text
= argv
[2]->arg
;
855 tok
.varname
= argv
[3]->arg
;
856 if (!strcmp(tok
.varname
, "-"))
859 cmd_variable_complete(&tok
, NULL
, comps
);
861 for (i
= 0; i
< vector_active(comps
); i
++) {
862 char *text
= vector_slot(comps
, i
);
863 vty_out(vty
, "%s\n", text
);
864 XFREE(MTYPE_COMPLETION
, text
);
872 * Generate possible tab-completions for the given input. This function only
873 * returns results that would result in a valid command if used as Readline
874 * completions (as is the case in vtysh). For instance, if the passed vline ends
875 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
877 * @param vline vectorized input line
879 * @param status location to store matcher status code in
880 * @return set of valid strings for use with Readline as tab-completions.
883 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
886 int original_node
= vty
->node
;
887 vector input_line
= vector_init(vector_count(vline
));
889 // if the first token is 'do' we'll want to execute the command in the
891 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
892 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
894 // construct the input line we'll be matching on
895 unsigned int offset
= (do_shortcut
) ? 1 : 0;
896 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
897 vector_set_index(input_line
, index
,
898 vector_lookup(vline
, index
+ offset
));
900 // get token completions -- this is a copying operation
901 vector comps
= NULL
, initial_comps
;
902 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
904 if (!MATCHER_ERROR(*status
)) {
905 assert(initial_comps
);
906 // filter out everything that is not suitable for a
908 comps
= vector_init(VECTOR_MIN_SIZE
);
909 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
911 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
912 if (token
->type
== WORD_TKN
)
913 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
915 else if (IS_VARYING_TOKEN(token
->type
)) {
916 const char *ref
= vector_lookup(
917 vline
, vector_active(vline
) - 1);
918 cmd_variable_complete(token
, ref
, comps
);
921 vector_free(initial_comps
);
923 // since we filtered results, we need to re-set status code
924 switch (vector_active(comps
)) {
926 *status
= CMD_ERR_NO_MATCH
;
929 *status
= CMD_COMPLETE_FULL_MATCH
;
932 *status
= CMD_COMPLETE_LIST_MATCH
;
935 // copy completions text into an array of char*
936 ret
= XMALLOC(MTYPE_TMP
,
937 (vector_active(comps
) + 1) * sizeof(char *));
939 for (i
= 0; i
< vector_active(comps
); i
++) {
940 ret
[i
] = vector_slot(comps
, i
);
942 // set the last element to NULL, because this array is used in
943 // a Readline completion_generator function which expects NULL
944 // as a sentinel value
948 } else if (initial_comps
)
949 vector_free(initial_comps
);
951 // comps should always be null here
954 // free the adjusted input line
955 vector_free(input_line
);
957 // reset vty->node to its original value
958 vty
->node
= original_node
;
963 /* return parent node */
964 /* MUST eventually converge on CONFIG_NODE */
965 enum node_type
node_parent(enum node_type node
)
969 assert(node
> CONFIG_NODE
);
974 case BGP_FLOWSPECV4_NODE
:
975 case BGP_FLOWSPECV6_NODE
:
976 case BGP_VRF_POLICY_NODE
:
977 case BGP_VNC_DEFAULTS_NODE
:
978 case BGP_VNC_NVE_GROUP_NODE
:
979 case BGP_VNC_L2_GROUP_NODE
:
990 case BGP_EVPN_VNI_NODE
:
993 case KEYCHAIN_KEY_NODE
:
996 case LINK_PARAMS_NODE
:
997 ret
= INTERFACE_NODE
;
1003 case LDP_IPV4_IFACE_NODE
:
1004 ret
= LDP_IPV4_NODE
;
1006 case LDP_IPV6_IFACE_NODE
:
1007 ret
= LDP_IPV6_NODE
;
1009 case LDP_PSEUDOWIRE_NODE
:
1010 ret
= LDP_L2VPN_NODE
;
1023 /* Execute command by argument vline vector. */
1024 static int cmd_execute_command_real(vector vline
, enum cmd_filter_type filter
,
1026 const struct cmd_element
**cmd
)
1028 struct list
*argv_list
;
1029 enum matcher_rv status
;
1030 const struct cmd_element
*matched_element
= NULL
;
1032 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
1033 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
1036 *cmd
= matched_element
;
1038 // if matcher error, return corresponding CMD_ERR
1039 if (MATCHER_ERROR(status
)) {
1041 list_delete(&argv_list
);
1043 case MATCHER_INCOMPLETE
:
1044 return CMD_ERR_INCOMPLETE
;
1045 case MATCHER_AMBIGUOUS
:
1046 return CMD_ERR_AMBIGUOUS
;
1048 return CMD_ERR_NO_MATCH
;
1052 // build argv array from argv list
1053 struct cmd_token
**argv
= XMALLOC(
1054 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
1055 struct listnode
*ln
;
1056 struct cmd_token
*token
;
1058 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
1061 int argc
= argv_list
->count
;
1064 if (matched_element
->daemon
)
1065 ret
= CMD_SUCCESS_DAEMON
;
1068 /* Clear array of enqueued configuration changes. */
1069 vty
->num_cfg_changes
= 0;
1070 memset(&vty
->cfg_changes
, 0, sizeof(vty
->cfg_changes
));
1072 /* Regenerate candidate configuration if necessary. */
1073 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
1074 && running_config
->version
1075 > vty
->candidate_config
->version
)
1076 nb_config_replace(vty
->candidate_config
,
1077 running_config
, true);
1080 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
1083 // delete list and cmd_token's in it
1084 list_delete(&argv_list
);
1085 XFREE(MTYPE_TMP
, argv
);
1091 * Execute a given command, handling things like "do ..." and checking
1092 * whether the given command might apply at a parent node if doesn't
1093 * apply for the current node.
1095 * @param vline Command line input, vector of char* where each element is
1097 * @param vty The vty context in which the command should be executed.
1098 * @param cmd Pointer where the struct cmd_element of the matched command
1099 * will be stored, if any. May be set to NULL if this info is
1101 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1102 * @return The status of the command that has been executed or an error code
1103 * as to why no command could be executed.
1105 int cmd_execute_command(vector vline
, struct vty
*vty
,
1106 const struct cmd_element
**cmd
, int vtysh
)
1108 int ret
, saved_ret
= 0;
1109 enum node_type onode
, try_node
;
1110 int orig_xpath_index
;
1112 onode
= try_node
= vty
->node
;
1113 orig_xpath_index
= vty
->xpath_index
;
1115 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
1116 vector shifted_vline
;
1119 vty
->node
= ENABLE_NODE
;
1120 vty
->xpath_index
= 0;
1121 /* We can try it on enable node, cos' the vty is authenticated
1124 shifted_vline
= vector_init(vector_count(vline
));
1126 for (index
= 1; index
< vector_active(vline
); index
++)
1127 vector_set_index(shifted_vline
, index
- 1,
1128 vector_lookup(vline
, index
));
1130 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
1133 vector_free(shifted_vline
);
1135 vty
->xpath_index
= orig_xpath_index
;
1140 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
1145 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1146 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1147 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
) {
1148 /* This assumes all nodes above CONFIG_NODE are childs of
1150 while (vty
->node
> CONFIG_NODE
) {
1151 try_node
= node_parent(try_node
);
1152 vty
->node
= try_node
;
1153 if (vty
->xpath_index
> 0)
1155 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1157 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
1158 || ret
== CMD_ERR_AMBIGUOUS
|| ret
== CMD_ERR_INCOMPLETE
1159 || ret
== CMD_NOT_MY_INSTANCE
1160 || ret
== CMD_WARNING_CONFIG_FAILED
)
1163 /* no command succeeded, reset the vty to the original node */
1165 vty
->xpath_index
= orig_xpath_index
;
1168 /* return command status for original node */
1173 * Execute a given command, matching it strictly against the current node.
1174 * This mode is used when reading config files.
1176 * @param vline Command line input, vector of char* where each element is
1178 * @param vty The vty context in which the command should be executed.
1179 * @param cmd Pointer where the struct cmd_element* of the matched command
1180 * will be stored, if any. May be set to NULL if this info is
1182 * @return The status of the command that has been executed or an error code
1183 * as to why no command could be executed.
1185 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1186 const struct cmd_element
**cmd
)
1188 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1192 * Hook for preprocessing command string before executing.
1194 * All subscribers are called with the raw command string that is to be
1195 * executed. If any changes are to be made, a new string should be allocated
1196 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1197 * is then responsible for freeing this string.
1199 * All processing functions must be mutually exclusive in their action, i.e. if
1200 * one subscriber decides to modify the command, all others must not modify it
1201 * when called. Feeding the output of one processing command into a subsequent
1202 * one is not supported.
1204 * This hook is intentionally internal to the command processing system.
1207 * The raw command string.
1210 * The result of any processing.
1212 DECLARE_HOOK(cmd_execute
,
1213 (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1214 (vty
, cmd_in
, cmd_out
));
1215 DEFINE_HOOK(cmd_execute
, (struct vty
*vty
, const char *cmd_in
, char **cmd_out
),
1216 (vty
, cmd_in
, cmd_out
));
1218 /* Hook executed after a CLI command. */
1219 DECLARE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1221 DEFINE_KOOH(cmd_execute_done
, (struct vty
*vty
, const char *cmd_exec
),
1225 * cmd_execute hook subscriber to handle `|` actions.
1227 static int handle_pipe_action(struct vty
*vty
, const char *cmd_in
,
1231 char *orig
, *working
, *token
, *u
;
1232 char *pipe
= strstr(cmd_in
, "| ");
1237 /* duplicate string for processing purposes, not including pipe */
1238 orig
= working
= XSTRDUP(MTYPE_TMP
, pipe
+ 2);
1240 /* retrieve action */
1241 token
= strsep(&working
, " ");
1244 /* match result to known actions */
1245 if (strmatch(token
, "include")) {
1246 /* the remaining text should be a regexp */
1247 char *regexp
= working
;
1250 vty_out(vty
, "%% Need a regexp to filter with\n");
1254 bool succ
= vty_set_include(vty
, regexp
);
1257 vty_out(vty
, "%% Bad regexp '%s'\n", regexp
);
1260 *cmd_out
= XSTRDUP(MTYPE_TMP
, cmd_in
);
1264 vty_out(vty
, "%% Unknown action '%s'\n", token
);
1269 XFREE(MTYPE_TMP
, orig
);
1273 static int handle_pipe_action_done(struct vty
*vty
, const char *cmd_exec
)
1276 vty_set_include(vty
, NULL
);
1281 int cmd_execute(struct vty
*vty
, const char *cmd
,
1282 const struct cmd_element
**matched
, int vtysh
)
1285 char *cmd_out
= NULL
;
1286 const char *cmd_exec
;
1289 hook_call(cmd_execute
, vty
, cmd
, &cmd_out
);
1290 cmd_exec
= cmd_out
? (const char *)cmd_out
: cmd
;
1292 vline
= cmd_make_strvec(cmd_exec
);
1295 ret
= cmd_execute_command(vline
, vty
, matched
, vtysh
);
1296 cmd_free_strvec(vline
);
1301 hook_call(cmd_execute_done
, vty
, cmd_exec
);
1303 XFREE(MTYPE_TMP
, cmd_out
);
1310 * Parse one line of config, walking up the parse tree attempting to find a
1313 * @param vty The vty context in which the command should be executed.
1314 * @param cmd Pointer where the struct cmd_element* of the match command
1315 * will be stored, if any. May be set to NULL if this info is
1317 * @param use_daemon Boolean to control whether or not we match on
1318 * CMD_SUCCESS_DAEMON
1320 * @return The status of the command that has been executed or an error code
1321 * as to why no command could be executed.
1323 int command_config_read_one_line(struct vty
*vty
,
1324 const struct cmd_element
**cmd
,
1325 uint32_t line_num
, int use_daemon
)
1330 vline
= cmd_make_strvec(vty
->buf
);
1332 /* In case of comment line */
1336 /* Execute configuration command : this is strict match */
1337 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1339 // Climb the tree and try the command again at each node
1340 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1341 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1342 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1343 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1344 && ret
!= CMD_NOT_MY_INSTANCE
&& ret
!= CMD_WARNING_CONFIG_FAILED
1345 && vty
->node
!= CONFIG_NODE
) {
1346 int saved_node
= vty
->node
;
1347 int saved_xpath_index
= vty
->xpath_index
;
1349 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1350 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1351 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1352 && ret
!= CMD_ERR_AMBIGUOUS
&& ret
!= CMD_ERR_INCOMPLETE
1353 && vty
->node
> CONFIG_NODE
) {
1354 vty
->node
= node_parent(vty
->node
);
1355 if (vty
->xpath_index
> 0)
1357 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1360 // If climbing the tree did not work then ignore the command and
1361 // stay at the same node
1362 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1363 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1364 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1365 vty
->node
= saved_node
;
1366 vty
->xpath_index
= saved_xpath_index
;
1370 if (ret
!= CMD_SUCCESS
&&
1371 ret
!= CMD_WARNING
&&
1372 ret
!= CMD_SUCCESS_DAEMON
) {
1373 struct vty_error
*ve
= XCALLOC(MTYPE_TMP
, sizeof(*ve
));
1375 memcpy(ve
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1376 ve
->line_num
= line_num
;
1378 vty
->error
= list_new();
1380 listnode_add(vty
->error
, ve
);
1383 cmd_free_strvec(vline
);
1388 /* Configuration make from file. */
1389 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1391 int ret
, error_ret
= 0;
1394 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1397 ret
= command_config_read_one_line(vty
, NULL
, *line_num
, 0);
1399 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1400 && ret
!= CMD_ERR_NOTHING_TODO
)
1411 /* Configuration from terminal */
1412 DEFUN (config_terminal
,
1413 config_terminal_cmd
,
1414 "configure [terminal]",
1415 "Configuration from vty interface\n"
1416 "Configuration terminal\n")
1418 return vty_config_enter(vty
, false, false);
1421 /* Enable command */
1425 "Turn on privileged mode command\n")
1427 /* If enable password is NULL, change to ENABLE_NODE */
1428 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1429 || vty
->type
== VTY_SHELL_SERV
)
1430 vty
->node
= ENABLE_NODE
;
1432 vty
->node
= AUTH_ENABLE_NODE
;
1437 /* Disable command */
1441 "Turn off privileged mode command\n")
1443 if (vty
->node
== ENABLE_NODE
)
1444 vty
->node
= VIEW_NODE
;
1448 /* Down vty node level. */
1452 "Exit current mode and down to previous mode\n")
1458 void cmd_exit(struct vty
*vty
)
1460 switch (vty
->node
) {
1466 vty
->status
= VTY_CLOSE
;
1469 vty
->node
= ENABLE_NODE
;
1470 vty_config_exit(vty
);
1472 case INTERFACE_NODE
:
1485 case LDP_L2VPN_NODE
:
1487 case OPENFABRIC_NODE
:
1493 vty
->node
= CONFIG_NODE
;
1496 case BGP_IPV4M_NODE
:
1497 case BGP_IPV4L_NODE
:
1498 case BGP_VPNV4_NODE
:
1499 case BGP_VPNV6_NODE
:
1500 case BGP_FLOWSPECV4_NODE
:
1501 case BGP_FLOWSPECV6_NODE
:
1502 case BGP_VRF_POLICY_NODE
:
1503 case BGP_VNC_DEFAULTS_NODE
:
1504 case BGP_VNC_NVE_GROUP_NODE
:
1505 case BGP_VNC_L2_GROUP_NODE
:
1507 case BGP_IPV6M_NODE
:
1509 case BGP_IPV6L_NODE
:
1511 vty
->node
= BGP_NODE
;
1513 case BGP_EVPN_VNI_NODE
:
1514 vty
->node
= BGP_EVPN_NODE
;
1518 vty
->node
= LDP_NODE
;
1520 case LDP_IPV4_IFACE_NODE
:
1521 vty
->node
= LDP_IPV4_NODE
;
1523 case LDP_IPV6_IFACE_NODE
:
1524 vty
->node
= LDP_IPV6_NODE
;
1526 case LDP_PSEUDOWIRE_NODE
:
1527 vty
->node
= LDP_L2VPN_NODE
;
1529 case KEYCHAIN_KEY_NODE
:
1530 vty
->node
= KEYCHAIN_NODE
;
1532 case LINK_PARAMS_NODE
:
1533 vty
->node
= INTERFACE_NODE
;
1536 vty
->node
= BFD_NODE
;
1542 if (vty
->xpath_index
> 0)
1550 "Exit current mode and down to previous mode\n")
1552 return config_exit(self
, vty
, argc
, argv
);
1556 /* End of configuration. */
1560 "End current mode and change to enable mode.\n")
1563 vty_config_exit(vty
);
1564 vty
->node
= ENABLE_NODE
;
1571 DEFUN (show_version
,
1575 "Displays zebra version\n")
1577 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1578 cmd_hostname_get() ? cmd_hostname_get() : "");
1579 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1580 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1585 /* Help display function for all node. */
1589 "Description of the interactive help system\n")
1592 "Quagga VTY provides advanced help feature. When you need help,\n\
1593 anytime at the command line please press '?'.\n\
1595 If nothing matches, the help list will be empty and you must backup\n\
1596 until entering a '?' shows the available options.\n\
1597 Two styles of help are provided:\n\
1598 1. Full help is available when you are ready to enter a\n\
1599 command argument (e.g. 'show ?') and describes each possible\n\
1601 2. Partial help is provided when an abbreviated argument is entered\n\
1602 and you want to know what arguments match the input\n\
1603 (e.g. 'show me?'.)\n\n");
1607 static void permute(struct graph_node
*start
, struct vty
*vty
)
1609 static struct list
*position
= NULL
;
1611 position
= list_new();
1613 struct cmd_token
*stok
= start
->data
;
1614 struct graph_node
*gnn
;
1615 struct listnode
*ln
;
1618 listnode_add(position
, start
);
1619 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1620 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1621 struct cmd_token
*tok
= gn
->data
;
1622 if (tok
->attr
== CMD_ATTR_HIDDEN
1623 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1625 else if (tok
->type
== END_TKN
|| gn
== start
) {
1627 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1628 struct cmd_token
*tt
= gnn
->data
;
1629 if (tt
->type
< SPECIAL_TKN
)
1630 vty_out(vty
, " %s", tt
->text
);
1633 vty_out(vty
, "...");
1637 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1638 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1647 list_delete_node(position
, listtail(position
));
1650 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1652 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1655 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1657 /* loop over all commands at this node */
1658 const struct cmd_element
*element
= NULL
;
1659 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1661 if ((element
= vector_slot(node
->cmd_vector
, i
))
1662 && element
->attr
!= CMD_ATTR_DEPRECATED
1663 && element
->attr
!= CMD_ATTR_HIDDEN
)
1664 vty_out(vty
, " %s\n", element
->string
);
1669 /* Help display function for all node. */
1672 "list [permutations]",
1673 "Print command list\n"
1674 "Print all possible command permutations\n")
1676 return cmd_list_cmds(vty
, argc
== 2);
1679 DEFUN (show_commandtree
,
1680 show_commandtree_cmd
,
1681 "show commandtree [permutations]",
1683 "Show command tree\n"
1684 "Permutations that we are interested in\n")
1686 return cmd_list_cmds(vty
, argc
== 3);
1689 DEFUN_HIDDEN(show_cli_graph
,
1694 "Dump current command space as DOT graph\n")
1696 struct cmd_node
*cn
= vector_slot(cmdvec
, vty
->node
);
1697 char *dot
= cmd_graph_dump_dot(cn
->cmdgraph
);
1699 vty_out(vty
, "%s\n", dot
);
1700 XFREE(MTYPE_TMP
, dot
);
1704 static int vty_write_config(struct vty
*vty
)
1707 struct cmd_node
*node
;
1712 nb_cli_show_config_prepare(running_config
, false);
1714 if (vty
->type
== VTY_TERM
) {
1715 vty_out(vty
, "\nCurrent configuration:\n");
1716 vty_out(vty
, "!\n");
1719 if (strcmp(frr_defaults_version(), FRR_VER_SHORT
))
1720 vty_out(vty
, "! loaded from %s\n", frr_defaults_version());
1721 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1722 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
1723 vty_out(vty
, "!\n");
1725 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1726 if ((node
= vector_slot(cmdvec
, i
)) && node
->func
) {
1727 if ((*node
->func
)(vty
))
1728 vty_out(vty
, "!\n");
1731 if (vty
->type
== VTY_TERM
) {
1732 vty_out(vty
, "end\n");
1738 static int file_write_config(struct vty
*vty
)
1741 char *config_file
, *slash
;
1742 char *config_file_tmp
= NULL
;
1743 char *config_file_sav
= NULL
;
1744 int ret
= CMD_WARNING
;
1745 struct vty
*file_vty
;
1746 struct stat conf_stat
;
1751 /* Check and see if we are operating under vtysh configuration */
1752 if (host
.config
== NULL
) {
1754 "Can't save to configuration file, using vtysh.\n");
1759 config_file
= host
.config
;
1762 #define O_DIRECTORY 0
1764 slash
= strrchr(config_file
, '/');
1766 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1767 config_dir
[slash
- config_file
] = '\0';
1768 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1769 XFREE(MTYPE_TMP
, config_dir
);
1771 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1772 /* if dirfd is invalid, directory sync fails, but we're still OK */
1774 size_t config_file_sav_sz
= strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1;
1775 config_file_sav
= XMALLOC(MTYPE_TMP
, config_file_sav_sz
);
1776 strlcpy(config_file_sav
, config_file
, config_file_sav_sz
);
1777 strlcat(config_file_sav
, CONF_BACKUP_EXT
, config_file_sav_sz
);
1780 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1781 sprintf(config_file_tmp
, "%s.XXXXXX", config_file
);
1783 /* Open file to configuration write. */
1784 fd
= mkstemp(config_file_tmp
);
1786 vty_out(vty
, "Can't open configuration file %s.\n",
1790 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1791 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1792 config_file_tmp
, safe_strerror(errno
), errno
);
1796 /* Make vty for configuration file. */
1797 file_vty
= vty_new();
1799 file_vty
->type
= VTY_FILE
;
1801 /* Config file header print. */
1802 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1803 vty_time_print(file_vty
, 1);
1804 vty_out(file_vty
, "!\n");
1805 vty_write_config(file_vty
);
1806 vty_close(file_vty
);
1808 if (stat(config_file
, &conf_stat
) >= 0) {
1809 if (unlink(config_file_sav
) != 0)
1810 if (errno
!= ENOENT
) {
1812 "Can't unlink backup configuration file %s.\n",
1816 if (link(config_file
, config_file_sav
) != 0) {
1818 "Can't backup old configuration file %s.\n",
1825 if (rename(config_file_tmp
, config_file
) != 0) {
1826 vty_out(vty
, "Can't save configuration file %s.\n",
1833 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1837 if (ret
!= CMD_SUCCESS
)
1838 unlink(config_file_tmp
);
1841 XFREE(MTYPE_TMP
, config_file_tmp
);
1842 XFREE(MTYPE_TMP
, config_file_sav
);
1846 /* Write current configuration into file. */
1848 DEFUN (config_write
,
1850 "write [<file|memory|terminal>]",
1851 "Write running configuration to memory, network, or terminal\n"
1852 "Write to configuration file\n"
1853 "Write configuration currently in memory\n"
1854 "Write configuration to terminal\n")
1856 const int idx_type
= 1;
1858 // if command was 'write terminal' or 'write memory'
1859 if (argc
== 2 && (!strcmp(argv
[idx_type
]->text
, "terminal"))) {
1860 return vty_write_config(vty
);
1863 return file_write_config(vty
);
1866 /* ALIAS_FIXME for 'write <terminal|memory>' */
1867 DEFUN (show_running_config
,
1868 show_running_config_cmd
,
1869 "show running-config",
1871 "running configuration (same as write terminal)\n")
1873 return vty_write_config(vty
);
1876 /* ALIAS_FIXME for 'write file' */
1877 DEFUN (copy_runningconf_startupconf
,
1878 copy_runningconf_startupconf_cmd
,
1879 "copy running-config startup-config",
1880 "Copy configuration\n"
1881 "Copy running config to... \n"
1882 "Copy running config to startup config (same as write file/memory)\n")
1884 return file_write_config(vty
);
1888 /* Write startup configuration into the terminal. */
1889 DEFUN (show_startup_config
,
1890 show_startup_config_cmd
,
1891 "show startup-config",
1893 "Contents of startup configuration\n")
1900 if (host
.config
== NULL
)
1903 confp
= fopen(host
.config
, "r");
1904 if (confp
== NULL
) {
1905 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1906 host
.config
, safe_strerror(errno
));
1910 while (fgets(buf
, BUFSIZ
, confp
)) {
1913 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1917 vty_out(vty
, "%s\n", buf
);
1925 int cmd_domainname_set(const char *domainname
)
1927 XFREE(MTYPE_HOST
, host
.domainname
);
1928 host
.domainname
= domainname
? XSTRDUP(MTYPE_HOST
, domainname
) : NULL
;
1932 /* Hostname configuration */
1933 DEFUN(config_domainname
,
1936 "Set system's domain name\n"
1937 "This system's domain name\n")
1939 struct cmd_token
*word
= argv
[1];
1941 if (!isalpha((unsigned char)word
->arg
[0])) {
1942 vty_out(vty
, "Please specify string starting with alphabet\n");
1943 return CMD_WARNING_CONFIG_FAILED
;
1946 return cmd_domainname_set(word
->arg
);
1949 DEFUN(config_no_domainname
,
1951 "no domainname [DOMAINNAME]",
1953 "Reset system's domain name\n"
1954 "domain name of this router\n")
1956 return cmd_domainname_set(NULL
);
1959 int cmd_hostname_set(const char *hostname
)
1961 XFREE(MTYPE_HOST
, host
.name
);
1962 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1966 /* Hostname configuration */
1967 DEFUN (config_hostname
,
1970 "Set system's network name\n"
1971 "This system's network name\n")
1973 struct cmd_token
*word
= argv
[1];
1975 if (!isalnum((unsigned char)word
->arg
[0])) {
1977 "Please specify string starting with alphabet or number\n");
1978 return CMD_WARNING_CONFIG_FAILED
;
1981 /* With reference to RFC 1123 Section 2.1 */
1982 if (strlen(word
->arg
) > HOSTNAME_LEN
) {
1983 vty_out(vty
, "Hostname length should be less than %d chars\n",
1985 return CMD_WARNING_CONFIG_FAILED
;
1988 return cmd_hostname_set(word
->arg
);
1991 DEFUN (config_no_hostname
,
1993 "no hostname [HOSTNAME]",
1995 "Reset system's network name\n"
1996 "Host name of this router\n")
1998 return cmd_hostname_set(NULL
);
2001 /* VTY interface password set. */
2002 DEFUN (config_password
,
2004 "password [(8-8)] WORD",
2005 "Modify the terminal connection password\n"
2006 "Specifies a HIDDEN password will follow\n"
2007 "The password string\n")
2011 if (argc
== 3) // '8' was specified
2014 XFREE(MTYPE_HOST
, host
.password
);
2015 host
.password
= NULL
;
2016 if (host
.password_encrypt
)
2017 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2018 host
.password_encrypt
=
2019 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2023 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2025 "Please specify string starting with alphanumeric\n");
2026 return CMD_WARNING_CONFIG_FAILED
;
2030 XFREE(MTYPE_HOST
, host
.password
);
2031 host
.password
= NULL
;
2034 if (host
.password_encrypt
)
2035 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2036 host
.password_encrypt
=
2037 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2039 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2044 /* VTY interface password delete. */
2045 DEFUN (no_config_password
,
2049 "Modify the terminal connection password\n")
2051 bool warned
= false;
2053 if (host
.password
) {
2054 if (!vty_shell_serv(vty
)) {
2055 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2058 XFREE(MTYPE_HOST
, host
.password
);
2060 host
.password
= NULL
;
2062 if (host
.password_encrypt
) {
2063 if (!warned
&& !vty_shell_serv(vty
))
2064 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2065 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2067 host
.password_encrypt
= NULL
;
2072 /* VTY enable password set. */
2073 DEFUN (config_enable_password
,
2074 enable_password_cmd
,
2075 "enable password [(8-8)] WORD",
2076 "Modify enable password parameters\n"
2077 "Assign the privileged level password\n"
2078 "Specifies a HIDDEN password will follow\n"
2079 "The HIDDEN 'enable' password string\n")
2084 /* Crypt type is specified. */
2086 if (argv
[idx_8
]->arg
[0] == '8') {
2088 XFREE(MTYPE_HOST
, host
.enable
);
2091 if (host
.enable_encrypt
)
2092 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2093 host
.enable_encrypt
=
2094 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
2098 vty_out(vty
, "Unknown encryption type.\n");
2099 return CMD_WARNING_CONFIG_FAILED
;
2103 if (!isalnum((unsigned char)argv
[idx_8
]->arg
[0])) {
2105 "Please specify string starting with alphanumeric\n");
2106 return CMD_WARNING_CONFIG_FAILED
;
2110 XFREE(MTYPE_HOST
, host
.enable
);
2113 /* Plain password input. */
2115 if (host
.enable_encrypt
)
2116 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2117 host
.enable_encrypt
=
2118 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
2120 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
2125 /* VTY enable password delete. */
2126 DEFUN (no_config_enable_password
,
2127 no_enable_password_cmd
,
2128 "no enable password",
2130 "Modify enable password parameters\n"
2131 "Assign the privileged level password\n")
2133 bool warned
= false;
2136 if (!vty_shell_serv(vty
)) {
2137 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2140 XFREE(MTYPE_HOST
, host
.enable
);
2144 if (host
.enable_encrypt
) {
2145 if (!warned
&& !vty_shell_serv(vty
))
2146 vty_out(vty
, NO_PASSWD_CMD_WARNING
);
2147 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2149 host
.enable_encrypt
= NULL
;
2154 DEFUN (service_password_encrypt
,
2155 service_password_encrypt_cmd
,
2156 "service password-encryption",
2157 "Set up miscellaneous service\n"
2158 "Enable encrypted passwords\n")
2165 if (host
.password
) {
2166 if (host
.password_encrypt
)
2167 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2168 host
.password_encrypt
=
2169 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
2172 if (host
.enable_encrypt
)
2173 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2174 host
.enable_encrypt
=
2175 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
2181 DEFUN (no_service_password_encrypt
,
2182 no_service_password_encrypt_cmd
,
2183 "no service password-encryption",
2185 "Set up miscellaneous service\n"
2186 "Enable encrypted passwords\n")
2193 if (host
.password_encrypt
)
2194 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2195 host
.password_encrypt
= NULL
;
2197 if (host
.enable_encrypt
)
2198 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2199 host
.enable_encrypt
= NULL
;
2204 DEFUN (config_terminal_length
,
2205 config_terminal_length_cmd
,
2206 "terminal length (0-512)",
2207 "Set terminal line parameters\n"
2208 "Set number of lines on a screen\n"
2209 "Number of lines on screen (0 for no pausing)\n")
2213 vty
->lines
= atoi(argv
[idx_number
]->arg
);
2218 DEFUN (config_terminal_no_length
,
2219 config_terminal_no_length_cmd
,
2220 "terminal no length",
2221 "Set terminal line parameters\n"
2223 "Set number of lines on a screen\n")
2229 DEFUN (service_terminal_length
,
2230 service_terminal_length_cmd
,
2231 "service terminal-length (0-512)",
2232 "Set up miscellaneous service\n"
2233 "System wide terminal length configuration\n"
2234 "Number of lines of VTY (0 means no line control)\n")
2238 host
.lines
= atoi(argv
[idx_number
]->arg
);
2243 DEFUN (no_service_terminal_length
,
2244 no_service_terminal_length_cmd
,
2245 "no service terminal-length [(0-512)]",
2247 "Set up miscellaneous service\n"
2248 "System wide terminal length configuration\n"
2249 "Number of lines of VTY (0 means no line control)\n")
2255 DEFUN_HIDDEN (do_echo
,
2258 "Echo a message back to the vty\n"
2259 "The message to echo\n")
2263 vty_out(vty
, "%s\n",
2264 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
2266 XFREE(MTYPE_TMP
, message
);
2270 DEFUN (config_logmsg
,
2272 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2273 "Send a message to enabled logging destinations\n"
2275 "The message to send\n")
2277 int idx_log_level
= 1;
2278 int idx_message
= 2;
2282 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2283 return CMD_ERR_NO_MATCH
;
2286 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
2288 XFREE(MTYPE_TMP
, message
);
2293 DEFUN (show_logging
,
2297 "Show current logging configuration\n")
2299 struct zlog
*zl
= zlog_default
;
2301 vty_out(vty
, "Syslog logging: ");
2302 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
2303 vty_out(vty
, "disabled");
2305 vty_out(vty
, "level %s, facility %s, ident %s",
2306 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
2307 facility_name(zl
->facility
), zl
->ident
);
2310 vty_out(vty
, "Stdout logging: ");
2311 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
2312 vty_out(vty
, "disabled");
2314 vty_out(vty
, "level %s",
2315 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
2318 vty_out(vty
, "Monitor logging: ");
2319 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
2320 vty_out(vty
, "disabled");
2322 vty_out(vty
, "level %s",
2323 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
2326 vty_out(vty
, "File logging: ");
2327 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
2328 vty_out(vty
, "disabled");
2330 vty_out(vty
, "level %s, filename %s",
2331 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
2335 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
2336 vty_out(vty
, "Record priority: %s\n",
2337 (zl
->record_priority
? "enabled" : "disabled"));
2338 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2343 DEFUN (config_log_stdout
,
2344 config_log_stdout_cmd
,
2345 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2347 "Set stdout logging level\n"
2350 int idx_log_level
= 2;
2352 if (argc
== idx_log_level
) {
2353 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2358 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2359 return CMD_ERR_NO_MATCH
;
2360 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2364 DEFUN (no_config_log_stdout
,
2365 no_config_log_stdout_cmd
,
2366 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2369 "Cancel logging to stdout\n"
2372 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2376 DEFUN (config_log_monitor
,
2377 config_log_monitor_cmd
,
2378 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2380 "Set terminal line (monitor) logging level\n"
2383 int idx_log_level
= 2;
2385 if (argc
== idx_log_level
) {
2386 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2391 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2392 return CMD_ERR_NO_MATCH
;
2393 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2397 DEFUN (no_config_log_monitor
,
2398 no_config_log_monitor_cmd
,
2399 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2402 "Disable terminal line (monitor) logging\n"
2405 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2409 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2413 const char *fullpath
;
2415 /* Path detection. */
2416 if (!IS_DIRECTORY_SEP(*fname
)) {
2417 char cwd
[MAXPATHLEN
+ 1];
2418 cwd
[MAXPATHLEN
] = '\0';
2420 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2421 flog_err_sys(EC_LIB_SYSTEM_CALL
,
2422 "config_log_file: Unable to alloc mem!");
2423 return CMD_WARNING_CONFIG_FAILED
;
2426 p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2);
2427 sprintf(p
, "%s/%s", cwd
, fname
);
2432 ret
= zlog_set_file(fullpath
, loglevel
);
2434 XFREE(MTYPE_TMP
, p
);
2438 vty_out(vty
, "can't open logfile %s\n", fname
);
2439 return CMD_WARNING_CONFIG_FAILED
;
2442 XFREE(MTYPE_HOST
, host
.logfile
);
2444 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2446 #if defined(HAVE_CUMULUS)
2447 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2448 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2453 void command_setup_early_logging(const char *dest
, const char *level
)
2458 int nlevel
= level_match(level
);
2460 if (nlevel
!= ZLOG_DISABLED
)
2461 zlog_default
->default_lvl
= nlevel
;
2467 if (strcmp(dest
, "stdout") == 0) {
2468 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2472 if (strcmp(dest
, "syslog") == 0) {
2473 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2477 token
= strstr(dest
, ":");
2483 set_log_file(NULL
, token
, zlog_default
->default_lvl
);
2486 DEFUN (config_log_file
,
2487 config_log_file_cmd
,
2488 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2491 "Logging filename\n"
2494 int idx_filename
= 2;
2495 int idx_log_levels
= 3;
2498 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2500 return CMD_ERR_NO_MATCH
;
2501 return set_log_file(vty
, argv
[idx_filename
]->arg
, level
);
2503 return set_log_file(vty
, argv
[idx_filename
]->arg
,
2504 zlog_default
->default_lvl
);
2507 static void disable_log_file(void)
2511 XFREE(MTYPE_HOST
, host
.logfile
);
2514 DEFUN (no_config_log_file
,
2515 no_config_log_file_cmd
,
2516 "no log file [FILENAME [LEVEL]]",
2519 "Cancel logging to file\n"
2520 "Logging file name\n"
2527 DEFUN (config_log_syslog
,
2528 config_log_syslog_cmd
,
2529 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2531 "Set syslog logging level\n"
2534 int idx_log_levels
= 2;
2538 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2540 return CMD_ERR_NO_MATCH
;
2541 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2544 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2549 DEFUN (no_config_log_syslog
,
2550 no_config_log_syslog_cmd
,
2551 "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>]",
2554 "Cancel logging to syslog\n"
2558 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2562 DEFUN (config_log_facility
,
2563 config_log_facility_cmd
,
2564 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2566 "Facility parameter for syslog messages\n"
2570 int facility
= facility_match(argv
[idx_target
]->arg
);
2572 zlog_default
->facility
= facility
;
2576 DEFUN (no_config_log_facility
,
2577 no_config_log_facility_cmd
,
2578 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2581 "Reset syslog facility to default (daemon)\n"
2584 zlog_default
->facility
= LOG_DAEMON
;
2588 DEFUN (config_log_record_priority
,
2589 config_log_record_priority_cmd
,
2590 "log record-priority",
2592 "Log the priority of the message within the message\n")
2594 zlog_default
->record_priority
= 1;
2598 DEFUN (no_config_log_record_priority
,
2599 no_config_log_record_priority_cmd
,
2600 "no log record-priority",
2603 "Do not log the priority of the message within the message\n")
2605 zlog_default
->record_priority
= 0;
2609 DEFUN (config_log_timestamp_precision
,
2610 config_log_timestamp_precision_cmd
,
2611 "log timestamp precision (0-6)",
2613 "Timestamp configuration\n"
2614 "Set the timestamp precision\n"
2615 "Number of subsecond digits\n")
2618 zlog_default
->timestamp_precision
=
2619 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2623 DEFUN (no_config_log_timestamp_precision
,
2624 no_config_log_timestamp_precision_cmd
,
2625 "no log timestamp precision",
2628 "Timestamp configuration\n"
2629 "Reset the timestamp precision to the default value of 0\n")
2631 zlog_default
->timestamp_precision
= 0;
2635 DEFUN (debug_memstats
,
2637 "[no] debug memstats-at-exit",
2640 "Print memory type statistics at exit\n")
2642 debug_memstats_at_exit
= !!strcmp(argv
[0]->text
, "no");
2646 int cmd_banner_motd_file(const char *file
)
2648 int success
= CMD_SUCCESS
;
2653 rpath
= realpath(file
, p
);
2655 return CMD_ERR_NO_FILE
;
2656 in
= strstr(rpath
, SYSCONFDIR
);
2658 XFREE(MTYPE_HOST
, host
.motdfile
);
2659 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2661 success
= CMD_WARNING_CONFIG_FAILED
;
2666 void cmd_banner_motd_line(const char *line
)
2668 XFREE(MTYPE_HOST
, host
.motd
);
2669 host
.motd
= XSTRDUP(MTYPE_HOST
, line
);
2672 DEFUN (banner_motd_file
,
2673 banner_motd_file_cmd
,
2674 "banner motd file FILE",
2677 "Banner from a file\n"
2681 const char *filename
= argv
[idx_file
]->arg
;
2682 int cmd
= cmd_banner_motd_file(filename
);
2684 if (cmd
== CMD_ERR_NO_FILE
)
2685 vty_out(vty
, "%s does not exist", filename
);
2686 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2687 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2692 DEFUN (banner_motd_line
,
2693 banner_motd_line_cmd
,
2694 "banner motd line LINE...",
2697 "Banner from an input\n"
2703 argv_find(argv
, argc
, "LINE", &idx
);
2704 motd
= argv_concat(argv
, argc
, idx
);
2706 cmd_banner_motd_line(motd
);
2707 XFREE(MTYPE_TMP
, motd
);
2712 DEFUN (banner_motd_default
,
2713 banner_motd_default_cmd
,
2714 "banner motd default",
2715 "Set banner string\n"
2716 "Strings for motd\n"
2719 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2723 DEFUN (no_banner_motd
,
2727 "Set banner string\n"
2728 "Strings for motd\n")
2732 XFREE(MTYPE_HOST
, host
.motdfile
);
2733 host
.motdfile
= NULL
;
2740 "Find CLI command matching a regular expression\n"
2741 "Search pattern (POSIX regex)\n")
2743 char *pattern
= argv
[1]->arg
;
2744 const struct cmd_node
*node
;
2745 const struct cmd_element
*cli
;
2750 int cr
= regcomp(&exp
, pattern
, REG_NOSUB
| REG_EXTENDED
);
2755 vty_out(vty
, "%% Invalid {...} expression\n");
2758 vty_out(vty
, "%% Bad repetition operator\n");
2761 vty_out(vty
, "%% Regex syntax error\n");
2764 vty_out(vty
, "%% Invalid collating element\n");
2767 vty_out(vty
, "%% Invalid character class name\n");
2771 "%% Regex ended with escape character (\\)\n");
2775 "%% Invalid number in \\digit construction\n");
2778 vty_out(vty
, "%% Unbalanced square brackets\n");
2781 vty_out(vty
, "%% Unbalanced parentheses\n");
2784 vty_out(vty
, "%% Unbalanced braces\n");
2788 "%% Invalid endpoint in range expression\n");
2791 vty_out(vty
, "%% Failed to compile (out of memory)\n");
2799 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++) {
2800 node
= vector_slot(cmdvec
, i
);
2803 clis
= node
->cmd_vector
;
2804 for (unsigned int j
= 0; j
< vector_active(clis
); j
++) {
2805 cli
= vector_slot(clis
, j
);
2807 if (regexec(&exp
, cli
->string
, 0, NULL
, 0) == 0)
2808 vty_out(vty
, " (%s) %s\n",
2809 node_names
[node
->node
], cli
->string
);
2818 /* Set config filename. Called from vty.c */
2819 void host_config_set(const char *filename
)
2821 XFREE(MTYPE_HOST
, host
.config
);
2822 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2825 const char *host_config_get(void)
2830 void install_default(enum node_type node
)
2832 install_element(node
, &config_exit_cmd
);
2833 install_element(node
, &config_quit_cmd
);
2834 install_element(node
, &config_end_cmd
);
2835 install_element(node
, &config_help_cmd
);
2836 install_element(node
, &config_list_cmd
);
2837 install_element(node
, &show_cli_graph_cmd
);
2838 install_element(node
, &find_cmd
);
2840 install_element(node
, &config_write_cmd
);
2841 install_element(node
, &show_running_config_cmd
);
2843 install_element(node
, &autocomplete_cmd
);
2845 nb_cli_install_default(node
);
2848 /* Initialize command interface. Install basic nodes and commands.
2850 * terminal = 0 -- vtysh / no logging, no config control
2851 * terminal = 1 -- normal daemon
2852 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2853 void cmd_init(int terminal
)
2855 struct utsname names
;
2857 if (array_size(node_names
) != NODE_TYPE_MAX
)
2858 assert(!"Update the CLI node description array!");
2863 /* register command preprocessors */
2864 hook_register(cmd_execute
, handle_pipe_action
);
2865 hook_register(cmd_execute_done
, handle_pipe_action_done
);
2867 varhandlers
= list_new();
2869 /* Allocate initial top vector of commands. */
2870 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2872 /* Default host value settings. */
2873 host
.name
= XSTRDUP(MTYPE_HOST
, names
.nodename
);
2874 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2875 if ((strcmp(names
.domainname
, "(none)") == 0))
2876 host
.domainname
= NULL
;
2878 host
.domainname
= XSTRDUP(MTYPE_HOST
, names
.domainname
);
2880 host
.domainname
= NULL
;
2882 host
.password
= NULL
;
2884 host
.logfile
= NULL
;
2886 host
.noconfig
= (terminal
< 0);
2888 cmd_banner_motd_line(FRR_DEFAULT_MOTD
);
2889 host
.motdfile
= NULL
;
2891 /* Install top nodes. */
2892 install_node(&view_node
, NULL
);
2893 install_node(&enable_node
, NULL
);
2894 install_node(&auth_node
, NULL
);
2895 install_node(&auth_enable_node
, NULL
);
2896 install_node(&config_node
, config_write_host
);
2898 /* Each node's basic commands. */
2899 install_element(VIEW_NODE
, &show_version_cmd
);
2900 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2903 install_element(ENABLE_NODE
, &debug_memstats_cmd
);
2905 install_element(VIEW_NODE
, &config_list_cmd
);
2906 install_element(VIEW_NODE
, &config_exit_cmd
);
2907 install_element(VIEW_NODE
, &config_quit_cmd
);
2908 install_element(VIEW_NODE
, &config_help_cmd
);
2909 install_element(VIEW_NODE
, &config_enable_cmd
);
2910 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2911 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2912 install_element(VIEW_NODE
, &show_logging_cmd
);
2913 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2914 install_element(VIEW_NODE
, &echo_cmd
);
2915 install_element(VIEW_NODE
, &autocomplete_cmd
);
2916 install_element(VIEW_NODE
, &find_cmd
);
2918 install_element(ENABLE_NODE
, &config_end_cmd
);
2919 install_element(ENABLE_NODE
, &config_disable_cmd
);
2920 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2921 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2922 install_element(ENABLE_NODE
, &config_write_cmd
);
2923 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2924 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2926 install_default(CONFIG_NODE
);
2929 workqueue_cmd_init();
2933 install_element(CONFIG_NODE
, &hostname_cmd
);
2934 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2935 install_element(CONFIG_NODE
, &domainname_cmd
);
2936 install_element(CONFIG_NODE
, &no_domainname_cmd
);
2939 install_element(CONFIG_NODE
, &debug_memstats_cmd
);
2941 install_element(CONFIG_NODE
, &password_cmd
);
2942 install_element(CONFIG_NODE
, &no_password_cmd
);
2943 install_element(CONFIG_NODE
, &enable_password_cmd
);
2944 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2946 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2947 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2948 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2949 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2950 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2951 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2952 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2953 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2954 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2955 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2956 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2957 install_element(CONFIG_NODE
,
2958 &no_config_log_record_priority_cmd
);
2959 install_element(CONFIG_NODE
,
2960 &config_log_timestamp_precision_cmd
);
2961 install_element(CONFIG_NODE
,
2962 &no_config_log_timestamp_precision_cmd
);
2963 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2964 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2965 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2966 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2967 install_element(CONFIG_NODE
, &banner_motd_line_cmd
);
2968 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2969 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2970 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2972 vrf_install_commands();
2976 grammar_sandbox_init();
2980 void cmd_terminate(void)
2982 struct cmd_node
*cmd_node
;
2984 hook_unregister(cmd_execute
, handle_pipe_action
);
2985 hook_unregister(cmd_execute_done
, handle_pipe_action_done
);
2988 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2989 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2990 // deleting the graph delets the cmd_element as
2992 graph_delete_graph(cmd_node
->cmdgraph
);
2993 vector_free(cmd_node
->cmd_vector
);
2994 hash_clean(cmd_node
->cmd_hash
, NULL
);
2995 hash_free(cmd_node
->cmd_hash
);
2996 cmd_node
->cmd_hash
= NULL
;
2999 vector_free(cmdvec
);
3003 XFREE(MTYPE_HOST
, host
.name
);
3004 XFREE(MTYPE_HOST
, host
.domainname
);
3005 XFREE(MTYPE_HOST
, host
.password
);
3006 XFREE(MTYPE_HOST
, host
.password_encrypt
);
3007 XFREE(MTYPE_HOST
, host
.enable
);
3008 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
3009 XFREE(MTYPE_HOST
, host
.logfile
);
3010 XFREE(MTYPE_HOST
, host
.motdfile
);
3011 XFREE(MTYPE_HOST
, host
.config
);
3012 XFREE(MTYPE_HOST
, host
.motd
);
3014 list_delete(&varhandlers
);