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