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