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