2 * CLI backend interface.
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
10 * This file is part of GNU Zebra.
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 #include <lib/version.h>
39 #include "workqueue.h"
41 #include "command_match.h"
42 #include "command_graph.h"
46 DEFINE_MTYPE(LIB
, HOST
, "Host config")
47 DEFINE_MTYPE(LIB
, STRVEC
, "String vector")
48 DEFINE_MTYPE(LIB
, COMPLETION
, "Completion item")
50 /* Command vector which includes some level of command lists. Normally
51 each daemon maintains each own cmdvec. */
54 /* Host information structure. */
57 /* Standard command node structures. */
58 static struct cmd_node auth_node
= {
59 AUTH_NODE
, "Password: ",
62 static struct cmd_node view_node
= {
66 static struct cmd_node auth_enable_node
= {
67 AUTH_ENABLE_NODE
, "Password: ",
70 static struct cmd_node enable_node
= {
74 static struct cmd_node config_node
= {CONFIG_NODE
, "%s(config)# ", 1};
76 /* Default motd string. */
77 static const char *default_motd
= FRR_DEFAULT_MOTD
;
79 static const struct facility_map
{
83 } syslog_facilities
[] = {
84 {LOG_KERN
, "kern", 1},
85 {LOG_USER
, "user", 2},
86 {LOG_MAIL
, "mail", 1},
87 {LOG_DAEMON
, "daemon", 1},
88 {LOG_AUTH
, "auth", 1},
89 {LOG_SYSLOG
, "syslog", 1},
91 {LOG_NEWS
, "news", 1},
92 {LOG_UUCP
, "uucp", 2},
93 {LOG_CRON
, "cron", 1},
97 {LOG_LOCAL0
, "local0", 6},
98 {LOG_LOCAL1
, "local1", 6},
99 {LOG_LOCAL2
, "local2", 6},
100 {LOG_LOCAL3
, "local3", 6},
101 {LOG_LOCAL4
, "local4", 6},
102 {LOG_LOCAL5
, "local5", 6},
103 {LOG_LOCAL6
, "local6", 6},
104 {LOG_LOCAL7
, "local7", 6},
108 static const char *facility_name(int facility
)
110 const struct facility_map
*fm
;
112 for (fm
= syslog_facilities
; fm
->name
; fm
++)
113 if (fm
->facility
== facility
)
118 static int facility_match(const char *str
)
120 const struct facility_map
*fm
;
122 for (fm
= syslog_facilities
; fm
->name
; fm
++)
123 if (!strncmp(str
, fm
->name
, fm
->match
))
128 static int level_match(const char *s
)
132 for (level
= 0; zlog_priority
[level
] != NULL
; level
++)
133 if (!strncmp(s
, zlog_priority
[level
], 2))
135 return ZLOG_DISABLED
;
138 /* This is called from main when a daemon is invoked with -v or --version. */
139 void print_version(const char *progname
)
141 printf("%s version %s\n", progname
, FRR_VERSION
);
142 printf("%s\n", FRR_COPYRIGHT
);
143 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS
);
147 /* Utility function to concatenate argv argument into a single string
148 with inserting ' ' character between each argument. */
149 char *argv_concat(struct cmd_token
**argv
, int argc
, int shift
)
157 for (i
= shift
; i
< argc
; i
++)
158 len
+= strlen(argv
[i
]->arg
) + 1;
161 p
= str
= XMALLOC(MTYPE_TMP
, len
);
162 for (i
= shift
; i
< argc
; i
++) {
164 memcpy(p
, argv
[i
]->arg
, (arglen
= strlen(argv
[i
]->arg
)));
173 * Convenience function for accessing argv data.
177 * @param text definition snippet of the desired token
178 * @param index the starting index, and where to store the
179 * index of the found token if it exists
180 * @return 1 if found, 0 otherwise
182 int argv_find(struct cmd_token
**argv
, int argc
, const char *text
, int *index
)
185 for (int i
= *index
; i
< argc
&& found
== 0; i
++)
186 if ((found
= strmatch(text
, argv
[i
]->text
)))
191 static unsigned int cmd_hash_key(void *p
)
196 static int cmd_hash_cmp(const void *a
, const void *b
)
201 /* Install top node of command vector. */
202 void install_node(struct cmd_node
*node
, int (*func
)(struct vty
*))
204 vector_set_index(cmdvec
, node
->node
, node
);
206 node
->cmdgraph
= graph_new();
207 node
->cmd_vector
= vector_init(VECTOR_MIN_SIZE
);
209 struct cmd_token
*token
=
210 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
211 graph_new_node(node
->cmdgraph
, token
,
212 (void (*)(void *)) & cmd_token_del
);
213 node
->cmd_hash
= hash_create(cmd_hash_key
, cmd_hash_cmp
, NULL
);
217 * Tokenizes a string, storing tokens in a vector.
218 * Whitespace is ignored.
220 * Delimiter string = " \n\r\t".
222 * @param string to tokenize
223 * @return tokenized string
225 vector
cmd_make_strvec(const char *string
)
230 char *copy
, *copystart
;
231 copystart
= copy
= XSTRDUP(MTYPE_TMP
, string
);
233 // skip leading whitespace
234 while (isspace((int)*copy
) && *copy
!= '\0')
237 // if the entire string was whitespace or a comment, return
238 if (*copy
== '\0' || *copy
== '!' || *copy
== '#') {
239 XFREE(MTYPE_TMP
, copystart
);
243 vector strvec
= vector_init(VECTOR_MIN_SIZE
);
244 const char *delim
= " \n\r\t", *tok
= NULL
;
246 tok
= strsep(©
, delim
);
248 vector_set(strvec
, XSTRDUP(MTYPE_STRVEC
, tok
));
251 XFREE(MTYPE_TMP
, copystart
);
255 /* Free allocated string vector. */
256 void cmd_free_strvec(vector v
)
264 for (i
= 0; i
< vector_active(v
); i
++)
265 if ((cp
= vector_slot(v
, i
)) != NULL
)
266 XFREE(MTYPE_STRVEC
, cp
);
271 /* Return prompt character of specified node. */
272 const char *cmd_prompt(enum node_type node
)
274 struct cmd_node
*cnode
;
276 cnode
= vector_slot(cmdvec
, node
);
277 return cnode
->prompt
;
280 /* Install a command into a node. */
281 void install_element(enum node_type ntype
, struct cmd_element
*cmd
)
283 struct cmd_node
*cnode
;
285 /* cmd_init hasn't been called */
287 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
292 cnode
= vector_slot(cmdvec
, ntype
);
296 "Command node %d doesn't exist, please check it\n",
299 "Have you called install_node before this install_element?\n");
303 if (hash_lookup(cnode
->cmd_hash
, cmd
) != NULL
) {
305 "Multiple command installs to node %d of command:\n%s\n",
310 assert(hash_get(cnode
->cmd_hash
, cmd
, hash_alloc_intern
));
312 struct graph
*graph
= graph_new();
313 struct cmd_token
*token
=
314 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
315 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
317 cmd_graph_parse(graph
, cmd
);
318 cmd_graph_names(graph
);
319 cmd_graph_merge(cnode
->cmdgraph
, graph
, +1);
320 graph_delete_graph(graph
);
322 vector_set(cnode
->cmd_vector
, cmd
);
324 if (ntype
== VIEW_NODE
)
325 install_element(ENABLE_NODE
, cmd
);
328 void uninstall_element(enum node_type ntype
, struct cmd_element
*cmd
)
330 struct cmd_node
*cnode
;
332 /* cmd_init hasn't been called */
334 fprintf(stderr
, "%s called before cmd_init, breakage likely\n",
339 cnode
= vector_slot(cmdvec
, ntype
);
343 "Command node %d doesn't exist, please check it\n",
346 "Have you called install_node before this install_element?\n");
350 if (hash_release(cnode
->cmd_hash
, cmd
) == NULL
) {
352 "Trying to uninstall non-installed command (node %d):\n%s\n",
357 vector_unset_value(cnode
->cmd_vector
, cmd
);
359 struct graph
*graph
= graph_new();
360 struct cmd_token
*token
=
361 cmd_token_new(START_TKN
, CMD_ATTR_NORMAL
, NULL
, NULL
);
362 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
364 cmd_graph_parse(graph
, cmd
);
365 cmd_graph_names(graph
);
366 cmd_graph_merge(cnode
->cmdgraph
, graph
, -1);
367 graph_delete_graph(graph
);
369 if (ntype
== VIEW_NODE
)
370 uninstall_element(ENABLE_NODE
, cmd
);
374 static const unsigned char itoa64
[] =
375 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
377 static void to64(char *s
, long v
, int n
)
380 *s
++ = itoa64
[v
& 0x3f];
385 static char *zencrypt(const char *passwd
)
389 char *crypt(const char *, const char *);
391 gettimeofday(&tv
, 0);
393 to64(&salt
[0], random(), 3);
394 to64(&salt
[3], tv
.tv_usec
, 3);
397 return crypt(passwd
, salt
);
400 /* This function write configuration of this host. */
401 static int config_write_host(struct vty
*vty
)
404 vty_out(vty
, "hostname %s\n", host
.name
);
407 if (host
.password_encrypt
)
408 vty_out(vty
, "password 8 %s\n", host
.password_encrypt
);
409 if (host
.enable_encrypt
)
410 vty_out(vty
, "enable password 8 %s\n",
411 host
.enable_encrypt
);
414 vty_out(vty
, "password %s\n", host
.password
);
416 vty_out(vty
, "enable password %s\n", host
.enable
);
419 if (zlog_default
->default_lvl
!= LOG_DEBUG
) {
420 vty_out(vty
, "! N.B. The 'log trap' command is deprecated.\n");
421 vty_out(vty
, "log trap %s\n",
422 zlog_priority
[zlog_default
->default_lvl
]);
426 && (zlog_default
->maxlvl
[ZLOG_DEST_FILE
] != ZLOG_DISABLED
)) {
427 vty_out(vty
, "log file %s", host
.logfile
);
428 if (zlog_default
->maxlvl
[ZLOG_DEST_FILE
]
429 != zlog_default
->default_lvl
)
432 [zlog_default
->maxlvl
[ZLOG_DEST_FILE
]]);
436 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
] != ZLOG_DISABLED
) {
437 vty_out(vty
, "log stdout");
438 if (zlog_default
->maxlvl
[ZLOG_DEST_STDOUT
]
439 != zlog_default
->default_lvl
)
441 zlog_priority
[zlog_default
->maxlvl
442 [ZLOG_DEST_STDOUT
]]);
446 if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
447 vty_out(vty
, "no log monitor\n");
448 else if (zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]
449 != zlog_default
->default_lvl
)
450 vty_out(vty
, "log monitor %s\n",
451 zlog_priority
[zlog_default
->maxlvl
[ZLOG_DEST_MONITOR
]]);
453 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
) {
454 vty_out(vty
, "log syslog");
455 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
]
456 != zlog_default
->default_lvl
)
458 zlog_priority
[zlog_default
->maxlvl
459 [ZLOG_DEST_SYSLOG
]]);
463 if (zlog_default
->facility
!= LOG_DAEMON
)
464 vty_out(vty
, "log facility %s\n",
465 facility_name(zlog_default
->facility
));
467 if (zlog_default
->record_priority
== 1)
468 vty_out(vty
, "log record-priority\n");
470 if (zlog_default
->timestamp_precision
> 0)
471 vty_out(vty
, "log timestamp precision %d\n",
472 zlog_default
->timestamp_precision
);
475 vty_out(vty
, "service advanced-vty\n");
478 vty_out(vty
, "service password-encryption\n");
481 vty_out(vty
, "service terminal-length %d\n", host
.lines
);
484 vty_out(vty
, "banner motd file %s\n", host
.motdfile
);
486 vty_out(vty
, "no banner motd\n");
491 /* Utility function for getting command graph. */
492 static struct graph
*cmd_node_graph(vector v
, enum node_type ntype
)
494 struct cmd_node
*cnode
= vector_slot(v
, ntype
);
495 return cnode
->cmdgraph
;
498 static int cmd_try_do_shortcut(enum node_type node
, char *first_word
)
500 if (first_word
!= NULL
&& node
!= AUTH_NODE
&& node
!= VIEW_NODE
501 && node
!= AUTH_ENABLE_NODE
&& 0 == strcmp("do", first_word
))
507 * Compare function for cmd_token.
508 * Used with qsort to sort command completions.
510 static int compare_completions(const void *fst
, const void *snd
)
512 struct cmd_token
*first
= *(struct cmd_token
**)fst
,
513 *secnd
= *(struct cmd_token
**)snd
;
514 return strcmp(first
->text
, secnd
->text
);
518 * Takes a list of completions returned by command_complete,
519 * dedeuplicates them based on both text and description,
520 * sorts them, and returns them as a vector.
522 * @param completions linked list of cmd_token
523 * @return deduplicated and sorted vector with
525 vector
completions_to_vec(struct list
*completions
)
527 vector comps
= vector_init(VECTOR_MIN_SIZE
);
530 struct cmd_token
*token
, *cr
= NULL
;
531 unsigned int i
, exists
;
532 for (ALL_LIST_ELEMENTS_RO(completions
, ln
, token
)) {
533 if (token
->type
== END_TKN
&& (cr
= token
))
536 // linear search for token in completions vector
538 for (i
= 0; i
< vector_active(comps
) && !exists
; i
++) {
539 struct cmd_token
*curr
= vector_slot(comps
, i
);
541 exists
= !strcmp(curr
->text
, token
->text
)
542 && !strcmp(curr
->desc
, token
->desc
);
544 exists
= !strcmp(curr
->text
, token
->text
);
545 #endif /* VTYSH_DEBUG */
549 vector_set(comps
, token
);
553 qsort(comps
->index
, vector_active(comps
), sizeof(void *),
554 &compare_completions
);
556 // make <cr> the first element, if it is present
558 vector_set_index(comps
, vector_active(comps
), NULL
);
559 memmove(comps
->index
+ 1, comps
->index
,
560 (comps
->alloced
- 1) * sizeof(void *));
561 vector_set_index(comps
, 0, cr
);
567 * Generates a vector of cmd_token representing possible completions
568 * on the current input.
570 * @param vline the vectorized input line
571 * @param vty the vty with the node to match on
572 * @param status pointer to matcher status code
573 * @return vector of struct cmd_token * with possible completions
575 static vector
cmd_complete_command_real(vector vline
, struct vty
*vty
,
578 struct list
*completions
;
579 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
581 enum matcher_rv rv
= command_complete(cmdgraph
, vline
, &completions
);
583 if (MATCHER_ERROR(rv
)) {
584 *status
= CMD_ERR_NO_MATCH
;
588 vector comps
= completions_to_vec(completions
);
589 list_delete(completions
);
591 // set status code appropriately
592 switch (vector_active(comps
)) {
594 *status
= CMD_ERR_NO_MATCH
;
597 *status
= CMD_COMPLETE_FULL_MATCH
;
600 *status
= CMD_COMPLETE_LIST_MATCH
;
606 vector
cmd_describe_command(vector vline
, struct vty
*vty
, int *status
)
610 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
611 enum node_type onode
;
612 vector shifted_vline
;
616 vty
->node
= ENABLE_NODE
;
617 /* We can try it on enable node, cos' the vty is authenticated
620 shifted_vline
= vector_init(vector_count(vline
));
622 for (index
= 1; index
< vector_active(vline
); index
++) {
623 vector_set_index(shifted_vline
, index
- 1,
624 vector_lookup(vline
, index
));
627 ret
= cmd_complete_command_real(shifted_vline
, vty
, status
);
629 vector_free(shifted_vline
);
634 return cmd_complete_command_real(vline
, vty
, status
);
637 static struct list
*varhandlers
= NULL
;
639 void cmd_variable_complete(struct cmd_token
*token
, const char *arg
,
643 const struct cmd_variable_handler
*cvh
;
647 tmpcomps
= arg
? vector_init(VECTOR_MIN_SIZE
) : comps
;
649 for (ALL_LIST_ELEMENTS_RO(varhandlers
, ln
, cvh
)) {
650 if (cvh
->tokenname
&& strcmp(cvh
->tokenname
, token
->text
))
652 if (cvh
->varname
&& (!token
->varname
653 || strcmp(cvh
->varname
, token
->varname
)))
655 cvh
->completions(tmpcomps
, token
);
663 for (i
= vector_active(tmpcomps
); i
; i
--) {
664 char *item
= vector_slot(tmpcomps
, i
- 1);
665 if (strlen(item
) >= argsz
&& !strncmp(item
, arg
, argsz
))
666 vector_set(comps
, item
);
668 XFREE(MTYPE_COMPLETION
, item
);
670 vector_free(tmpcomps
);
673 #define AUTOCOMP_INDENT 5
675 char *cmd_variable_comp2str(vector comps
, unsigned short cols
)
678 char *buf
= XCALLOC(MTYPE_TMP
, bsz
);
679 int lc
= AUTOCOMP_INDENT
;
680 size_t cs
= AUTOCOMP_INDENT
;
682 snprintf(buf
, bsz
, "%*s", AUTOCOMP_INDENT
, "");
683 for (size_t j
= 0; j
< vector_active(comps
); j
++) {
684 char *item
= vector_slot(comps
, j
);
685 itemlen
= strlen(item
);
687 if (cs
+ itemlen
+ AUTOCOMP_INDENT
+ 3 >= bsz
)
688 buf
= XREALLOC(MTYPE_TMP
, buf
, (bsz
*= 2));
690 if (lc
+ itemlen
+ 1 >= cols
) {
691 cs
+= snprintf(&buf
[cs
], bsz
- cs
, "\n%*s",
692 AUTOCOMP_INDENT
, "");
693 lc
= AUTOCOMP_INDENT
;
696 size_t written
= snprintf(&buf
[cs
], bsz
- cs
, "%s ", item
);
699 XFREE(MTYPE_COMPLETION
, item
);
700 vector_set_index(comps
, j
, NULL
);
705 void cmd_variable_handler_register(const struct cmd_variable_handler
*cvh
)
710 for (; cvh
->completions
; cvh
++)
711 listnode_add(varhandlers
, (void *)cvh
);
714 DEFUN_HIDDEN (autocomplete
,
716 "autocomplete TYPE TEXT VARNAME",
717 "Autocompletion handler (internal, for vtysh)\n"
720 "cmd_token->varname\n")
722 struct cmd_token tok
;
723 vector comps
= vector_init(32);
726 memset(&tok
, 0, sizeof(tok
));
727 tok
.type
= atoi(argv
[1]->arg
);
728 tok
.text
= argv
[2]->arg
;
729 tok
.varname
= argv
[3]->arg
;
730 if (!strcmp(tok
.varname
, "-"))
733 cmd_variable_complete(&tok
, NULL
, comps
);
735 for (i
= 0; i
< vector_active(comps
); i
++) {
736 char *text
= vector_slot(comps
, i
);
737 vty_out(vty
, "%s\n", text
);
738 XFREE(MTYPE_COMPLETION
, text
);
746 * Generate possible tab-completions for the given input. This function only
747 * returns results that would result in a valid command if used as Readline
748 * completions (as is the case in vtysh). For instance, if the passed vline ends
749 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
751 * @param vline vectorized input line
753 * @param status location to store matcher status code in
754 * @return set of valid strings for use with Readline as tab-completions.
757 char **cmd_complete_command(vector vline
, struct vty
*vty
, int *status
)
760 int original_node
= vty
->node
;
761 vector input_line
= vector_init(vector_count(vline
));
763 // if the first token is 'do' we'll want to execute the command in the
765 int do_shortcut
= cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0));
766 vty
->node
= do_shortcut
? ENABLE_NODE
: original_node
;
768 // construct the input line we'll be matching on
769 unsigned int offset
= (do_shortcut
) ? 1 : 0;
770 for (unsigned index
= 0; index
+ offset
< vector_active(vline
); index
++)
771 vector_set_index(input_line
, index
,
772 vector_lookup(vline
, index
+ offset
));
774 // get token completions -- this is a copying operation
775 vector comps
= NULL
, initial_comps
;
776 initial_comps
= cmd_complete_command_real(input_line
, vty
, status
);
778 if (!MATCHER_ERROR(*status
)) {
779 assert(initial_comps
);
780 // filter out everything that is not suitable for a
782 comps
= vector_init(VECTOR_MIN_SIZE
);
783 for (unsigned int i
= 0; i
< vector_active(initial_comps
);
785 struct cmd_token
*token
= vector_slot(initial_comps
, i
);
786 if (token
->type
== WORD_TKN
)
787 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
,
789 else if (IS_VARYING_TOKEN(token
->type
)) {
790 const char *ref
= vector_lookup(
791 vline
, vector_active(vline
) - 1);
792 cmd_variable_complete(token
, ref
, comps
);
795 vector_free(initial_comps
);
797 // since we filtered results, we need to re-set status code
798 switch (vector_active(comps
)) {
800 *status
= CMD_ERR_NO_MATCH
;
803 *status
= CMD_COMPLETE_FULL_MATCH
;
806 *status
= CMD_COMPLETE_LIST_MATCH
;
809 // copy completions text into an array of char*
810 ret
= XMALLOC(MTYPE_TMP
,
811 (vector_active(comps
) + 1) * sizeof(char *));
813 for (i
= 0; i
< vector_active(comps
); i
++) {
814 ret
[i
] = vector_slot(comps
, i
);
816 // set the last element to NULL, because this array is used in
817 // a Readline completion_generator function which expects NULL
818 // as a sentinel value
822 } else if (initial_comps
)
823 vector_free(initial_comps
);
825 // comps should always be null here
828 // free the adjusted input line
829 vector_free(input_line
);
831 // reset vty->node to its original value
832 vty
->node
= original_node
;
837 /* return parent node */
838 /* MUST eventually converge on CONFIG_NODE */
839 enum node_type
node_parent(enum node_type node
)
843 assert(node
> CONFIG_NODE
);
848 case BGP_VRF_POLICY_NODE
:
849 case BGP_VNC_DEFAULTS_NODE
:
850 case BGP_VNC_NVE_GROUP_NODE
:
851 case BGP_VNC_L2_GROUP_NODE
:
861 case BGP_EVPN_VNI_NODE
:
864 case KEYCHAIN_KEY_NODE
:
867 case LINK_PARAMS_NODE
:
868 ret
= INTERFACE_NODE
;
874 case LDP_IPV4_IFACE_NODE
:
877 case LDP_IPV6_IFACE_NODE
:
880 case LDP_PSEUDOWIRE_NODE
:
881 ret
= LDP_L2VPN_NODE
;
891 /* Execute command by argument vline vector. */
892 static int cmd_execute_command_real(vector vline
, enum filter_type filter
,
894 const struct cmd_element
**cmd
)
896 struct list
*argv_list
;
897 enum matcher_rv status
;
898 const struct cmd_element
*matched_element
= NULL
;
900 struct graph
*cmdgraph
= cmd_node_graph(cmdvec
, vty
->node
);
901 status
= command_match(cmdgraph
, vline
, &argv_list
, &matched_element
);
904 *cmd
= matched_element
;
906 // if matcher error, return corresponding CMD_ERR
907 if (MATCHER_ERROR(status
)) {
909 list_delete(argv_list
);
911 case MATCHER_INCOMPLETE
:
912 return CMD_ERR_INCOMPLETE
;
913 case MATCHER_AMBIGUOUS
:
914 return CMD_ERR_AMBIGUOUS
;
916 return CMD_ERR_NO_MATCH
;
920 // build argv array from argv list
921 struct cmd_token
**argv
= XMALLOC(
922 MTYPE_TMP
, argv_list
->count
* sizeof(struct cmd_token
*));
924 struct cmd_token
*token
;
926 for (ALL_LIST_ELEMENTS_RO(argv_list
, ln
, token
))
929 int argc
= argv_list
->count
;
932 if (matched_element
->daemon
)
933 ret
= CMD_SUCCESS_DAEMON
;
935 ret
= matched_element
->func(matched_element
, vty
, argc
, argv
);
937 // delete list and cmd_token's in it
938 list_delete(argv_list
);
939 XFREE(MTYPE_TMP
, argv
);
945 * Execute a given command, handling things like "do ..." and checking
946 * whether the given command might apply at a parent node if doesn't
947 * apply for the current node.
949 * @param vline Command line input, vector of char* where each element is
951 * @param vty The vty context in which the command should be executed.
952 * @param cmd Pointer where the struct cmd_element of the matched command
953 * will be stored, if any. May be set to NULL if this info is
955 * @param vtysh If set != 0, don't lookup the command at parent nodes.
956 * @return The status of the command that has been executed or an error code
957 * as to why no command could be executed.
959 int cmd_execute_command(vector vline
, struct vty
*vty
,
960 const struct cmd_element
**cmd
, int vtysh
)
962 int ret
, saved_ret
= 0;
963 enum node_type onode
, try_node
;
965 onode
= try_node
= vty
->node
;
967 if (cmd_try_do_shortcut(vty
->node
, vector_slot(vline
, 0))) {
968 vector shifted_vline
;
971 vty
->node
= ENABLE_NODE
;
972 /* We can try it on enable node, cos' the vty is authenticated
975 shifted_vline
= vector_init(vector_count(vline
));
977 for (index
= 1; index
< vector_active(vline
); index
++)
978 vector_set_index(shifted_vline
, index
- 1,
979 vector_lookup(vline
, index
));
981 ret
= cmd_execute_command_real(shifted_vline
, FILTER_RELAXED
,
984 vector_free(shifted_vline
);
990 cmd_execute_command_real(vline
, FILTER_RELAXED
, vty
, cmd
);
995 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
996 /* This assumes all nodes above CONFIG_NODE are childs of
998 while (vty
->node
> CONFIG_NODE
) {
999 try_node
= node_parent(try_node
);
1000 vty
->node
= try_node
;
1001 ret
= cmd_execute_command_real(vline
, FILTER_RELAXED
,
1003 if (ret
== CMD_SUCCESS
|| ret
== CMD_WARNING
)
1006 /* no command succeeded, reset the vty to the original node */
1010 /* return command status for original node */
1015 * Execute a given command, matching it strictly against the current node.
1016 * This mode is used when reading config files.
1018 * @param vline Command line input, vector of char* where each element is
1020 * @param vty The vty context in which the command should be executed.
1021 * @param cmd Pointer where the struct cmd_element* of the matched command
1022 * will be stored, if any. May be set to NULL if this info is
1024 * @return The status of the command that has been executed or an error code
1025 * as to why no command could be executed.
1027 int cmd_execute_command_strict(vector vline
, struct vty
*vty
,
1028 const struct cmd_element
**cmd
)
1030 return cmd_execute_command_real(vline
, FILTER_STRICT
, vty
, cmd
);
1034 * Parse one line of config, walking up the parse tree attempting to find a
1037 * @param vty The vty context in which the command should be executed.
1038 * @param cmd Pointer where the struct cmd_element* of the match command
1039 * will be stored, if any. May be set to NULL if this info is
1041 * @param use_daemon Boolean to control whether or not we match on
1042 * CMD_SUCCESS_DAEMON
1044 * @return The status of the command that has been executed or an error code
1045 * as to why no command could be executed.
1047 int command_config_read_one_line(struct vty
*vty
,
1048 const struct cmd_element
**cmd
, int use_daemon
)
1054 vline
= cmd_make_strvec(vty
->buf
);
1056 /* In case of comment line */
1060 /* Execute configuration command : this is strict match */
1061 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1063 // Climb the tree and try the command again at each node
1064 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1065 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1066 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1067 && vty
->node
!= CONFIG_NODE
) {
1069 saved_node
= vty
->node
;
1071 while (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1072 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1073 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1074 && vty
->node
> CONFIG_NODE
) {
1075 vty
->node
= node_parent(vty
->node
);
1076 ret
= cmd_execute_command_strict(vline
, vty
, cmd
);
1079 // If climbing the tree did not work then ignore the command and
1080 // stay at the same node
1081 if (!(use_daemon
&& ret
== CMD_SUCCESS_DAEMON
)
1082 && !(!use_daemon
&& ret
== CMD_ERR_NOTHING_TODO
)
1083 && ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
) {
1084 vty
->node
= saved_node
;
1088 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
)
1089 memcpy(vty
->error_buf
, vty
->buf
, VTY_BUFSIZ
);
1091 cmd_free_strvec(vline
);
1096 /* Configuration make from file. */
1097 int config_from_file(struct vty
*vty
, FILE *fp
, unsigned int *line_num
)
1099 int ret
, error_ret
= 0;
1102 while (fgets(vty
->buf
, VTY_BUFSIZ
, fp
)) {
1106 ret
= command_config_read_one_line(vty
, NULL
, 0);
1108 if (ret
!= CMD_SUCCESS
&& ret
!= CMD_WARNING
1109 && ret
!= CMD_ERR_NOTHING_TODO
)
1120 /* Configuration from terminal */
1121 DEFUN (config_terminal
,
1122 config_terminal_cmd
,
1123 "configure terminal",
1124 "Configuration from vty interface\n"
1125 "Configuration terminal\n")
1127 if (vty_config_lock(vty
))
1128 vty
->node
= CONFIG_NODE
;
1130 vty_out(vty
, "VTY configuration is locked by other VTY\n");
1131 return CMD_WARNING_CONFIG_FAILED
;
1136 /* Enable command */
1140 "Turn on privileged mode command\n")
1142 /* If enable password is NULL, change to ENABLE_NODE */
1143 if ((host
.enable
== NULL
&& host
.enable_encrypt
== NULL
)
1144 || vty
->type
== VTY_SHELL_SERV
)
1145 vty
->node
= ENABLE_NODE
;
1147 vty
->node
= AUTH_ENABLE_NODE
;
1152 /* Disable command */
1156 "Turn off privileged mode command\n")
1158 if (vty
->node
== ENABLE_NODE
)
1159 vty
->node
= VIEW_NODE
;
1163 /* Down vty node level. */
1167 "Exit current mode and down to previous mode\n")
1173 void cmd_exit(struct vty
*vty
)
1175 switch (vty
->node
) {
1181 vty
->status
= VTY_CLOSE
;
1184 vty
->node
= ENABLE_NODE
;
1185 vty_config_unlock(vty
);
1187 case INTERFACE_NODE
:
1199 case LDP_L2VPN_NODE
:
1206 vty
->node
= CONFIG_NODE
;
1209 case BGP_IPV4M_NODE
:
1210 case BGP_IPV4L_NODE
:
1211 case BGP_VPNV4_NODE
:
1212 case BGP_VPNV6_NODE
:
1213 case BGP_VRF_POLICY_NODE
:
1214 case BGP_VNC_DEFAULTS_NODE
:
1215 case BGP_VNC_NVE_GROUP_NODE
:
1216 case BGP_VNC_L2_GROUP_NODE
:
1218 case BGP_IPV6M_NODE
:
1220 case BGP_IPV6L_NODE
:
1221 vty
->node
= BGP_NODE
;
1223 case BGP_EVPN_VNI_NODE
:
1224 vty
->node
= BGP_EVPN_NODE
;
1228 vty
->node
= LDP_NODE
;
1230 case LDP_IPV4_IFACE_NODE
:
1231 vty
->node
= LDP_IPV4_NODE
;
1233 case LDP_IPV6_IFACE_NODE
:
1234 vty
->node
= LDP_IPV6_NODE
;
1236 case LDP_PSEUDOWIRE_NODE
:
1237 vty
->node
= LDP_L2VPN_NODE
;
1239 case KEYCHAIN_KEY_NODE
:
1240 vty
->node
= KEYCHAIN_NODE
;
1242 case LINK_PARAMS_NODE
:
1243 vty
->node
= INTERFACE_NODE
;
1254 "Exit current mode and down to previous mode\n")
1256 return config_exit(self
, vty
, argc
, argv
);
1260 /* End of configuration. */
1264 "End current mode and change to enable mode.")
1266 switch (vty
->node
) {
1269 /* Nothing to do. */
1272 case INTERFACE_NODE
:
1281 case BGP_VRF_POLICY_NODE
:
1282 case BGP_VNC_DEFAULTS_NODE
:
1283 case BGP_VNC_NVE_GROUP_NODE
:
1284 case BGP_VNC_L2_GROUP_NODE
:
1285 case BGP_VPNV4_NODE
:
1286 case BGP_VPNV6_NODE
:
1288 case BGP_IPV4M_NODE
:
1289 case BGP_IPV4L_NODE
:
1291 case BGP_IPV6M_NODE
:
1293 case BGP_EVPN_VNI_NODE
:
1294 case BGP_IPV6L_NODE
:
1301 case LDP_IPV4_IFACE_NODE
:
1302 case LDP_IPV6_IFACE_NODE
:
1303 case LDP_L2VPN_NODE
:
1304 case LDP_PSEUDOWIRE_NODE
:
1307 case KEYCHAIN_KEY_NODE
:
1311 case LINK_PARAMS_NODE
:
1312 vty_config_unlock(vty
);
1313 vty
->node
= ENABLE_NODE
;
1322 DEFUN (show_version
,
1326 "Displays zebra version\n")
1328 vty_out(vty
, "%s %s (%s).\n", FRR_FULL_NAME
, FRR_VERSION
,
1329 host
.name
? host
.name
: "");
1330 vty_out(vty
, "%s%s\n", FRR_COPYRIGHT
, GIT_INFO
);
1331 vty_out(vty
, "configured with:\n %s\n", FRR_CONFIG_ARGS
);
1336 /* "Set" version ... ignore version tags */
1337 DEFUN (frr_version_defaults
,
1338 frr_version_defaults_cmd
,
1339 "frr <version|defaults> LINE...",
1340 "FRRouting global parameters\n"
1341 "version configuration was written by\n"
1342 "set of configuration defaults used\n"
1348 /* Help display function for all node. */
1352 "Description of the interactive help system\n")
1355 "Quagga VTY provides advanced help feature. When you need help,\n\
1356 anytime at the command line please press '?'.\n\
1358 If nothing matches, the help list will be empty and you must backup\n\
1359 until entering a '?' shows the available options.\n\
1360 Two styles of help are provided:\n\
1361 1. Full help is available when you are ready to enter a\n\
1362 command argument (e.g. 'show ?') and describes each possible\n\
1364 2. Partial help is provided when an abbreviated argument is entered\n\
1365 and you want to know what arguments match the input\n\
1366 (e.g. 'show me?'.)\n\n");
1370 static void permute(struct graph_node
*start
, struct vty
*vty
)
1372 static struct list
*position
= NULL
;
1374 position
= list_new();
1376 struct cmd_token
*stok
= start
->data
;
1377 struct graph_node
*gnn
;
1378 struct listnode
*ln
;
1381 listnode_add(position
, start
);
1382 for (unsigned int i
= 0; i
< vector_active(start
->to
); i
++) {
1383 struct graph_node
*gn
= vector_slot(start
->to
, i
);
1384 struct cmd_token
*tok
= gn
->data
;
1385 if (tok
->attr
== CMD_ATTR_HIDDEN
1386 || tok
->attr
== CMD_ATTR_DEPRECATED
)
1388 else if (tok
->type
== END_TKN
|| gn
== start
) {
1390 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
)) {
1391 struct cmd_token
*tt
= gnn
->data
;
1392 if (tt
->type
< SPECIAL_TKN
)
1393 vty_out(vty
, " %s", tt
->text
);
1396 vty_out(vty
, "...");
1400 if (stok
->type
== FORK_TKN
&& tok
->type
!= FORK_TKN
)
1401 for (ALL_LIST_ELEMENTS_RO(position
, ln
, gnn
))
1410 list_delete_node(position
, listtail(position
));
1413 int cmd_list_cmds(struct vty
*vty
, int do_permute
)
1415 struct cmd_node
*node
= vector_slot(cmdvec
, vty
->node
);
1418 permute(vector_slot(node
->cmdgraph
->nodes
, 0), vty
);
1420 /* loop over all commands at this node */
1421 struct cmd_element
*element
= NULL
;
1422 for (unsigned int i
= 0; i
< vector_active(node
->cmd_vector
);
1424 if ((element
= vector_slot(node
->cmd_vector
, i
))
1425 && element
->attr
!= CMD_ATTR_DEPRECATED
1426 && element
->attr
!= CMD_ATTR_HIDDEN
)
1427 vty_out(vty
, " %s\n", element
->string
);
1432 /* Help display function for all node. */
1435 "list [permutations]",
1436 "Print command list\n"
1437 "Print all possible command permutations\n")
1439 return cmd_list_cmds(vty
, argc
== 2);
1442 DEFUN (show_commandtree
,
1443 show_commandtree_cmd
,
1444 "show commandtree [permutations]",
1446 "Show command tree\n"
1447 "Permutations that we are interested in\n")
1449 return cmd_list_cmds(vty
, argc
== 3);
1452 static void vty_write_config(struct vty
*vty
)
1455 struct cmd_node
*node
;
1457 if (vty
->type
== VTY_TERM
) {
1458 vty_out(vty
, "\nCurrent configuration:\n");
1459 vty_out(vty
, "!\n");
1462 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
1463 vty_out(vty
, "frr defaults %s\n", DFLT_NAME
);
1464 vty_out(vty
, "!\n");
1466 for (i
= 0; i
< vector_active(cmdvec
); i
++)
1467 if ((node
= vector_slot(cmdvec
, i
)) && node
->func
1468 && (node
->vtysh
|| vty
->type
!= VTY_SHELL
)) {
1469 if ((*node
->func
)(vty
))
1470 vty_out(vty
, "!\n");
1473 if (vty
->type
== VTY_TERM
) {
1474 vty_out(vty
, "end\n");
1478 /* Write current configuration into file. */
1480 DEFUN (config_write
,
1482 "write [<file|memory|terminal>]",
1483 "Write running configuration to memory, network, or terminal\n"
1484 "Write to configuration file\n"
1485 "Write configuration currently in memory\n"
1486 "Write configuration to terminal\n")
1490 char *config_file
, *slash
;
1491 char *config_file_tmp
= NULL
;
1492 char *config_file_sav
= NULL
;
1493 int ret
= CMD_WARNING
;
1494 struct vty
*file_vty
;
1495 struct stat conf_stat
;
1497 // if command was 'write terminal' or 'show running-config'
1498 if (argc
== 2 && (strmatch(argv
[idx_type
]->text
, "terminal")
1499 || strmatch(argv
[0]->text
, "show"))) {
1500 vty_write_config(vty
);
1507 /* Check and see if we are operating under vtysh configuration */
1508 if (host
.config
== NULL
) {
1510 "Can't save to configuration file, using vtysh.\n");
1515 config_file
= host
.config
;
1518 #define O_DIRECTORY 0
1520 slash
= strrchr(config_file
, '/');
1522 char *config_dir
= XSTRDUP(MTYPE_TMP
, config_file
);
1523 config_dir
[slash
- config_file
] = '\0';
1524 dirfd
= open(config_dir
, O_DIRECTORY
| O_RDONLY
);
1525 XFREE(MTYPE_TMP
, config_dir
);
1527 dirfd
= open(".", O_DIRECTORY
| O_RDONLY
);
1528 /* if dirfd is invalid, directory sync fails, but we're still OK */
1530 config_file_sav
= XMALLOC(
1531 MTYPE_TMP
, strlen(config_file
) + strlen(CONF_BACKUP_EXT
) + 1);
1532 strcpy(config_file_sav
, config_file
);
1533 strcat(config_file_sav
, CONF_BACKUP_EXT
);
1536 config_file_tmp
= XMALLOC(MTYPE_TMP
, strlen(config_file
) + 8);
1537 sprintf(config_file_tmp
, "%s.XXXXXX", config_file
);
1539 /* Open file to configuration write. */
1540 fd
= mkstemp(config_file_tmp
);
1542 vty_out(vty
, "Can't open configuration file %s.\n",
1546 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
1547 vty_out(vty
, "Can't chmod configuration file %s: %s (%d).\n",
1548 config_file_tmp
, safe_strerror(errno
), errno
);
1552 /* Make vty for configuration file. */
1553 file_vty
= vty_new();
1555 file_vty
->type
= VTY_FILE
;
1557 /* Config file header print. */
1558 vty_out(file_vty
, "!\n! Zebra configuration saved from vty\n! ");
1559 vty_time_print(file_vty
, 1);
1560 vty_out(file_vty
, "!\n");
1561 vty_write_config(file_vty
);
1562 vty_close(file_vty
);
1564 if (stat(config_file
, &conf_stat
) >= 0) {
1565 if (unlink(config_file_sav
) != 0)
1566 if (errno
!= ENOENT
) {
1568 "Can't unlink backup configuration file %s.\n",
1572 if (link(config_file
, config_file_sav
) != 0) {
1574 "Can't backup old configuration file %s.\n",
1581 if (rename(config_file_tmp
, config_file
) != 0) {
1582 vty_out(vty
, "Can't save configuration file %s.\n",
1589 vty_out(vty
, "Configuration saved to %s\n", config_file
);
1593 if (ret
!= CMD_SUCCESS
)
1594 unlink(config_file_tmp
);
1597 XFREE(MTYPE_TMP
, config_file_tmp
);
1598 XFREE(MTYPE_TMP
, config_file_sav
);
1602 /* ALIAS_FIXME for 'write <terminal|memory>' */
1603 DEFUN (show_running_config
,
1604 show_running_config_cmd
,
1605 "show running-config",
1607 "running configuration (same as write terminal/memory)\n")
1609 return config_write(self
, vty
, argc
, argv
);
1612 /* ALIAS_FIXME for 'write file' */
1613 DEFUN (copy_runningconf_startupconf
,
1614 copy_runningconf_startupconf_cmd
,
1615 "copy running-config startup-config",
1616 "Copy configuration\n"
1617 "Copy running config to... \n"
1618 "Copy running config to startup config (same as write file)\n")
1621 vty_write_config(vty
);
1626 /* Write startup configuration into the terminal. */
1627 DEFUN (show_startup_config
,
1628 show_startup_config_cmd
,
1629 "show startup-config",
1631 "Contents of startup configuration\n")
1638 if (host
.config
== NULL
)
1641 confp
= fopen(host
.config
, "r");
1642 if (confp
== NULL
) {
1643 vty_out(vty
, "Can't open configuration file [%s] due to '%s'\n",
1644 host
.config
, safe_strerror(errno
));
1648 while (fgets(buf
, BUFSIZ
, confp
)) {
1651 while (*cp
!= '\r' && *cp
!= '\n' && *cp
!= '\0')
1655 vty_out(vty
, "%s\n", buf
);
1663 int cmd_hostname_set(const char *hostname
)
1665 XFREE(MTYPE_HOST
, host
.name
);
1666 host
.name
= hostname
? XSTRDUP(MTYPE_HOST
, hostname
) : NULL
;
1670 /* Hostname configuration */
1671 DEFUN (config_hostname
,
1674 "Set system's network name\n"
1675 "This system's network name\n")
1677 struct cmd_token
*word
= argv
[1];
1679 if (!isalpha((int)word
->arg
[0])) {
1680 vty_out(vty
, "Please specify string starting with alphabet\n");
1681 return CMD_WARNING_CONFIG_FAILED
;
1684 return cmd_hostname_set(word
->arg
);
1687 DEFUN (config_no_hostname
,
1689 "no hostname [HOSTNAME]",
1691 "Reset system's network name\n"
1692 "Host name of this router\n")
1694 return cmd_hostname_set(NULL
);
1697 /* VTY interface password set. */
1698 DEFUN (config_password
,
1700 "password [(8-8)] WORD",
1701 "Assign the terminal connection password\n"
1702 "Specifies a HIDDEN password will follow\n"
1703 "The password string\n")
1707 if (argc
== 3) // '8' was specified
1710 XFREE(MTYPE_HOST
, host
.password
);
1711 host
.password
= NULL
;
1712 if (host
.password_encrypt
)
1713 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1714 host
.password_encrypt
=
1715 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1719 if (!isalnum(argv
[idx_8
]->arg
[0])) {
1721 "Please specify string starting with alphanumeric\n");
1722 return CMD_WARNING_CONFIG_FAILED
;
1726 XFREE(MTYPE_HOST
, host
.password
);
1727 host
.password
= NULL
;
1730 if (host
.password_encrypt
)
1731 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1732 host
.password_encrypt
=
1733 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1735 host
.password
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1740 /* VTY enable password set. */
1741 DEFUN (config_enable_password
,
1742 enable_password_cmd
,
1743 "enable password [(8-8)] WORD",
1744 "Modify enable password parameters\n"
1745 "Assign the privileged level password\n"
1746 "Specifies a HIDDEN password will follow\n"
1747 "The HIDDEN 'enable' password string\n")
1752 /* Crypt type is specified. */
1754 if (argv
[idx_8
]->arg
[0] == '8') {
1756 XFREE(MTYPE_HOST
, host
.enable
);
1759 if (host
.enable_encrypt
)
1760 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1761 host
.enable_encrypt
=
1762 XSTRDUP(MTYPE_HOST
, argv
[idx_word
]->arg
);
1766 vty_out(vty
, "Unknown encryption type.\n");
1767 return CMD_WARNING_CONFIG_FAILED
;
1771 if (!isalnum(argv
[idx_8
]->arg
[0])) {
1773 "Please specify string starting with alphanumeric\n");
1774 return CMD_WARNING_CONFIG_FAILED
;
1778 XFREE(MTYPE_HOST
, host
.enable
);
1781 /* Plain password input. */
1783 if (host
.enable_encrypt
)
1784 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1785 host
.enable_encrypt
=
1786 XSTRDUP(MTYPE_HOST
, zencrypt(argv
[idx_8
]->arg
));
1788 host
.enable
= XSTRDUP(MTYPE_HOST
, argv
[idx_8
]->arg
);
1793 /* VTY enable password delete. */
1794 DEFUN (no_config_enable_password
,
1795 no_enable_password_cmd
,
1796 "no enable password",
1798 "Modify enable password parameters\n"
1799 "Assign the privileged level password\n")
1802 XFREE(MTYPE_HOST
, host
.enable
);
1805 if (host
.enable_encrypt
)
1806 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1807 host
.enable_encrypt
= NULL
;
1812 DEFUN (service_password_encrypt
,
1813 service_password_encrypt_cmd
,
1814 "service password-encryption",
1815 "Set up miscellaneous service\n"
1816 "Enable encrypted passwords\n")
1823 if (host
.password
) {
1824 if (host
.password_encrypt
)
1825 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1826 host
.password_encrypt
=
1827 XSTRDUP(MTYPE_HOST
, zencrypt(host
.password
));
1830 if (host
.enable_encrypt
)
1831 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1832 host
.enable_encrypt
=
1833 XSTRDUP(MTYPE_HOST
, zencrypt(host
.enable
));
1839 DEFUN (no_service_password_encrypt
,
1840 no_service_password_encrypt_cmd
,
1841 "no service password-encryption",
1843 "Set up miscellaneous service\n"
1844 "Enable encrypted passwords\n")
1851 if (host
.password_encrypt
)
1852 XFREE(MTYPE_HOST
, host
.password_encrypt
);
1853 host
.password_encrypt
= NULL
;
1855 if (host
.enable_encrypt
)
1856 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
1857 host
.enable_encrypt
= NULL
;
1862 DEFUN (config_terminal_length
,
1863 config_terminal_length_cmd
,
1864 "terminal length (0-512)",
1865 "Set terminal line parameters\n"
1866 "Set number of lines on a screen\n"
1867 "Number of lines on screen (0 for no pausing)\n")
1871 vty
->lines
= atoi(argv
[idx_number
]->arg
);
1876 DEFUN (config_terminal_no_length
,
1877 config_terminal_no_length_cmd
,
1878 "terminal no length",
1879 "Set terminal line parameters\n"
1881 "Set number of lines on a screen\n")
1887 DEFUN (service_terminal_length
,
1888 service_terminal_length_cmd
,
1889 "service terminal-length (0-512)",
1890 "Set up miscellaneous service\n"
1891 "System wide terminal length configuration\n"
1892 "Number of lines of VTY (0 means no line control)\n")
1896 host
.lines
= atoi(argv
[idx_number
]->arg
);
1901 DEFUN (no_service_terminal_length
,
1902 no_service_terminal_length_cmd
,
1903 "no service terminal-length [(0-512)]",
1905 "Set up miscellaneous service\n"
1906 "System wide terminal length configuration\n"
1907 "Number of lines of VTY (0 means no line control)\n")
1913 DEFUN_HIDDEN (do_echo
,
1916 "Echo a message back to the vty\n"
1917 "The message to echo\n")
1921 vty_out(vty
, "%s\n",
1922 ((message
= argv_concat(argv
, argc
, 1)) ? message
: ""));
1924 XFREE(MTYPE_TMP
, message
);
1928 DEFUN (config_logmsg
,
1930 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
1931 "Send a message to enabled logging destinations\n"
1933 "The message to send\n")
1935 int idx_log_level
= 1;
1936 int idx_message
= 2;
1940 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
1941 return CMD_ERR_NO_MATCH
;
1944 ((message
= argv_concat(argv
, argc
, idx_message
)) ? message
: ""));
1946 XFREE(MTYPE_TMP
, message
);
1951 DEFUN (show_logging
,
1955 "Show current logging configuration\n")
1957 struct zlog
*zl
= zlog_default
;
1959 vty_out(vty
, "Syslog logging: ");
1960 if (zl
->maxlvl
[ZLOG_DEST_SYSLOG
] == ZLOG_DISABLED
)
1961 vty_out(vty
, "disabled");
1963 vty_out(vty
, "level %s, facility %s, ident %s",
1964 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_SYSLOG
]],
1965 facility_name(zl
->facility
), zl
->ident
);
1968 vty_out(vty
, "Stdout logging: ");
1969 if (zl
->maxlvl
[ZLOG_DEST_STDOUT
] == ZLOG_DISABLED
)
1970 vty_out(vty
, "disabled");
1972 vty_out(vty
, "level %s",
1973 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_STDOUT
]]);
1976 vty_out(vty
, "Monitor logging: ");
1977 if (zl
->maxlvl
[ZLOG_DEST_MONITOR
] == ZLOG_DISABLED
)
1978 vty_out(vty
, "disabled");
1980 vty_out(vty
, "level %s",
1981 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_MONITOR
]]);
1984 vty_out(vty
, "File logging: ");
1985 if ((zl
->maxlvl
[ZLOG_DEST_FILE
] == ZLOG_DISABLED
) || !zl
->fp
)
1986 vty_out(vty
, "disabled");
1988 vty_out(vty
, "level %s, filename %s",
1989 zlog_priority
[zl
->maxlvl
[ZLOG_DEST_FILE
]],
1993 vty_out(vty
, "Protocol name: %s\n", zl
->protoname
);
1994 vty_out(vty
, "Record priority: %s\n",
1995 (zl
->record_priority
? "enabled" : "disabled"));
1996 vty_out(vty
, "Timestamp precision: %d\n", zl
->timestamp_precision
);
2001 DEFUN (config_log_stdout
,
2002 config_log_stdout_cmd
,
2003 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2005 "Set stdout logging level\n"
2008 int idx_log_level
= 2;
2010 if (argc
== idx_log_level
) {
2011 zlog_set_level(ZLOG_DEST_STDOUT
, zlog_default
->default_lvl
);
2016 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2017 return CMD_ERR_NO_MATCH
;
2018 zlog_set_level(ZLOG_DEST_STDOUT
, level
);
2022 DEFUN (no_config_log_stdout
,
2023 no_config_log_stdout_cmd
,
2024 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2027 "Cancel logging to stdout\n"
2030 zlog_set_level(ZLOG_DEST_STDOUT
, ZLOG_DISABLED
);
2034 DEFUN (config_log_monitor
,
2035 config_log_monitor_cmd
,
2036 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2038 "Set terminal line (monitor) logging level\n"
2041 int idx_log_level
= 2;
2043 if (argc
== idx_log_level
) {
2044 zlog_set_level(ZLOG_DEST_MONITOR
, zlog_default
->default_lvl
);
2049 if ((level
= level_match(argv
[idx_log_level
]->arg
)) == ZLOG_DISABLED
)
2050 return CMD_ERR_NO_MATCH
;
2051 zlog_set_level(ZLOG_DEST_MONITOR
, level
);
2055 DEFUN (no_config_log_monitor
,
2056 no_config_log_monitor_cmd
,
2057 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2060 "Disable terminal line (monitor) logging\n"
2063 zlog_set_level(ZLOG_DEST_MONITOR
, ZLOG_DISABLED
);
2067 static int set_log_file(struct vty
*vty
, const char *fname
, int loglevel
)
2071 const char *fullpath
;
2073 /* Path detection. */
2074 if (!IS_DIRECTORY_SEP(*fname
)) {
2075 char cwd
[MAXPATHLEN
+ 1];
2076 cwd
[MAXPATHLEN
] = '\0';
2078 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2079 zlog_err("config_log_file: Unable to alloc mem!");
2080 return CMD_WARNING_CONFIG_FAILED
;
2083 if ((p
= XMALLOC(MTYPE_TMP
, strlen(cwd
) + strlen(fname
) + 2))
2085 zlog_err("config_log_file: Unable to alloc mem!");
2086 return CMD_WARNING_CONFIG_FAILED
;
2088 sprintf(p
, "%s/%s", cwd
, fname
);
2093 ret
= zlog_set_file(fullpath
, loglevel
);
2096 XFREE(MTYPE_TMP
, p
);
2099 vty_out(vty
, "can't open logfile %s\n", fname
);
2100 return CMD_WARNING_CONFIG_FAILED
;
2104 XFREE(MTYPE_HOST
, host
.logfile
);
2106 host
.logfile
= XSTRDUP(MTYPE_HOST
, fname
);
2108 #if defined(HAVE_CUMULUS)
2109 if (zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] != ZLOG_DISABLED
)
2110 zlog_default
->maxlvl
[ZLOG_DEST_SYSLOG
] = ZLOG_DISABLED
;
2115 DEFUN (config_log_file
,
2116 config_log_file_cmd
,
2117 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2120 "Logging filename\n"
2123 int idx_filename
= 2;
2124 int idx_log_levels
= 3;
2127 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2129 return CMD_ERR_NO_MATCH
;
2130 return set_log_file(vty
, argv
[idx_filename
]->arg
, level
);
2132 return set_log_file(vty
, argv
[idx_filename
]->arg
,
2133 zlog_default
->default_lvl
);
2136 DEFUN (no_config_log_file
,
2137 no_config_log_file_cmd
,
2138 "no log file [FILENAME [LEVEL]]",
2141 "Cancel logging to file\n"
2142 "Logging file name\n"
2148 XFREE(MTYPE_HOST
, host
.logfile
);
2150 host
.logfile
= NULL
;
2155 DEFUN (config_log_syslog
,
2156 config_log_syslog_cmd
,
2157 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2159 "Set syslog logging level\n"
2162 int idx_log_levels
= 2;
2165 if ((level
= level_match(argv
[idx_log_levels
]->arg
))
2167 return CMD_ERR_NO_MATCH
;
2168 zlog_set_level(ZLOG_DEST_SYSLOG
, level
);
2171 zlog_set_level(ZLOG_DEST_SYSLOG
, zlog_default
->default_lvl
);
2176 DEFUN (no_config_log_syslog
,
2177 no_config_log_syslog_cmd
,
2178 "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>]",
2181 "Cancel logging to syslog\n"
2185 zlog_set_level(ZLOG_DEST_SYSLOG
, ZLOG_DISABLED
);
2189 DEFUN (config_log_facility
,
2190 config_log_facility_cmd
,
2191 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2193 "Facility parameter for syslog messages\n"
2197 int facility
= facility_match(argv
[idx_target
]->arg
);
2199 zlog_default
->facility
= facility
;
2203 DEFUN (no_config_log_facility
,
2204 no_config_log_facility_cmd
,
2205 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2208 "Reset syslog facility to default (daemon)\n"
2211 zlog_default
->facility
= LOG_DAEMON
;
2216 config_log_trap
, config_log_trap_cmd
,
2217 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2219 "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC
)
2224 if ((new_level
= level_match(argv
[2]->arg
)) == ZLOG_DISABLED
)
2225 return CMD_ERR_NO_MATCH
;
2227 zlog_default
->default_lvl
= new_level
;
2228 for (i
= 0; i
< ZLOG_NUM_DESTS
; i
++)
2229 if (zlog_default
->maxlvl
[i
] != ZLOG_DISABLED
)
2230 zlog_default
->maxlvl
[i
] = new_level
;
2235 no_config_log_trap
, no_config_log_trap_cmd
,
2236 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2239 "Permit all logging information\n" LOG_LEVEL_DESC
)
2241 zlog_default
->default_lvl
= LOG_DEBUG
;
2245 DEFUN (config_log_record_priority
,
2246 config_log_record_priority_cmd
,
2247 "log record-priority",
2249 "Log the priority of the message within the message\n")
2251 zlog_default
->record_priority
= 1;
2255 DEFUN (no_config_log_record_priority
,
2256 no_config_log_record_priority_cmd
,
2257 "no log record-priority",
2260 "Do not log the priority of the message within the message\n")
2262 zlog_default
->record_priority
= 0;
2266 DEFUN (config_log_timestamp_precision
,
2267 config_log_timestamp_precision_cmd
,
2268 "log timestamp precision (0-6)",
2270 "Timestamp configuration\n"
2271 "Set the timestamp precision\n"
2272 "Number of subsecond digits\n")
2275 zlog_default
->timestamp_precision
=
2276 strtoul(argv
[idx_number
]->arg
, NULL
, 10);
2280 DEFUN (no_config_log_timestamp_precision
,
2281 no_config_log_timestamp_precision_cmd
,
2282 "no log timestamp precision",
2285 "Timestamp configuration\n"
2286 "Reset the timestamp precision to the default value of 0\n")
2288 zlog_default
->timestamp_precision
= 0;
2292 int cmd_banner_motd_file(const char *file
)
2294 int success
= CMD_SUCCESS
;
2299 rpath
= realpath(file
, p
);
2301 return CMD_ERR_NO_FILE
;
2302 in
= strstr(rpath
, SYSCONFDIR
);
2305 XFREE(MTYPE_HOST
, host
.motdfile
);
2306 host
.motdfile
= XSTRDUP(MTYPE_HOST
, file
);
2308 success
= CMD_WARNING_CONFIG_FAILED
;
2313 DEFUN (banner_motd_file
,
2314 banner_motd_file_cmd
,
2315 "banner motd file FILE",
2318 "Banner from a file\n"
2322 const char *filename
= argv
[idx_file
]->arg
;
2323 int cmd
= cmd_banner_motd_file(filename
);
2325 if (cmd
== CMD_ERR_NO_FILE
)
2326 vty_out(vty
, "%s does not exist", filename
);
2327 else if (cmd
== CMD_WARNING_CONFIG_FAILED
)
2328 vty_out(vty
, "%s must be in %s", filename
, SYSCONFDIR
);
2333 DEFUN (banner_motd_default
,
2334 banner_motd_default_cmd
,
2335 "banner motd default",
2336 "Set banner string\n"
2337 "Strings for motd\n"
2340 host
.motd
= default_motd
;
2344 DEFUN (no_banner_motd
,
2348 "Set banner string\n"
2349 "Strings for motd\n")
2353 XFREE(MTYPE_HOST
, host
.motdfile
);
2354 host
.motdfile
= NULL
;
2358 /* Set config filename. Called from vty.c */
2359 void host_config_set(const char *filename
)
2362 XFREE(MTYPE_HOST
, host
.config
);
2363 host
.config
= XSTRDUP(MTYPE_HOST
, filename
);
2366 const char *host_config_get(void)
2371 void install_default(enum node_type node
)
2373 install_element(node
, &config_exit_cmd
);
2374 install_element(node
, &config_quit_cmd
);
2375 install_element(node
, &config_end_cmd
);
2376 install_element(node
, &config_help_cmd
);
2377 install_element(node
, &config_list_cmd
);
2379 install_element(node
, &config_write_cmd
);
2380 install_element(node
, &show_running_config_cmd
);
2382 install_element(node
, &autocomplete_cmd
);
2385 /* Initialize command interface. Install basic nodes and commands.
2387 * terminal = 0 -- vtysh / no logging, no config control
2388 * terminal = 1 -- normal daemon
2389 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2390 void cmd_init(int terminal
)
2394 varhandlers
= list_new();
2396 /* Allocate initial top vector of commands. */
2397 cmdvec
= vector_init(VECTOR_MIN_SIZE
);
2399 /* Default host value settings. */
2401 host
.password
= NULL
;
2403 host
.logfile
= NULL
;
2405 host
.noconfig
= (terminal
< 0);
2407 host
.motd
= default_motd
;
2408 host
.motdfile
= NULL
;
2410 /* Install top nodes. */
2411 install_node(&view_node
, NULL
);
2412 install_node(&enable_node
, NULL
);
2413 install_node(&auth_node
, NULL
);
2414 install_node(&auth_enable_node
, NULL
);
2415 install_node(&config_node
, config_write_host
);
2417 /* Each node's basic commands. */
2418 install_element(VIEW_NODE
, &show_version_cmd
);
2420 install_element(VIEW_NODE
, &config_list_cmd
);
2421 install_element(VIEW_NODE
, &config_exit_cmd
);
2422 install_element(VIEW_NODE
, &config_quit_cmd
);
2423 install_element(VIEW_NODE
, &config_help_cmd
);
2424 install_element(VIEW_NODE
, &config_enable_cmd
);
2425 install_element(VIEW_NODE
, &config_terminal_length_cmd
);
2426 install_element(VIEW_NODE
, &config_terminal_no_length_cmd
);
2427 install_element(VIEW_NODE
, &show_logging_cmd
);
2428 install_element(VIEW_NODE
, &show_commandtree_cmd
);
2429 install_element(VIEW_NODE
, &echo_cmd
);
2430 install_element(VIEW_NODE
, &autocomplete_cmd
);
2434 install_element(ENABLE_NODE
, &config_end_cmd
);
2435 install_element(ENABLE_NODE
, &config_disable_cmd
);
2436 install_element(ENABLE_NODE
, &config_terminal_cmd
);
2437 install_element(ENABLE_NODE
, ©_runningconf_startupconf_cmd
);
2438 install_element(ENABLE_NODE
, &config_write_cmd
);
2439 install_element(ENABLE_NODE
, &show_running_config_cmd
);
2441 install_element(ENABLE_NODE
, &show_startup_config_cmd
);
2444 install_element(ENABLE_NODE
, &config_logmsg_cmd
);
2445 install_default(CONFIG_NODE
);
2448 workqueue_cmd_init();
2452 install_element(CONFIG_NODE
, &hostname_cmd
);
2453 install_element(CONFIG_NODE
, &no_hostname_cmd
);
2454 install_element(CONFIG_NODE
, &frr_version_defaults_cmd
);
2457 install_element(CONFIG_NODE
, &password_cmd
);
2458 install_element(CONFIG_NODE
, &enable_password_cmd
);
2459 install_element(CONFIG_NODE
, &no_enable_password_cmd
);
2461 install_element(CONFIG_NODE
, &config_log_stdout_cmd
);
2462 install_element(CONFIG_NODE
, &no_config_log_stdout_cmd
);
2463 install_element(CONFIG_NODE
, &config_log_monitor_cmd
);
2464 install_element(CONFIG_NODE
, &no_config_log_monitor_cmd
);
2465 install_element(CONFIG_NODE
, &config_log_file_cmd
);
2466 install_element(CONFIG_NODE
, &no_config_log_file_cmd
);
2467 install_element(CONFIG_NODE
, &config_log_syslog_cmd
);
2468 install_element(CONFIG_NODE
, &no_config_log_syslog_cmd
);
2469 install_element(CONFIG_NODE
, &config_log_facility_cmd
);
2470 install_element(CONFIG_NODE
, &no_config_log_facility_cmd
);
2471 install_element(CONFIG_NODE
, &config_log_trap_cmd
);
2472 install_element(CONFIG_NODE
, &no_config_log_trap_cmd
);
2473 install_element(CONFIG_NODE
, &config_log_record_priority_cmd
);
2474 install_element(CONFIG_NODE
,
2475 &no_config_log_record_priority_cmd
);
2476 install_element(CONFIG_NODE
,
2477 &config_log_timestamp_precision_cmd
);
2478 install_element(CONFIG_NODE
,
2479 &no_config_log_timestamp_precision_cmd
);
2480 install_element(CONFIG_NODE
, &service_password_encrypt_cmd
);
2481 install_element(CONFIG_NODE
, &no_service_password_encrypt_cmd
);
2482 install_element(CONFIG_NODE
, &banner_motd_default_cmd
);
2483 install_element(CONFIG_NODE
, &banner_motd_file_cmd
);
2484 install_element(CONFIG_NODE
, &no_banner_motd_cmd
);
2485 install_element(CONFIG_NODE
, &service_terminal_length_cmd
);
2486 install_element(CONFIG_NODE
, &no_service_terminal_length_cmd
);
2488 vrf_install_commands();
2492 grammar_sandbox_init();
2496 void cmd_terminate()
2498 struct cmd_node
*cmd_node
;
2501 for (unsigned int i
= 0; i
< vector_active(cmdvec
); i
++)
2502 if ((cmd_node
= vector_slot(cmdvec
, i
)) != NULL
) {
2503 // deleting the graph delets the cmd_element as
2505 graph_delete_graph(cmd_node
->cmdgraph
);
2506 vector_free(cmd_node
->cmd_vector
);
2507 hash_clean(cmd_node
->cmd_hash
, NULL
);
2508 hash_free(cmd_node
->cmd_hash
);
2509 cmd_node
->cmd_hash
= NULL
;
2512 vector_free(cmdvec
);
2517 XFREE(MTYPE_HOST
, host
.name
);
2519 XFREE(MTYPE_HOST
, host
.password
);
2520 if (host
.password_encrypt
)
2521 XFREE(MTYPE_HOST
, host
.password_encrypt
);
2523 XFREE(MTYPE_HOST
, host
.enable
);
2524 if (host
.enable_encrypt
)
2525 XFREE(MTYPE_HOST
, host
.enable_encrypt
);
2527 XFREE(MTYPE_HOST
, host
.logfile
);
2529 XFREE(MTYPE_HOST
, host
.motdfile
);
2531 XFREE(MTYPE_HOST
, host
.config
);
2533 list_delete(varhandlers
);