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