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