]> git.proxmox.com Git - mirror_frr.git/blob - lib/command.c
2bff3b17a2e5321a7c30f9f2030fb46348c30fb6
[mirror_frr.git] / lib / command.c
1 /*
2 * CLI backend interface.
3 *
4 * --
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")
9 *
10 * This file is part of GNU Zebra.
11 *
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
15 * later version.
16 *
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.
21 *
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
25 */
26
27 #include <zebra.h>
28
29
30 #include "memory.h"
31 #include "log.h"
32 #include "log_int.h"
33 #include <lib/version.h>
34 #include "thread.h"
35 #include "vector.h"
36 #include "linklist.h"
37 #include "vty.h"
38 #include "command.h"
39 #include "workqueue.h"
40 #include "vrf.h"
41 #include "command_match.h"
42 #include "command_graph.h"
43 #include "qobj.h"
44 #include "defaults.h"
45 #include "libfrr.h"
46 #include "jhash.h"
47
48 DEFINE_MTYPE(LIB, HOST, "Host config")
49 DEFINE_MTYPE(LIB, STRVEC, "String vector")
50 DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
51
52 const char *node_names[] = {
53 "auth", // AUTH_NODE,
54 "view", // VIEW_NODE,
55 "auth enable", // AUTH_ENABLE_NODE,
56 "enable", // ENABLE_NODE,
57 "config", // CONFIG_NODE,
58 "debug", // DEBUG_NODE,
59 "vrf debug", // VRF_DEBUG_NODE,
60 "vnc debug", // DEBUG_VNC_NODE,
61 "aaa", // AAA_NODE,
62 "keychain", // KEYCHAIN_NODE,
63 "keychain key", // KEYCHAIN_KEY_NODE,
64 "logical-router", // LOGICALROUTER_NODE,
65 "vrf", // VRF_NODE,
66 "interface", // INTERFACE_NODE,
67 "nexthop-group", // NH_GROUP_NODE,
68 "zebra", // ZEBRA_NODE,
69 "table", // TABLE_NODE,
70 "rip", // RIP_NODE,
71 "ripng", // RIPNG_NODE,
72 "babel", // BABEL_NODE,
73 "eigrp", // EIGRP_NODE,
74 "bgp", // BGP_NODE,
75 "bgp vpnv4", // BGP_VPNV4_NODE,
76 "bgp vpnv6", // BGP_VPNV6_NODE,
77 "bgp ipv4 unicast", // BGP_IPV4_NODE,
78 "bgp ipv4 multicast", // BGP_IPV4M_NODE,
79 "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE,
80 "bgp ipv6", // BGP_IPV6_NODE,
81 "bgp ipv6 multicast", // BGP_IPV6M_NODE,
82 "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE,
83 "bgp vrf policy", // BGP_VRF_POLICY_NODE,
84 "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE,
85 "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE,
86 "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE,
87 "rfp defaults", // RFP_DEFAULTS_NODE,
88 "bgp evpn", // BGP_EVPN_NODE,
89 "ospf", // OSPF_NODE,
90 "ospf6", // OSPF6_NODE,
91 "ldp", // LDP_NODE,
92 "ldp ipv4", // LDP_IPV4_NODE,
93 "ldp ipv6", // LDP_IPV6_NODE,
94 "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE,
95 "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE,
96 "ldp l2vpn", // LDP_L2VPN_NODE,
97 "ldp", // LDP_PSEUDOWIRE_NODE,
98 "isis", // ISIS_NODE,
99 "static ip", // IP_NODE,
100 "ipv4 access list", // ACCESS_NODE,
101 "ipv4 prefix list", // PREFIX_NODE,
102 "ipv6 access list", // ACCESS_IPV6_NODE,
103 "MAC access list", // ACCESS_MAC_NODE,
104 "ipv6 prefix list", // PREFIX_IPV6_NODE,
105 "as list", // AS_LIST_NODE,
106 "community list", // COMMUNITY_LIST_NODE,
107 "routemap", // RMAP_NODE,
108 "pbr-map", // PBRMAP_NODE,
109 "smux", // SMUX_NODE,
110 "dump", // DUMP_NODE,
111 "forwarding", // FORWARDING_NODE,
112 "protocol", // PROTOCOL_NODE,
113 "mpls", // MPLS_NODE,
114 "pw", // PW_NODE,
115 "vty", // VTY_NODE,
116 "link-params", // LINK_PARAMS_NODE,
117 "bgp evpn vni", // BGP_EVPN_VNI_NODE,
118 "rpki", // RPKI_NODE
119 "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE
120 */
121 "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
122 */
123 };
124
125 /* Command vector which includes some level of command lists. Normally
126 each daemon maintains each own cmdvec. */
127 vector cmdvec = NULL;
128
129 /* Host information structure. */
130 struct host host;
131
132 /*
133 * Returns host.name if any, otherwise
134 * it returns the system hostname.
135 */
136 const char *cmd_hostname_get(void)
137 {
138 return host.name;
139 }
140
141 /*
142 * Returns unix domainname
143 */
144 const char *cmd_domainname_get(void)
145 {
146 return host.domainname;
147 }
148
149 /* Standard command node structures. */
150 static struct cmd_node auth_node = {
151 AUTH_NODE, "Password: ",
152 };
153
154 static struct cmd_node view_node = {
155 VIEW_NODE, "%s> ",
156 };
157
158 static struct cmd_node auth_enable_node = {
159 AUTH_ENABLE_NODE, "Password: ",
160 };
161
162 static struct cmd_node enable_node = {
163 ENABLE_NODE, "%s# ",
164 };
165
166 static struct cmd_node config_node = {CONFIG_NODE, "%s(config)# ", 1};
167
168 /* Default motd string. */
169 static const char *default_motd = FRR_DEFAULT_MOTD;
170
171 static const struct facility_map {
172 int facility;
173 const char *name;
174 size_t match;
175 } syslog_facilities[] = {
176 {LOG_KERN, "kern", 1},
177 {LOG_USER, "user", 2},
178 {LOG_MAIL, "mail", 1},
179 {LOG_DAEMON, "daemon", 1},
180 {LOG_AUTH, "auth", 1},
181 {LOG_SYSLOG, "syslog", 1},
182 {LOG_LPR, "lpr", 2},
183 {LOG_NEWS, "news", 1},
184 {LOG_UUCP, "uucp", 2},
185 {LOG_CRON, "cron", 1},
186 #ifdef LOG_FTP
187 {LOG_FTP, "ftp", 1},
188 #endif
189 {LOG_LOCAL0, "local0", 6},
190 {LOG_LOCAL1, "local1", 6},
191 {LOG_LOCAL2, "local2", 6},
192 {LOG_LOCAL3, "local3", 6},
193 {LOG_LOCAL4, "local4", 6},
194 {LOG_LOCAL5, "local5", 6},
195 {LOG_LOCAL6, "local6", 6},
196 {LOG_LOCAL7, "local7", 6},
197 {0, NULL, 0},
198 };
199
200 static const char *facility_name(int facility)
201 {
202 const struct facility_map *fm;
203
204 for (fm = syslog_facilities; fm->name; fm++)
205 if (fm->facility == facility)
206 return fm->name;
207 return "";
208 }
209
210 static int facility_match(const char *str)
211 {
212 const struct facility_map *fm;
213
214 for (fm = syslog_facilities; fm->name; fm++)
215 if (!strncmp(str, fm->name, fm->match))
216 return fm->facility;
217 return -1;
218 }
219
220 static int level_match(const char *s)
221 {
222 int level;
223
224 for (level = 0; zlog_priority[level] != NULL; level++)
225 if (!strncmp(s, zlog_priority[level], 2))
226 return level;
227 return ZLOG_DISABLED;
228 }
229
230 /* This is called from main when a daemon is invoked with -v or --version. */
231 void print_version(const char *progname)
232 {
233 printf("%s version %s\n", progname, FRR_VERSION);
234 printf("%s\n", FRR_COPYRIGHT);
235 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
236 }
237
238
239 /* Utility function to concatenate argv argument into a single string
240 with inserting ' ' character between each argument. */
241 char *argv_concat(struct cmd_token **argv, int argc, int shift)
242 {
243 int i;
244 size_t len;
245 char *str;
246 char *p;
247
248 len = 0;
249 for (i = shift; i < argc; i++)
250 len += strlen(argv[i]->arg) + 1;
251 if (!len)
252 return NULL;
253 p = str = XMALLOC(MTYPE_TMP, len);
254 for (i = shift; i < argc; i++) {
255 size_t arglen;
256 memcpy(p, argv[i]->arg, (arglen = strlen(argv[i]->arg)));
257 p += arglen;
258 *p++ = ' ';
259 }
260 *(p - 1) = '\0';
261 return str;
262 }
263
264 /**
265 * Convenience function for accessing argv data.
266 *
267 * @param argc
268 * @param argv
269 * @param text definition snippet of the desired token
270 * @param index the starting index, and where to store the
271 * index of the found token if it exists
272 * @return 1 if found, 0 otherwise
273 */
274 int argv_find(struct cmd_token **argv, int argc, const char *text, int *index)
275 {
276 int found = 0;
277 for (int i = *index; i < argc && found == 0; i++)
278 if ((found = strmatch(text, argv[i]->text)))
279 *index = i;
280 return found;
281 }
282
283 static unsigned int cmd_hash_key(void *p)
284 {
285 int size = sizeof(p);
286
287 return jhash(p, size, 0);
288 }
289
290 static int cmd_hash_cmp(const void *a, const void *b)
291 {
292 return a == b;
293 }
294
295 /* Install top node of command vector. */
296 void install_node(struct cmd_node *node, int (*func)(struct vty *))
297 {
298 vector_set_index(cmdvec, node->node, node);
299 node->func = func;
300 node->cmdgraph = graph_new();
301 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
302 // add start node
303 struct cmd_token *token =
304 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
305 graph_new_node(node->cmdgraph, token,
306 (void (*)(void *)) & cmd_token_del);
307 node->cmd_hash = hash_create_size(16, cmd_hash_key, cmd_hash_cmp,
308 "Command Hash");
309 }
310
311 /**
312 * Tokenizes a string, storing tokens in a vector.
313 * Whitespace is ignored.
314 *
315 * Delimiter string = " \n\r\t".
316 *
317 * @param string to tokenize
318 * @return tokenized string
319 */
320 vector cmd_make_strvec(const char *string)
321 {
322 if (!string)
323 return NULL;
324
325 char *copy, *copystart;
326 copystart = copy = XSTRDUP(MTYPE_TMP, string);
327
328 // skip leading whitespace
329 while (isspace((int)*copy) && *copy != '\0')
330 copy++;
331
332 // if the entire string was whitespace or a comment, return
333 if (*copy == '\0' || *copy == '!' || *copy == '#') {
334 XFREE(MTYPE_TMP, copystart);
335 return NULL;
336 }
337
338 vector strvec = vector_init(VECTOR_MIN_SIZE);
339 const char *delim = " \n\r\t", *tok = NULL;
340 while (copy) {
341 tok = strsep(&copy, delim);
342 if (*tok != '\0')
343 vector_set(strvec, XSTRDUP(MTYPE_STRVEC, tok));
344 }
345
346 XFREE(MTYPE_TMP, copystart);
347 return strvec;
348 }
349
350 /* Free allocated string vector. */
351 void cmd_free_strvec(vector v)
352 {
353 unsigned int i;
354 char *cp;
355
356 if (!v)
357 return;
358
359 for (i = 0; i < vector_active(v); i++)
360 if ((cp = vector_slot(v, i)) != NULL)
361 XFREE(MTYPE_STRVEC, cp);
362
363 vector_free(v);
364 }
365
366 /* Return prompt character of specified node. */
367 const char *cmd_prompt(enum node_type node)
368 {
369 struct cmd_node *cnode;
370
371 cnode = vector_slot(cmdvec, node);
372 return cnode->prompt;
373 }
374
375 /* Install a command into a node. */
376 void install_element(enum node_type ntype, struct cmd_element *cmd)
377 {
378 struct cmd_node *cnode;
379
380 /* cmd_init hasn't been called */
381 if (!cmdvec) {
382 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
383 __func__);
384 return;
385 }
386
387 cnode = vector_lookup(cmdvec, ntype);
388
389 if (cnode == NULL) {
390 fprintf(stderr,
391 "%s[%s]:\n"
392 "\tnode %d (%s) does not exist.\n"
393 "\tplease call install_node() before install_element()\n",
394 cmd->name, cmd->string, ntype, node_names[ntype]);
395 exit(EXIT_FAILURE);
396 }
397
398 if (hash_lookup(cnode->cmd_hash, cmd) != NULL) {
399 fprintf(stderr,
400 "%s[%s]:\n"
401 "\tnode %d (%s) already has this command installed.\n"
402 "\tduplicate install_element call?\n",
403 cmd->name, cmd->string, ntype, node_names[ntype]);
404 return;
405 }
406
407 assert(hash_get(cnode->cmd_hash, cmd, hash_alloc_intern));
408
409 struct graph *graph = graph_new();
410 struct cmd_token *token =
411 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
412 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
413
414 cmd_graph_parse(graph, cmd);
415 cmd_graph_names(graph);
416 cmd_graph_merge(cnode->cmdgraph, graph, +1);
417 graph_delete_graph(graph);
418
419 vector_set(cnode->cmd_vector, cmd);
420
421 if (ntype == VIEW_NODE)
422 install_element(ENABLE_NODE, cmd);
423 }
424
425 void uninstall_element(enum node_type ntype, struct cmd_element *cmd)
426 {
427 struct cmd_node *cnode;
428
429 /* cmd_init hasn't been called */
430 if (!cmdvec) {
431 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
432 __func__);
433 return;
434 }
435
436 cnode = vector_lookup(cmdvec, ntype);
437
438 if (cnode == NULL) {
439 fprintf(stderr,
440 "%s[%s]:\n"
441 "\tnode %d (%s) does not exist.\n"
442 "\tplease call install_node() before uninstall_element()\n",
443 cmd->name, cmd->string, ntype, node_names[ntype]);
444 exit(EXIT_FAILURE);
445 }
446
447 if (hash_release(cnode->cmd_hash, cmd) == NULL) {
448 fprintf(stderr,
449 "%s[%s]:\n"
450 "\tnode %d (%s) does not have this command installed.\n"
451 "\tduplicate uninstall_element call?\n",
452 cmd->name, cmd->string, ntype, node_names[ntype]);
453 return;
454 }
455
456 vector_unset_value(cnode->cmd_vector, cmd);
457
458 struct graph *graph = graph_new();
459 struct cmd_token *token =
460 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
461 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
462
463 cmd_graph_parse(graph, cmd);
464 cmd_graph_names(graph);
465 cmd_graph_merge(cnode->cmdgraph, graph, -1);
466 graph_delete_graph(graph);
467
468 if (ntype == VIEW_NODE)
469 uninstall_element(ENABLE_NODE, cmd);
470 }
471
472
473 static const unsigned char itoa64[] =
474 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
475
476 static void to64(char *s, long v, int n)
477 {
478 while (--n >= 0) {
479 *s++ = itoa64[v & 0x3f];
480 v >>= 6;
481 }
482 }
483
484 static char *zencrypt(const char *passwd)
485 {
486 char salt[6];
487 struct timeval tv;
488 char *crypt(const char *, const char *);
489
490 gettimeofday(&tv, 0);
491
492 to64(&salt[0], random(), 3);
493 to64(&salt[3], tv.tv_usec, 3);
494 salt[5] = '\0';
495
496 return crypt(passwd, salt);
497 }
498
499 /* This function write configuration of this host. */
500 static int config_write_host(struct vty *vty)
501 {
502 if (cmd_hostname_get())
503 vty_out(vty, "hostname %s\n", cmd_hostname_get());
504
505 if (cmd_domainname_get())
506 vty_out(vty, "domainname %s\n", cmd_domainname_get());
507
508 if (host.encrypt) {
509 if (host.password_encrypt)
510 vty_out(vty, "password 8 %s\n", host.password_encrypt);
511 if (host.enable_encrypt)
512 vty_out(vty, "enable password 8 %s\n",
513 host.enable_encrypt);
514 } else {
515 if (host.password)
516 vty_out(vty, "password %s\n", host.password);
517 if (host.enable)
518 vty_out(vty, "enable password %s\n", host.enable);
519 }
520
521 if (zlog_default->default_lvl != LOG_DEBUG) {
522 vty_out(vty, "! N.B. The 'log trap' command is deprecated.\n");
523 vty_out(vty, "log trap %s\n",
524 zlog_priority[zlog_default->default_lvl]);
525 }
526
527 if (host.logfile
528 && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) {
529 vty_out(vty, "log file %s", host.logfile);
530 if (zlog_default->maxlvl[ZLOG_DEST_FILE]
531 != zlog_default->default_lvl)
532 vty_out(vty, " %s",
533 zlog_priority
534 [zlog_default->maxlvl[ZLOG_DEST_FILE]]);
535 vty_out(vty, "\n");
536 }
537
538 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
539 vty_out(vty, "log stdout");
540 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT]
541 != zlog_default->default_lvl)
542 vty_out(vty, " %s",
543 zlog_priority[zlog_default->maxlvl
544 [ZLOG_DEST_STDOUT]]);
545 vty_out(vty, "\n");
546 }
547
548 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
549 vty_out(vty, "no log monitor\n");
550 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
551 != zlog_default->default_lvl)
552 vty_out(vty, "log monitor %s\n",
553 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]]);
554
555 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
556 vty_out(vty, "log syslog");
557 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG]
558 != zlog_default->default_lvl)
559 vty_out(vty, " %s",
560 zlog_priority[zlog_default->maxlvl
561 [ZLOG_DEST_SYSLOG]]);
562 vty_out(vty, "\n");
563 }
564
565 if (zlog_default->facility != LOG_DAEMON)
566 vty_out(vty, "log facility %s\n",
567 facility_name(zlog_default->facility));
568
569 if (zlog_default->record_priority == 1)
570 vty_out(vty, "log record-priority\n");
571
572 if (zlog_default->timestamp_precision > 0)
573 vty_out(vty, "log timestamp precision %d\n",
574 zlog_default->timestamp_precision);
575
576 if (host.advanced)
577 vty_out(vty, "service advanced-vty\n");
578
579 if (host.encrypt)
580 vty_out(vty, "service password-encryption\n");
581
582 if (host.lines >= 0)
583 vty_out(vty, "service terminal-length %d\n", host.lines);
584
585 if (host.motdfile)
586 vty_out(vty, "banner motd file %s\n", host.motdfile);
587 else if (!host.motd)
588 vty_out(vty, "no banner motd\n");
589
590 if (debug_memstats_at_exit)
591 vty_out(vty, "!\ndebug memstats-at-exit\n");
592
593 return 1;
594 }
595
596 /* Utility function for getting command graph. */
597 static struct graph *cmd_node_graph(vector v, enum node_type ntype)
598 {
599 struct cmd_node *cnode = vector_slot(v, ntype);
600 return cnode->cmdgraph;
601 }
602
603 static int cmd_try_do_shortcut(enum node_type node, char *first_word)
604 {
605 if (first_word != NULL && node != AUTH_NODE && node != VIEW_NODE
606 && node != AUTH_ENABLE_NODE && 0 == strcmp("do", first_word))
607 return 1;
608 return 0;
609 }
610
611 /**
612 * Compare function for cmd_token.
613 * Used with qsort to sort command completions.
614 */
615 static int compare_completions(const void *fst, const void *snd)
616 {
617 struct cmd_token *first = *(struct cmd_token **)fst,
618 *secnd = *(struct cmd_token **)snd;
619 return strcmp(first->text, secnd->text);
620 }
621
622 /**
623 * Takes a list of completions returned by command_complete,
624 * dedeuplicates them based on both text and description,
625 * sorts them, and returns them as a vector.
626 *
627 * @param completions linked list of cmd_token
628 * @return deduplicated and sorted vector with
629 */
630 vector completions_to_vec(struct list *completions)
631 {
632 vector comps = vector_init(VECTOR_MIN_SIZE);
633
634 struct listnode *ln;
635 struct cmd_token *token, *cr = NULL;
636 unsigned int i, exists;
637 for (ALL_LIST_ELEMENTS_RO(completions, ln, token)) {
638 if (token->type == END_TKN && (cr = token))
639 continue;
640
641 // linear search for token in completions vector
642 exists = 0;
643 for (i = 0; i < vector_active(comps) && !exists; i++) {
644 struct cmd_token *curr = vector_slot(comps, i);
645 #ifdef VTYSH_DEBUG
646 exists = !strcmp(curr->text, token->text)
647 && !strcmp(curr->desc, token->desc);
648 #else
649 exists = !strcmp(curr->text, token->text);
650 #endif /* VTYSH_DEBUG */
651 }
652
653 if (!exists)
654 vector_set(comps, token);
655 }
656
657 // sort completions
658 qsort(comps->index, vector_active(comps), sizeof(void *),
659 &compare_completions);
660
661 // make <cr> the first element, if it is present
662 if (cr) {
663 vector_set_index(comps, vector_active(comps), NULL);
664 memmove(comps->index + 1, comps->index,
665 (comps->alloced - 1) * sizeof(void *));
666 vector_set_index(comps, 0, cr);
667 }
668
669 return comps;
670 }
671 /**
672 * Generates a vector of cmd_token representing possible completions
673 * on the current input.
674 *
675 * @param vline the vectorized input line
676 * @param vty the vty with the node to match on
677 * @param status pointer to matcher status code
678 * @return vector of struct cmd_token * with possible completions
679 */
680 static vector cmd_complete_command_real(vector vline, struct vty *vty,
681 int *status)
682 {
683 struct list *completions;
684 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
685
686 enum matcher_rv rv = command_complete(cmdgraph, vline, &completions);
687
688 if (MATCHER_ERROR(rv)) {
689 *status = CMD_ERR_NO_MATCH;
690 return NULL;
691 }
692
693 vector comps = completions_to_vec(completions);
694 list_delete_and_null(&completions);
695
696 // set status code appropriately
697 switch (vector_active(comps)) {
698 case 0:
699 *status = CMD_ERR_NO_MATCH;
700 break;
701 case 1:
702 *status = CMD_COMPLETE_FULL_MATCH;
703 break;
704 default:
705 *status = CMD_COMPLETE_LIST_MATCH;
706 }
707
708 return comps;
709 }
710
711 vector cmd_describe_command(vector vline, struct vty *vty, int *status)
712 {
713 vector ret;
714
715 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
716 enum node_type onode;
717 vector shifted_vline;
718 unsigned int index;
719
720 onode = vty->node;
721 vty->node = ENABLE_NODE;
722 /* We can try it on enable node, cos' the vty is authenticated
723 */
724
725 shifted_vline = vector_init(vector_count(vline));
726 /* use memcpy? */
727 for (index = 1; index < vector_active(vline); index++) {
728 vector_set_index(shifted_vline, index - 1,
729 vector_lookup(vline, index));
730 }
731
732 ret = cmd_complete_command_real(shifted_vline, vty, status);
733
734 vector_free(shifted_vline);
735 vty->node = onode;
736 return ret;
737 }
738
739 return cmd_complete_command_real(vline, vty, status);
740 }
741
742 static struct list *varhandlers = NULL;
743
744 void cmd_variable_complete(struct cmd_token *token, const char *arg,
745 vector comps)
746 {
747 struct listnode *ln;
748 const struct cmd_variable_handler *cvh;
749 size_t i, argsz;
750 vector tmpcomps;
751
752 tmpcomps = arg ? vector_init(VECTOR_MIN_SIZE) : comps;
753
754 for (ALL_LIST_ELEMENTS_RO(varhandlers, ln, cvh)) {
755 if (cvh->tokenname && strcmp(cvh->tokenname, token->text))
756 continue;
757 if (cvh->varname && (!token->varname
758 || strcmp(cvh->varname, token->varname)))
759 continue;
760 cvh->completions(tmpcomps, token);
761 break;
762 }
763
764 if (!arg)
765 return;
766
767 argsz = strlen(arg);
768 for (i = vector_active(tmpcomps); i; i--) {
769 char *item = vector_slot(tmpcomps, i - 1);
770 if (strlen(item) >= argsz && !strncmp(item, arg, argsz))
771 vector_set(comps, item);
772 else
773 XFREE(MTYPE_COMPLETION, item);
774 }
775 vector_free(tmpcomps);
776 }
777
778 #define AUTOCOMP_INDENT 5
779
780 char *cmd_variable_comp2str(vector comps, unsigned short cols)
781 {
782 size_t bsz = 16;
783 char *buf = XCALLOC(MTYPE_TMP, bsz);
784 int lc = AUTOCOMP_INDENT;
785 size_t cs = AUTOCOMP_INDENT;
786 size_t itemlen;
787 snprintf(buf, bsz, "%*s", AUTOCOMP_INDENT, "");
788 for (size_t j = 0; j < vector_active(comps); j++) {
789 char *item = vector_slot(comps, j);
790 itemlen = strlen(item);
791
792 if (cs + itemlen + AUTOCOMP_INDENT + 3 >= bsz)
793 buf = XREALLOC(MTYPE_TMP, buf, (bsz *= 2));
794
795 if (lc + itemlen + 1 >= cols) {
796 cs += snprintf(&buf[cs], bsz - cs, "\n%*s",
797 AUTOCOMP_INDENT, "");
798 lc = AUTOCOMP_INDENT;
799 }
800
801 size_t written = snprintf(&buf[cs], bsz - cs, "%s ", item);
802 lc += written;
803 cs += written;
804 XFREE(MTYPE_COMPLETION, item);
805 vector_set_index(comps, j, NULL);
806 }
807 return buf;
808 }
809
810 void cmd_variable_handler_register(const struct cmd_variable_handler *cvh)
811 {
812 if (!varhandlers)
813 return;
814
815 for (; cvh->completions; cvh++)
816 listnode_add(varhandlers, (void *)cvh);
817 }
818
819 DEFUN_HIDDEN (autocomplete,
820 autocomplete_cmd,
821 "autocomplete TYPE TEXT VARNAME",
822 "Autocompletion handler (internal, for vtysh)\n"
823 "cmd_token->type\n"
824 "cmd_token->text\n"
825 "cmd_token->varname\n")
826 {
827 struct cmd_token tok;
828 vector comps = vector_init(32);
829 size_t i;
830
831 memset(&tok, 0, sizeof(tok));
832 tok.type = atoi(argv[1]->arg);
833 tok.text = argv[2]->arg;
834 tok.varname = argv[3]->arg;
835 if (!strcmp(tok.varname, "-"))
836 tok.varname = NULL;
837
838 cmd_variable_complete(&tok, NULL, comps);
839
840 for (i = 0; i < vector_active(comps); i++) {
841 char *text = vector_slot(comps, i);
842 vty_out(vty, "%s\n", text);
843 XFREE(MTYPE_COMPLETION, text);
844 }
845
846 vector_free(comps);
847 return CMD_SUCCESS;
848 }
849
850 /**
851 * Generate possible tab-completions for the given input. This function only
852 * returns results that would result in a valid command if used as Readline
853 * completions (as is the case in vtysh). For instance, if the passed vline ends
854 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
855 *
856 * @param vline vectorized input line
857 * @param vty the vty
858 * @param status location to store matcher status code in
859 * @return set of valid strings for use with Readline as tab-completions.
860 */
861
862 char **cmd_complete_command(vector vline, struct vty *vty, int *status)
863 {
864 char **ret = NULL;
865 int original_node = vty->node;
866 vector input_line = vector_init(vector_count(vline));
867
868 // if the first token is 'do' we'll want to execute the command in the
869 // enable node
870 int do_shortcut = cmd_try_do_shortcut(vty->node, vector_slot(vline, 0));
871 vty->node = do_shortcut ? ENABLE_NODE : original_node;
872
873 // construct the input line we'll be matching on
874 unsigned int offset = (do_shortcut) ? 1 : 0;
875 for (unsigned index = 0; index + offset < vector_active(vline); index++)
876 vector_set_index(input_line, index,
877 vector_lookup(vline, index + offset));
878
879 // get token completions -- this is a copying operation
880 vector comps = NULL, initial_comps;
881 initial_comps = cmd_complete_command_real(input_line, vty, status);
882
883 if (!MATCHER_ERROR(*status)) {
884 assert(initial_comps);
885 // filter out everything that is not suitable for a
886 // tab-completion
887 comps = vector_init(VECTOR_MIN_SIZE);
888 for (unsigned int i = 0; i < vector_active(initial_comps);
889 i++) {
890 struct cmd_token *token = vector_slot(initial_comps, i);
891 if (token->type == WORD_TKN)
892 vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
893 token->text));
894 else if (IS_VARYING_TOKEN(token->type)) {
895 const char *ref = vector_lookup(
896 vline, vector_active(vline) - 1);
897 cmd_variable_complete(token, ref, comps);
898 }
899 }
900 vector_free(initial_comps);
901
902 // since we filtered results, we need to re-set status code
903 switch (vector_active(comps)) {
904 case 0:
905 *status = CMD_ERR_NO_MATCH;
906 break;
907 case 1:
908 *status = CMD_COMPLETE_FULL_MATCH;
909 break;
910 default:
911 *status = CMD_COMPLETE_LIST_MATCH;
912 }
913
914 // copy completions text into an array of char*
915 ret = XMALLOC(MTYPE_TMP,
916 (vector_active(comps) + 1) * sizeof(char *));
917 unsigned int i;
918 for (i = 0; i < vector_active(comps); i++) {
919 ret[i] = vector_slot(comps, i);
920 }
921 // set the last element to NULL, because this array is used in
922 // a Readline completion_generator function which expects NULL
923 // as a sentinel value
924 ret[i] = NULL;
925 vector_free(comps);
926 comps = NULL;
927 } else if (initial_comps)
928 vector_free(initial_comps);
929
930 // comps should always be null here
931 assert(!comps);
932
933 // free the adjusted input line
934 vector_free(input_line);
935
936 // reset vty->node to its original value
937 vty->node = original_node;
938
939 return ret;
940 }
941
942 /* return parent node */
943 /* MUST eventually converge on CONFIG_NODE */
944 enum node_type node_parent(enum node_type node)
945 {
946 enum node_type ret;
947
948 assert(node > CONFIG_NODE);
949
950 switch (node) {
951 case BGP_VPNV4_NODE:
952 case BGP_VPNV6_NODE:
953 case BGP_FLOWSPECV4_NODE:
954 case BGP_FLOWSPECV6_NODE:
955 case BGP_VRF_POLICY_NODE:
956 case BGP_VNC_DEFAULTS_NODE:
957 case BGP_VNC_NVE_GROUP_NODE:
958 case BGP_VNC_L2_GROUP_NODE:
959 case BGP_IPV4_NODE:
960 case BGP_IPV4M_NODE:
961 case BGP_IPV4L_NODE:
962 case BGP_IPV6_NODE:
963 case BGP_IPV6M_NODE:
964 case BGP_EVPN_NODE:
965 case BGP_IPV6L_NODE:
966 ret = BGP_NODE;
967 break;
968 case BGP_EVPN_VNI_NODE:
969 ret = BGP_EVPN_NODE;
970 break;
971 case KEYCHAIN_KEY_NODE:
972 ret = KEYCHAIN_NODE;
973 break;
974 case LINK_PARAMS_NODE:
975 ret = INTERFACE_NODE;
976 break;
977 case LDP_IPV4_NODE:
978 case LDP_IPV6_NODE:
979 ret = LDP_NODE;
980 break;
981 case LDP_IPV4_IFACE_NODE:
982 ret = LDP_IPV4_NODE;
983 break;
984 case LDP_IPV6_IFACE_NODE:
985 ret = LDP_IPV6_NODE;
986 break;
987 case LDP_PSEUDOWIRE_NODE:
988 ret = LDP_L2VPN_NODE;
989 break;
990 default:
991 ret = CONFIG_NODE;
992 break;
993 }
994
995 return ret;
996 }
997
998 /* Execute command by argument vline vector. */
999 static int cmd_execute_command_real(vector vline, enum filter_type filter,
1000 struct vty *vty,
1001 const struct cmd_element **cmd)
1002 {
1003 struct list *argv_list;
1004 enum matcher_rv status;
1005 const struct cmd_element *matched_element = NULL;
1006
1007 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
1008 status = command_match(cmdgraph, vline, &argv_list, &matched_element);
1009
1010 if (cmd)
1011 *cmd = matched_element;
1012
1013 // if matcher error, return corresponding CMD_ERR
1014 if (MATCHER_ERROR(status)) {
1015 if (argv_list)
1016 list_delete_and_null(&argv_list);
1017 switch (status) {
1018 case MATCHER_INCOMPLETE:
1019 return CMD_ERR_INCOMPLETE;
1020 case MATCHER_AMBIGUOUS:
1021 return CMD_ERR_AMBIGUOUS;
1022 default:
1023 return CMD_ERR_NO_MATCH;
1024 }
1025 }
1026
1027 // build argv array from argv list
1028 struct cmd_token **argv = XMALLOC(
1029 MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *));
1030 struct listnode *ln;
1031 struct cmd_token *token;
1032 unsigned int i = 0;
1033 for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token))
1034 argv[i++] = token;
1035
1036 int argc = argv_list->count;
1037
1038 int ret;
1039 if (matched_element->daemon)
1040 ret = CMD_SUCCESS_DAEMON;
1041 else
1042 ret = matched_element->func(matched_element, vty, argc, argv);
1043
1044 // delete list and cmd_token's in it
1045 list_delete_and_null(&argv_list);
1046 XFREE(MTYPE_TMP, argv);
1047
1048 return ret;
1049 }
1050
1051 /**
1052 * Execute a given command, handling things like "do ..." and checking
1053 * whether the given command might apply at a parent node if doesn't
1054 * apply for the current node.
1055 *
1056 * @param vline Command line input, vector of char* where each element is
1057 * one input token.
1058 * @param vty The vty context in which the command should be executed.
1059 * @param cmd Pointer where the struct cmd_element of the matched command
1060 * will be stored, if any. May be set to NULL if this info is
1061 * not needed.
1062 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1063 * @return The status of the command that has been executed or an error code
1064 * as to why no command could be executed.
1065 */
1066 int cmd_execute_command(vector vline, struct vty *vty,
1067 const struct cmd_element **cmd, int vtysh)
1068 {
1069 int ret, saved_ret = 0;
1070 enum node_type onode, try_node;
1071
1072 onode = try_node = vty->node;
1073
1074 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1075 vector shifted_vline;
1076 unsigned int index;
1077
1078 vty->node = ENABLE_NODE;
1079 /* We can try it on enable node, cos' the vty is authenticated
1080 */
1081
1082 shifted_vline = vector_init(vector_count(vline));
1083 /* use memcpy? */
1084 for (index = 1; index < vector_active(vline); index++)
1085 vector_set_index(shifted_vline, index - 1,
1086 vector_lookup(vline, index));
1087
1088 ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
1089 vty, cmd);
1090
1091 vector_free(shifted_vline);
1092 vty->node = onode;
1093 return ret;
1094 }
1095
1096 saved_ret = ret =
1097 cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd);
1098
1099 if (vtysh)
1100 return saved_ret;
1101
1102 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1103 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
1104 /* This assumes all nodes above CONFIG_NODE are childs of
1105 * CONFIG_NODE */
1106 while (vty->node > CONFIG_NODE) {
1107 try_node = node_parent(try_node);
1108 vty->node = try_node;
1109 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
1110 vty, cmd);
1111 if (ret == CMD_SUCCESS || ret == CMD_WARNING
1112 || ret == CMD_NOT_MY_INSTANCE
1113 || ret == CMD_WARNING_CONFIG_FAILED)
1114 return ret;
1115 }
1116 /* no command succeeded, reset the vty to the original node */
1117 vty->node = onode;
1118 }
1119
1120 /* return command status for original node */
1121 return saved_ret;
1122 }
1123
1124 /**
1125 * Execute a given command, matching it strictly against the current node.
1126 * This mode is used when reading config files.
1127 *
1128 * @param vline Command line input, vector of char* where each element is
1129 * one input token.
1130 * @param vty The vty context in which the command should be executed.
1131 * @param cmd Pointer where the struct cmd_element* of the matched command
1132 * will be stored, if any. May be set to NULL if this info is
1133 * not needed.
1134 * @return The status of the command that has been executed or an error code
1135 * as to why no command could be executed.
1136 */
1137 int cmd_execute_command_strict(vector vline, struct vty *vty,
1138 const struct cmd_element **cmd)
1139 {
1140 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
1141 }
1142
1143 /**
1144 * Parse one line of config, walking up the parse tree attempting to find a
1145 * match
1146 *
1147 * @param vty The vty context in which the command should be executed.
1148 * @param cmd Pointer where the struct cmd_element* of the match command
1149 * will be stored, if any. May be set to NULL if this info is
1150 * not needed.
1151 * @param use_daemon Boolean to control whether or not we match on
1152 * CMD_SUCCESS_DAEMON
1153 * or not.
1154 * @return The status of the command that has been executed or an error code
1155 * as to why no command could be executed.
1156 */
1157 int command_config_read_one_line(struct vty *vty,
1158 const struct cmd_element **cmd, int use_daemon)
1159 {
1160 vector vline;
1161 int saved_node;
1162 int ret;
1163
1164 vline = cmd_make_strvec(vty->buf);
1165
1166 /* In case of comment line */
1167 if (vline == NULL)
1168 return CMD_SUCCESS;
1169
1170 /* Execute configuration command : this is strict match */
1171 ret = cmd_execute_command_strict(vline, vty, cmd);
1172
1173 // Climb the tree and try the command again at each node
1174 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1175 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1176 && ret != CMD_SUCCESS && ret != CMD_WARNING
1177 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
1178 && vty->node != CONFIG_NODE) {
1179
1180 saved_node = vty->node;
1181
1182 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1183 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1184 && ret != CMD_SUCCESS && ret != CMD_WARNING
1185 && vty->node > CONFIG_NODE) {
1186 vty->node = node_parent(vty->node);
1187 ret = cmd_execute_command_strict(vline, vty, cmd);
1188 }
1189
1190 // If climbing the tree did not work then ignore the command and
1191 // stay at the same node
1192 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1193 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1194 && ret != CMD_SUCCESS && ret != CMD_WARNING) {
1195 vty->node = saved_node;
1196 }
1197 }
1198
1199 if (ret != CMD_SUCCESS && ret != CMD_WARNING)
1200 memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ);
1201
1202 cmd_free_strvec(vline);
1203
1204 return ret;
1205 }
1206
1207 /* Configuration make from file. */
1208 int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
1209 {
1210 int ret, error_ret = 0;
1211 *line_num = 0;
1212
1213 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
1214 if (!error_ret)
1215 ++(*line_num);
1216
1217 ret = command_config_read_one_line(vty, NULL, 0);
1218
1219 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1220 && ret != CMD_ERR_NOTHING_TODO)
1221 error_ret = ret;
1222 }
1223
1224 if (error_ret) {
1225 return error_ret;
1226 }
1227
1228 return CMD_SUCCESS;
1229 }
1230
1231 /* Configuration from terminal */
1232 DEFUN (config_terminal,
1233 config_terminal_cmd,
1234 "configure terminal",
1235 "Configuration from vty interface\n"
1236 "Configuration terminal\n")
1237 {
1238 if (vty_config_lock(vty))
1239 vty->node = CONFIG_NODE;
1240 else {
1241 vty_out(vty, "VTY configuration is locked by other VTY\n");
1242 return CMD_WARNING_CONFIG_FAILED;
1243 }
1244 return CMD_SUCCESS;
1245 }
1246
1247 /* Enable command */
1248 DEFUN (enable,
1249 config_enable_cmd,
1250 "enable",
1251 "Turn on privileged mode command\n")
1252 {
1253 /* If enable password is NULL, change to ENABLE_NODE */
1254 if ((host.enable == NULL && host.enable_encrypt == NULL)
1255 || vty->type == VTY_SHELL_SERV)
1256 vty->node = ENABLE_NODE;
1257 else
1258 vty->node = AUTH_ENABLE_NODE;
1259
1260 return CMD_SUCCESS;
1261 }
1262
1263 /* Disable command */
1264 DEFUN (disable,
1265 config_disable_cmd,
1266 "disable",
1267 "Turn off privileged mode command\n")
1268 {
1269 if (vty->node == ENABLE_NODE)
1270 vty->node = VIEW_NODE;
1271 return CMD_SUCCESS;
1272 }
1273
1274 /* Down vty node level. */
1275 DEFUN (config_exit,
1276 config_exit_cmd,
1277 "exit",
1278 "Exit current mode and down to previous mode\n")
1279 {
1280 cmd_exit(vty);
1281 return CMD_SUCCESS;
1282 }
1283
1284 void cmd_exit(struct vty *vty)
1285 {
1286 switch (vty->node) {
1287 case VIEW_NODE:
1288 case ENABLE_NODE:
1289 if (vty_shell(vty))
1290 exit(0);
1291 else
1292 vty->status = VTY_CLOSE;
1293 break;
1294 case CONFIG_NODE:
1295 vty->node = ENABLE_NODE;
1296 vty_config_unlock(vty);
1297 break;
1298 case INTERFACE_NODE:
1299 case PW_NODE:
1300 case LOGICALROUTER_NODE:
1301 case VRF_NODE:
1302 case NH_GROUP_NODE:
1303 case ZEBRA_NODE:
1304 case BGP_NODE:
1305 case RIP_NODE:
1306 case EIGRP_NODE:
1307 case BABEL_NODE:
1308 case RIPNG_NODE:
1309 case OSPF_NODE:
1310 case OSPF6_NODE:
1311 case LDP_NODE:
1312 case LDP_L2VPN_NODE:
1313 case ISIS_NODE:
1314 case KEYCHAIN_NODE:
1315 case RMAP_NODE:
1316 case PBRMAP_NODE:
1317 case VTY_NODE:
1318 vty->node = CONFIG_NODE;
1319 break;
1320 case BGP_IPV4_NODE:
1321 case BGP_IPV4M_NODE:
1322 case BGP_IPV4L_NODE:
1323 case BGP_VPNV4_NODE:
1324 case BGP_VPNV6_NODE:
1325 case BGP_FLOWSPECV4_NODE:
1326 case BGP_FLOWSPECV6_NODE:
1327 case BGP_VRF_POLICY_NODE:
1328 case BGP_VNC_DEFAULTS_NODE:
1329 case BGP_VNC_NVE_GROUP_NODE:
1330 case BGP_VNC_L2_GROUP_NODE:
1331 case BGP_IPV6_NODE:
1332 case BGP_IPV6M_NODE:
1333 case BGP_EVPN_NODE:
1334 case BGP_IPV6L_NODE:
1335 vty->node = BGP_NODE;
1336 break;
1337 case BGP_EVPN_VNI_NODE:
1338 vty->node = BGP_EVPN_NODE;
1339 break;
1340 case LDP_IPV4_NODE:
1341 case LDP_IPV6_NODE:
1342 vty->node = LDP_NODE;
1343 break;
1344 case LDP_IPV4_IFACE_NODE:
1345 vty->node = LDP_IPV4_NODE;
1346 break;
1347 case LDP_IPV6_IFACE_NODE:
1348 vty->node = LDP_IPV6_NODE;
1349 break;
1350 case LDP_PSEUDOWIRE_NODE:
1351 vty->node = LDP_L2VPN_NODE;
1352 break;
1353 case KEYCHAIN_KEY_NODE:
1354 vty->node = KEYCHAIN_NODE;
1355 break;
1356 case LINK_PARAMS_NODE:
1357 vty->node = INTERFACE_NODE;
1358 break;
1359 default:
1360 break;
1361 }
1362 }
1363
1364 /* ALIAS_FIXME */
1365 DEFUN (config_quit,
1366 config_quit_cmd,
1367 "quit",
1368 "Exit current mode and down to previous mode\n")
1369 {
1370 return config_exit(self, vty, argc, argv);
1371 }
1372
1373
1374 /* End of configuration. */
1375 DEFUN (config_end,
1376 config_end_cmd,
1377 "end",
1378 "End current mode and change to enable mode.\n")
1379 {
1380 switch (vty->node) {
1381 case VIEW_NODE:
1382 case ENABLE_NODE:
1383 /* Nothing to do. */
1384 break;
1385 case CONFIG_NODE:
1386 case INTERFACE_NODE:
1387 case PW_NODE:
1388 case LOGICALROUTER_NODE:
1389 case VRF_NODE:
1390 case NH_GROUP_NODE:
1391 case ZEBRA_NODE:
1392 case RIP_NODE:
1393 case RIPNG_NODE:
1394 case EIGRP_NODE:
1395 case BABEL_NODE:
1396 case BGP_NODE:
1397 case BGP_VRF_POLICY_NODE:
1398 case BGP_VNC_DEFAULTS_NODE:
1399 case BGP_VNC_NVE_GROUP_NODE:
1400 case BGP_VNC_L2_GROUP_NODE:
1401 case BGP_VPNV4_NODE:
1402 case BGP_VPNV6_NODE:
1403 case BGP_FLOWSPECV4_NODE:
1404 case BGP_FLOWSPECV6_NODE:
1405 case BGP_IPV4_NODE:
1406 case BGP_IPV4M_NODE:
1407 case BGP_IPV4L_NODE:
1408 case BGP_IPV6_NODE:
1409 case BGP_IPV6M_NODE:
1410 case BGP_EVPN_NODE:
1411 case BGP_EVPN_VNI_NODE:
1412 case BGP_IPV6L_NODE:
1413 case RMAP_NODE:
1414 case PBRMAP_NODE:
1415 case OSPF_NODE:
1416 case OSPF6_NODE:
1417 case LDP_NODE:
1418 case LDP_IPV4_NODE:
1419 case LDP_IPV6_NODE:
1420 case LDP_IPV4_IFACE_NODE:
1421 case LDP_IPV6_IFACE_NODE:
1422 case LDP_L2VPN_NODE:
1423 case LDP_PSEUDOWIRE_NODE:
1424 case ISIS_NODE:
1425 case KEYCHAIN_NODE:
1426 case KEYCHAIN_KEY_NODE:
1427 case VTY_NODE:
1428 case LINK_PARAMS_NODE:
1429 vty_config_unlock(vty);
1430 vty->node = ENABLE_NODE;
1431 break;
1432 default:
1433 break;
1434 }
1435 return CMD_SUCCESS;
1436 }
1437
1438 /* Show version. */
1439 DEFUN (show_version,
1440 show_version_cmd,
1441 "show version",
1442 SHOW_STR
1443 "Displays zebra version\n")
1444 {
1445 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
1446 cmd_hostname_get() ? cmd_hostname_get() : "");
1447 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
1448 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
1449
1450 return CMD_SUCCESS;
1451 }
1452
1453 /* "Set" version ... ignore version tags */
1454 DEFUN (frr_version_defaults,
1455 frr_version_defaults_cmd,
1456 "frr <version|defaults> LINE...",
1457 "FRRouting global parameters\n"
1458 "version configuration was written by\n"
1459 "set of configuration defaults used\n"
1460 "version string\n")
1461 {
1462 return CMD_SUCCESS;
1463 }
1464
1465 /* Help display function for all node. */
1466 DEFUN (config_help,
1467 config_help_cmd,
1468 "help",
1469 "Description of the interactive help system\n")
1470 {
1471 vty_out(vty,
1472 "Quagga VTY provides advanced help feature. When you need help,\n\
1473 anytime at the command line please press '?'.\n\
1474 \n\
1475 If nothing matches, the help list will be empty and you must backup\n\
1476 until entering a '?' shows the available options.\n\
1477 Two styles of help are provided:\n\
1478 1. Full help is available when you are ready to enter a\n\
1479 command argument (e.g. 'show ?') and describes each possible\n\
1480 argument.\n\
1481 2. Partial help is provided when an abbreviated argument is entered\n\
1482 and you want to know what arguments match the input\n\
1483 (e.g. 'show me?'.)\n\n");
1484 return CMD_SUCCESS;
1485 }
1486
1487 static void permute(struct graph_node *start, struct vty *vty)
1488 {
1489 static struct list *position = NULL;
1490 if (!position)
1491 position = list_new();
1492
1493 struct cmd_token *stok = start->data;
1494 struct graph_node *gnn;
1495 struct listnode *ln;
1496
1497 // recursive dfs
1498 listnode_add(position, start);
1499 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1500 struct graph_node *gn = vector_slot(start->to, i);
1501 struct cmd_token *tok = gn->data;
1502 if (tok->attr == CMD_ATTR_HIDDEN
1503 || tok->attr == CMD_ATTR_DEPRECATED)
1504 continue;
1505 else if (tok->type == END_TKN || gn == start) {
1506 vty_out(vty, " ");
1507 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1508 struct cmd_token *tt = gnn->data;
1509 if (tt->type < SPECIAL_TKN)
1510 vty_out(vty, " %s", tt->text);
1511 }
1512 if (gn == start)
1513 vty_out(vty, "...");
1514 vty_out(vty, "\n");
1515 } else {
1516 bool skip = false;
1517 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1518 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1519 if (gnn == gn) {
1520 skip = true;
1521 break;
1522 }
1523 if (!skip)
1524 permute(gn, vty);
1525 }
1526 }
1527 list_delete_node(position, listtail(position));
1528 }
1529
1530 int cmd_list_cmds(struct vty *vty, int do_permute)
1531 {
1532 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1533
1534 if (do_permute)
1535 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1536 else {
1537 /* loop over all commands at this node */
1538 struct cmd_element *element = NULL;
1539 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1540 i++)
1541 if ((element = vector_slot(node->cmd_vector, i))
1542 && element->attr != CMD_ATTR_DEPRECATED
1543 && element->attr != CMD_ATTR_HIDDEN)
1544 vty_out(vty, " %s\n", element->string);
1545 }
1546 return CMD_SUCCESS;
1547 }
1548
1549 /* Help display function for all node. */
1550 DEFUN (config_list,
1551 config_list_cmd,
1552 "list [permutations]",
1553 "Print command list\n"
1554 "Print all possible command permutations\n")
1555 {
1556 return cmd_list_cmds(vty, argc == 2);
1557 }
1558
1559 DEFUN (show_commandtree,
1560 show_commandtree_cmd,
1561 "show commandtree [permutations]",
1562 SHOW_STR
1563 "Show command tree\n"
1564 "Permutations that we are interested in\n")
1565 {
1566 return cmd_list_cmds(vty, argc == 3);
1567 }
1568
1569 static int vty_write_config(struct vty *vty)
1570 {
1571 size_t i;
1572 struct cmd_node *node;
1573
1574 if (host.noconfig)
1575 return CMD_SUCCESS;
1576
1577 if (vty->type == VTY_TERM) {
1578 vty_out(vty, "\nCurrent configuration:\n");
1579 vty_out(vty, "!\n");
1580 }
1581
1582 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
1583 vty_out(vty, "frr defaults %s\n", DFLT_NAME);
1584 vty_out(vty, "!\n");
1585
1586 for (i = 0; i < vector_active(cmdvec); i++)
1587 if ((node = vector_slot(cmdvec, i)) && node->func
1588 && (node->vtysh || vty->type != VTY_SHELL)) {
1589 if ((*node->func)(vty))
1590 vty_out(vty, "!\n");
1591 }
1592
1593 if (vty->type == VTY_TERM) {
1594 vty_out(vty, "end\n");
1595 }
1596
1597 return CMD_SUCCESS;
1598 }
1599
1600 static int file_write_config(struct vty *vty)
1601 {
1602 int fd, dirfd;
1603 char *config_file, *slash;
1604 char *config_file_tmp = NULL;
1605 char *config_file_sav = NULL;
1606 int ret = CMD_WARNING;
1607 struct vty *file_vty;
1608 struct stat conf_stat;
1609
1610 if (host.noconfig)
1611 return CMD_SUCCESS;
1612
1613 /* Check and see if we are operating under vtysh configuration */
1614 if (host.config == NULL) {
1615 vty_out(vty,
1616 "Can't save to configuration file, using vtysh.\n");
1617 return CMD_WARNING;
1618 }
1619
1620 /* Get filename. */
1621 config_file = host.config;
1622
1623 #ifndef O_DIRECTORY
1624 #define O_DIRECTORY 0
1625 #endif
1626 slash = strrchr(config_file, '/');
1627 if (slash) {
1628 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1629 config_dir[slash - config_file] = '\0';
1630 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1631 XFREE(MTYPE_TMP, config_dir);
1632 } else
1633 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1634 /* if dirfd is invalid, directory sync fails, but we're still OK */
1635
1636 config_file_sav = XMALLOC(
1637 MTYPE_TMP, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1);
1638 strcpy(config_file_sav, config_file);
1639 strcat(config_file_sav, CONF_BACKUP_EXT);
1640
1641
1642 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
1643 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
1644
1645 /* Open file to configuration write. */
1646 fd = mkstemp(config_file_tmp);
1647 if (fd < 0) {
1648 vty_out(vty, "Can't open configuration file %s.\n",
1649 config_file_tmp);
1650 goto finished;
1651 }
1652 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1653 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1654 config_file_tmp, safe_strerror(errno), errno);
1655 goto finished;
1656 }
1657
1658 /* Make vty for configuration file. */
1659 file_vty = vty_new();
1660 file_vty->wfd = fd;
1661 file_vty->type = VTY_FILE;
1662
1663 /* Config file header print. */
1664 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1665 vty_time_print(file_vty, 1);
1666 vty_out(file_vty, "!\n");
1667 vty_write_config(file_vty);
1668 vty_close(file_vty);
1669
1670 if (stat(config_file, &conf_stat) >= 0) {
1671 if (unlink(config_file_sav) != 0)
1672 if (errno != ENOENT) {
1673 vty_out(vty,
1674 "Can't unlink backup configuration file %s.\n",
1675 config_file_sav);
1676 goto finished;
1677 }
1678 if (link(config_file, config_file_sav) != 0) {
1679 vty_out(vty,
1680 "Can't backup old configuration file %s.\n",
1681 config_file_sav);
1682 goto finished;
1683 }
1684 if (dirfd >= 0)
1685 fsync(dirfd);
1686 }
1687 if (rename(config_file_tmp, config_file) != 0) {
1688 vty_out(vty, "Can't save configuration file %s.\n",
1689 config_file);
1690 goto finished;
1691 }
1692 if (dirfd >= 0)
1693 fsync(dirfd);
1694
1695 vty_out(vty, "Configuration saved to %s\n", config_file);
1696 ret = CMD_SUCCESS;
1697
1698 finished:
1699 if (ret != CMD_SUCCESS)
1700 unlink(config_file_tmp);
1701 if (dirfd >= 0)
1702 close(dirfd);
1703 XFREE(MTYPE_TMP, config_file_tmp);
1704 XFREE(MTYPE_TMP, config_file_sav);
1705 return ret;
1706 }
1707
1708 /* Write current configuration into file. */
1709
1710 DEFUN (config_write,
1711 config_write_cmd,
1712 "write [<file|memory|terminal>]",
1713 "Write running configuration to memory, network, or terminal\n"
1714 "Write to configuration file\n"
1715 "Write configuration currently in memory\n"
1716 "Write configuration to terminal\n")
1717 {
1718 const int idx_type = 1;
1719
1720 // if command was 'write terminal' or 'write memory'
1721 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1722 return vty_write_config(vty);
1723 }
1724
1725 return file_write_config(vty);
1726 }
1727
1728 /* ALIAS_FIXME for 'write <terminal|memory>' */
1729 DEFUN (show_running_config,
1730 show_running_config_cmd,
1731 "show running-config",
1732 SHOW_STR
1733 "running configuration (same as write terminal)\n")
1734 {
1735 return vty_write_config(vty);
1736 }
1737
1738 /* ALIAS_FIXME for 'write file' */
1739 DEFUN (copy_runningconf_startupconf,
1740 copy_runningconf_startupconf_cmd,
1741 "copy running-config startup-config",
1742 "Copy configuration\n"
1743 "Copy running config to... \n"
1744 "Copy running config to startup config (same as write file/memory)\n")
1745 {
1746 return file_write_config(vty);
1747 }
1748 /** -- **/
1749
1750 /* Write startup configuration into the terminal. */
1751 DEFUN (show_startup_config,
1752 show_startup_config_cmd,
1753 "show startup-config",
1754 SHOW_STR
1755 "Contents of startup configuration\n")
1756 {
1757 char buf[BUFSIZ];
1758 FILE *confp;
1759
1760 if (host.noconfig)
1761 return CMD_SUCCESS;
1762 if (host.config == NULL)
1763 return CMD_WARNING;
1764
1765 confp = fopen(host.config, "r");
1766 if (confp == NULL) {
1767 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1768 host.config, safe_strerror(errno));
1769 return CMD_WARNING;
1770 }
1771
1772 while (fgets(buf, BUFSIZ, confp)) {
1773 char *cp = buf;
1774
1775 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1776 cp++;
1777 *cp = '\0';
1778
1779 vty_out(vty, "%s\n", buf);
1780 }
1781
1782 fclose(confp);
1783
1784 return CMD_SUCCESS;
1785 }
1786
1787 int cmd_domainname_set(const char *domainname)
1788 {
1789 XFREE(MTYPE_HOST, host.domainname);
1790 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1791 return CMD_SUCCESS;
1792 }
1793
1794 /* Hostname configuration */
1795 DEFUN(config_domainname,
1796 domainname_cmd,
1797 "domainname WORD",
1798 "Set system's domain name\n"
1799 "This system's domain name\n")
1800 {
1801 struct cmd_token *word = argv[1];
1802
1803 if (!isalpha((int)word->arg[0])) {
1804 vty_out(vty, "Please specify string starting with alphabet\n");
1805 return CMD_WARNING_CONFIG_FAILED;
1806 }
1807
1808 return cmd_domainname_set(word->arg);
1809 }
1810
1811 DEFUN(config_no_domainname,
1812 no_domainname_cmd,
1813 "no domainname [DOMAINNAME]",
1814 NO_STR
1815 "Reset system's domain name\n"
1816 "domain name of this router\n")
1817 {
1818 return cmd_domainname_set(NULL);
1819 }
1820
1821 int cmd_hostname_set(const char *hostname)
1822 {
1823 XFREE(MTYPE_HOST, host.name);
1824 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1825 return CMD_SUCCESS;
1826 }
1827
1828 /* Hostname configuration */
1829 DEFUN (config_hostname,
1830 hostname_cmd,
1831 "hostname WORD",
1832 "Set system's network name\n"
1833 "This system's network name\n")
1834 {
1835 struct cmd_token *word = argv[1];
1836
1837 if (!isalpha((int)word->arg[0])) {
1838 vty_out(vty, "Please specify string starting with alphabet\n");
1839 return CMD_WARNING_CONFIG_FAILED;
1840 }
1841
1842 return cmd_hostname_set(word->arg);
1843 }
1844
1845 DEFUN (config_no_hostname,
1846 no_hostname_cmd,
1847 "no hostname [HOSTNAME]",
1848 NO_STR
1849 "Reset system's network name\n"
1850 "Host name of this router\n")
1851 {
1852 return cmd_hostname_set(NULL);
1853 }
1854
1855 /* VTY interface password set. */
1856 DEFUN (config_password,
1857 password_cmd,
1858 "password [(8-8)] WORD",
1859 "Assign the terminal connection password\n"
1860 "Specifies a HIDDEN password will follow\n"
1861 "The password string\n")
1862 {
1863 int idx_8 = 1;
1864 int idx_word = 2;
1865 if (argc == 3) // '8' was specified
1866 {
1867 if (host.password)
1868 XFREE(MTYPE_HOST, host.password);
1869 host.password = NULL;
1870 if (host.password_encrypt)
1871 XFREE(MTYPE_HOST, host.password_encrypt);
1872 host.password_encrypt =
1873 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1874 return CMD_SUCCESS;
1875 }
1876
1877 if (!isalnum((int)argv[idx_8]->arg[0])) {
1878 vty_out(vty,
1879 "Please specify string starting with alphanumeric\n");
1880 return CMD_WARNING_CONFIG_FAILED;
1881 }
1882
1883 if (host.password)
1884 XFREE(MTYPE_HOST, host.password);
1885 host.password = NULL;
1886
1887 if (host.encrypt) {
1888 if (host.password_encrypt)
1889 XFREE(MTYPE_HOST, host.password_encrypt);
1890 host.password_encrypt =
1891 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1892 } else
1893 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1894
1895 return CMD_SUCCESS;
1896 }
1897
1898 /* VTY enable password set. */
1899 DEFUN (config_enable_password,
1900 enable_password_cmd,
1901 "enable password [(8-8)] WORD",
1902 "Modify enable password parameters\n"
1903 "Assign the privileged level password\n"
1904 "Specifies a HIDDEN password will follow\n"
1905 "The HIDDEN 'enable' password string\n")
1906 {
1907 int idx_8 = 2;
1908 int idx_word = 3;
1909
1910 /* Crypt type is specified. */
1911 if (argc == 4) {
1912 if (argv[idx_8]->arg[0] == '8') {
1913 if (host.enable)
1914 XFREE(MTYPE_HOST, host.enable);
1915 host.enable = NULL;
1916
1917 if (host.enable_encrypt)
1918 XFREE(MTYPE_HOST, host.enable_encrypt);
1919 host.enable_encrypt =
1920 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1921
1922 return CMD_SUCCESS;
1923 } else {
1924 vty_out(vty, "Unknown encryption type.\n");
1925 return CMD_WARNING_CONFIG_FAILED;
1926 }
1927 }
1928
1929 if (!isalnum((int)argv[idx_8]->arg[0])) {
1930 vty_out(vty,
1931 "Please specify string starting with alphanumeric\n");
1932 return CMD_WARNING_CONFIG_FAILED;
1933 }
1934
1935 if (host.enable)
1936 XFREE(MTYPE_HOST, host.enable);
1937 host.enable = NULL;
1938
1939 /* Plain password input. */
1940 if (host.encrypt) {
1941 if (host.enable_encrypt)
1942 XFREE(MTYPE_HOST, host.enable_encrypt);
1943 host.enable_encrypt =
1944 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1945 } else
1946 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1947
1948 return CMD_SUCCESS;
1949 }
1950
1951 /* VTY enable password delete. */
1952 DEFUN (no_config_enable_password,
1953 no_enable_password_cmd,
1954 "no enable password",
1955 NO_STR
1956 "Modify enable password parameters\n"
1957 "Assign the privileged level password\n")
1958 {
1959 if (host.enable)
1960 XFREE(MTYPE_HOST, host.enable);
1961 host.enable = NULL;
1962
1963 if (host.enable_encrypt)
1964 XFREE(MTYPE_HOST, host.enable_encrypt);
1965 host.enable_encrypt = NULL;
1966
1967 return CMD_SUCCESS;
1968 }
1969
1970 DEFUN (service_password_encrypt,
1971 service_password_encrypt_cmd,
1972 "service password-encryption",
1973 "Set up miscellaneous service\n"
1974 "Enable encrypted passwords\n")
1975 {
1976 if (host.encrypt)
1977 return CMD_SUCCESS;
1978
1979 host.encrypt = 1;
1980
1981 if (host.password) {
1982 if (host.password_encrypt)
1983 XFREE(MTYPE_HOST, host.password_encrypt);
1984 host.password_encrypt =
1985 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
1986 }
1987 if (host.enable) {
1988 if (host.enable_encrypt)
1989 XFREE(MTYPE_HOST, host.enable_encrypt);
1990 host.enable_encrypt =
1991 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
1992 }
1993
1994 return CMD_SUCCESS;
1995 }
1996
1997 DEFUN (no_service_password_encrypt,
1998 no_service_password_encrypt_cmd,
1999 "no service password-encryption",
2000 NO_STR
2001 "Set up miscellaneous service\n"
2002 "Enable encrypted passwords\n")
2003 {
2004 if (!host.encrypt)
2005 return CMD_SUCCESS;
2006
2007 host.encrypt = 0;
2008
2009 if (host.password_encrypt)
2010 XFREE(MTYPE_HOST, host.password_encrypt);
2011 host.password_encrypt = NULL;
2012
2013 if (host.enable_encrypt)
2014 XFREE(MTYPE_HOST, host.enable_encrypt);
2015 host.enable_encrypt = NULL;
2016
2017 return CMD_SUCCESS;
2018 }
2019
2020 DEFUN (config_terminal_length,
2021 config_terminal_length_cmd,
2022 "terminal length (0-512)",
2023 "Set terminal line parameters\n"
2024 "Set number of lines on a screen\n"
2025 "Number of lines on screen (0 for no pausing)\n")
2026 {
2027 int idx_number = 2;
2028
2029 vty->lines = atoi(argv[idx_number]->arg);
2030
2031 return CMD_SUCCESS;
2032 }
2033
2034 DEFUN (config_terminal_no_length,
2035 config_terminal_no_length_cmd,
2036 "terminal no length",
2037 "Set terminal line parameters\n"
2038 NO_STR
2039 "Set number of lines on a screen\n")
2040 {
2041 vty->lines = -1;
2042 return CMD_SUCCESS;
2043 }
2044
2045 DEFUN (service_terminal_length,
2046 service_terminal_length_cmd,
2047 "service terminal-length (0-512)",
2048 "Set up miscellaneous service\n"
2049 "System wide terminal length configuration\n"
2050 "Number of lines of VTY (0 means no line control)\n")
2051 {
2052 int idx_number = 2;
2053
2054 host.lines = atoi(argv[idx_number]->arg);
2055
2056 return CMD_SUCCESS;
2057 }
2058
2059 DEFUN (no_service_terminal_length,
2060 no_service_terminal_length_cmd,
2061 "no service terminal-length [(0-512)]",
2062 NO_STR
2063 "Set up miscellaneous service\n"
2064 "System wide terminal length configuration\n"
2065 "Number of lines of VTY (0 means no line control)\n")
2066 {
2067 host.lines = -1;
2068 return CMD_SUCCESS;
2069 }
2070
2071 DEFUN_HIDDEN (do_echo,
2072 echo_cmd,
2073 "echo MESSAGE...",
2074 "Echo a message back to the vty\n"
2075 "The message to echo\n")
2076 {
2077 char *message;
2078
2079 vty_out(vty, "%s\n",
2080 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2081 if (message)
2082 XFREE(MTYPE_TMP, message);
2083 return CMD_SUCCESS;
2084 }
2085
2086 DEFUN (config_logmsg,
2087 config_logmsg_cmd,
2088 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2089 "Send a message to enabled logging destinations\n"
2090 LOG_LEVEL_DESC
2091 "The message to send\n")
2092 {
2093 int idx_log_level = 1;
2094 int idx_message = 2;
2095 int level;
2096 char *message;
2097
2098 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2099 return CMD_ERR_NO_MATCH;
2100
2101 zlog(level, "%s",
2102 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2103 if (message)
2104 XFREE(MTYPE_TMP, message);
2105
2106 return CMD_SUCCESS;
2107 }
2108
2109 DEFUN (show_logging,
2110 show_logging_cmd,
2111 "show logging",
2112 SHOW_STR
2113 "Show current logging configuration\n")
2114 {
2115 struct zlog *zl = zlog_default;
2116
2117 vty_out(vty, "Syslog logging: ");
2118 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
2119 vty_out(vty, "disabled");
2120 else
2121 vty_out(vty, "level %s, facility %s, ident %s",
2122 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
2123 facility_name(zl->facility), zl->ident);
2124 vty_out(vty, "\n");
2125
2126 vty_out(vty, "Stdout logging: ");
2127 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
2128 vty_out(vty, "disabled");
2129 else
2130 vty_out(vty, "level %s",
2131 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
2132 vty_out(vty, "\n");
2133
2134 vty_out(vty, "Monitor logging: ");
2135 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
2136 vty_out(vty, "disabled");
2137 else
2138 vty_out(vty, "level %s",
2139 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
2140 vty_out(vty, "\n");
2141
2142 vty_out(vty, "File logging: ");
2143 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
2144 vty_out(vty, "disabled");
2145 else
2146 vty_out(vty, "level %s, filename %s",
2147 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
2148 zl->filename);
2149 vty_out(vty, "\n");
2150
2151 vty_out(vty, "Protocol name: %s\n", zl->protoname);
2152 vty_out(vty, "Record priority: %s\n",
2153 (zl->record_priority ? "enabled" : "disabled"));
2154 vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision);
2155
2156 return CMD_SUCCESS;
2157 }
2158
2159 DEFUN (config_log_stdout,
2160 config_log_stdout_cmd,
2161 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2162 "Logging control\n"
2163 "Set stdout logging level\n"
2164 LOG_LEVEL_DESC)
2165 {
2166 int idx_log_level = 2;
2167
2168 if (argc == idx_log_level) {
2169 zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
2170 return CMD_SUCCESS;
2171 }
2172 int level;
2173
2174 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2175 return CMD_ERR_NO_MATCH;
2176 zlog_set_level(ZLOG_DEST_STDOUT, level);
2177 return CMD_SUCCESS;
2178 }
2179
2180 DEFUN (no_config_log_stdout,
2181 no_config_log_stdout_cmd,
2182 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2183 NO_STR
2184 "Logging control\n"
2185 "Cancel logging to stdout\n"
2186 LOG_LEVEL_DESC)
2187 {
2188 zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
2189 return CMD_SUCCESS;
2190 }
2191
2192 DEFUN (config_log_monitor,
2193 config_log_monitor_cmd,
2194 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2195 "Logging control\n"
2196 "Set terminal line (monitor) logging level\n"
2197 LOG_LEVEL_DESC)
2198 {
2199 int idx_log_level = 2;
2200
2201 if (argc == idx_log_level) {
2202 zlog_set_level(ZLOG_DEST_MONITOR, zlog_default->default_lvl);
2203 return CMD_SUCCESS;
2204 }
2205 int level;
2206
2207 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2208 return CMD_ERR_NO_MATCH;
2209 zlog_set_level(ZLOG_DEST_MONITOR, level);
2210 return CMD_SUCCESS;
2211 }
2212
2213 DEFUN (no_config_log_monitor,
2214 no_config_log_monitor_cmd,
2215 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2216 NO_STR
2217 "Logging control\n"
2218 "Disable terminal line (monitor) logging\n"
2219 LOG_LEVEL_DESC)
2220 {
2221 zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
2222 return CMD_SUCCESS;
2223 }
2224
2225 static int set_log_file(struct vty *vty, const char *fname, int loglevel)
2226 {
2227 int ret;
2228 char *p = NULL;
2229 const char *fullpath;
2230
2231 /* Path detection. */
2232 if (!IS_DIRECTORY_SEP(*fname)) {
2233 char cwd[MAXPATHLEN + 1];
2234 cwd[MAXPATHLEN] = '\0';
2235
2236 if (getcwd(cwd, MAXPATHLEN) == NULL) {
2237 zlog_err("config_log_file: Unable to alloc mem!");
2238 return CMD_WARNING_CONFIG_FAILED;
2239 }
2240
2241 if ((p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2))
2242 == NULL) {
2243 zlog_err("config_log_file: Unable to alloc mem!");
2244 return CMD_WARNING_CONFIG_FAILED;
2245 }
2246 sprintf(p, "%s/%s", cwd, fname);
2247 fullpath = p;
2248 } else
2249 fullpath = fname;
2250
2251 ret = zlog_set_file(fullpath, loglevel);
2252
2253 if (p)
2254 XFREE(MTYPE_TMP, p);
2255
2256 if (!ret) {
2257 vty_out(vty, "can't open logfile %s\n", fname);
2258 return CMD_WARNING_CONFIG_FAILED;
2259 }
2260
2261 if (host.logfile)
2262 XFREE(MTYPE_HOST, host.logfile);
2263
2264 host.logfile = XSTRDUP(MTYPE_HOST, fname);
2265
2266 #if defined(HAVE_CUMULUS)
2267 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
2268 zlog_default->maxlvl[ZLOG_DEST_SYSLOG] = ZLOG_DISABLED;
2269 #endif
2270 return CMD_SUCCESS;
2271 }
2272
2273 DEFUN (config_log_file,
2274 config_log_file_cmd,
2275 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2276 "Logging control\n"
2277 "Logging to file\n"
2278 "Logging filename\n"
2279 LOG_LEVEL_DESC)
2280 {
2281 int idx_filename = 2;
2282 int idx_log_levels = 3;
2283 if (argc == 4) {
2284 int level;
2285 if ((level = level_match(argv[idx_log_levels]->arg))
2286 == ZLOG_DISABLED)
2287 return CMD_ERR_NO_MATCH;
2288 return set_log_file(vty, argv[idx_filename]->arg, level);
2289 } else
2290 return set_log_file(vty, argv[idx_filename]->arg,
2291 zlog_default->default_lvl);
2292 }
2293
2294 DEFUN (no_config_log_file,
2295 no_config_log_file_cmd,
2296 "no log file [FILENAME [LEVEL]]",
2297 NO_STR
2298 "Logging control\n"
2299 "Cancel logging to file\n"
2300 "Logging file name\n"
2301 "Logging level\n")
2302 {
2303 zlog_reset_file();
2304
2305 if (host.logfile)
2306 XFREE(MTYPE_HOST, host.logfile);
2307
2308 host.logfile = NULL;
2309
2310 return CMD_SUCCESS;
2311 }
2312
2313 DEFUN (config_log_syslog,
2314 config_log_syslog_cmd,
2315 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2316 "Logging control\n"
2317 "Set syslog logging level\n"
2318 LOG_LEVEL_DESC)
2319 {
2320 int idx_log_levels = 2;
2321 if (argc == 3) {
2322 int level;
2323 if ((level = level_match(argv[idx_log_levels]->arg))
2324 == ZLOG_DISABLED)
2325 return CMD_ERR_NO_MATCH;
2326 zlog_set_level(ZLOG_DEST_SYSLOG, level);
2327 return CMD_SUCCESS;
2328 } else {
2329 zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
2330 return CMD_SUCCESS;
2331 }
2332 }
2333
2334 DEFUN (no_config_log_syslog,
2335 no_config_log_syslog_cmd,
2336 "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>]",
2337 NO_STR
2338 "Logging control\n"
2339 "Cancel logging to syslog\n"
2340 LOG_FACILITY_DESC
2341 LOG_LEVEL_DESC)
2342 {
2343 zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
2344 return CMD_SUCCESS;
2345 }
2346
2347 DEFUN (config_log_facility,
2348 config_log_facility_cmd,
2349 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2350 "Logging control\n"
2351 "Facility parameter for syslog messages\n"
2352 LOG_FACILITY_DESC)
2353 {
2354 int idx_target = 2;
2355 int facility = facility_match(argv[idx_target]->arg);
2356
2357 zlog_default->facility = facility;
2358 return CMD_SUCCESS;
2359 }
2360
2361 DEFUN (no_config_log_facility,
2362 no_config_log_facility_cmd,
2363 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2364 NO_STR
2365 "Logging control\n"
2366 "Reset syslog facility to default (daemon)\n"
2367 LOG_FACILITY_DESC)
2368 {
2369 zlog_default->facility = LOG_DAEMON;
2370 return CMD_SUCCESS;
2371 }
2372
2373 DEFUN_DEPRECATED(
2374 config_log_trap, config_log_trap_cmd,
2375 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2376 "Logging control\n"
2377 "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC)
2378 {
2379 int new_level;
2380 int i;
2381
2382 if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED)
2383 return CMD_ERR_NO_MATCH;
2384
2385 zlog_default->default_lvl = new_level;
2386 for (i = 0; i < ZLOG_NUM_DESTS; i++)
2387 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
2388 zlog_default->maxlvl[i] = new_level;
2389 return CMD_SUCCESS;
2390 }
2391
2392 DEFUN_DEPRECATED(
2393 no_config_log_trap, no_config_log_trap_cmd,
2394 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2395 NO_STR
2396 "Logging control\n"
2397 "Permit all logging information\n" LOG_LEVEL_DESC)
2398 {
2399 zlog_default->default_lvl = LOG_DEBUG;
2400 return CMD_SUCCESS;
2401 }
2402
2403 DEFUN (config_log_record_priority,
2404 config_log_record_priority_cmd,
2405 "log record-priority",
2406 "Logging control\n"
2407 "Log the priority of the message within the message\n")
2408 {
2409 zlog_default->record_priority = 1;
2410 return CMD_SUCCESS;
2411 }
2412
2413 DEFUN (no_config_log_record_priority,
2414 no_config_log_record_priority_cmd,
2415 "no log record-priority",
2416 NO_STR
2417 "Logging control\n"
2418 "Do not log the priority of the message within the message\n")
2419 {
2420 zlog_default->record_priority = 0;
2421 return CMD_SUCCESS;
2422 }
2423
2424 DEFUN (config_log_timestamp_precision,
2425 config_log_timestamp_precision_cmd,
2426 "log timestamp precision (0-6)",
2427 "Logging control\n"
2428 "Timestamp configuration\n"
2429 "Set the timestamp precision\n"
2430 "Number of subsecond digits\n")
2431 {
2432 int idx_number = 3;
2433 zlog_default->timestamp_precision =
2434 strtoul(argv[idx_number]->arg, NULL, 10);
2435 return CMD_SUCCESS;
2436 }
2437
2438 DEFUN (no_config_log_timestamp_precision,
2439 no_config_log_timestamp_precision_cmd,
2440 "no log timestamp precision",
2441 NO_STR
2442 "Logging control\n"
2443 "Timestamp configuration\n"
2444 "Reset the timestamp precision to the default value of 0\n")
2445 {
2446 zlog_default->timestamp_precision = 0;
2447 return CMD_SUCCESS;
2448 }
2449
2450 DEFUN (debug_memstats,
2451 debug_memstats_cmd,
2452 "[no] debug memstats-at-exit",
2453 NO_STR
2454 DEBUG_STR
2455 "Print memory type statistics at exit\n")
2456 {
2457 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2458 return CMD_SUCCESS;
2459 }
2460
2461 int cmd_banner_motd_file(const char *file)
2462 {
2463 int success = CMD_SUCCESS;
2464 char p[PATH_MAX];
2465 char *rpath;
2466 char *in;
2467
2468 rpath = realpath(file, p);
2469 if (!rpath)
2470 return CMD_ERR_NO_FILE;
2471 in = strstr(rpath, SYSCONFDIR);
2472 if (in == rpath) {
2473 if (host.motdfile)
2474 XFREE(MTYPE_HOST, host.motdfile);
2475 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2476 } else
2477 success = CMD_WARNING_CONFIG_FAILED;
2478
2479 return success;
2480 }
2481
2482 DEFUN (banner_motd_file,
2483 banner_motd_file_cmd,
2484 "banner motd file FILE",
2485 "Set banner\n"
2486 "Banner for motd\n"
2487 "Banner from a file\n"
2488 "Filename\n")
2489 {
2490 int idx_file = 3;
2491 const char *filename = argv[idx_file]->arg;
2492 int cmd = cmd_banner_motd_file(filename);
2493
2494 if (cmd == CMD_ERR_NO_FILE)
2495 vty_out(vty, "%s does not exist", filename);
2496 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2497 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
2498
2499 return cmd;
2500 }
2501
2502 DEFUN (banner_motd_default,
2503 banner_motd_default_cmd,
2504 "banner motd default",
2505 "Set banner string\n"
2506 "Strings for motd\n"
2507 "Default string\n")
2508 {
2509 host.motd = default_motd;
2510 return CMD_SUCCESS;
2511 }
2512
2513 DEFUN (no_banner_motd,
2514 no_banner_motd_cmd,
2515 "no banner motd",
2516 NO_STR
2517 "Set banner string\n"
2518 "Strings for motd\n")
2519 {
2520 host.motd = NULL;
2521 if (host.motdfile)
2522 XFREE(MTYPE_HOST, host.motdfile);
2523 host.motdfile = NULL;
2524 return CMD_SUCCESS;
2525 }
2526
2527 DEFUN(find,
2528 find_cmd,
2529 "find COMMAND...",
2530 "Find CLI command containing text\n"
2531 "Text to search for\n")
2532 {
2533 char *text = argv_concat(argv, argc, 1);
2534 const struct cmd_node *node;
2535 const struct cmd_element *cli;
2536 vector clis;
2537
2538 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
2539 node = vector_slot(cmdvec, i);
2540 if (!node)
2541 continue;
2542 clis = node->cmd_vector;
2543 for (unsigned int j = 0; j < vector_active(clis); j++) {
2544 cli = vector_slot(clis, j);
2545 if (strcasestr(cli->string, text))
2546 vty_out(vty, " (%s) %s\n",
2547 node_names[node->node], cli->string);
2548 }
2549 }
2550
2551 XFREE(MTYPE_TMP, text);
2552
2553 return CMD_SUCCESS;
2554 }
2555
2556 /* Set config filename. Called from vty.c */
2557 void host_config_set(const char *filename)
2558 {
2559 if (host.config)
2560 XFREE(MTYPE_HOST, host.config);
2561 host.config = XSTRDUP(MTYPE_HOST, filename);
2562 }
2563
2564 const char *host_config_get(void)
2565 {
2566 return host.config;
2567 }
2568
2569 void install_default(enum node_type node)
2570 {
2571 install_element(node, &config_exit_cmd);
2572 install_element(node, &config_quit_cmd);
2573 install_element(node, &config_end_cmd);
2574 install_element(node, &config_help_cmd);
2575 install_element(node, &config_list_cmd);
2576 install_element(node, &find_cmd);
2577
2578 install_element(node, &config_write_cmd);
2579 install_element(node, &show_running_config_cmd);
2580
2581 install_element(node, &autocomplete_cmd);
2582 }
2583
2584 /* Initialize command interface. Install basic nodes and commands.
2585 *
2586 * terminal = 0 -- vtysh / no logging, no config control
2587 * terminal = 1 -- normal daemon
2588 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2589 void cmd_init(int terminal)
2590 {
2591 struct utsname names;
2592
2593 if (array_size(node_names) != NODE_TYPE_MAX)
2594 assert(!"Update the CLI node description array!");
2595
2596 uname(&names);
2597 qobj_init();
2598
2599 varhandlers = list_new();
2600
2601 /* Allocate initial top vector of commands. */
2602 cmdvec = vector_init(VECTOR_MIN_SIZE);
2603
2604 /* Default host value settings. */
2605 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2606 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2607 if ((strcmp(names.domainname, "(none)") == 0))
2608 host.domainname = NULL;
2609 else
2610 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
2611 #else
2612 host.domainname = NULL;
2613 #endif
2614 host.password = NULL;
2615 host.enable = NULL;
2616 host.logfile = NULL;
2617 host.config = NULL;
2618 host.noconfig = (terminal < 0);
2619 host.lines = -1;
2620 host.motd = default_motd;
2621 host.motdfile = NULL;
2622
2623 /* Install top nodes. */
2624 install_node(&view_node, NULL);
2625 install_node(&enable_node, NULL);
2626 install_node(&auth_node, NULL);
2627 install_node(&auth_enable_node, NULL);
2628 install_node(&config_node, config_write_host);
2629
2630 /* Each node's basic commands. */
2631 install_element(VIEW_NODE, &show_version_cmd);
2632 install_element(ENABLE_NODE, &show_startup_config_cmd);
2633 install_element(ENABLE_NODE, &debug_memstats_cmd);
2634
2635 if (terminal) {
2636 install_element(VIEW_NODE, &config_list_cmd);
2637 install_element(VIEW_NODE, &config_exit_cmd);
2638 install_element(VIEW_NODE, &config_quit_cmd);
2639 install_element(VIEW_NODE, &config_help_cmd);
2640 install_element(VIEW_NODE, &config_enable_cmd);
2641 install_element(VIEW_NODE, &config_terminal_length_cmd);
2642 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2643 install_element(VIEW_NODE, &show_logging_cmd);
2644 install_element(VIEW_NODE, &show_commandtree_cmd);
2645 install_element(VIEW_NODE, &echo_cmd);
2646 install_element(VIEW_NODE, &autocomplete_cmd);
2647 install_element(VIEW_NODE, &find_cmd);
2648
2649 install_element(ENABLE_NODE, &config_end_cmd);
2650 install_element(ENABLE_NODE, &config_disable_cmd);
2651 install_element(ENABLE_NODE, &config_terminal_cmd);
2652 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2653 install_element(ENABLE_NODE, &config_write_cmd);
2654 install_element(ENABLE_NODE, &show_running_config_cmd);
2655 install_element(ENABLE_NODE, &config_logmsg_cmd);
2656
2657 install_default(CONFIG_NODE);
2658
2659 thread_cmd_init();
2660 workqueue_cmd_init();
2661 hash_cmd_init();
2662 }
2663
2664 install_element(CONFIG_NODE, &hostname_cmd);
2665 install_element(CONFIG_NODE, &no_hostname_cmd);
2666 install_element(CONFIG_NODE, &domainname_cmd);
2667 install_element(CONFIG_NODE, &no_domainname_cmd);
2668 install_element(CONFIG_NODE, &frr_version_defaults_cmd);
2669 install_element(CONFIG_NODE, &debug_memstats_cmd);
2670
2671 if (terminal > 0) {
2672 install_element(CONFIG_NODE, &password_cmd);
2673 install_element(CONFIG_NODE, &enable_password_cmd);
2674 install_element(CONFIG_NODE, &no_enable_password_cmd);
2675
2676 install_element(CONFIG_NODE, &config_log_stdout_cmd);
2677 install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
2678 install_element(CONFIG_NODE, &config_log_monitor_cmd);
2679 install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
2680 install_element(CONFIG_NODE, &config_log_file_cmd);
2681 install_element(CONFIG_NODE, &no_config_log_file_cmd);
2682 install_element(CONFIG_NODE, &config_log_syslog_cmd);
2683 install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
2684 install_element(CONFIG_NODE, &config_log_facility_cmd);
2685 install_element(CONFIG_NODE, &no_config_log_facility_cmd);
2686 install_element(CONFIG_NODE, &config_log_trap_cmd);
2687 install_element(CONFIG_NODE, &no_config_log_trap_cmd);
2688 install_element(CONFIG_NODE, &config_log_record_priority_cmd);
2689 install_element(CONFIG_NODE,
2690 &no_config_log_record_priority_cmd);
2691 install_element(CONFIG_NODE,
2692 &config_log_timestamp_precision_cmd);
2693 install_element(CONFIG_NODE,
2694 &no_config_log_timestamp_precision_cmd);
2695 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2696 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2697 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2698 install_element(CONFIG_NODE, &banner_motd_file_cmd);
2699 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2700 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2701 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2702
2703 vrf_install_commands();
2704 }
2705
2706 #ifdef DEV_BUILD
2707 grammar_sandbox_init();
2708 #endif
2709 }
2710
2711 void cmd_terminate()
2712 {
2713 struct cmd_node *cmd_node;
2714
2715 if (cmdvec) {
2716 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2717 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2718 // deleting the graph delets the cmd_element as
2719 // well
2720 graph_delete_graph(cmd_node->cmdgraph);
2721 vector_free(cmd_node->cmd_vector);
2722 hash_clean(cmd_node->cmd_hash, NULL);
2723 hash_free(cmd_node->cmd_hash);
2724 cmd_node->cmd_hash = NULL;
2725 }
2726
2727 vector_free(cmdvec);
2728 cmdvec = NULL;
2729 }
2730
2731 if (host.name)
2732 XFREE(MTYPE_HOST, host.name);
2733 if (host.domainname)
2734 XFREE(MTYPE_HOST, host.domainname);
2735 if (host.password)
2736 XFREE(MTYPE_HOST, host.password);
2737 if (host.password_encrypt)
2738 XFREE(MTYPE_HOST, host.password_encrypt);
2739 if (host.enable)
2740 XFREE(MTYPE_HOST, host.enable);
2741 if (host.enable_encrypt)
2742 XFREE(MTYPE_HOST, host.enable_encrypt);
2743 if (host.logfile)
2744 XFREE(MTYPE_HOST, host.logfile);
2745 if (host.motdfile)
2746 XFREE(MTYPE_HOST, host.motdfile);
2747 if (host.config)
2748 XFREE(MTYPE_HOST, host.config);
2749
2750 list_delete_and_null(&varhandlers);
2751 qobj_finish();
2752 }