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