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