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