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