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