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