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