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