]> git.proxmox.com Git - mirror_frr.git/blob - lib/command.c
lib: Convert internal sequence number to int64_t
[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 "Assign 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 enable password set. */
1938 DEFUN (config_enable_password,
1939 enable_password_cmd,
1940 "enable password [(8-8)] WORD",
1941 "Modify enable password parameters\n"
1942 "Assign the privileged level password\n"
1943 "Specifies a HIDDEN password will follow\n"
1944 "The HIDDEN 'enable' password string\n")
1945 {
1946 int idx_8 = 2;
1947 int idx_word = 3;
1948
1949 /* Crypt type is specified. */
1950 if (argc == 4) {
1951 if (argv[idx_8]->arg[0] == '8') {
1952 if (host.enable)
1953 XFREE(MTYPE_HOST, host.enable);
1954 host.enable = NULL;
1955
1956 if (host.enable_encrypt)
1957 XFREE(MTYPE_HOST, host.enable_encrypt);
1958 host.enable_encrypt =
1959 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1960
1961 return CMD_SUCCESS;
1962 } else {
1963 vty_out(vty, "Unknown encryption type.\n");
1964 return CMD_WARNING_CONFIG_FAILED;
1965 }
1966 }
1967
1968 if (!isalnum((int)argv[idx_8]->arg[0])) {
1969 vty_out(vty,
1970 "Please specify string starting with alphanumeric\n");
1971 return CMD_WARNING_CONFIG_FAILED;
1972 }
1973
1974 if (host.enable)
1975 XFREE(MTYPE_HOST, host.enable);
1976 host.enable = NULL;
1977
1978 /* Plain password input. */
1979 if (host.encrypt) {
1980 if (host.enable_encrypt)
1981 XFREE(MTYPE_HOST, host.enable_encrypt);
1982 host.enable_encrypt =
1983 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1984 } else
1985 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1986
1987 return CMD_SUCCESS;
1988 }
1989
1990 /* VTY enable password delete. */
1991 DEFUN (no_config_enable_password,
1992 no_enable_password_cmd,
1993 "no enable password",
1994 NO_STR
1995 "Modify enable password parameters\n"
1996 "Assign the privileged level password\n")
1997 {
1998 if (host.enable)
1999 XFREE(MTYPE_HOST, host.enable);
2000 host.enable = NULL;
2001
2002 if (host.enable_encrypt)
2003 XFREE(MTYPE_HOST, host.enable_encrypt);
2004 host.enable_encrypt = NULL;
2005
2006 return CMD_SUCCESS;
2007 }
2008
2009 DEFUN (service_password_encrypt,
2010 service_password_encrypt_cmd,
2011 "service password-encryption",
2012 "Set up miscellaneous service\n"
2013 "Enable encrypted passwords\n")
2014 {
2015 if (host.encrypt)
2016 return CMD_SUCCESS;
2017
2018 host.encrypt = 1;
2019
2020 if (host.password) {
2021 if (host.password_encrypt)
2022 XFREE(MTYPE_HOST, host.password_encrypt);
2023 host.password_encrypt =
2024 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
2025 }
2026 if (host.enable) {
2027 if (host.enable_encrypt)
2028 XFREE(MTYPE_HOST, host.enable_encrypt);
2029 host.enable_encrypt =
2030 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
2031 }
2032
2033 return CMD_SUCCESS;
2034 }
2035
2036 DEFUN (no_service_password_encrypt,
2037 no_service_password_encrypt_cmd,
2038 "no service password-encryption",
2039 NO_STR
2040 "Set up miscellaneous service\n"
2041 "Enable encrypted passwords\n")
2042 {
2043 if (!host.encrypt)
2044 return CMD_SUCCESS;
2045
2046 host.encrypt = 0;
2047
2048 if (host.password_encrypt)
2049 XFREE(MTYPE_HOST, host.password_encrypt);
2050 host.password_encrypt = NULL;
2051
2052 if (host.enable_encrypt)
2053 XFREE(MTYPE_HOST, host.enable_encrypt);
2054 host.enable_encrypt = NULL;
2055
2056 return CMD_SUCCESS;
2057 }
2058
2059 DEFUN (config_terminal_length,
2060 config_terminal_length_cmd,
2061 "terminal length (0-512)",
2062 "Set terminal line parameters\n"
2063 "Set number of lines on a screen\n"
2064 "Number of lines on screen (0 for no pausing)\n")
2065 {
2066 int idx_number = 2;
2067
2068 vty->lines = atoi(argv[idx_number]->arg);
2069
2070 return CMD_SUCCESS;
2071 }
2072
2073 DEFUN (config_terminal_no_length,
2074 config_terminal_no_length_cmd,
2075 "terminal no length",
2076 "Set terminal line parameters\n"
2077 NO_STR
2078 "Set number of lines on a screen\n")
2079 {
2080 vty->lines = -1;
2081 return CMD_SUCCESS;
2082 }
2083
2084 DEFUN (service_terminal_length,
2085 service_terminal_length_cmd,
2086 "service terminal-length (0-512)",
2087 "Set up miscellaneous service\n"
2088 "System wide terminal length configuration\n"
2089 "Number of lines of VTY (0 means no line control)\n")
2090 {
2091 int idx_number = 2;
2092
2093 host.lines = atoi(argv[idx_number]->arg);
2094
2095 return CMD_SUCCESS;
2096 }
2097
2098 DEFUN (no_service_terminal_length,
2099 no_service_terminal_length_cmd,
2100 "no service terminal-length [(0-512)]",
2101 NO_STR
2102 "Set up miscellaneous service\n"
2103 "System wide terminal length configuration\n"
2104 "Number of lines of VTY (0 means no line control)\n")
2105 {
2106 host.lines = -1;
2107 return CMD_SUCCESS;
2108 }
2109
2110 DEFUN_HIDDEN (do_echo,
2111 echo_cmd,
2112 "echo MESSAGE...",
2113 "Echo a message back to the vty\n"
2114 "The message to echo\n")
2115 {
2116 char *message;
2117
2118 vty_out(vty, "%s\n",
2119 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2120 if (message)
2121 XFREE(MTYPE_TMP, message);
2122 return CMD_SUCCESS;
2123 }
2124
2125 DEFUN (config_logmsg,
2126 config_logmsg_cmd,
2127 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2128 "Send a message to enabled logging destinations\n"
2129 LOG_LEVEL_DESC
2130 "The message to send\n")
2131 {
2132 int idx_log_level = 1;
2133 int idx_message = 2;
2134 int level;
2135 char *message;
2136
2137 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2138 return CMD_ERR_NO_MATCH;
2139
2140 zlog(level, "%s",
2141 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2142 if (message)
2143 XFREE(MTYPE_TMP, message);
2144
2145 return CMD_SUCCESS;
2146 }
2147
2148 DEFUN (show_logging,
2149 show_logging_cmd,
2150 "show logging",
2151 SHOW_STR
2152 "Show current logging configuration\n")
2153 {
2154 struct zlog *zl = zlog_default;
2155
2156 vty_out(vty, "Syslog logging: ");
2157 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
2158 vty_out(vty, "disabled");
2159 else
2160 vty_out(vty, "level %s, facility %s, ident %s",
2161 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
2162 facility_name(zl->facility), zl->ident);
2163 vty_out(vty, "\n");
2164
2165 vty_out(vty, "Stdout logging: ");
2166 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
2167 vty_out(vty, "disabled");
2168 else
2169 vty_out(vty, "level %s",
2170 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
2171 vty_out(vty, "\n");
2172
2173 vty_out(vty, "Monitor logging: ");
2174 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
2175 vty_out(vty, "disabled");
2176 else
2177 vty_out(vty, "level %s",
2178 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
2179 vty_out(vty, "\n");
2180
2181 vty_out(vty, "File logging: ");
2182 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
2183 vty_out(vty, "disabled");
2184 else
2185 vty_out(vty, "level %s, filename %s",
2186 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
2187 zl->filename);
2188 vty_out(vty, "\n");
2189
2190 vty_out(vty, "Protocol name: %s\n", zl->protoname);
2191 vty_out(vty, "Record priority: %s\n",
2192 (zl->record_priority ? "enabled" : "disabled"));
2193 vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision);
2194
2195 return CMD_SUCCESS;
2196 }
2197
2198 DEFUN (config_log_stdout,
2199 config_log_stdout_cmd,
2200 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2201 "Logging control\n"
2202 "Set stdout logging level\n"
2203 LOG_LEVEL_DESC)
2204 {
2205 int idx_log_level = 2;
2206
2207 if (argc == idx_log_level) {
2208 zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
2209 return CMD_SUCCESS;
2210 }
2211 int level;
2212
2213 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2214 return CMD_ERR_NO_MATCH;
2215 zlog_set_level(ZLOG_DEST_STDOUT, level);
2216 return CMD_SUCCESS;
2217 }
2218
2219 DEFUN (no_config_log_stdout,
2220 no_config_log_stdout_cmd,
2221 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2222 NO_STR
2223 "Logging control\n"
2224 "Cancel logging to stdout\n"
2225 LOG_LEVEL_DESC)
2226 {
2227 zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
2228 return CMD_SUCCESS;
2229 }
2230
2231 DEFUN (config_log_monitor,
2232 config_log_monitor_cmd,
2233 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2234 "Logging control\n"
2235 "Set terminal line (monitor) logging level\n"
2236 LOG_LEVEL_DESC)
2237 {
2238 int idx_log_level = 2;
2239
2240 if (argc == idx_log_level) {
2241 zlog_set_level(ZLOG_DEST_MONITOR, zlog_default->default_lvl);
2242 return CMD_SUCCESS;
2243 }
2244 int level;
2245
2246 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2247 return CMD_ERR_NO_MATCH;
2248 zlog_set_level(ZLOG_DEST_MONITOR, level);
2249 return CMD_SUCCESS;
2250 }
2251
2252 DEFUN (no_config_log_monitor,
2253 no_config_log_monitor_cmd,
2254 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2255 NO_STR
2256 "Logging control\n"
2257 "Disable terminal line (monitor) logging\n"
2258 LOG_LEVEL_DESC)
2259 {
2260 zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
2261 return CMD_SUCCESS;
2262 }
2263
2264 static int set_log_file(struct vty *vty, const char *fname, int loglevel)
2265 {
2266 int ret;
2267 char *p = NULL;
2268 const char *fullpath;
2269
2270 /* Path detection. */
2271 if (!IS_DIRECTORY_SEP(*fname)) {
2272 char cwd[MAXPATHLEN + 1];
2273 cwd[MAXPATHLEN] = '\0';
2274
2275 if (getcwd(cwd, MAXPATHLEN) == NULL) {
2276 zlog_err("config_log_file: Unable to alloc mem!");
2277 return CMD_WARNING_CONFIG_FAILED;
2278 }
2279
2280 if ((p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2))
2281 == NULL) {
2282 zlog_err("config_log_file: Unable to alloc mem!");
2283 return CMD_WARNING_CONFIG_FAILED;
2284 }
2285 sprintf(p, "%s/%s", cwd, fname);
2286 fullpath = p;
2287 } else
2288 fullpath = fname;
2289
2290 ret = zlog_set_file(fullpath, loglevel);
2291
2292 if (p)
2293 XFREE(MTYPE_TMP, p);
2294
2295 if (!ret) {
2296 vty_out(vty, "can't open logfile %s\n", fname);
2297 return CMD_WARNING_CONFIG_FAILED;
2298 }
2299
2300 if (host.logfile)
2301 XFREE(MTYPE_HOST, host.logfile);
2302
2303 host.logfile = XSTRDUP(MTYPE_HOST, fname);
2304
2305 #if defined(HAVE_CUMULUS)
2306 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
2307 zlog_default->maxlvl[ZLOG_DEST_SYSLOG] = ZLOG_DISABLED;
2308 #endif
2309 return CMD_SUCCESS;
2310 }
2311
2312 DEFUN (config_log_file,
2313 config_log_file_cmd,
2314 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2315 "Logging control\n"
2316 "Logging to file\n"
2317 "Logging filename\n"
2318 LOG_LEVEL_DESC)
2319 {
2320 int idx_filename = 2;
2321 int idx_log_levels = 3;
2322 if (argc == 4) {
2323 int level;
2324 if ((level = level_match(argv[idx_log_levels]->arg))
2325 == ZLOG_DISABLED)
2326 return CMD_ERR_NO_MATCH;
2327 return set_log_file(vty, argv[idx_filename]->arg, level);
2328 } else
2329 return set_log_file(vty, argv[idx_filename]->arg,
2330 zlog_default->default_lvl);
2331 }
2332
2333 DEFUN (no_config_log_file,
2334 no_config_log_file_cmd,
2335 "no log file [FILENAME [LEVEL]]",
2336 NO_STR
2337 "Logging control\n"
2338 "Cancel logging to file\n"
2339 "Logging file name\n"
2340 "Logging level\n")
2341 {
2342 zlog_reset_file();
2343
2344 if (host.logfile)
2345 XFREE(MTYPE_HOST, host.logfile);
2346
2347 host.logfile = NULL;
2348
2349 return CMD_SUCCESS;
2350 }
2351
2352 DEFUN (config_log_syslog,
2353 config_log_syslog_cmd,
2354 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2355 "Logging control\n"
2356 "Set syslog logging level\n"
2357 LOG_LEVEL_DESC)
2358 {
2359 int idx_log_levels = 2;
2360 if (argc == 3) {
2361 int level;
2362 if ((level = level_match(argv[idx_log_levels]->arg))
2363 == ZLOG_DISABLED)
2364 return CMD_ERR_NO_MATCH;
2365 zlog_set_level(ZLOG_DEST_SYSLOG, level);
2366 return CMD_SUCCESS;
2367 } else {
2368 zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
2369 return CMD_SUCCESS;
2370 }
2371 }
2372
2373 DEFUN (no_config_log_syslog,
2374 no_config_log_syslog_cmd,
2375 "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>]",
2376 NO_STR
2377 "Logging control\n"
2378 "Cancel logging to syslog\n"
2379 LOG_FACILITY_DESC
2380 LOG_LEVEL_DESC)
2381 {
2382 zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
2383 return CMD_SUCCESS;
2384 }
2385
2386 DEFUN (config_log_facility,
2387 config_log_facility_cmd,
2388 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2389 "Logging control\n"
2390 "Facility parameter for syslog messages\n"
2391 LOG_FACILITY_DESC)
2392 {
2393 int idx_target = 2;
2394 int facility = facility_match(argv[idx_target]->arg);
2395
2396 zlog_default->facility = facility;
2397 return CMD_SUCCESS;
2398 }
2399
2400 DEFUN (no_config_log_facility,
2401 no_config_log_facility_cmd,
2402 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2403 NO_STR
2404 "Logging control\n"
2405 "Reset syslog facility to default (daemon)\n"
2406 LOG_FACILITY_DESC)
2407 {
2408 zlog_default->facility = LOG_DAEMON;
2409 return CMD_SUCCESS;
2410 }
2411
2412 DEFUN_DEPRECATED(
2413 config_log_trap, config_log_trap_cmd,
2414 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2415 "Logging control\n"
2416 "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC)
2417 {
2418 int new_level;
2419 int i;
2420
2421 if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED)
2422 return CMD_ERR_NO_MATCH;
2423
2424 zlog_default->default_lvl = new_level;
2425 for (i = 0; i < ZLOG_NUM_DESTS; i++)
2426 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
2427 zlog_default->maxlvl[i] = new_level;
2428 return CMD_SUCCESS;
2429 }
2430
2431 DEFUN_DEPRECATED(
2432 no_config_log_trap, no_config_log_trap_cmd,
2433 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2434 NO_STR
2435 "Logging control\n"
2436 "Permit all logging information\n" LOG_LEVEL_DESC)
2437 {
2438 zlog_default->default_lvl = LOG_DEBUG;
2439 return CMD_SUCCESS;
2440 }
2441
2442 DEFUN (config_log_record_priority,
2443 config_log_record_priority_cmd,
2444 "log record-priority",
2445 "Logging control\n"
2446 "Log the priority of the message within the message\n")
2447 {
2448 zlog_default->record_priority = 1;
2449 return CMD_SUCCESS;
2450 }
2451
2452 DEFUN (no_config_log_record_priority,
2453 no_config_log_record_priority_cmd,
2454 "no log record-priority",
2455 NO_STR
2456 "Logging control\n"
2457 "Do not log the priority of the message within the message\n")
2458 {
2459 zlog_default->record_priority = 0;
2460 return CMD_SUCCESS;
2461 }
2462
2463 DEFUN (config_log_timestamp_precision,
2464 config_log_timestamp_precision_cmd,
2465 "log timestamp precision (0-6)",
2466 "Logging control\n"
2467 "Timestamp configuration\n"
2468 "Set the timestamp precision\n"
2469 "Number of subsecond digits\n")
2470 {
2471 int idx_number = 3;
2472 zlog_default->timestamp_precision =
2473 strtoul(argv[idx_number]->arg, NULL, 10);
2474 return CMD_SUCCESS;
2475 }
2476
2477 DEFUN (no_config_log_timestamp_precision,
2478 no_config_log_timestamp_precision_cmd,
2479 "no log timestamp precision",
2480 NO_STR
2481 "Logging control\n"
2482 "Timestamp configuration\n"
2483 "Reset the timestamp precision to the default value of 0\n")
2484 {
2485 zlog_default->timestamp_precision = 0;
2486 return CMD_SUCCESS;
2487 }
2488
2489 DEFUN (debug_memstats,
2490 debug_memstats_cmd,
2491 "[no] debug memstats-at-exit",
2492 NO_STR
2493 DEBUG_STR
2494 "Print memory type statistics at exit\n")
2495 {
2496 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2497 return CMD_SUCCESS;
2498 }
2499
2500 int cmd_banner_motd_file(const char *file)
2501 {
2502 int success = CMD_SUCCESS;
2503 char p[PATH_MAX];
2504 char *rpath;
2505 char *in;
2506
2507 rpath = realpath(file, p);
2508 if (!rpath)
2509 return CMD_ERR_NO_FILE;
2510 in = strstr(rpath, SYSCONFDIR);
2511 if (in == rpath) {
2512 if (host.motdfile)
2513 XFREE(MTYPE_HOST, host.motdfile);
2514 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2515 } else
2516 success = CMD_WARNING_CONFIG_FAILED;
2517
2518 return success;
2519 }
2520
2521 DEFUN (banner_motd_file,
2522 banner_motd_file_cmd,
2523 "banner motd file FILE",
2524 "Set banner\n"
2525 "Banner for motd\n"
2526 "Banner from a file\n"
2527 "Filename\n")
2528 {
2529 int idx_file = 3;
2530 const char *filename = argv[idx_file]->arg;
2531 int cmd = cmd_banner_motd_file(filename);
2532
2533 if (cmd == CMD_ERR_NO_FILE)
2534 vty_out(vty, "%s does not exist", filename);
2535 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2536 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
2537
2538 return cmd;
2539 }
2540
2541 DEFUN (banner_motd_default,
2542 banner_motd_default_cmd,
2543 "banner motd default",
2544 "Set banner string\n"
2545 "Strings for motd\n"
2546 "Default string\n")
2547 {
2548 host.motd = default_motd;
2549 return CMD_SUCCESS;
2550 }
2551
2552 DEFUN (no_banner_motd,
2553 no_banner_motd_cmd,
2554 "no banner motd",
2555 NO_STR
2556 "Set banner string\n"
2557 "Strings for motd\n")
2558 {
2559 host.motd = NULL;
2560 if (host.motdfile)
2561 XFREE(MTYPE_HOST, host.motdfile);
2562 host.motdfile = NULL;
2563 return CMD_SUCCESS;
2564 }
2565
2566 DEFUN(find,
2567 find_cmd,
2568 "find COMMAND...",
2569 "Find CLI command containing text\n"
2570 "Text to search for\n")
2571 {
2572 char *text = argv_concat(argv, argc, 1);
2573 const struct cmd_node *node;
2574 const struct cmd_element *cli;
2575 vector clis;
2576
2577 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
2578 node = vector_slot(cmdvec, i);
2579 if (!node)
2580 continue;
2581 clis = node->cmd_vector;
2582 for (unsigned int j = 0; j < vector_active(clis); j++) {
2583 cli = vector_slot(clis, j);
2584 if (strcasestr(cli->string, text))
2585 vty_out(vty, " (%s) %s\n",
2586 node_names[node->node], cli->string);
2587 }
2588 }
2589
2590 XFREE(MTYPE_TMP, text);
2591
2592 return CMD_SUCCESS;
2593 }
2594
2595 /* Set config filename. Called from vty.c */
2596 void host_config_set(const char *filename)
2597 {
2598 if (host.config)
2599 XFREE(MTYPE_HOST, host.config);
2600 host.config = XSTRDUP(MTYPE_HOST, filename);
2601 }
2602
2603 const char *host_config_get(void)
2604 {
2605 return host.config;
2606 }
2607
2608 void install_default(enum node_type node)
2609 {
2610 install_element(node, &config_exit_cmd);
2611 install_element(node, &config_quit_cmd);
2612 install_element(node, &config_end_cmd);
2613 install_element(node, &config_help_cmd);
2614 install_element(node, &config_list_cmd);
2615 install_element(node, &show_cli_graph_cmd);
2616 install_element(node, &find_cmd);
2617
2618 install_element(node, &config_write_cmd);
2619 install_element(node, &show_running_config_cmd);
2620
2621 install_element(node, &autocomplete_cmd);
2622 }
2623
2624 /* Initialize command interface. Install basic nodes and commands.
2625 *
2626 * terminal = 0 -- vtysh / no logging, no config control
2627 * terminal = 1 -- normal daemon
2628 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2629 void cmd_init(int terminal)
2630 {
2631 struct utsname names;
2632
2633 if (array_size(node_names) != NODE_TYPE_MAX)
2634 assert(!"Update the CLI node description array!");
2635
2636 uname(&names);
2637 qobj_init();
2638
2639 varhandlers = list_new();
2640
2641 /* Allocate initial top vector of commands. */
2642 cmdvec = vector_init(VECTOR_MIN_SIZE);
2643
2644 /* Default host value settings. */
2645 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2646 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2647 if ((strcmp(names.domainname, "(none)") == 0))
2648 host.domainname = NULL;
2649 else
2650 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
2651 #else
2652 host.domainname = NULL;
2653 #endif
2654 host.password = NULL;
2655 host.enable = NULL;
2656 host.logfile = NULL;
2657 host.config = NULL;
2658 host.noconfig = (terminal < 0);
2659 host.lines = -1;
2660 host.motd = default_motd;
2661 host.motdfile = NULL;
2662
2663 /* Install top nodes. */
2664 install_node(&view_node, NULL);
2665 install_node(&enable_node, NULL);
2666 install_node(&auth_node, NULL);
2667 install_node(&auth_enable_node, NULL);
2668 install_node(&config_node, config_write_host);
2669
2670 /* Each node's basic commands. */
2671 install_element(VIEW_NODE, &show_version_cmd);
2672 install_element(ENABLE_NODE, &show_startup_config_cmd);
2673 install_element(ENABLE_NODE, &debug_memstats_cmd);
2674
2675 if (terminal) {
2676 install_element(VIEW_NODE, &config_list_cmd);
2677 install_element(VIEW_NODE, &config_exit_cmd);
2678 install_element(VIEW_NODE, &config_quit_cmd);
2679 install_element(VIEW_NODE, &config_help_cmd);
2680 install_element(VIEW_NODE, &config_enable_cmd);
2681 install_element(VIEW_NODE, &config_terminal_length_cmd);
2682 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2683 install_element(VIEW_NODE, &show_logging_cmd);
2684 install_element(VIEW_NODE, &show_commandtree_cmd);
2685 install_element(VIEW_NODE, &echo_cmd);
2686 install_element(VIEW_NODE, &autocomplete_cmd);
2687 install_element(VIEW_NODE, &find_cmd);
2688
2689 install_element(ENABLE_NODE, &config_end_cmd);
2690 install_element(ENABLE_NODE, &config_disable_cmd);
2691 install_element(ENABLE_NODE, &config_terminal_cmd);
2692 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2693 install_element(ENABLE_NODE, &config_write_cmd);
2694 install_element(ENABLE_NODE, &show_running_config_cmd);
2695 install_element(ENABLE_NODE, &config_logmsg_cmd);
2696
2697 install_default(CONFIG_NODE);
2698
2699 thread_cmd_init();
2700 workqueue_cmd_init();
2701 hash_cmd_init();
2702 }
2703
2704 install_element(CONFIG_NODE, &hostname_cmd);
2705 install_element(CONFIG_NODE, &no_hostname_cmd);
2706 install_element(CONFIG_NODE, &domainname_cmd);
2707 install_element(CONFIG_NODE, &no_domainname_cmd);
2708 install_element(CONFIG_NODE, &frr_version_defaults_cmd);
2709 install_element(CONFIG_NODE, &debug_memstats_cmd);
2710
2711 if (terminal > 0) {
2712 install_element(CONFIG_NODE, &password_cmd);
2713 install_element(CONFIG_NODE, &enable_password_cmd);
2714 install_element(CONFIG_NODE, &no_enable_password_cmd);
2715
2716 install_element(CONFIG_NODE, &config_log_stdout_cmd);
2717 install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
2718 install_element(CONFIG_NODE, &config_log_monitor_cmd);
2719 install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
2720 install_element(CONFIG_NODE, &config_log_file_cmd);
2721 install_element(CONFIG_NODE, &no_config_log_file_cmd);
2722 install_element(CONFIG_NODE, &config_log_syslog_cmd);
2723 install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
2724 install_element(CONFIG_NODE, &config_log_facility_cmd);
2725 install_element(CONFIG_NODE, &no_config_log_facility_cmd);
2726 install_element(CONFIG_NODE, &config_log_trap_cmd);
2727 install_element(CONFIG_NODE, &no_config_log_trap_cmd);
2728 install_element(CONFIG_NODE, &config_log_record_priority_cmd);
2729 install_element(CONFIG_NODE,
2730 &no_config_log_record_priority_cmd);
2731 install_element(CONFIG_NODE,
2732 &config_log_timestamp_precision_cmd);
2733 install_element(CONFIG_NODE,
2734 &no_config_log_timestamp_precision_cmd);
2735 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2736 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2737 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2738 install_element(CONFIG_NODE, &banner_motd_file_cmd);
2739 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2740 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2741 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2742
2743 vrf_install_commands();
2744 }
2745
2746 #ifdef DEV_BUILD
2747 grammar_sandbox_init();
2748 #endif
2749 }
2750
2751 void cmd_terminate()
2752 {
2753 struct cmd_node *cmd_node;
2754
2755 if (cmdvec) {
2756 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2757 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2758 // deleting the graph delets the cmd_element as
2759 // well
2760 graph_delete_graph(cmd_node->cmdgraph);
2761 vector_free(cmd_node->cmd_vector);
2762 hash_clean(cmd_node->cmd_hash, NULL);
2763 hash_free(cmd_node->cmd_hash);
2764 cmd_node->cmd_hash = NULL;
2765 }
2766
2767 vector_free(cmdvec);
2768 cmdvec = NULL;
2769 }
2770
2771 if (host.name)
2772 XFREE(MTYPE_HOST, host.name);
2773 if (host.domainname)
2774 XFREE(MTYPE_HOST, host.domainname);
2775 if (host.password)
2776 XFREE(MTYPE_HOST, host.password);
2777 if (host.password_encrypt)
2778 XFREE(MTYPE_HOST, host.password_encrypt);
2779 if (host.enable)
2780 XFREE(MTYPE_HOST, host.enable);
2781 if (host.enable_encrypt)
2782 XFREE(MTYPE_HOST, host.enable_encrypt);
2783 if (host.logfile)
2784 XFREE(MTYPE_HOST, host.logfile);
2785 if (host.motdfile)
2786 XFREE(MTYPE_HOST, host.motdfile);
2787 if (host.config)
2788 XFREE(MTYPE_HOST, host.config);
2789
2790 list_delete_and_null(&varhandlers);
2791 qobj_finish();
2792 }