]> git.proxmox.com Git - mirror_frr.git/blame - lib/command.c
Merge pull request #9083 from mobash-rasool/pim-upst-3
[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"
d0bfb22c 51
3d19ffc5
QY
52#include "frrscript.h"
53
bf8d3d6a
DL
54DEFINE_MTYPE_STATIC(LIB, HOST, "Host config");
55DEFINE_MTYPE(LIB, COMPLETION, "Completion item");
4a1ab8e4 56
26fbe472
QY
57#define item(x) \
58 { \
59 x, #x \
60 }
61
62/* clang-format off */
63const struct message tokennames[] = {
64 item(WORD_TKN),
65 item(VARIABLE_TKN),
66 item(RANGE_TKN),
67 item(IPV4_TKN),
68 item(IPV4_PREFIX_TKN),
69 item(IPV6_TKN),
70 item(IPV6_PREFIX_TKN),
71 item(MAC_TKN),
72 item(MAC_PREFIX_TKN),
73 item(FORK_TKN),
74 item(JOIN_TKN),
75 item(START_TKN),
76 item(END_TKN),
90c8406c 77 item(NEG_ONLY_TKN),
26fbe472
QY
78 {0},
79};
26fbe472 80/* clang-format on */
a83a5331 81
718e3744 82/* Command vector which includes some level of command lists. Normally
83 each daemon maintains each own cmdvec. */
eb820afe 84vector cmdvec = NULL;
718e3744 85
86/* Host information structure. */
87struct host host;
88
0e06eb8b
DL
89/* for vtysh, put together CLI trees only when switching into node */
90static bool defer_cli_tree;
91
419cd5a0
MK
92/*
93 * Returns host.name if any, otherwise
94 * it returns the system hostname.
95 */
6b3ee3a0 96const char *cmd_hostname_get(void)
419cd5a0
MK
97{
98 return host.name;
99}
100
101/*
102 * Returns unix domainname
103 */
6b3ee3a0 104const char *cmd_domainname_get(void)
419cd5a0
MK
105{
106 return host.domainname;
107}
108
791ded4a
DL
109static int root_on_exit(struct vty *vty);
110
718e3744 111/* Standard command node structures. */
d62a17ae 112static struct cmd_node auth_node = {
f4b8291f 113 .name = "auth",
62b346ee
DL
114 .node = AUTH_NODE,
115 .prompt = "Password: ",
718e3744 116};
117
d62a17ae 118static struct cmd_node view_node = {
f4b8291f 119 .name = "view",
62b346ee
DL
120 .node = VIEW_NODE,
121 .prompt = "%s> ",
791ded4a 122 .node_exit = root_on_exit,
718e3744 123};
124
d62a17ae 125static struct cmd_node auth_enable_node = {
f4b8291f 126 .name = "auth enable",
62b346ee
DL
127 .node = AUTH_ENABLE_NODE,
128 .prompt = "Password: ",
718e3744 129};
130
d62a17ae 131static struct cmd_node enable_node = {
f4b8291f 132 .name = "enable",
62b346ee
DL
133 .node = ENABLE_NODE,
134 .prompt = "%s# ",
791ded4a 135 .node_exit = root_on_exit,
718e3744 136};
137
612c2c15 138static int config_write_host(struct vty *vty);
62b346ee 139static struct cmd_node config_node = {
f4b8291f 140 .name = "config",
62b346ee 141 .node = CONFIG_NODE,
24389580 142 .parent_node = ENABLE_NODE,
62b346ee 143 .prompt = "%s(config)# ",
612c2c15 144 .config_write = config_write_host,
791ded4a 145 .node_exit = vty_config_node_exit,
62b346ee 146};
6590f2c3 147
dd90823b
CS
148static bool vty_check_node_for_xpath_decrement(enum node_type target_node,
149 enum node_type node)
150{
151 /* bgp afi-safi (`address-family <afi> <safi>`) node
152 * does not increment xpath_index.
153 * In order to use (`router bgp`) BGP_NODE's xpath as a base,
154 * retain xpath_index as 1 upon exiting from
155 * afi-safi node.
156 */
157
158 if (target_node == BGP_NODE
159 && (node == BGP_IPV4_NODE || node == BGP_IPV6_NODE
160 || node == BGP_IPV4M_NODE || node == BGP_IPV6M_NODE
161 || node == BGP_VPNV4_NODE || node == BGP_VPNV6_NODE
162 || node == BGP_EVPN_NODE || node == BGP_IPV4L_NODE
1e93fbed
DS
163 || node == BGP_IPV6L_NODE || node == BGP_FLOWSPECV4_NODE
164 || node == BGP_FLOWSPECV6_NODE))
dd90823b
CS
165 return false;
166
d9331fc6
IR
167 if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE)
168 return false;
169
dd90823b
CS
170 return true;
171}
172
cb585b65 173/* This is called from main when a daemon is invoked with -v or --version. */
d62a17ae 174void print_version(const char *progname)
6590f2c3 175{
d62a17ae 176 printf("%s version %s\n", progname, FRR_VERSION);
177 printf("%s\n", FRR_COPYRIGHT);
e063f271 178#ifdef ENABLE_VERSION_BUILD_CONFIG
d62a17ae 179 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
e063f271 180#endif
6590f2c3 181}
182
d62a17ae 183char *argv_concat(struct cmd_token **argv, int argc, int shift)
184{
a6a87d63 185 int cnt = MAX(argc - shift, 0);
186 const char *argstr[cnt + 1];
187
188 if (!cnt)
189 return NULL;
fe011935
QY
190
191 for (int i = 0; i < cnt; i++)
192 argstr[i] = argv[i + shift]->arg;
193
194 return frrstr_join(argstr, cnt, " ");
195}
196
197vector cmd_make_strvec(const char *string)
198{
199 if (!string)
d62a17ae 200 return NULL;
fe011935
QY
201
202 const char *copy = string;
203
204 /* skip leading whitespace */
fefa5e0f 205 while (isspace((unsigned char)*copy) && *copy != '\0')
fe011935
QY
206 copy++;
207
208 /* if the entire string was whitespace or a comment, return */
209 if (*copy == '\0' || *copy == '!' || *copy == '#')
210 return NULL;
211
0a334343 212 vector result = frrstr_split_vec(copy, "\n\r\t ");
fe011935
QY
213
214 for (unsigned int i = 0; i < vector_active(result); i++) {
215 if (strlen(vector_slot(result, i)) == 0) {
216 XFREE(MTYPE_TMP, vector_slot(result, i));
f428cb8a 217 vector_unset(result, i);
fe011935 218 }
d62a17ae 219 }
f428cb8a
QY
220
221 vector_compact(result);
222
fe011935
QY
223 return result;
224}
225
226void cmd_free_strvec(vector v)
227{
228 frrstr_strvec_free(v);
718e3744 229}
230
ae19d7dd
QY
231/**
232 * Convenience function for accessing argv data.
233 *
234 * @param argc
235 * @param argv
236 * @param text definition snippet of the desired token
237 * @param index the starting index, and where to store the
238 * index of the found token if it exists
239 * @return 1 if found, 0 otherwise
240 */
d62a17ae 241int argv_find(struct cmd_token **argv, int argc, const char *text, int *index)
ae19d7dd 242{
d62a17ae 243 int found = 0;
244 for (int i = *index; i < argc && found == 0; i++)
245 if ((found = strmatch(text, argv[i]->text)))
246 *index = i;
247 return found;
ae19d7dd
QY
248}
249
d8b87afe 250static unsigned int cmd_hash_key(const void *p)
274f29b2 251{
bd74dc61
DS
252 int size = sizeof(p);
253
254 return jhash(p, size, 0);
274f29b2
PJ
255}
256
74df8d6d 257static bool cmd_hash_cmp(const void *a, const void *b)
274f29b2 258{
d62a17ae 259 return a == b;
274f29b2
PJ
260}
261
718e3744 262/* Install top node of command vector. */
612c2c15 263void install_node(struct cmd_node *node)
718e3744 264{
c81d78cf
DS
265#define CMD_HASH_STR_SIZE 256
266 char hash_name[CMD_HASH_STR_SIZE];
267
d62a17ae 268 vector_set_index(cmdvec, node->node, node);
d62a17ae 269 node->cmdgraph = graph_new();
270 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
271 // add start node
272 struct cmd_token *token =
273 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
274 graph_new_node(node->cmdgraph, token,
275 (void (*)(void *)) & cmd_token_del);
c81d78cf
DS
276
277 snprintf(hash_name, sizeof(hash_name), "Command Hash: %s", node->name);
278 node->cmd_hash =
279 hash_create_size(16, cmd_hash_key, cmd_hash_cmp, hash_name);
718e3744 280}
281
718e3744 282/* Return prompt character of specified node. */
d62a17ae 283const char *cmd_prompt(enum node_type node)
718e3744 284{
d62a17ae 285 struct cmd_node *cnode;
718e3744 286
d62a17ae 287 cnode = vector_slot(cmdvec, node);
288 return cnode->prompt;
718e3744 289}
290
0e06eb8b
DL
291void cmd_defer_tree(bool val)
292{
293 defer_cli_tree = val;
294}
295
718e3744 296/* Install a command into a node. */
01485adb 297void _install_element(enum node_type ntype, const struct cmd_element *cmd)
718e3744 298{
d62a17ae 299 struct cmd_node *cnode;
d0bfb22c 300
d62a17ae 301 /* cmd_init hasn't been called */
302 if (!cmdvec) {
303 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
304 __func__);
305 return;
306 }
ebacb4ed 307
3cbb67f2 308 cnode = vector_lookup(cmdvec, ntype);
718e3744 309
d62a17ae 310 if (cnode == NULL) {
311 fprintf(stderr,
3cbb67f2 312 "%s[%s]:\n"
f4b8291f 313 "\tnode %d does not exist.\n"
3cbb67f2 314 "\tplease call install_node() before install_element()\n",
f4b8291f 315 cmd->name, cmd->string, ntype);
d62a17ae 316 exit(EXIT_FAILURE);
317 }
ebacb4ed 318
154e9ca1 319 if (hash_lookup(cnode->cmd_hash, (void *)cmd) != NULL) {
d62a17ae 320 fprintf(stderr,
3cbb67f2
DL
321 "%s[%s]:\n"
322 "\tnode %d (%s) already has this command installed.\n"
323 "\tduplicate install_element call?\n",
f4b8291f 324 cmd->name, cmd->string, ntype, cnode->name);
d62a17ae 325 return;
326 }
ebacb4ed 327
154e9ca1 328 assert(hash_get(cnode->cmd_hash, (void *)cmd, hash_alloc_intern));
ebacb4ed 329
0e06eb8b
DL
330 if (cnode->graph_built || !defer_cli_tree) {
331 struct graph *graph = graph_new();
332 struct cmd_token *token =
333 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
334 graph_new_node(graph, token,
335 (void (*)(void *)) & cmd_token_del);
336
337 cmd_graph_parse(graph, cmd);
338 cmd_graph_names(graph);
339 cmd_graph_merge(cnode->cmdgraph, graph, +1);
340 graph_delete_graph(graph);
341
342 cnode->graph_built = true;
343 }
344
345 vector_set(cnode->cmd_vector, (void *)cmd);
346
347 if (ntype == VIEW_NODE)
348 _install_element(ENABLE_NODE, cmd);
349}
350
351static void cmd_finalize_iter(struct hash_bucket *hb, void *arg)
352{
353 struct cmd_node *cnode = arg;
354 const struct cmd_element *cmd = hb->data;
d62a17ae 355 struct graph *graph = graph_new();
356 struct cmd_token *token =
357 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
0e06eb8b 358
d62a17ae 359 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
de8f7a39 360
d62a17ae 361 cmd_graph_parse(graph, cmd);
362 cmd_graph_names(graph);
363 cmd_graph_merge(cnode->cmdgraph, graph, +1);
364 graph_delete_graph(graph);
0e06eb8b 365}
de8f7a39 366
0e06eb8b
DL
367void cmd_finalize_node(struct cmd_node *cnode)
368{
369 if (cnode->graph_built)
370 return;
735e62a0 371
0e06eb8b
DL
372 hash_iterate(cnode->cmd_hash, cmd_finalize_iter, cnode);
373 cnode->graph_built = true;
718e3744 374}
375
154e9ca1 376void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
de8f7a39 377{
d62a17ae 378 struct cmd_node *cnode;
de8f7a39 379
d62a17ae 380 /* cmd_init hasn't been called */
381 if (!cmdvec) {
382 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
383 __func__);
384 return;
385 }
de8f7a39 386
3cbb67f2 387 cnode = vector_lookup(cmdvec, ntype);
de8f7a39 388
d62a17ae 389 if (cnode == NULL) {
390 fprintf(stderr,
3cbb67f2 391 "%s[%s]:\n"
f4b8291f 392 "\tnode %d does not exist.\n"
3cbb67f2 393 "\tplease call install_node() before uninstall_element()\n",
f4b8291f 394 cmd->name, cmd->string, ntype);
d62a17ae 395 exit(EXIT_FAILURE);
396 }
de8f7a39 397
154e9ca1 398 if (hash_release(cnode->cmd_hash, (void *)cmd) == NULL) {
d62a17ae 399 fprintf(stderr,
3cbb67f2
DL
400 "%s[%s]:\n"
401 "\tnode %d (%s) does not have this command installed.\n"
402 "\tduplicate uninstall_element call?\n",
f4b8291f 403 cmd->name, cmd->string, ntype, cnode->name);
d62a17ae 404 return;
405 }
de8f7a39 406
154e9ca1 407 vector_unset_value(cnode->cmd_vector, (void *)cmd);
de8f7a39 408
0e06eb8b
DL
409 if (cnode->graph_built) {
410 struct graph *graph = graph_new();
411 struct cmd_token *token =
412 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
413 graph_new_node(graph, token,
414 (void (*)(void *)) & cmd_token_del);
415
416 cmd_graph_parse(graph, cmd);
417 cmd_graph_names(graph);
418 cmd_graph_merge(cnode->cmdgraph, graph, -1);
419 graph_delete_graph(graph);
420 }
de8f7a39 421
d62a17ae 422 if (ntype == VIEW_NODE)
423 uninstall_element(ENABLE_NODE, cmd);
de8f7a39
DL
424}
425
426
2d362d10 427static const unsigned char itoa64[] =
d62a17ae 428 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
718e3744 429
d62a17ae 430static void to64(char *s, long v, int n)
718e3744 431{
d62a17ae 432 while (--n >= 0) {
433 *s++ = itoa64[v & 0x3f];
434 v >>= 6;
435 }
718e3744 436}
437
d62a17ae 438static char *zencrypt(const char *passwd)
718e3744 439{
d62a17ae 440 char salt[6];
441 struct timeval tv;
442 char *crypt(const char *, const char *);
718e3744 443
d62a17ae 444 gettimeofday(&tv, 0);
d0bfb22c 445
5920b3eb 446 to64(&salt[0], frr_weak_random(), 3);
d62a17ae 447 to64(&salt[3], tv.tv_usec, 3);
448 salt[5] = '\0';
718e3744 449
d62a17ae 450 return crypt(passwd, salt);
718e3744 451}
452
0bdeb5e5
DL
453static bool full_cli;
454
718e3744 455/* This function write configuration of this host. */
d62a17ae 456static int config_write_host(struct vty *vty)
457{
6b3ee3a0
MK
458 if (cmd_hostname_get())
459 vty_out(vty, "hostname %s\n", cmd_hostname_get());
d62a17ae 460
3b103fec
MK
461 if (cmd_domainname_get())
462 vty_out(vty, "domainname %s\n", cmd_domainname_get());
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",
505 cputime_threshold);
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",
515 walltime_threshold);
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++) {
925 if (node <= CONFIG_NODE)
926 return CMD_NO_LEVEL_UP;
927
928 node = node_parent(node);
929
930 if (xpath_index > 0
931 && vty_check_node_for_xpath_decrement(node, vty->node))
932 xpath_index--;
933 }
934
935 struct graph *cmdgraph = cmd_node_graph(cmdvec, node);
d62a17ae 936 status = command_match(cmdgraph, vline, &argv_list, &matched_element);
937
938 if (cmd)
939 *cmd = matched_element;
940
941 // if matcher error, return corresponding CMD_ERR
942 if (MATCHER_ERROR(status)) {
943 if (argv_list)
6a154c88 944 list_delete(&argv_list);
d62a17ae 945 switch (status) {
946 case MATCHER_INCOMPLETE:
947 return CMD_ERR_INCOMPLETE;
948 case MATCHER_AMBIGUOUS:
949 return CMD_ERR_AMBIGUOUS;
950 default:
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 */
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) {
1065 try_node = node_parent(try_node);
1066 vty->node = try_node;
dd90823b
CS
1067 if (vty->xpath_index > 0
1068 && vty_check_node_for_xpath_decrement(try_node,
1069 onode))
1c2facd1 1070 vty->xpath_index--;
d62a17ae 1071 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
e3476061 1072 vty, cmd, 0);
825d5fbf 1073 if (ret == CMD_SUCCESS || ret == CMD_WARNING
4bb7f7c2 1074 || ret == CMD_ERR_AMBIGUOUS || ret == CMD_ERR_INCOMPLETE
825d5fbf
CS
1075 || ret == CMD_NOT_MY_INSTANCE
1076 || ret == CMD_WARNING_CONFIG_FAILED)
d62a17ae 1077 return ret;
1078 }
1079 /* no command succeeded, reset the vty to the original node */
1080 vty->node = onode;
1c2facd1 1081 vty->xpath_index = orig_xpath_index;
d62a17ae 1082 }
04e64062 1083
d62a17ae 1084 /* return command status for original node */
1085 return saved_ret;
b92938a7 1086}
1087
cd40b329
CF
1088/**
1089 * Execute a given command, matching it strictly against the current node.
1090 * This mode is used when reading config files.
1091 *
1092 * @param vline Command line input, vector of char* where each element is
1093 * one input token.
1094 * @param vty The vty context in which the command should be executed.
1095 * @param cmd Pointer where the struct cmd_element* of the matched command
1096 * will be stored, if any. May be set to NULL if this info is
1097 * not needed.
1098 * @return The status of the command that has been executed or an error code
1099 * as to why no command could be executed.
1100 */
d62a17ae 1101int cmd_execute_command_strict(vector vline, struct vty *vty,
1102 const struct cmd_element **cmd)
718e3744 1103{
e3476061 1104 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, 0);
718e3744 1105}
1106
01e24c4a
QY
1107/*
1108 * Hook for preprocessing command string before executing.
1109 *
1110 * All subscribers are called with the raw command string that is to be
1111 * executed. If any changes are to be made, a new string should be allocated
1112 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1113 * is then responsible for freeing this string.
1114 *
1115 * All processing functions must be mutually exclusive in their action, i.e. if
1116 * one subscriber decides to modify the command, all others must not modify it
1117 * when called. Feeding the output of one processing command into a subsequent
1118 * one is not supported.
1119 *
1120 * This hook is intentionally internal to the command processing system.
1121 *
1122 * cmd_in
1123 * The raw command string.
1124 *
1125 * cmd_out
1126 * The result of any processing.
1127 */
1128DECLARE_HOOK(cmd_execute,
0a334343 1129 (struct vty *vty, const char *cmd_in, char **cmd_out),
01e24c4a 1130 (vty, cmd_in, cmd_out));
0a334343 1131DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
01e24c4a
QY
1132 (vty, cmd_in, cmd_out));
1133
1134/* Hook executed after a CLI command. */
0a334343 1135DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
01e24c4a 1136 (vty, cmd_exec));
0a334343 1137DEFINE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
01e24c4a
QY
1138 (vty, cmd_exec));
1139
fe6b47b9
QY
1140/*
1141 * cmd_execute hook subscriber to handle `|` actions.
1142 */
1143static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1144 char **cmd_out)
1145{
1146 /* look for `|` */
9f227e4c 1147 char *orig, *working, *token, *u;
fe6b47b9 1148 char *pipe = strstr(cmd_in, "| ");
2f272cb4 1149 int ret = 0;
fe6b47b9
QY
1150
1151 if (!pipe)
1152 return 0;
1153
1154 /* duplicate string for processing purposes, not including pipe */
1155 orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1156
1157 /* retrieve action */
1158 token = strsep(&working, " ");
4f4060f6 1159 assert(token);
fe6b47b9
QY
1160
1161 /* match result to known actions */
1162 if (strmatch(token, "include")) {
1163 /* the remaining text should be a regexp */
1164 char *regexp = working;
5d806ec6
QY
1165
1166 if (!regexp) {
1167 vty_out(vty, "%% Need a regexp to filter with\n");
2f272cb4 1168 ret = 1;
5d806ec6
QY
1169 goto fail;
1170 }
1171
fe6b47b9 1172 bool succ = vty_set_include(vty, regexp);
0a334343 1173
fe6b47b9 1174 if (!succ) {
5d806ec6 1175 vty_out(vty, "%% Bad regexp '%s'\n", regexp);
2f272cb4 1176 ret = 1;
fe6b47b9
QY
1177 goto fail;
1178 }
2cddf2ff 1179 *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
9f227e4c 1180 u = *cmd_out;
1181 strsep(&u, "|");
fe6b47b9 1182 } else {
5d806ec6 1183 vty_out(vty, "%% Unknown action '%s'\n", token);
2f272cb4 1184 ret = 1;
fe6b47b9
QY
1185 goto fail;
1186 }
1187
1188fail:
1189 XFREE(MTYPE_TMP, orig);
2f272cb4 1190 return ret;
fe6b47b9
QY
1191}
1192
1193static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1194{
0b42d81a 1195 if (vty->filter)
fe6b47b9 1196 vty_set_include(vty, NULL);
0b42d81a 1197
fe6b47b9
QY
1198 return 0;
1199}
1200
01e24c4a
QY
1201int cmd_execute(struct vty *vty, const char *cmd,
1202 const struct cmd_element **matched, int vtysh)
1203{
1204 int ret;
1205 char *cmd_out = NULL;
2f272cb4 1206 const char *cmd_exec = NULL;
01e24c4a
QY
1207 vector vline;
1208
2f272cb4
IR
1209 ret = hook_call(cmd_execute, vty, cmd, &cmd_out);
1210 if (ret) {
1211 ret = CMD_WARNING;
1212 goto free;
1213 }
1214
01e24c4a
QY
1215 cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1216
1217 vline = cmd_make_strvec(cmd_exec);
1218
1219 if (vline) {
1220 ret = cmd_execute_command(vline, vty, matched, vtysh);
1221 cmd_free_strvec(vline);
1222 } else {
1223 ret = CMD_SUCCESS;
1224 }
1225
2f272cb4 1226free:
01e24c4a
QY
1227 hook_call(cmd_execute_done, vty, cmd_exec);
1228
0a22ddfb 1229 XFREE(MTYPE_TMP, cmd_out);
01e24c4a
QY
1230
1231 return ret;
1232}
1233
1234
bed578b8 1235/**
d62a17ae 1236 * Parse one line of config, walking up the parse tree attempting to find a
1237 * match
bed578b8
DS
1238 *
1239 * @param vty The vty context in which the command should be executed.
1240 * @param cmd Pointer where the struct cmd_element* of the match command
1241 * will be stored, if any. May be set to NULL if this info is
1242 * not needed.
d62a17ae 1243 * @param use_daemon Boolean to control whether or not we match on
1244 * CMD_SUCCESS_DAEMON
bed578b8
DS
1245 * or not.
1246 * @return The status of the command that has been executed or an error code
1247 * as to why no command could be executed.
1248 */
d62a17ae 1249int command_config_read_one_line(struct vty *vty,
7ab57d19
DS
1250 const struct cmd_element **cmd,
1251 uint32_t line_num, int use_daemon)
bed578b8 1252{
d62a17ae 1253 vector vline;
d62a17ae 1254 int ret;
e3476061 1255 unsigned up_level = 0;
bed578b8 1256
d62a17ae 1257 vline = cmd_make_strvec(vty->buf);
bed578b8 1258
d62a17ae 1259 /* In case of comment line */
1260 if (vline == NULL)
1261 return CMD_SUCCESS;
bed578b8 1262
d62a17ae 1263 /* Execute configuration command : this is strict match */
1264 ret = cmd_execute_command_strict(vline, vty, cmd);
bed578b8 1265
e3476061
DL
1266 /* The logic for trying parent nodes is in cmd_execute_command_real()
1267 * since calling ->node_exit() correctly is a bit involved. This is
1268 * also the only reason CMD_NO_LEVEL_UP exists.
1269 */
1270 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1271 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1272 && ret != CMD_SUCCESS && ret != CMD_WARNING
4bb7f7c2 1273 && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
e3476061
DL
1274 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
1275 && ret != CMD_NO_LEVEL_UP)
1276 ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd,
1277 ++up_level);
1278
1279 if (ret == CMD_NO_LEVEL_UP)
1280 ret = CMD_ERR_NO_MATCH;
bed578b8 1281
b45d8ccc
DS
1282 if (ret != CMD_SUCCESS &&
1283 ret != CMD_WARNING &&
1284 ret != CMD_SUCCESS_DAEMON) {
7ab57d19
DS
1285 struct vty_error *ve = XCALLOC(MTYPE_TMP, sizeof(*ve));
1286
1287 memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
1288 ve->line_num = line_num;
1289 if (!vty->error)
1290 vty->error = list_new();
1291
1292 listnode_add(vty->error, ve);
1293 }
cbd7259d 1294
d62a17ae 1295 cmd_free_strvec(vline);
bed578b8 1296
d62a17ae 1297 return ret;
bed578b8
DS
1298}
1299
5689fe5f 1300/* Configuration make from file. */
d62a17ae 1301int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
718e3744 1302{
d62a17ae 1303 int ret, error_ret = 0;
1304 *line_num = 0;
718e3744 1305
d62a17ae 1306 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
7ab57d19 1307 ++(*line_num);
13fbc82d 1308
7ab57d19 1309 ret = command_config_read_one_line(vty, NULL, *line_num, 0);
718e3744 1310
d62a17ae 1311 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1312 && ret != CMD_ERR_NOTHING_TODO)
1313 error_ret = ret;
1314 }
5689fe5f 1315
d62a17ae 1316 if (error_ret) {
1317 return error_ret;
1318 }
5689fe5f 1319
d62a17ae 1320 return CMD_SUCCESS;
718e3744 1321}
1322
5689fe5f 1323/* Configuration from terminal */
718e3744 1324DEFUN (config_terminal,
1325 config_terminal_cmd,
dc1c13c0 1326 "configure [terminal]",
718e3744 1327 "Configuration from vty interface\n"
1328 "Configuration terminal\n")
1329{
f344c66e 1330 return vty_config_enter(vty, false, false);
718e3744 1331}
1332
1333/* Enable command */
d0bfb22c 1334DEFUN (enable,
718e3744 1335 config_enable_cmd,
1336 "enable",
1337 "Turn on privileged mode command\n")
1338{
d62a17ae 1339 /* If enable password is NULL, change to ENABLE_NODE */
1340 if ((host.enable == NULL && host.enable_encrypt == NULL)
1341 || vty->type == VTY_SHELL_SERV)
1342 vty->node = ENABLE_NODE;
1343 else
1344 vty->node = AUTH_ENABLE_NODE;
718e3744 1345
d62a17ae 1346 return CMD_SUCCESS;
718e3744 1347}
1348
1349/* Disable command */
d0bfb22c 1350DEFUN (disable,
718e3744 1351 config_disable_cmd,
1352 "disable",
1353 "Turn off privileged mode command\n")
1354{
d62a17ae 1355 if (vty->node == ENABLE_NODE)
1356 vty->node = VIEW_NODE;
1357 return CMD_SUCCESS;
718e3744 1358}
1359
1360/* Down vty node level. */
1361DEFUN (config_exit,
1362 config_exit_cmd,
1363 "exit",
1364 "Exit current mode and down to previous mode\n")
0b84f294 1365{
d62a17ae 1366 cmd_exit(vty);
1367 return CMD_SUCCESS;
1368}
1369
791ded4a
DL
1370static int root_on_exit(struct vty *vty)
1371{
1372 if (vty_shell(vty))
1373 exit(0);
1374 else
1375 vty->status = VTY_CLOSE;
1376 return 0;
1377}
1378
d62a17ae 1379void cmd_exit(struct vty *vty)
1380{
24389580 1381 struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
1c2facd1 1382
791ded4a
DL
1383 if (cnode->node_exit) {
1384 if (!cnode->node_exit(vty))
1385 return;
d62a17ae 1386 }
791ded4a
DL
1387 if (cnode->parent_node)
1388 vty->node = cnode->parent_node;
dd90823b
CS
1389 if (vty->xpath_index > 0
1390 && vty_check_node_for_xpath_decrement(vty->node, cnode->node))
1c2facd1 1391 vty->xpath_index--;
718e3744 1392}
1393
f667a580
QY
1394/* ALIAS_FIXME */
1395DEFUN (config_quit,
1396 config_quit_cmd,
1397 "quit",
1398 "Exit current mode and down to previous mode\n")
1399{
d62a17ae 1400 return config_exit(self, vty, argc, argv);
f667a580
QY
1401}
1402
d0bfb22c 1403
718e3744 1404/* End of configuration. */
1405DEFUN (config_end,
1406 config_end_cmd,
1407 "end",
efd7904e 1408 "End current mode and change to enable mode.\n")
718e3744 1409{
cf09d3ca 1410 if (vty->config) {
f344c66e 1411 vty_config_exit(vty);
d62a17ae 1412 vty->node = ENABLE_NODE;
d62a17ae 1413 }
1414 return CMD_SUCCESS;
718e3744 1415}
1416
1417/* Show version. */
1418DEFUN (show_version,
1419 show_version_cmd,
1420 "show version",
1421 SHOW_STR
1422 "Displays zebra version\n")
1423{
d62a17ae 1424 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
6b3ee3a0 1425 cmd_hostname_get() ? cmd_hostname_get() : "");
d62a17ae 1426 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
e063f271 1427#ifdef ENABLE_VERSION_BUILD_CONFIG
d62a17ae 1428 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
e063f271 1429#endif
d62a17ae 1430 return CMD_SUCCESS;
718e3744 1431}
1432
1433/* Help display function for all node. */
1434DEFUN (config_help,
1435 config_help_cmd,
1436 "help",
1437 "Description of the interactive help system\n")
1438{
d62a17ae 1439 vty_out(vty,
1440 "Quagga VTY provides advanced help feature. When you need help,\n\
61b7d449
DL
1441anytime at the command line please press '?'.\n\
1442\n\
1443If nothing matches, the help list will be empty and you must backup\n\
1444 until entering a '?' shows the available options.\n\
1445Two styles of help are provided:\n\
14461. Full help is available when you are ready to enter a\n\
1447command argument (e.g. 'show ?') and describes each possible\n\
1448argument.\n\
14492. Partial help is provided when an abbreviated argument is entered\n\
1450 and you want to know what arguments match the input\n\
1451 (e.g. 'show me?'.)\n\n");
d62a17ae 1452 return CMD_SUCCESS;
1453}
1454
1455static void permute(struct graph_node *start, struct vty *vty)
1456{
1457 static struct list *position = NULL;
1458 if (!position)
1459 position = list_new();
1460
1461 struct cmd_token *stok = start->data;
1462 struct graph_node *gnn;
1463 struct listnode *ln;
1464
1465 // recursive dfs
1466 listnode_add(position, start);
1467 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1468 struct graph_node *gn = vector_slot(start->to, i);
1469 struct cmd_token *tok = gn->data;
1470 if (tok->attr == CMD_ATTR_HIDDEN
1471 || tok->attr == CMD_ATTR_DEPRECATED)
1472 continue;
1473 else if (tok->type == END_TKN || gn == start) {
1474 vty_out(vty, " ");
1475 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1476 struct cmd_token *tt = gnn->data;
1477 if (tt->type < SPECIAL_TKN)
1478 vty_out(vty, " %s", tt->text);
1479 }
1480 if (gn == start)
1481 vty_out(vty, "...");
1482 vty_out(vty, "\n");
1483 } else {
1484 bool skip = false;
1485 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1486 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1487 if (gnn == gn) {
1488 skip = true;
1489 break;
1490 }
1491 if (!skip)
1492 permute(gn, vty);
1493 }
1494 }
1495 list_delete_node(position, listtail(position));
1496}
1497
5b96d81c
IR
1498static void print_cmd(struct vty *vty, const char *cmd)
1499{
1500 int i, j, len = strlen(cmd);
77af3a34 1501 char buf[len + 1];
5b96d81c
IR
1502 bool skip = false;
1503
1504 j = 0;
1505 for (i = 0; i < len; i++) {
1506 /* skip varname */
1507 if (cmd[i] == '$')
1508 skip = true;
1509 else if (strchr(" ()<>[]{}|", cmd[i]))
1510 skip = false;
1511
1512 if (skip)
1513 continue;
1514
1515 if (isspace(cmd[i])) {
1516 /* skip leading whitespace */
1517 if (i == 0)
1518 continue;
1519 /* skip trailing whitespace */
1520 if (i == len - 1)
1521 continue;
1522 /* skip all whitespace after opening brackets or pipe */
1523 if (strchr("(<[{|", cmd[i - 1])) {
1524 while (isspace(cmd[i + 1]))
1525 i++;
1526 continue;
1527 }
1528 /* skip repeated whitespace */
1529 if (isspace(cmd[i + 1]))
1530 continue;
1531 /* skip whitespace before closing brackets or pipe */
1532 if (strchr(")>]}|", cmd[i + 1]))
1533 continue;
1534 /* convert tabs to spaces */
1535 if (cmd[i] == '\t') {
1536 buf[j++] = ' ';
1537 continue;
1538 }
1539 }
1540
1541 buf[j++] = cmd[i];
1542 }
1543 buf[j] = 0;
1544
1545 vty_out(vty, "%s\n", buf);
1546}
1547
d62a17ae 1548int cmd_list_cmds(struct vty *vty, int do_permute)
1549{
1550 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1551
0e06eb8b
DL
1552 if (do_permute) {
1553 cmd_finalize_node(node);
d62a17ae 1554 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
0e06eb8b 1555 } else {
d62a17ae 1556 /* loop over all commands at this node */
154e9ca1 1557 const struct cmd_element *element = NULL;
d62a17ae 1558 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1559 i++)
1560 if ((element = vector_slot(node->cmd_vector, i))
1561 && element->attr != CMD_ATTR_DEPRECATED
5b96d81c
IR
1562 && element->attr != CMD_ATTR_HIDDEN) {
1563 vty_out(vty, " ");
1564 print_cmd(vty, element->string);
1565 }
d62a17ae 1566 }
1567 return CMD_SUCCESS;
718e3744 1568}
1569
0b84f294
DL
1570/* Help display function for all node. */
1571DEFUN (config_list,
1572 config_list_cmd,
1573 "list [permutations]",
1574 "Print command list\n"
1575 "Print all possible command permutations\n")
1576{
d62a17ae 1577 return cmd_list_cmds(vty, argc == 2);
0b84f294
DL
1578}
1579
a2454870
QY
1580DEFUN (show_commandtree,
1581 show_commandtree_cmd,
1582 "show commandtree [permutations]",
1583 SHOW_STR
5a1945e4
DS
1584 "Show command tree\n"
1585 "Permutations that we are interested in\n")
a2454870 1586{
d62a17ae 1587 return cmd_list_cmds(vty, argc == 3);
a2454870
QY
1588}
1589
26fbe472
QY
1590DEFUN_HIDDEN(show_cli_graph,
1591 show_cli_graph_cmd,
1592 "show cli graph",
1593 SHOW_STR
1594 "CLI reflection\n"
1595 "Dump current command space as DOT graph\n")
1596{
1597 struct cmd_node *cn = vector_slot(cmdvec, vty->node);
0e06eb8b
DL
1598 char *dot;
1599
1600 cmd_finalize_node(cn);
1601 dot = cmd_graph_dump_dot(cn->cmdgraph);
26fbe472
QY
1602
1603 vty_out(vty, "%s\n", dot);
1604 XFREE(MTYPE_TMP, dot);
1605 return CMD_SUCCESS;
1606}
1607
f806f29c 1608static int vty_write_config(struct vty *vty)
8efe88ea 1609{
d62a17ae 1610 size_t i;
1611 struct cmd_node *node;
8efe88ea 1612
f806f29c 1613 if (host.noconfig)
1614 return CMD_SUCCESS;
1615
5e6a9350
RW
1616 nb_cli_show_config_prepare(running_config, false);
1617
d62a17ae 1618 if (vty->type == VTY_TERM) {
1619 vty_out(vty, "\nCurrent configuration:\n");
1620 vty_out(vty, "!\n");
1621 }
8efe88ea 1622
ac4adef4
DL
1623 if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
1624 vty_out(vty, "! loaded from %s\n", frr_defaults_version());
d62a17ae 1625 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
ac4adef4 1626 vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
d62a17ae 1627 vty_out(vty, "!\n");
8efe88ea 1628
8685be73 1629 for (i = 0; i < vector_active(cmdvec); i++)
612c2c15
DL
1630 if ((node = vector_slot(cmdvec, i)) && node->config_write) {
1631 if ((*node->config_write)(vty))
8685be73
RW
1632 vty_out(vty, "!\n");
1633 }
8efe88ea 1634
d62a17ae 1635 if (vty->type == VTY_TERM) {
1636 vty_out(vty, "end\n");
1637 }
8efe88ea 1638
f806f29c 1639 return CMD_SUCCESS;
1640}
d862bffb 1641
f806f29c 1642static int file_write_config(struct vty *vty)
718e3744 1643{
d62a17ae 1644 int fd, dirfd;
1645 char *config_file, *slash;
1646 char *config_file_tmp = NULL;
1647 char *config_file_sav = NULL;
1648 int ret = CMD_WARNING;
1649 struct vty *file_vty;
1650 struct stat conf_stat;
1651
d62a17ae 1652 if (host.noconfig)
1653 return CMD_SUCCESS;
1654
1655 /* Check and see if we are operating under vtysh configuration */
1656 if (host.config == NULL) {
1657 vty_out(vty,
1658 "Can't save to configuration file, using vtysh.\n");
1659 return CMD_WARNING;
1660 }
1661
1662 /* Get filename. */
1663 config_file = host.config;
d0bfb22c 1664
056cfe49
DL
1665#ifndef O_DIRECTORY
1666#define O_DIRECTORY 0
1667#endif
d62a17ae 1668 slash = strrchr(config_file, '/');
1669 if (slash) {
1670 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1671 config_dir[slash - config_file] = '\0';
1672 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1673 XFREE(MTYPE_TMP, config_dir);
1674 } else
1675 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1676 /* if dirfd is invalid, directory sync fails, but we're still OK */
1677
9f73d2c9
QY
1678 size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
1679 config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
1680 strlcpy(config_file_sav, config_file, config_file_sav_sz);
1681 strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
d62a17ae 1682
1683
1684 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
fc746f1c
QY
1685 snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX",
1686 config_file);
d62a17ae 1687
1688 /* Open file to configuration write. */
1689 fd = mkstemp(config_file_tmp);
1690 if (fd < 0) {
1691 vty_out(vty, "Can't open configuration file %s.\n",
1692 config_file_tmp);
1693 goto finished;
1694 }
1695 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1696 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1697 config_file_tmp, safe_strerror(errno), errno);
1698 goto finished;
1699 }
1700
1701 /* Make vty for configuration file. */
1702 file_vty = vty_new();
1703 file_vty->wfd = fd;
1704 file_vty->type = VTY_FILE;
1705
1706 /* Config file header print. */
1707 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1708 vty_time_print(file_vty, 1);
1709 vty_out(file_vty, "!\n");
1710 vty_write_config(file_vty);
1711 vty_close(file_vty);
1712
1713 if (stat(config_file, &conf_stat) >= 0) {
1714 if (unlink(config_file_sav) != 0)
1715 if (errno != ENOENT) {
1716 vty_out(vty,
1717 "Can't unlink backup configuration file %s.\n",
1718 config_file_sav);
1719 goto finished;
1720 }
1721 if (link(config_file, config_file_sav) != 0) {
1722 vty_out(vty,
1723 "Can't backup old configuration file %s.\n",
1724 config_file_sav);
1725 goto finished;
1726 }
1727 if (dirfd >= 0)
1728 fsync(dirfd);
1729 }
1730 if (rename(config_file_tmp, config_file) != 0) {
1731 vty_out(vty, "Can't save configuration file %s.\n",
1732 config_file);
1733 goto finished;
1734 }
1735 if (dirfd >= 0)
1736 fsync(dirfd);
1737
1738 vty_out(vty, "Configuration saved to %s\n", config_file);
1739 ret = CMD_SUCCESS;
05865c90 1740
1741finished:
d62a17ae 1742 if (ret != CMD_SUCCESS)
1743 unlink(config_file_tmp);
1744 if (dirfd >= 0)
1745 close(dirfd);
1746 XFREE(MTYPE_TMP, config_file_tmp);
1747 XFREE(MTYPE_TMP, config_file_sav);
1748 return ret;
718e3744 1749}
1750
f806f29c 1751/* Write current configuration into file. */
1752
1753DEFUN (config_write,
1754 config_write_cmd,
1755 "write [<file|memory|terminal>]",
1756 "Write running configuration to memory, network, or terminal\n"
1757 "Write to configuration file\n"
1758 "Write configuration currently in memory\n"
1759 "Write configuration to terminal\n")
1760{
1761 const int idx_type = 1;
1762
1763 // if command was 'write terminal' or 'write memory'
1764 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1765 return vty_write_config(vty);
1766 }
1767
1768 return file_write_config(vty);
1769}
1770
d862bffb
QY
1771/* ALIAS_FIXME for 'write <terminal|memory>' */
1772DEFUN (show_running_config,
1773 show_running_config_cmd,
1774 "show running-config",
1775 SHOW_STR
f806f29c 1776 "running configuration (same as write terminal)\n")
d862bffb 1777{
f806f29c 1778 return vty_write_config(vty);
d862bffb 1779}
718e3744 1780
d862bffb
QY
1781/* ALIAS_FIXME for 'write file' */
1782DEFUN (copy_runningconf_startupconf,
1783 copy_runningconf_startupconf_cmd,
1784 "copy running-config startup-config",
1785 "Copy configuration\n"
1786 "Copy running config to... \n"
f806f29c 1787 "Copy running config to startup config (same as write file/memory)\n")
d862bffb 1788{
f806f29c 1789 return file_write_config(vty);
d862bffb
QY
1790}
1791/** -- **/
718e3744 1792
1793/* Write startup configuration into the terminal. */
1794DEFUN (show_startup_config,
1795 show_startup_config_cmd,
1796 "show startup-config",
1797 SHOW_STR
d0bfb22c 1798 "Contents of startup configuration\n")
718e3744 1799{
d62a17ae 1800 char buf[BUFSIZ];
1801 FILE *confp;
718e3744 1802
d62a17ae 1803 if (host.noconfig)
1804 return CMD_SUCCESS;
1805 if (host.config == NULL)
1806 return CMD_WARNING;
87f44e2f 1807
d62a17ae 1808 confp = fopen(host.config, "r");
1809 if (confp == NULL) {
1810 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1811 host.config, safe_strerror(errno));
1812 return CMD_WARNING;
1813 }
718e3744 1814
d62a17ae 1815 while (fgets(buf, BUFSIZ, confp)) {
1816 char *cp = buf;
718e3744 1817
d62a17ae 1818 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1819 cp++;
1820 *cp = '\0';
718e3744 1821
d62a17ae 1822 vty_out(vty, "%s\n", buf);
1823 }
718e3744 1824
d62a17ae 1825 fclose(confp);
718e3744 1826
d62a17ae 1827 return CMD_SUCCESS;
718e3744 1828}
1829
6b3ee3a0
MK
1830int cmd_domainname_set(const char *domainname)
1831{
1832 XFREE(MTYPE_HOST, host.domainname);
1833 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1834 return CMD_SUCCESS;
1835}
1836
1837/* Hostname configuration */
60466a63
QY
1838DEFUN(config_domainname,
1839 domainname_cmd,
1840 "domainname WORD",
1841 "Set system's domain name\n"
1842 "This system's domain name\n")
6b3ee3a0
MK
1843{
1844 struct cmd_token *word = argv[1];
1845
fefa5e0f 1846 if (!isalpha((unsigned char)word->arg[0])) {
6b3ee3a0
MK
1847 vty_out(vty, "Please specify string starting with alphabet\n");
1848 return CMD_WARNING_CONFIG_FAILED;
1849 }
1850
1851 return cmd_domainname_set(word->arg);
1852}
1853
60466a63
QY
1854DEFUN(config_no_domainname,
1855 no_domainname_cmd,
1856 "no domainname [DOMAINNAME]",
1857 NO_STR
1858 "Reset system's domain name\n"
1859 "domain name of this router\n")
6b3ee3a0
MK
1860{
1861 return cmd_domainname_set(NULL);
1862}
1863
d62a17ae 1864int cmd_hostname_set(const char *hostname)
bff9c3e9 1865{
d62a17ae 1866 XFREE(MTYPE_HOST, host.name);
1867 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1868 return CMD_SUCCESS;
bff9c3e9
DL
1869}
1870
718e3744 1871/* Hostname configuration */
d0bfb22c 1872DEFUN (config_hostname,
718e3744 1873 hostname_cmd,
1874 "hostname WORD",
1875 "Set system's network name\n"
1876 "This system's network name\n")
1877{
d62a17ae 1878 struct cmd_token *word = argv[1];
d0bfb22c 1879
fefa5e0f 1880 if (!isalnum((unsigned char)word->arg[0])) {
63e653a2
LK
1881 vty_out(vty,
1882 "Please specify string starting with alphabet or number\n");
1883 return CMD_WARNING_CONFIG_FAILED;
1884 }
1885
1886 /* With reference to RFC 1123 Section 2.1 */
1887 if (strlen(word->arg) > HOSTNAME_LEN) {
1888 vty_out(vty, "Hostname length should be less than %d chars\n",
1889 HOSTNAME_LEN);
d62a17ae 1890 return CMD_WARNING_CONFIG_FAILED;
1891 }
718e3744 1892
d62a17ae 1893 return cmd_hostname_set(word->arg);
718e3744 1894}
1895
d0bfb22c 1896DEFUN (config_no_hostname,
718e3744 1897 no_hostname_cmd,
1898 "no hostname [HOSTNAME]",
1899 NO_STR
1900 "Reset system's network name\n"
1901 "Host name of this router\n")
1902{
d62a17ae 1903 return cmd_hostname_set(NULL);
718e3744 1904}
1905
1906/* VTY interface password set. */
f412b39a
DW
1907DEFUN (config_password,
1908 password_cmd,
98463e0a 1909 "password [(8-8)] WORD",
322e2d5c 1910 "Modify the terminal connection password\n"
718e3744 1911 "Specifies a HIDDEN password will follow\n"
d0bfb22c 1912 "The password string\n")
718e3744 1913{
d62a17ae 1914 int idx_8 = 1;
1915 int idx_word = 2;
1916 if (argc == 3) // '8' was specified
1917 {
1918 if (host.password)
1919 XFREE(MTYPE_HOST, host.password);
1920 host.password = NULL;
1921 if (host.password_encrypt)
1922 XFREE(MTYPE_HOST, host.password_encrypt);
1923 host.password_encrypt =
1924 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1925 return CMD_SUCCESS;
1926 }
1927
fefa5e0f 1928 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
d62a17ae 1929 vty_out(vty,
1930 "Please specify string starting with alphanumeric\n");
1931 return CMD_WARNING_CONFIG_FAILED;
1932 }
1933
1934 if (host.password)
1935 XFREE(MTYPE_HOST, host.password);
1936 host.password = NULL;
1937
1938 if (host.encrypt) {
1939 if (host.password_encrypt)
1940 XFREE(MTYPE_HOST, host.password_encrypt);
1941 host.password_encrypt =
1942 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1943 } else
1944 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1945
1946 return CMD_SUCCESS;
718e3744 1947}
1948
322e2d5c
PM
1949/* VTY interface password delete. */
1950DEFUN (no_config_password,
1951 no_password_cmd,
1952 "no password",
1953 NO_STR
1954 "Modify the terminal connection password\n")
1955{
1956 bool warned = false;
1957
1958 if (host.password) {
eb83f7ce 1959 if (!vty_shell_serv(vty)) {
4911ca9c 1960 vty_out(vty, NO_PASSWD_CMD_WARNING);
eb83f7ce
PM
1961 warned = true;
1962 }
322e2d5c
PM
1963 XFREE(MTYPE_HOST, host.password);
1964 }
1965 host.password = NULL;
1966
1967 if (host.password_encrypt) {
eb83f7ce 1968 if (!warned && !vty_shell_serv(vty))
4911ca9c 1969 vty_out(vty, NO_PASSWD_CMD_WARNING);
322e2d5c
PM
1970 XFREE(MTYPE_HOST, host.password_encrypt);
1971 }
1972 host.password_encrypt = NULL;
1973
1974 return CMD_SUCCESS;
1975}
1976
718e3744 1977/* VTY enable password set. */
f412b39a
DW
1978DEFUN (config_enable_password,
1979 enable_password_cmd,
98463e0a 1980 "enable password [(8-8)] WORD",
718e3744 1981 "Modify enable password parameters\n"
1982 "Assign the privileged level password\n"
1983 "Specifies a HIDDEN password will follow\n"
718e3744 1984 "The HIDDEN 'enable' password string\n")
1985{
d62a17ae 1986 int idx_8 = 2;
1987 int idx_word = 3;
1988
1989 /* Crypt type is specified. */
1990 if (argc == 4) {
1991 if (argv[idx_8]->arg[0] == '8') {
1992 if (host.enable)
1993 XFREE(MTYPE_HOST, host.enable);
1994 host.enable = NULL;
1995
1996 if (host.enable_encrypt)
1997 XFREE(MTYPE_HOST, host.enable_encrypt);
1998 host.enable_encrypt =
1999 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
2000
2001 return CMD_SUCCESS;
2002 } else {
2003 vty_out(vty, "Unknown encryption type.\n");
2004 return CMD_WARNING_CONFIG_FAILED;
2005 }
2006 }
2007
fefa5e0f 2008 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
d62a17ae 2009 vty_out(vty,
2010 "Please specify string starting with alphanumeric\n");
2011 return CMD_WARNING_CONFIG_FAILED;
2012 }
2013
2014 if (host.enable)
2015 XFREE(MTYPE_HOST, host.enable);
2016 host.enable = NULL;
2017
2018 /* Plain password input. */
2019 if (host.encrypt) {
2020 if (host.enable_encrypt)
2021 XFREE(MTYPE_HOST, host.enable_encrypt);
2022 host.enable_encrypt =
2023 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
2024 } else
2025 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
2026
2027 return CMD_SUCCESS;
718e3744 2028}
2029
718e3744 2030/* VTY enable password delete. */
f412b39a
DW
2031DEFUN (no_config_enable_password,
2032 no_enable_password_cmd,
718e3744 2033 "no enable password",
2034 NO_STR
2035 "Modify enable password parameters\n"
2036 "Assign the privileged level password\n")
2037{
322e2d5c
PM
2038 bool warned = false;
2039
2040 if (host.enable) {
eb83f7ce 2041 if (!vty_shell_serv(vty)) {
4911ca9c 2042 vty_out(vty, NO_PASSWD_CMD_WARNING);
eb83f7ce
PM
2043 warned = true;
2044 }
d62a17ae 2045 XFREE(MTYPE_HOST, host.enable);
322e2d5c 2046 }
d62a17ae 2047 host.enable = NULL;
718e3744 2048
322e2d5c 2049 if (host.enable_encrypt) {
eb83f7ce 2050 if (!warned && !vty_shell_serv(vty))
4911ca9c 2051 vty_out(vty, NO_PASSWD_CMD_WARNING);
d62a17ae 2052 XFREE(MTYPE_HOST, host.enable_encrypt);
322e2d5c 2053 }
d62a17ae 2054 host.enable_encrypt = NULL;
718e3744 2055
d62a17ae 2056 return CMD_SUCCESS;
718e3744 2057}
d0bfb22c 2058
718e3744 2059DEFUN (service_password_encrypt,
2060 service_password_encrypt_cmd,
2061 "service password-encryption",
2062 "Set up miscellaneous service\n"
2063 "Enable encrypted passwords\n")
2064{
d62a17ae 2065 if (host.encrypt)
2066 return CMD_SUCCESS;
718e3744 2067
d62a17ae 2068 host.encrypt = 1;
718e3744 2069
d62a17ae 2070 if (host.password) {
2071 if (host.password_encrypt)
2072 XFREE(MTYPE_HOST, host.password_encrypt);
2073 host.password_encrypt =
2074 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
2075 }
2076 if (host.enable) {
2077 if (host.enable_encrypt)
2078 XFREE(MTYPE_HOST, host.enable_encrypt);
2079 host.enable_encrypt =
2080 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
2081 }
718e3744 2082
d62a17ae 2083 return CMD_SUCCESS;
718e3744 2084}
2085
2086DEFUN (no_service_password_encrypt,
2087 no_service_password_encrypt_cmd,
2088 "no service password-encryption",
2089 NO_STR
2090 "Set up miscellaneous service\n"
2091 "Enable encrypted passwords\n")
2092{
d62a17ae 2093 if (!host.encrypt)
2094 return CMD_SUCCESS;
718e3744 2095
d62a17ae 2096 host.encrypt = 0;
718e3744 2097
d62a17ae 2098 if (host.password_encrypt)
2099 XFREE(MTYPE_HOST, host.password_encrypt);
2100 host.password_encrypt = NULL;
718e3744 2101
d62a17ae 2102 if (host.enable_encrypt)
2103 XFREE(MTYPE_HOST, host.enable_encrypt);
2104 host.enable_encrypt = NULL;
718e3744 2105
d62a17ae 2106 return CMD_SUCCESS;
718e3744 2107}
2108
f412b39a
DW
2109DEFUN (config_terminal_length,
2110 config_terminal_length_cmd,
d0bfb22c 2111 "terminal length (0-512)",
718e3744 2112 "Set terminal line parameters\n"
2113 "Set number of lines on a screen\n"
2114 "Number of lines on screen (0 for no pausing)\n")
2115{
d62a17ae 2116 int idx_number = 2;
718e3744 2117
d62a17ae 2118 vty->lines = atoi(argv[idx_number]->arg);
718e3744 2119
d62a17ae 2120 return CMD_SUCCESS;
718e3744 2121}
2122
f412b39a
DW
2123DEFUN (config_terminal_no_length,
2124 config_terminal_no_length_cmd,
718e3744 2125 "terminal no length",
2126 "Set terminal line parameters\n"
2127 NO_STR
2128 "Set number of lines on a screen\n")
2129{
d62a17ae 2130 vty->lines = -1;
2131 return CMD_SUCCESS;
718e3744 2132}
2133
f412b39a
DW
2134DEFUN (service_terminal_length,
2135 service_terminal_length_cmd,
d0bfb22c 2136 "service terminal-length (0-512)",
718e3744 2137 "Set up miscellaneous service\n"
2138 "System wide terminal length configuration\n"
2139 "Number of lines of VTY (0 means no line control)\n")
2140{
d62a17ae 2141 int idx_number = 2;
718e3744 2142
d62a17ae 2143 host.lines = atoi(argv[idx_number]->arg);
718e3744 2144
d62a17ae 2145 return CMD_SUCCESS;
718e3744 2146}
2147
f412b39a
DW
2148DEFUN (no_service_terminal_length,
2149 no_service_terminal_length_cmd,
d0bfb22c 2150 "no service terminal-length [(0-512)]",
718e3744 2151 NO_STR
2152 "Set up miscellaneous service\n"
2153 "System wide terminal length configuration\n"
2154 "Number of lines of VTY (0 means no line control)\n")
2155{
d62a17ae 2156 host.lines = -1;
2157 return CMD_SUCCESS;
718e3744 2158}
2159
2885f72d 2160DEFUN_HIDDEN (do_echo,
d0bfb22c
QY
2161 echo_cmd,
2162 "echo MESSAGE...",
2163 "Echo a message back to the vty\n"
2164 "The message to echo\n")
2885f72d 2165{
d62a17ae 2166 char *message;
2885f72d 2167
d62a17ae 2168 vty_out(vty, "%s\n",
2169 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2170 if (message)
2171 XFREE(MTYPE_TMP, message);
2172 return CMD_SUCCESS;
2885f72d 2173}
2174
274a4a44 2175DEFUN (config_logmsg,
2176 config_logmsg_cmd,
199d90a1 2177 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
274a4a44 2178 "Send a message to enabled logging destinations\n"
2179 LOG_LEVEL_DESC
2180 "The message to send\n")
2181{
d62a17ae 2182 int idx_log_level = 1;
2183 int idx_message = 2;
2184 int level;
2185 char *message;
274a4a44 2186
0bdeb5e5
DL
2187 level = log_level_match(argv[idx_log_level]->arg);
2188 if (level == ZLOG_DISABLED)
d62a17ae 2189 return CMD_ERR_NO_MATCH;
274a4a44 2190
d62a17ae 2191 zlog(level, "%s",
2192 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2193 if (message)
2194 XFREE(MTYPE_TMP, message);
65efcfce 2195
d62a17ae 2196 return CMD_SUCCESS;
274a4a44 2197}
2198
9eed278b
DL
2199DEFUN (debug_memstats,
2200 debug_memstats_cmd,
2201 "[no] debug memstats-at-exit",
2202 NO_STR
2203 DEBUG_STR
2204 "Print memory type statistics at exit\n")
2205{
2206 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2207 return CMD_SUCCESS;
2208}
2209
d62a17ae 2210int cmd_banner_motd_file(const char *file)
7cfc61d3 2211{
d62a17ae 2212 int success = CMD_SUCCESS;
2213 char p[PATH_MAX];
2214 char *rpath;
2215 char *in;
7cfc61d3 2216
d62a17ae 2217 rpath = realpath(file, p);
2218 if (!rpath)
2219 return CMD_ERR_NO_FILE;
2220 in = strstr(rpath, SYSCONFDIR);
2221 if (in == rpath) {
0a22ddfb 2222 XFREE(MTYPE_HOST, host.motdfile);
d62a17ae 2223 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2224 } else
2225 success = CMD_WARNING_CONFIG_FAILED;
1ee08155 2226
d62a17ae 2227 return success;
7cfc61d3
DS
2228}
2229
19d61463
DA
2230void cmd_banner_motd_line(const char *line)
2231{
e1b36e13 2232 XFREE(MTYPE_HOST, host.motd);
19d61463
DA
2233 host.motd = XSTRDUP(MTYPE_HOST, line);
2234}
2235
3b0c5d9a 2236DEFUN (banner_motd_file,
2237 banner_motd_file_cmd,
4d833e55 2238 "banner motd file FILE",
3b0c5d9a 2239 "Set banner\n"
2240 "Banner for motd\n"
2241 "Banner from a file\n"
2242 "Filename\n")
2243{
d62a17ae 2244 int idx_file = 3;
2245 const char *filename = argv[idx_file]->arg;
2246 int cmd = cmd_banner_motd_file(filename);
1ee08155 2247
d62a17ae 2248 if (cmd == CMD_ERR_NO_FILE)
2249 vty_out(vty, "%s does not exist", filename);
2250 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2251 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
1ee08155 2252
d62a17ae 2253 return cmd;
3b0c5d9a 2254}
718e3744 2255
19d61463
DA
2256DEFUN (banner_motd_line,
2257 banner_motd_line_cmd,
2258 "banner motd line LINE...",
2259 "Set banner\n"
2260 "Banner for motd\n"
2261 "Banner from an input\n"
2262 "Text\n")
2263{
2264 int idx = 0;
2265 char *motd;
2266
2267 argv_find(argv, argc, "LINE", &idx);
2268 motd = argv_concat(argv, argc, idx);
2269
2270 cmd_banner_motd_line(motd);
2271 XFREE(MTYPE_TMP, motd);
2272
2273 return CMD_SUCCESS;
2274}
2275
718e3744 2276DEFUN (banner_motd_default,
2277 banner_motd_default_cmd,
2278 "banner motd default",
2279 "Set banner string\n"
2280 "Strings for motd\n"
2281 "Default string\n")
2282{
19d61463 2283 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
d62a17ae 2284 return CMD_SUCCESS;
718e3744 2285}
2286
2287DEFUN (no_banner_motd,
2288 no_banner_motd_cmd,
2289 "no banner motd",
2290 NO_STR
2291 "Set banner string\n"
2292 "Strings for motd\n")
2293{
d62a17ae 2294 host.motd = NULL;
2295 if (host.motdfile)
2296 XFREE(MTYPE_HOST, host.motdfile);
2297 host.motdfile = NULL;
2298 return CMD_SUCCESS;
718e3744 2299}
2300
c2278962 2301int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc)
a83a5331 2302{
cf6c83e7
QY
2303 const struct cmd_node *node;
2304 const struct cmd_element *cli;
2305 vector clis;
a83a5331 2306
68912a20
QY
2307 regex_t exp = {};
2308
767f67a4 2309 char *pattern = argv_concat(argv, argc, 1);
68912a20 2310 int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
767f67a4 2311 XFREE(MTYPE_TMP, pattern);
68912a20
QY
2312
2313 if (cr != 0) {
2314 switch (cr) {
2315 case REG_BADBR:
2316 vty_out(vty, "%% Invalid {...} expression\n");
2317 break;
2318 case REG_BADRPT:
2319 vty_out(vty, "%% Bad repetition operator\n");
2320 break;
2321 case REG_BADPAT:
2322 vty_out(vty, "%% Regex syntax error\n");
2323 break;
2324 case REG_ECOLLATE:
2325 vty_out(vty, "%% Invalid collating element\n");
2326 break;
2327 case REG_ECTYPE:
2328 vty_out(vty, "%% Invalid character class name\n");
2329 break;
2330 case REG_EESCAPE:
2331 vty_out(vty,
2332 "%% Regex ended with escape character (\\)\n");
2333 break;
2334 case REG_ESUBREG:
2335 vty_out(vty,
2336 "%% Invalid number in \\digit construction\n");
2337 break;
2338 case REG_EBRACK:
2339 vty_out(vty, "%% Unbalanced square brackets\n");
2340 break;
2341 case REG_EPAREN:
2342 vty_out(vty, "%% Unbalanced parentheses\n");
2343 break;
2344 case REG_EBRACE:
2345 vty_out(vty, "%% Unbalanced braces\n");
2346 break;
2347 case REG_ERANGE:
2348 vty_out(vty,
2349 "%% Invalid endpoint in range expression\n");
2350 break;
2351 case REG_ESPACE:
2352 vty_out(vty, "%% Failed to compile (out of memory)\n");
2353 break;
2354 }
2355
2356 goto done;
2357 }
2358
2359
a83a5331 2360 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
cf6c83e7 2361 node = vector_slot(cmdvec, i);
a83a5331
QY
2362 if (!node)
2363 continue;
cf6c83e7 2364 clis = node->cmd_vector;
a83a5331 2365 for (unsigned int j = 0; j < vector_active(clis); j++) {
cf6c83e7 2366 cli = vector_slot(clis, j);
68912a20 2367
5b96d81c
IR
2368 if (regexec(&exp, cli->string, 0, NULL, 0) == 0) {
2369 vty_out(vty, " (%s) ", node->name);
2370 print_cmd(vty, cli->string);
2371 }
a83a5331
QY
2372 }
2373 }
2374
68912a20
QY
2375done:
2376 regfree(&exp);
a83a5331
QY
2377 return CMD_SUCCESS;
2378}
2379
c2278962
IR
2380DEFUN(find,
2381 find_cmd,
2382 "find REGEX...",
2383 "Find CLI command matching a regular expression\n"
2384 "Search pattern (POSIX regex)\n")
2385{
2386 return cmd_find_cmds(vty, argv, argc);
2387}
2388
fa22080d 2389#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
8be973f5
DL
2390DEFUN(script, script_cmd, "script SCRIPT FUNCTION",
2391 "Test command - execute a function in a script\n"
2392 "Script name (same as filename in /etc/frr/scripts/)\n"
2393 "Function name (in the script)\n")
5f98c815 2394{
3d19ffc5 2395 struct prefix p;
45e56ec4
DS
2396
2397 (void)str2prefix("1.2.3.4/24", &p);
a71096fb 2398 struct frrscript *fs = frrscript_new(argv[1]->arg);
5f98c815 2399
596b44af
DL
2400 if (frrscript_load(fs, argv[2]->arg, NULL)) {
2401 vty_out(vty,
8be973f5 2402 "/etc/frr/scripts/%s.lua or function '%s' not found\n",
596b44af 2403 argv[1]->arg, argv[2]->arg);
3d19ffc5 2404 }
5f98c815 2405
a71096fb
DL
2406 int ret = frrscript_call(fs, argv[2]->arg, ("p", &p));
2407 char buf[40];
2408 prefix2str(&p, buf, sizeof(buf));
2409 vty_out(vty, "p: %s\n", buf);
2410 vty_out(vty, "Script result: %d\n", ret);
5f98c815 2411
64d457d7 2412 frrscript_delete(fs);
ad6e9b85 2413
5f98c815
QY
2414 return CMD_SUCCESS;
2415}
2416#endif
2417
718e3744 2418/* Set config filename. Called from vty.c */
d62a17ae 2419void host_config_set(const char *filename)
718e3744 2420{
0a22ddfb 2421 XFREE(MTYPE_HOST, host.config);
d62a17ae 2422 host.config = XSTRDUP(MTYPE_HOST, filename);
718e3744 2423}
2424
d62a17ae 2425const char *host_config_get(void)
57387fb2 2426{
d62a17ae 2427 return host.config;
57387fb2
CF
2428}
2429
d62a17ae 2430void install_default(enum node_type node)
718e3744 2431{
01485adb
DL
2432 _install_element(node, &config_exit_cmd);
2433 _install_element(node, &config_quit_cmd);
2434 _install_element(node, &config_end_cmd);
2435 _install_element(node, &config_help_cmd);
2436 _install_element(node, &config_list_cmd);
2437 _install_element(node, &show_cli_graph_cmd);
2438 _install_element(node, &find_cmd);
718e3744 2439
01485adb
DL
2440 _install_element(node, &config_write_cmd);
2441 _install_element(node, &show_running_config_cmd);
7f059ea6 2442
01485adb 2443 _install_element(node, &autocomplete_cmd);
1c2facd1
RW
2444
2445 nb_cli_install_default(node);
718e3744 2446}
2447
87f44e2f
DL
2448/* Initialize command interface. Install basic nodes and commands.
2449 *
2450 * terminal = 0 -- vtysh / no logging, no config control
2451 * terminal = 1 -- normal daemon
9473e340 2452 * terminal = -1 -- watchfrr / no logging, but minimal config control */
d62a17ae 2453void cmd_init(int terminal)
2454{
419cd5a0
MK
2455 struct utsname names;
2456
419cd5a0 2457 uname(&names);
d62a17ae 2458 qobj_init();
2459
fe6b47b9
QY
2460 /* register command preprocessors */
2461 hook_register(cmd_execute, handle_pipe_action);
2462 hook_register(cmd_execute_done, handle_pipe_action_done);
2463
d62a17ae 2464 varhandlers = list_new();
2465
2466 /* Allocate initial top vector of commands. */
2467 cmdvec = vector_init(VECTOR_MIN_SIZE);
2468
2469 /* Default host value settings. */
419cd5a0
MK
2470 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2471#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
6b3ee3a0
MK
2472 if ((strcmp(names.domainname, "(none)") == 0))
2473 host.domainname = NULL;
2474 else
2475 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
419cd5a0
MK
2476#else
2477 host.domainname = NULL;
2478#endif
d62a17ae 2479 host.password = NULL;
2480 host.enable = NULL;
d62a17ae 2481 host.config = NULL;
2482 host.noconfig = (terminal < 0);
2483 host.lines = -1;
19d61463 2484 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
d62a17ae 2485 host.motdfile = NULL;
2486
2487 /* Install top nodes. */
612c2c15
DL
2488 install_node(&view_node);
2489 install_node(&enable_node);
2490 install_node(&auth_node);
2491 install_node(&auth_enable_node);
2492 install_node(&config_node);
d62a17ae 2493
2494 /* Each node's basic commands. */
2495 install_element(VIEW_NODE, &show_version_cmd);
a83a5331
QY
2496 install_element(ENABLE_NODE, &show_startup_config_cmd);
2497
d62a17ae 2498 if (terminal) {
85a6806d
MS
2499 install_element(ENABLE_NODE, &debug_memstats_cmd);
2500
d62a17ae 2501 install_element(VIEW_NODE, &config_list_cmd);
2502 install_element(VIEW_NODE, &config_exit_cmd);
2503 install_element(VIEW_NODE, &config_quit_cmd);
2504 install_element(VIEW_NODE, &config_help_cmd);
2505 install_element(VIEW_NODE, &config_enable_cmd);
2506 install_element(VIEW_NODE, &config_terminal_length_cmd);
2507 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
d62a17ae 2508 install_element(VIEW_NODE, &show_commandtree_cmd);
2509 install_element(VIEW_NODE, &echo_cmd);
2510 install_element(VIEW_NODE, &autocomplete_cmd);
a83a5331 2511 install_element(VIEW_NODE, &find_cmd);
fa22080d 2512#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
3d19ffc5
QY
2513 install_element(VIEW_NODE, &script_cmd);
2514#endif
2515
d62a17ae 2516
d62a17ae 2517 install_element(ENABLE_NODE, &config_end_cmd);
2518 install_element(ENABLE_NODE, &config_disable_cmd);
2519 install_element(ENABLE_NODE, &config_terminal_cmd);
2520 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2521 install_element(ENABLE_NODE, &config_write_cmd);
2522 install_element(ENABLE_NODE, &show_running_config_cmd);
d62a17ae 2523 install_element(ENABLE_NODE, &config_logmsg_cmd);
a83a5331 2524
d62a17ae 2525 install_default(CONFIG_NODE);
2526
2527 thread_cmd_init();
2528 workqueue_cmd_init();
2529 hash_cmd_init();
2530 }
2531
2532 install_element(CONFIG_NODE, &hostname_cmd);
2533 install_element(CONFIG_NODE, &no_hostname_cmd);
6b3ee3a0
MK
2534 install_element(CONFIG_NODE, &domainname_cmd);
2535 install_element(CONFIG_NODE, &no_domainname_cmd);
d62a17ae 2536
2537 if (terminal > 0) {
0bdeb5e5
DL
2538 full_cli = true;
2539
85a6806d
MS
2540 install_element(CONFIG_NODE, &debug_memstats_cmd);
2541
d62a17ae 2542 install_element(CONFIG_NODE, &password_cmd);
322e2d5c 2543 install_element(CONFIG_NODE, &no_password_cmd);
d62a17ae 2544 install_element(CONFIG_NODE, &enable_password_cmd);
2545 install_element(CONFIG_NODE, &no_enable_password_cmd);
2546
d62a17ae 2547 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2548 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2549 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2550 install_element(CONFIG_NODE, &banner_motd_file_cmd);
19d61463 2551 install_element(CONFIG_NODE, &banner_motd_line_cmd);
d62a17ae 2552 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2553 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2554 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2555
0bdeb5e5 2556 log_cmd_init();
d62a17ae 2557 vrf_install_commands();
2558 }
af2567b6
DL
2559
2560#ifdef DEV_BUILD
d62a17ae 2561 grammar_sandbox_init();
af2567b6 2562#endif
718e3744 2563}
228da428 2564
4d762f26 2565void cmd_terminate(void)
d62a17ae 2566{
2567 struct cmd_node *cmd_node;
2568
5d806ec6
QY
2569 hook_unregister(cmd_execute, handle_pipe_action);
2570 hook_unregister(cmd_execute_done, handle_pipe_action_done);
2571
d62a17ae 2572 if (cmdvec) {
2573 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2574 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2575 // deleting the graph delets the cmd_element as
2576 // well
2577 graph_delete_graph(cmd_node->cmdgraph);
2578 vector_free(cmd_node->cmd_vector);
2579 hash_clean(cmd_node->cmd_hash, NULL);
2580 hash_free(cmd_node->cmd_hash);
2581 cmd_node->cmd_hash = NULL;
2582 }
2583
2584 vector_free(cmdvec);
2585 cmdvec = NULL;
2586 }
2587
0a22ddfb
QY
2588 XFREE(MTYPE_HOST, host.name);
2589 XFREE(MTYPE_HOST, host.domainname);
2590 XFREE(MTYPE_HOST, host.password);
2591 XFREE(MTYPE_HOST, host.password_encrypt);
2592 XFREE(MTYPE_HOST, host.enable);
2593 XFREE(MTYPE_HOST, host.enable_encrypt);
0a22ddfb
QY
2594 XFREE(MTYPE_HOST, host.motdfile);
2595 XFREE(MTYPE_HOST, host.config);
19d61463 2596 XFREE(MTYPE_HOST, host.motd);
d62a17ae 2597
6a154c88 2598 list_delete(&varhandlers);
d62a17ae 2599 qobj_finish();
228da428 2600}