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