]> git.proxmox.com Git - mirror_frr.git/blame - lib/command.c
*: introduce DEFPY_YANG & friends
[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);
907 }
a6233bfc 908
d62a17ae 909 ret = matched_element->func(matched_element, vty, argc, argv);
a6233bfc 910 }
d62a17ae 911
912 // delete list and cmd_token's in it
6a154c88 913 list_delete(&argv_list);
d62a17ae 914 XFREE(MTYPE_TMP, argv);
915
916 return ret;
718e3744 917}
918
cd40b329
CF
919/**
920 * Execute a given command, handling things like "do ..." and checking
921 * whether the given command might apply at a parent node if doesn't
922 * apply for the current node.
923 *
924 * @param vline Command line input, vector of char* where each element is
925 * one input token.
926 * @param vty The vty context in which the command should be executed.
927 * @param cmd Pointer where the struct cmd_element of the matched command
928 * will be stored, if any. May be set to NULL if this info is
929 * not needed.
930 * @param vtysh If set != 0, don't lookup the command at parent nodes.
931 * @return The status of the command that has been executed or an error code
932 * as to why no command could be executed.
933 */
d62a17ae 934int cmd_execute_command(vector vline, struct vty *vty,
935 const struct cmd_element **cmd, int vtysh)
17aca20b 936{
d62a17ae 937 int ret, saved_ret = 0;
938 enum node_type onode, try_node;
1c2facd1 939 int orig_xpath_index;
eda031f6 940
d62a17ae 941 onode = try_node = vty->node;
1c2facd1 942 orig_xpath_index = vty->xpath_index;
b92938a7 943
d62a17ae 944 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
945 vector shifted_vline;
946 unsigned int index;
b92938a7 947
d62a17ae 948 vty->node = ENABLE_NODE;
1c2facd1 949 vty->xpath_index = 0;
d62a17ae 950 /* We can try it on enable node, cos' the vty is authenticated
951 */
b92938a7 952
d62a17ae 953 shifted_vline = vector_init(vector_count(vline));
954 /* use memcpy? */
955 for (index = 1; index < vector_active(vline); index++)
956 vector_set_index(shifted_vline, index - 1,
957 vector_lookup(vline, index));
b92938a7 958
d62a17ae 959 ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
960 vty, cmd);
b92938a7 961
d62a17ae 962 vector_free(shifted_vline);
963 vty->node = onode;
1c2facd1 964 vty->xpath_index = orig_xpath_index;
d62a17ae 965 return ret;
966 }
b92938a7 967
d62a17ae 968 saved_ret = ret =
969 cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd);
b92938a7 970
d62a17ae 971 if (vtysh)
972 return saved_ret;
87d683b0 973
825d5fbf
CS
974 if (ret != CMD_SUCCESS && ret != CMD_WARNING
975 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
d62a17ae 976 /* This assumes all nodes above CONFIG_NODE are childs of
977 * CONFIG_NODE */
978 while (vty->node > CONFIG_NODE) {
979 try_node = node_parent(try_node);
980 vty->node = try_node;
1c2facd1
RW
981 if (vty->xpath_index > 0)
982 vty->xpath_index--;
d62a17ae 983 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
984 vty, cmd);
825d5fbf
CS
985 if (ret == CMD_SUCCESS || ret == CMD_WARNING
986 || ret == CMD_NOT_MY_INSTANCE
987 || ret == CMD_WARNING_CONFIG_FAILED)
d62a17ae 988 return ret;
989 }
990 /* no command succeeded, reset the vty to the original node */
991 vty->node = onode;
1c2facd1 992 vty->xpath_index = orig_xpath_index;
d62a17ae 993 }
04e64062 994
d62a17ae 995 /* return command status for original node */
996 return saved_ret;
b92938a7 997}
998
cd40b329
CF
999/**
1000 * Execute a given command, matching it strictly against the current node.
1001 * This mode is used when reading config files.
1002 *
1003 * @param vline Command line input, vector of char* where each element is
1004 * one input token.
1005 * @param vty The vty context in which the command should be executed.
1006 * @param cmd Pointer where the struct cmd_element* of the matched command
1007 * will be stored, if any. May be set to NULL if this info is
1008 * not needed.
1009 * @return The status of the command that has been executed or an error code
1010 * as to why no command could be executed.
1011 */
d62a17ae 1012int cmd_execute_command_strict(vector vline, struct vty *vty,
1013 const struct cmd_element **cmd)
718e3744 1014{
d62a17ae 1015 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
718e3744 1016}
1017
01e24c4a
QY
1018/*
1019 * Hook for preprocessing command string before executing.
1020 *
1021 * All subscribers are called with the raw command string that is to be
1022 * executed. If any changes are to be made, a new string should be allocated
1023 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1024 * is then responsible for freeing this string.
1025 *
1026 * All processing functions must be mutually exclusive in their action, i.e. if
1027 * one subscriber decides to modify the command, all others must not modify it
1028 * when called. Feeding the output of one processing command into a subsequent
1029 * one is not supported.
1030 *
1031 * This hook is intentionally internal to the command processing system.
1032 *
1033 * cmd_in
1034 * The raw command string.
1035 *
1036 * cmd_out
1037 * The result of any processing.
1038 */
1039DECLARE_HOOK(cmd_execute,
0a334343 1040 (struct vty *vty, const char *cmd_in, char **cmd_out),
01e24c4a 1041 (vty, cmd_in, cmd_out));
0a334343 1042DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
01e24c4a
QY
1043 (vty, cmd_in, cmd_out));
1044
1045/* Hook executed after a CLI command. */
0a334343 1046DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
01e24c4a 1047 (vty, cmd_exec));
0a334343 1048DEFINE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
01e24c4a
QY
1049 (vty, cmd_exec));
1050
fe6b47b9
QY
1051/*
1052 * cmd_execute hook subscriber to handle `|` actions.
1053 */
1054static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1055 char **cmd_out)
1056{
1057 /* look for `|` */
9f227e4c 1058 char *orig, *working, *token, *u;
fe6b47b9
QY
1059 char *pipe = strstr(cmd_in, "| ");
1060
1061 if (!pipe)
1062 return 0;
1063
1064 /* duplicate string for processing purposes, not including pipe */
1065 orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1066
1067 /* retrieve action */
1068 token = strsep(&working, " ");
4f4060f6 1069 assert(token);
fe6b47b9
QY
1070
1071 /* match result to known actions */
1072 if (strmatch(token, "include")) {
1073 /* the remaining text should be a regexp */
1074 char *regexp = working;
5d806ec6
QY
1075
1076 if (!regexp) {
1077 vty_out(vty, "%% Need a regexp to filter with\n");
1078 goto fail;
1079 }
1080
fe6b47b9 1081 bool succ = vty_set_include(vty, regexp);
0a334343 1082
fe6b47b9 1083 if (!succ) {
5d806ec6 1084 vty_out(vty, "%% Bad regexp '%s'\n", regexp);
fe6b47b9
QY
1085 goto fail;
1086 }
2cddf2ff 1087 *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
9f227e4c 1088 u = *cmd_out;
1089 strsep(&u, "|");
fe6b47b9 1090 } else {
5d806ec6 1091 vty_out(vty, "%% Unknown action '%s'\n", token);
fe6b47b9
QY
1092 goto fail;
1093 }
1094
1095fail:
1096 XFREE(MTYPE_TMP, orig);
1097 return 0;
1098}
1099
1100static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1101{
0b42d81a 1102 if (vty->filter)
fe6b47b9 1103 vty_set_include(vty, NULL);
0b42d81a 1104
fe6b47b9
QY
1105 return 0;
1106}
1107
01e24c4a
QY
1108int cmd_execute(struct vty *vty, const char *cmd,
1109 const struct cmd_element **matched, int vtysh)
1110{
1111 int ret;
1112 char *cmd_out = NULL;
1113 const char *cmd_exec;
1114 vector vline;
1115
1116 hook_call(cmd_execute, vty, cmd, &cmd_out);
1117 cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1118
1119 vline = cmd_make_strvec(cmd_exec);
1120
1121 if (vline) {
1122 ret = cmd_execute_command(vline, vty, matched, vtysh);
1123 cmd_free_strvec(vline);
1124 } else {
1125 ret = CMD_SUCCESS;
1126 }
1127
1128 hook_call(cmd_execute_done, vty, cmd_exec);
1129
0a22ddfb 1130 XFREE(MTYPE_TMP, cmd_out);
01e24c4a
QY
1131
1132 return ret;
1133}
1134
1135
bed578b8 1136/**
d62a17ae 1137 * Parse one line of config, walking up the parse tree attempting to find a
1138 * match
bed578b8
DS
1139 *
1140 * @param vty The vty context in which the command should be executed.
1141 * @param cmd Pointer where the struct cmd_element* of the match command
1142 * will be stored, if any. May be set to NULL if this info is
1143 * not needed.
d62a17ae 1144 * @param use_daemon Boolean to control whether or not we match on
1145 * CMD_SUCCESS_DAEMON
bed578b8
DS
1146 * or not.
1147 * @return The status of the command that has been executed or an error code
1148 * as to why no command could be executed.
1149 */
d62a17ae 1150int command_config_read_one_line(struct vty *vty,
7ab57d19
DS
1151 const struct cmd_element **cmd,
1152 uint32_t line_num, int use_daemon)
bed578b8 1153{
d62a17ae 1154 vector vline;
d62a17ae 1155 int ret;
bed578b8 1156
d62a17ae 1157 vline = cmd_make_strvec(vty->buf);
bed578b8 1158
d62a17ae 1159 /* In case of comment line */
1160 if (vline == NULL)
1161 return CMD_SUCCESS;
bed578b8 1162
d62a17ae 1163 /* Execute configuration command : this is strict match */
1164 ret = cmd_execute_command_strict(vline, vty, cmd);
bed578b8 1165
d62a17ae 1166 // Climb the tree and try the command again at each node
1167 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1168 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1169 && ret != CMD_SUCCESS && ret != CMD_WARNING
996c9314 1170 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
d62a17ae 1171 && vty->node != CONFIG_NODE) {
1c2facd1
RW
1172 int saved_node = vty->node;
1173 int saved_xpath_index = vty->xpath_index;
bed578b8 1174
d62a17ae 1175 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1176 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1177 && ret != CMD_SUCCESS && ret != CMD_WARNING
1178 && vty->node > CONFIG_NODE) {
1179 vty->node = node_parent(vty->node);
1c2facd1
RW
1180 if (vty->xpath_index > 0)
1181 vty->xpath_index--;
d62a17ae 1182 ret = cmd_execute_command_strict(vline, vty, cmd);
1183 }
bed578b8 1184
d62a17ae 1185 // If climbing the tree did not work then ignore the command and
1186 // stay at the same node
1187 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1188 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1189 && ret != CMD_SUCCESS && ret != CMD_WARNING) {
1190 vty->node = saved_node;
1c2facd1 1191 vty->xpath_index = saved_xpath_index;
d62a17ae 1192 }
1193 }
bed578b8 1194
b45d8ccc
DS
1195 if (ret != CMD_SUCCESS &&
1196 ret != CMD_WARNING &&
1197 ret != CMD_SUCCESS_DAEMON) {
7ab57d19
DS
1198 struct vty_error *ve = XCALLOC(MTYPE_TMP, sizeof(*ve));
1199
1200 memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
1201 ve->line_num = line_num;
1202 if (!vty->error)
1203 vty->error = list_new();
1204
1205 listnode_add(vty->error, ve);
1206 }
cbd7259d 1207
d62a17ae 1208 cmd_free_strvec(vline);
bed578b8 1209
d62a17ae 1210 return ret;
bed578b8
DS
1211}
1212
5689fe5f 1213/* Configuration make from file. */
d62a17ae 1214int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
718e3744 1215{
d62a17ae 1216 int ret, error_ret = 0;
1217 *line_num = 0;
718e3744 1218
d62a17ae 1219 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
7ab57d19 1220 ++(*line_num);
13fbc82d 1221
7ab57d19 1222 ret = command_config_read_one_line(vty, NULL, *line_num, 0);
718e3744 1223
d62a17ae 1224 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1225 && ret != CMD_ERR_NOTHING_TODO)
1226 error_ret = ret;
1227 }
5689fe5f 1228
d62a17ae 1229 if (error_ret) {
1230 return error_ret;
1231 }
5689fe5f 1232
d62a17ae 1233 return CMD_SUCCESS;
718e3744 1234}
1235
5689fe5f 1236/* Configuration from terminal */
718e3744 1237DEFUN (config_terminal,
1238 config_terminal_cmd,
dc1c13c0 1239 "configure [terminal]",
718e3744 1240 "Configuration from vty interface\n"
1241 "Configuration terminal\n")
1242{
f344c66e 1243 return vty_config_enter(vty, false, false);
718e3744 1244}
1245
1246/* Enable command */
d0bfb22c 1247DEFUN (enable,
718e3744 1248 config_enable_cmd,
1249 "enable",
1250 "Turn on privileged mode command\n")
1251{
d62a17ae 1252 /* If enable password is NULL, change to ENABLE_NODE */
1253 if ((host.enable == NULL && host.enable_encrypt == NULL)
1254 || vty->type == VTY_SHELL_SERV)
1255 vty->node = ENABLE_NODE;
1256 else
1257 vty->node = AUTH_ENABLE_NODE;
718e3744 1258
d62a17ae 1259 return CMD_SUCCESS;
718e3744 1260}
1261
1262/* Disable command */
d0bfb22c 1263DEFUN (disable,
718e3744 1264 config_disable_cmd,
1265 "disable",
1266 "Turn off privileged mode command\n")
1267{
d62a17ae 1268 if (vty->node == ENABLE_NODE)
1269 vty->node = VIEW_NODE;
1270 return CMD_SUCCESS;
718e3744 1271}
1272
1273/* Down vty node level. */
1274DEFUN (config_exit,
1275 config_exit_cmd,
1276 "exit",
1277 "Exit current mode and down to previous mode\n")
0b84f294 1278{
d62a17ae 1279 cmd_exit(vty);
1280 return CMD_SUCCESS;
1281}
1282
791ded4a
DL
1283static int root_on_exit(struct vty *vty)
1284{
1285 if (vty_shell(vty))
1286 exit(0);
1287 else
1288 vty->status = VTY_CLOSE;
1289 return 0;
1290}
1291
d62a17ae 1292void cmd_exit(struct vty *vty)
1293{
24389580 1294 struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
1c2facd1 1295
791ded4a
DL
1296 if (cnode->node_exit) {
1297 if (!cnode->node_exit(vty))
1298 return;
d62a17ae 1299 }
791ded4a
DL
1300 if (cnode->parent_node)
1301 vty->node = cnode->parent_node;
1c2facd1
RW
1302 if (vty->xpath_index > 0)
1303 vty->xpath_index--;
718e3744 1304}
1305
f667a580
QY
1306/* ALIAS_FIXME */
1307DEFUN (config_quit,
1308 config_quit_cmd,
1309 "quit",
1310 "Exit current mode and down to previous mode\n")
1311{
d62a17ae 1312 return config_exit(self, vty, argc, argv);
f667a580
QY
1313}
1314
d0bfb22c 1315
718e3744 1316/* End of configuration. */
1317DEFUN (config_end,
1318 config_end_cmd,
1319 "end",
efd7904e 1320 "End current mode and change to enable mode.\n")
718e3744 1321{
cf09d3ca 1322 if (vty->config) {
f344c66e 1323 vty_config_exit(vty);
d62a17ae 1324 vty->node = ENABLE_NODE;
d62a17ae 1325 }
1326 return CMD_SUCCESS;
718e3744 1327}
1328
1329/* Show version. */
1330DEFUN (show_version,
1331 show_version_cmd,
1332 "show version",
1333 SHOW_STR
1334 "Displays zebra version\n")
1335{
d62a17ae 1336 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
6b3ee3a0 1337 cmd_hostname_get() ? cmd_hostname_get() : "");
d62a17ae 1338 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
e063f271 1339#ifdef ENABLE_VERSION_BUILD_CONFIG
d62a17ae 1340 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
e063f271 1341#endif
d62a17ae 1342 return CMD_SUCCESS;
718e3744 1343}
1344
1345/* Help display function for all node. */
1346DEFUN (config_help,
1347 config_help_cmd,
1348 "help",
1349 "Description of the interactive help system\n")
1350{
d62a17ae 1351 vty_out(vty,
1352 "Quagga VTY provides advanced help feature. When you need help,\n\
61b7d449
DL
1353anytime at the command line please press '?'.\n\
1354\n\
1355If nothing matches, the help list will be empty and you must backup\n\
1356 until entering a '?' shows the available options.\n\
1357Two styles of help are provided:\n\
13581. Full help is available when you are ready to enter a\n\
1359command argument (e.g. 'show ?') and describes each possible\n\
1360argument.\n\
13612. Partial help is provided when an abbreviated argument is entered\n\
1362 and you want to know what arguments match the input\n\
1363 (e.g. 'show me?'.)\n\n");
d62a17ae 1364 return CMD_SUCCESS;
1365}
1366
1367static void permute(struct graph_node *start, struct vty *vty)
1368{
1369 static struct list *position = NULL;
1370 if (!position)
1371 position = list_new();
1372
1373 struct cmd_token *stok = start->data;
1374 struct graph_node *gnn;
1375 struct listnode *ln;
1376
1377 // recursive dfs
1378 listnode_add(position, start);
1379 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1380 struct graph_node *gn = vector_slot(start->to, i);
1381 struct cmd_token *tok = gn->data;
1382 if (tok->attr == CMD_ATTR_HIDDEN
1383 || tok->attr == CMD_ATTR_DEPRECATED)
1384 continue;
1385 else if (tok->type == END_TKN || gn == start) {
1386 vty_out(vty, " ");
1387 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1388 struct cmd_token *tt = gnn->data;
1389 if (tt->type < SPECIAL_TKN)
1390 vty_out(vty, " %s", tt->text);
1391 }
1392 if (gn == start)
1393 vty_out(vty, "...");
1394 vty_out(vty, "\n");
1395 } else {
1396 bool skip = false;
1397 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1398 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1399 if (gnn == gn) {
1400 skip = true;
1401 break;
1402 }
1403 if (!skip)
1404 permute(gn, vty);
1405 }
1406 }
1407 list_delete_node(position, listtail(position));
1408}
1409
1410int cmd_list_cmds(struct vty *vty, int do_permute)
1411{
1412 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1413
1414 if (do_permute)
1415 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1416 else {
1417 /* loop over all commands at this node */
154e9ca1 1418 const struct cmd_element *element = NULL;
d62a17ae 1419 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1420 i++)
1421 if ((element = vector_slot(node->cmd_vector, i))
1422 && element->attr != CMD_ATTR_DEPRECATED
1423 && element->attr != CMD_ATTR_HIDDEN)
1424 vty_out(vty, " %s\n", element->string);
1425 }
1426 return CMD_SUCCESS;
718e3744 1427}
1428
0b84f294
DL
1429/* Help display function for all node. */
1430DEFUN (config_list,
1431 config_list_cmd,
1432 "list [permutations]",
1433 "Print command list\n"
1434 "Print all possible command permutations\n")
1435{
d62a17ae 1436 return cmd_list_cmds(vty, argc == 2);
0b84f294
DL
1437}
1438
a2454870
QY
1439DEFUN (show_commandtree,
1440 show_commandtree_cmd,
1441 "show commandtree [permutations]",
1442 SHOW_STR
5a1945e4
DS
1443 "Show command tree\n"
1444 "Permutations that we are interested in\n")
a2454870 1445{
d62a17ae 1446 return cmd_list_cmds(vty, argc == 3);
a2454870
QY
1447}
1448
26fbe472
QY
1449DEFUN_HIDDEN(show_cli_graph,
1450 show_cli_graph_cmd,
1451 "show cli graph",
1452 SHOW_STR
1453 "CLI reflection\n"
1454 "Dump current command space as DOT graph\n")
1455{
1456 struct cmd_node *cn = vector_slot(cmdvec, vty->node);
1457 char *dot = cmd_graph_dump_dot(cn->cmdgraph);
1458
1459 vty_out(vty, "%s\n", dot);
1460 XFREE(MTYPE_TMP, dot);
1461 return CMD_SUCCESS;
1462}
1463
f806f29c 1464static int vty_write_config(struct vty *vty)
8efe88ea 1465{
d62a17ae 1466 size_t i;
1467 struct cmd_node *node;
8efe88ea 1468
f806f29c 1469 if (host.noconfig)
1470 return CMD_SUCCESS;
1471
5e6a9350
RW
1472 nb_cli_show_config_prepare(running_config, false);
1473
d62a17ae 1474 if (vty->type == VTY_TERM) {
1475 vty_out(vty, "\nCurrent configuration:\n");
1476 vty_out(vty, "!\n");
1477 }
8efe88ea 1478
ac4adef4
DL
1479 if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
1480 vty_out(vty, "! loaded from %s\n", frr_defaults_version());
d62a17ae 1481 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
ac4adef4 1482 vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
d62a17ae 1483 vty_out(vty, "!\n");
8efe88ea 1484
8685be73 1485 for (i = 0; i < vector_active(cmdvec); i++)
612c2c15
DL
1486 if ((node = vector_slot(cmdvec, i)) && node->config_write) {
1487 if ((*node->config_write)(vty))
8685be73
RW
1488 vty_out(vty, "!\n");
1489 }
8efe88ea 1490
d62a17ae 1491 if (vty->type == VTY_TERM) {
1492 vty_out(vty, "end\n");
1493 }
8efe88ea 1494
f806f29c 1495 return CMD_SUCCESS;
1496}
d862bffb 1497
f806f29c 1498static int file_write_config(struct vty *vty)
718e3744 1499{
d62a17ae 1500 int fd, dirfd;
1501 char *config_file, *slash;
1502 char *config_file_tmp = NULL;
1503 char *config_file_sav = NULL;
1504 int ret = CMD_WARNING;
1505 struct vty *file_vty;
1506 struct stat conf_stat;
1507
d62a17ae 1508 if (host.noconfig)
1509 return CMD_SUCCESS;
1510
1511 /* Check and see if we are operating under vtysh configuration */
1512 if (host.config == NULL) {
1513 vty_out(vty,
1514 "Can't save to configuration file, using vtysh.\n");
1515 return CMD_WARNING;
1516 }
1517
1518 /* Get filename. */
1519 config_file = host.config;
d0bfb22c 1520
056cfe49
DL
1521#ifndef O_DIRECTORY
1522#define O_DIRECTORY 0
1523#endif
d62a17ae 1524 slash = strrchr(config_file, '/');
1525 if (slash) {
1526 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1527 config_dir[slash - config_file] = '\0';
1528 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1529 XFREE(MTYPE_TMP, config_dir);
1530 } else
1531 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1532 /* if dirfd is invalid, directory sync fails, but we're still OK */
1533
9f73d2c9
QY
1534 size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
1535 config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
1536 strlcpy(config_file_sav, config_file, config_file_sav_sz);
1537 strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
d62a17ae 1538
1539
1540 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
fc746f1c
QY
1541 snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX",
1542 config_file);
d62a17ae 1543
1544 /* Open file to configuration write. */
1545 fd = mkstemp(config_file_tmp);
1546 if (fd < 0) {
1547 vty_out(vty, "Can't open configuration file %s.\n",
1548 config_file_tmp);
1549 goto finished;
1550 }
1551 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1552 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1553 config_file_tmp, safe_strerror(errno), errno);
1554 goto finished;
1555 }
1556
1557 /* Make vty for configuration file. */
1558 file_vty = vty_new();
1559 file_vty->wfd = fd;
1560 file_vty->type = VTY_FILE;
1561
1562 /* Config file header print. */
1563 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1564 vty_time_print(file_vty, 1);
1565 vty_out(file_vty, "!\n");
1566 vty_write_config(file_vty);
1567 vty_close(file_vty);
1568
1569 if (stat(config_file, &conf_stat) >= 0) {
1570 if (unlink(config_file_sav) != 0)
1571 if (errno != ENOENT) {
1572 vty_out(vty,
1573 "Can't unlink backup configuration file %s.\n",
1574 config_file_sav);
1575 goto finished;
1576 }
1577 if (link(config_file, config_file_sav) != 0) {
1578 vty_out(vty,
1579 "Can't backup old configuration file %s.\n",
1580 config_file_sav);
1581 goto finished;
1582 }
1583 if (dirfd >= 0)
1584 fsync(dirfd);
1585 }
1586 if (rename(config_file_tmp, config_file) != 0) {
1587 vty_out(vty, "Can't save configuration file %s.\n",
1588 config_file);
1589 goto finished;
1590 }
1591 if (dirfd >= 0)
1592 fsync(dirfd);
1593
1594 vty_out(vty, "Configuration saved to %s\n", config_file);
1595 ret = CMD_SUCCESS;
05865c90 1596
1597finished:
d62a17ae 1598 if (ret != CMD_SUCCESS)
1599 unlink(config_file_tmp);
1600 if (dirfd >= 0)
1601 close(dirfd);
1602 XFREE(MTYPE_TMP, config_file_tmp);
1603 XFREE(MTYPE_TMP, config_file_sav);
1604 return ret;
718e3744 1605}
1606
f806f29c 1607/* Write current configuration into file. */
1608
1609DEFUN (config_write,
1610 config_write_cmd,
1611 "write [<file|memory|terminal>]",
1612 "Write running configuration to memory, network, or terminal\n"
1613 "Write to configuration file\n"
1614 "Write configuration currently in memory\n"
1615 "Write configuration to terminal\n")
1616{
1617 const int idx_type = 1;
1618
1619 // if command was 'write terminal' or 'write memory'
1620 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1621 return vty_write_config(vty);
1622 }
1623
1624 return file_write_config(vty);
1625}
1626
d862bffb
QY
1627/* ALIAS_FIXME for 'write <terminal|memory>' */
1628DEFUN (show_running_config,
1629 show_running_config_cmd,
1630 "show running-config",
1631 SHOW_STR
f806f29c 1632 "running configuration (same as write terminal)\n")
d862bffb 1633{
f806f29c 1634 return vty_write_config(vty);
d862bffb 1635}
718e3744 1636
d862bffb
QY
1637/* ALIAS_FIXME for 'write file' */
1638DEFUN (copy_runningconf_startupconf,
1639 copy_runningconf_startupconf_cmd,
1640 "copy running-config startup-config",
1641 "Copy configuration\n"
1642 "Copy running config to... \n"
f806f29c 1643 "Copy running config to startup config (same as write file/memory)\n")
d862bffb 1644{
f806f29c 1645 return file_write_config(vty);
d862bffb
QY
1646}
1647/** -- **/
718e3744 1648
1649/* Write startup configuration into the terminal. */
1650DEFUN (show_startup_config,
1651 show_startup_config_cmd,
1652 "show startup-config",
1653 SHOW_STR
d0bfb22c 1654 "Contents of startup configuration\n")
718e3744 1655{
d62a17ae 1656 char buf[BUFSIZ];
1657 FILE *confp;
718e3744 1658
d62a17ae 1659 if (host.noconfig)
1660 return CMD_SUCCESS;
1661 if (host.config == NULL)
1662 return CMD_WARNING;
87f44e2f 1663
d62a17ae 1664 confp = fopen(host.config, "r");
1665 if (confp == NULL) {
1666 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1667 host.config, safe_strerror(errno));
1668 return CMD_WARNING;
1669 }
718e3744 1670
d62a17ae 1671 while (fgets(buf, BUFSIZ, confp)) {
1672 char *cp = buf;
718e3744 1673
d62a17ae 1674 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1675 cp++;
1676 *cp = '\0';
718e3744 1677
d62a17ae 1678 vty_out(vty, "%s\n", buf);
1679 }
718e3744 1680
d62a17ae 1681 fclose(confp);
718e3744 1682
d62a17ae 1683 return CMD_SUCCESS;
718e3744 1684}
1685
6b3ee3a0
MK
1686int cmd_domainname_set(const char *domainname)
1687{
1688 XFREE(MTYPE_HOST, host.domainname);
1689 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1690 return CMD_SUCCESS;
1691}
1692
1693/* Hostname configuration */
60466a63
QY
1694DEFUN(config_domainname,
1695 domainname_cmd,
1696 "domainname WORD",
1697 "Set system's domain name\n"
1698 "This system's domain name\n")
6b3ee3a0
MK
1699{
1700 struct cmd_token *word = argv[1];
1701
fefa5e0f 1702 if (!isalpha((unsigned char)word->arg[0])) {
6b3ee3a0
MK
1703 vty_out(vty, "Please specify string starting with alphabet\n");
1704 return CMD_WARNING_CONFIG_FAILED;
1705 }
1706
1707 return cmd_domainname_set(word->arg);
1708}
1709
60466a63
QY
1710DEFUN(config_no_domainname,
1711 no_domainname_cmd,
1712 "no domainname [DOMAINNAME]",
1713 NO_STR
1714 "Reset system's domain name\n"
1715 "domain name of this router\n")
6b3ee3a0
MK
1716{
1717 return cmd_domainname_set(NULL);
1718}
1719
d62a17ae 1720int cmd_hostname_set(const char *hostname)
bff9c3e9 1721{
d62a17ae 1722 XFREE(MTYPE_HOST, host.name);
1723 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1724 return CMD_SUCCESS;
bff9c3e9
DL
1725}
1726
718e3744 1727/* Hostname configuration */
d0bfb22c 1728DEFUN (config_hostname,
718e3744 1729 hostname_cmd,
1730 "hostname WORD",
1731 "Set system's network name\n"
1732 "This system's network name\n")
1733{
d62a17ae 1734 struct cmd_token *word = argv[1];
d0bfb22c 1735
fefa5e0f 1736 if (!isalnum((unsigned char)word->arg[0])) {
63e653a2
LK
1737 vty_out(vty,
1738 "Please specify string starting with alphabet or number\n");
1739 return CMD_WARNING_CONFIG_FAILED;
1740 }
1741
1742 /* With reference to RFC 1123 Section 2.1 */
1743 if (strlen(word->arg) > HOSTNAME_LEN) {
1744 vty_out(vty, "Hostname length should be less than %d chars\n",
1745 HOSTNAME_LEN);
d62a17ae 1746 return CMD_WARNING_CONFIG_FAILED;
1747 }
718e3744 1748
d62a17ae 1749 return cmd_hostname_set(word->arg);
718e3744 1750}
1751
d0bfb22c 1752DEFUN (config_no_hostname,
718e3744 1753 no_hostname_cmd,
1754 "no hostname [HOSTNAME]",
1755 NO_STR
1756 "Reset system's network name\n"
1757 "Host name of this router\n")
1758{
d62a17ae 1759 return cmd_hostname_set(NULL);
718e3744 1760}
1761
1762/* VTY interface password set. */
f412b39a
DW
1763DEFUN (config_password,
1764 password_cmd,
98463e0a 1765 "password [(8-8)] WORD",
322e2d5c 1766 "Modify the terminal connection password\n"
718e3744 1767 "Specifies a HIDDEN password will follow\n"
d0bfb22c 1768 "The password string\n")
718e3744 1769{
d62a17ae 1770 int idx_8 = 1;
1771 int idx_word = 2;
1772 if (argc == 3) // '8' was specified
1773 {
1774 if (host.password)
1775 XFREE(MTYPE_HOST, host.password);
1776 host.password = NULL;
1777 if (host.password_encrypt)
1778 XFREE(MTYPE_HOST, host.password_encrypt);
1779 host.password_encrypt =
1780 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1781 return CMD_SUCCESS;
1782 }
1783
fefa5e0f 1784 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
d62a17ae 1785 vty_out(vty,
1786 "Please specify string starting with alphanumeric\n");
1787 return CMD_WARNING_CONFIG_FAILED;
1788 }
1789
1790 if (host.password)
1791 XFREE(MTYPE_HOST, host.password);
1792 host.password = NULL;
1793
1794 if (host.encrypt) {
1795 if (host.password_encrypt)
1796 XFREE(MTYPE_HOST, host.password_encrypt);
1797 host.password_encrypt =
1798 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1799 } else
1800 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1801
1802 return CMD_SUCCESS;
718e3744 1803}
1804
322e2d5c
PM
1805/* VTY interface password delete. */
1806DEFUN (no_config_password,
1807 no_password_cmd,
1808 "no password",
1809 NO_STR
1810 "Modify the terminal connection password\n")
1811{
1812 bool warned = false;
1813
1814 if (host.password) {
eb83f7ce 1815 if (!vty_shell_serv(vty)) {
4911ca9c 1816 vty_out(vty, NO_PASSWD_CMD_WARNING);
eb83f7ce
PM
1817 warned = true;
1818 }
322e2d5c
PM
1819 XFREE(MTYPE_HOST, host.password);
1820 }
1821 host.password = NULL;
1822
1823 if (host.password_encrypt) {
eb83f7ce 1824 if (!warned && !vty_shell_serv(vty))
4911ca9c 1825 vty_out(vty, NO_PASSWD_CMD_WARNING);
322e2d5c
PM
1826 XFREE(MTYPE_HOST, host.password_encrypt);
1827 }
1828 host.password_encrypt = NULL;
1829
1830 return CMD_SUCCESS;
1831}
1832
718e3744 1833/* VTY enable password set. */
f412b39a
DW
1834DEFUN (config_enable_password,
1835 enable_password_cmd,
98463e0a 1836 "enable password [(8-8)] WORD",
718e3744 1837 "Modify enable password parameters\n"
1838 "Assign the privileged level password\n"
1839 "Specifies a HIDDEN password will follow\n"
718e3744 1840 "The HIDDEN 'enable' password string\n")
1841{
d62a17ae 1842 int idx_8 = 2;
1843 int idx_word = 3;
1844
1845 /* Crypt type is specified. */
1846 if (argc == 4) {
1847 if (argv[idx_8]->arg[0] == '8') {
1848 if (host.enable)
1849 XFREE(MTYPE_HOST, host.enable);
1850 host.enable = NULL;
1851
1852 if (host.enable_encrypt)
1853 XFREE(MTYPE_HOST, host.enable_encrypt);
1854 host.enable_encrypt =
1855 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1856
1857 return CMD_SUCCESS;
1858 } else {
1859 vty_out(vty, "Unknown encryption type.\n");
1860 return CMD_WARNING_CONFIG_FAILED;
1861 }
1862 }
1863
fefa5e0f 1864 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
d62a17ae 1865 vty_out(vty,
1866 "Please specify string starting with alphanumeric\n");
1867 return CMD_WARNING_CONFIG_FAILED;
1868 }
1869
1870 if (host.enable)
1871 XFREE(MTYPE_HOST, host.enable);
1872 host.enable = NULL;
1873
1874 /* Plain password input. */
1875 if (host.encrypt) {
1876 if (host.enable_encrypt)
1877 XFREE(MTYPE_HOST, host.enable_encrypt);
1878 host.enable_encrypt =
1879 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1880 } else
1881 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1882
1883 return CMD_SUCCESS;
718e3744 1884}
1885
718e3744 1886/* VTY enable password delete. */
f412b39a
DW
1887DEFUN (no_config_enable_password,
1888 no_enable_password_cmd,
718e3744 1889 "no enable password",
1890 NO_STR
1891 "Modify enable password parameters\n"
1892 "Assign the privileged level password\n")
1893{
322e2d5c
PM
1894 bool warned = false;
1895
1896 if (host.enable) {
eb83f7ce 1897 if (!vty_shell_serv(vty)) {
4911ca9c 1898 vty_out(vty, NO_PASSWD_CMD_WARNING);
eb83f7ce
PM
1899 warned = true;
1900 }
d62a17ae 1901 XFREE(MTYPE_HOST, host.enable);
322e2d5c 1902 }
d62a17ae 1903 host.enable = NULL;
718e3744 1904
322e2d5c 1905 if (host.enable_encrypt) {
eb83f7ce 1906 if (!warned && !vty_shell_serv(vty))
4911ca9c 1907 vty_out(vty, NO_PASSWD_CMD_WARNING);
d62a17ae 1908 XFREE(MTYPE_HOST, host.enable_encrypt);
322e2d5c 1909 }
d62a17ae 1910 host.enable_encrypt = NULL;
718e3744 1911
d62a17ae 1912 return CMD_SUCCESS;
718e3744 1913}
d0bfb22c 1914
718e3744 1915DEFUN (service_password_encrypt,
1916 service_password_encrypt_cmd,
1917 "service password-encryption",
1918 "Set up miscellaneous service\n"
1919 "Enable encrypted passwords\n")
1920{
d62a17ae 1921 if (host.encrypt)
1922 return CMD_SUCCESS;
718e3744 1923
d62a17ae 1924 host.encrypt = 1;
718e3744 1925
d62a17ae 1926 if (host.password) {
1927 if (host.password_encrypt)
1928 XFREE(MTYPE_HOST, host.password_encrypt);
1929 host.password_encrypt =
1930 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
1931 }
1932 if (host.enable) {
1933 if (host.enable_encrypt)
1934 XFREE(MTYPE_HOST, host.enable_encrypt);
1935 host.enable_encrypt =
1936 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
1937 }
718e3744 1938
d62a17ae 1939 return CMD_SUCCESS;
718e3744 1940}
1941
1942DEFUN (no_service_password_encrypt,
1943 no_service_password_encrypt_cmd,
1944 "no service password-encryption",
1945 NO_STR
1946 "Set up miscellaneous service\n"
1947 "Enable encrypted passwords\n")
1948{
d62a17ae 1949 if (!host.encrypt)
1950 return CMD_SUCCESS;
718e3744 1951
d62a17ae 1952 host.encrypt = 0;
718e3744 1953
d62a17ae 1954 if (host.password_encrypt)
1955 XFREE(MTYPE_HOST, host.password_encrypt);
1956 host.password_encrypt = NULL;
718e3744 1957
d62a17ae 1958 if (host.enable_encrypt)
1959 XFREE(MTYPE_HOST, host.enable_encrypt);
1960 host.enable_encrypt = NULL;
718e3744 1961
d62a17ae 1962 return CMD_SUCCESS;
718e3744 1963}
1964
f412b39a
DW
1965DEFUN (config_terminal_length,
1966 config_terminal_length_cmd,
d0bfb22c 1967 "terminal length (0-512)",
718e3744 1968 "Set terminal line parameters\n"
1969 "Set number of lines on a screen\n"
1970 "Number of lines on screen (0 for no pausing)\n")
1971{
d62a17ae 1972 int idx_number = 2;
718e3744 1973
d62a17ae 1974 vty->lines = atoi(argv[idx_number]->arg);
718e3744 1975
d62a17ae 1976 return CMD_SUCCESS;
718e3744 1977}
1978
f412b39a
DW
1979DEFUN (config_terminal_no_length,
1980 config_terminal_no_length_cmd,
718e3744 1981 "terminal no length",
1982 "Set terminal line parameters\n"
1983 NO_STR
1984 "Set number of lines on a screen\n")
1985{
d62a17ae 1986 vty->lines = -1;
1987 return CMD_SUCCESS;
718e3744 1988}
1989
f412b39a
DW
1990DEFUN (service_terminal_length,
1991 service_terminal_length_cmd,
d0bfb22c 1992 "service terminal-length (0-512)",
718e3744 1993 "Set up miscellaneous service\n"
1994 "System wide terminal length configuration\n"
1995 "Number of lines of VTY (0 means no line control)\n")
1996{
d62a17ae 1997 int idx_number = 2;
718e3744 1998
d62a17ae 1999 host.lines = atoi(argv[idx_number]->arg);
718e3744 2000
d62a17ae 2001 return CMD_SUCCESS;
718e3744 2002}
2003
f412b39a
DW
2004DEFUN (no_service_terminal_length,
2005 no_service_terminal_length_cmd,
d0bfb22c 2006 "no service terminal-length [(0-512)]",
718e3744 2007 NO_STR
2008 "Set up miscellaneous service\n"
2009 "System wide terminal length configuration\n"
2010 "Number of lines of VTY (0 means no line control)\n")
2011{
d62a17ae 2012 host.lines = -1;
2013 return CMD_SUCCESS;
718e3744 2014}
2015
2885f72d 2016DEFUN_HIDDEN (do_echo,
d0bfb22c
QY
2017 echo_cmd,
2018 "echo MESSAGE...",
2019 "Echo a message back to the vty\n"
2020 "The message to echo\n")
2885f72d 2021{
d62a17ae 2022 char *message;
2885f72d 2023
d62a17ae 2024 vty_out(vty, "%s\n",
2025 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2026 if (message)
2027 XFREE(MTYPE_TMP, message);
2028 return CMD_SUCCESS;
2885f72d 2029}
2030
274a4a44 2031DEFUN (config_logmsg,
2032 config_logmsg_cmd,
199d90a1 2033 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
274a4a44 2034 "Send a message to enabled logging destinations\n"
2035 LOG_LEVEL_DESC
2036 "The message to send\n")
2037{
d62a17ae 2038 int idx_log_level = 1;
2039 int idx_message = 2;
2040 int level;
2041 char *message;
274a4a44 2042
0bdeb5e5
DL
2043 level = log_level_match(argv[idx_log_level]->arg);
2044 if (level == ZLOG_DISABLED)
d62a17ae 2045 return CMD_ERR_NO_MATCH;
274a4a44 2046
d62a17ae 2047 zlog(level, "%s",
2048 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2049 if (message)
2050 XFREE(MTYPE_TMP, message);
65efcfce 2051
d62a17ae 2052 return CMD_SUCCESS;
274a4a44 2053}
2054
9eed278b
DL
2055DEFUN (debug_memstats,
2056 debug_memstats_cmd,
2057 "[no] debug memstats-at-exit",
2058 NO_STR
2059 DEBUG_STR
2060 "Print memory type statistics at exit\n")
2061{
2062 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2063 return CMD_SUCCESS;
2064}
2065
d62a17ae 2066int cmd_banner_motd_file(const char *file)
7cfc61d3 2067{
d62a17ae 2068 int success = CMD_SUCCESS;
2069 char p[PATH_MAX];
2070 char *rpath;
2071 char *in;
7cfc61d3 2072
d62a17ae 2073 rpath = realpath(file, p);
2074 if (!rpath)
2075 return CMD_ERR_NO_FILE;
2076 in = strstr(rpath, SYSCONFDIR);
2077 if (in == rpath) {
0a22ddfb 2078 XFREE(MTYPE_HOST, host.motdfile);
d62a17ae 2079 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2080 } else
2081 success = CMD_WARNING_CONFIG_FAILED;
1ee08155 2082
d62a17ae 2083 return success;
7cfc61d3
DS
2084}
2085
19d61463
DA
2086void cmd_banner_motd_line(const char *line)
2087{
e1b36e13 2088 XFREE(MTYPE_HOST, host.motd);
19d61463
DA
2089 host.motd = XSTRDUP(MTYPE_HOST, line);
2090}
2091
3b0c5d9a 2092DEFUN (banner_motd_file,
2093 banner_motd_file_cmd,
4d833e55 2094 "banner motd file FILE",
3b0c5d9a 2095 "Set banner\n"
2096 "Banner for motd\n"
2097 "Banner from a file\n"
2098 "Filename\n")
2099{
d62a17ae 2100 int idx_file = 3;
2101 const char *filename = argv[idx_file]->arg;
2102 int cmd = cmd_banner_motd_file(filename);
1ee08155 2103
d62a17ae 2104 if (cmd == CMD_ERR_NO_FILE)
2105 vty_out(vty, "%s does not exist", filename);
2106 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2107 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
1ee08155 2108
d62a17ae 2109 return cmd;
3b0c5d9a 2110}
718e3744 2111
19d61463
DA
2112DEFUN (banner_motd_line,
2113 banner_motd_line_cmd,
2114 "banner motd line LINE...",
2115 "Set banner\n"
2116 "Banner for motd\n"
2117 "Banner from an input\n"
2118 "Text\n")
2119{
2120 int idx = 0;
2121 char *motd;
2122
2123 argv_find(argv, argc, "LINE", &idx);
2124 motd = argv_concat(argv, argc, idx);
2125
2126 cmd_banner_motd_line(motd);
2127 XFREE(MTYPE_TMP, motd);
2128
2129 return CMD_SUCCESS;
2130}
2131
718e3744 2132DEFUN (banner_motd_default,
2133 banner_motd_default_cmd,
2134 "banner motd default",
2135 "Set banner string\n"
2136 "Strings for motd\n"
2137 "Default string\n")
2138{
19d61463 2139 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
d62a17ae 2140 return CMD_SUCCESS;
718e3744 2141}
2142
2143DEFUN (no_banner_motd,
2144 no_banner_motd_cmd,
2145 "no banner motd",
2146 NO_STR
2147 "Set banner string\n"
2148 "Strings for motd\n")
2149{
d62a17ae 2150 host.motd = NULL;
2151 if (host.motdfile)
2152 XFREE(MTYPE_HOST, host.motdfile);
2153 host.motdfile = NULL;
2154 return CMD_SUCCESS;
718e3744 2155}
2156
a83a5331
QY
2157DEFUN(find,
2158 find_cmd,
68912a20
QY
2159 "find REGEX",
2160 "Find CLI command matching a regular expression\n"
2161 "Search pattern (POSIX regex)\n")
a83a5331 2162{
68912a20 2163 char *pattern = argv[1]->arg;
cf6c83e7
QY
2164 const struct cmd_node *node;
2165 const struct cmd_element *cli;
2166 vector clis;
a83a5331 2167
68912a20
QY
2168 regex_t exp = {};
2169
2170 int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
2171
2172 if (cr != 0) {
2173 switch (cr) {
2174 case REG_BADBR:
2175 vty_out(vty, "%% Invalid {...} expression\n");
2176 break;
2177 case REG_BADRPT:
2178 vty_out(vty, "%% Bad repetition operator\n");
2179 break;
2180 case REG_BADPAT:
2181 vty_out(vty, "%% Regex syntax error\n");
2182 break;
2183 case REG_ECOLLATE:
2184 vty_out(vty, "%% Invalid collating element\n");
2185 break;
2186 case REG_ECTYPE:
2187 vty_out(vty, "%% Invalid character class name\n");
2188 break;
2189 case REG_EESCAPE:
2190 vty_out(vty,
2191 "%% Regex ended with escape character (\\)\n");
2192 break;
2193 case REG_ESUBREG:
2194 vty_out(vty,
2195 "%% Invalid number in \\digit construction\n");
2196 break;
2197 case REG_EBRACK:
2198 vty_out(vty, "%% Unbalanced square brackets\n");
2199 break;
2200 case REG_EPAREN:
2201 vty_out(vty, "%% Unbalanced parentheses\n");
2202 break;
2203 case REG_EBRACE:
2204 vty_out(vty, "%% Unbalanced braces\n");
2205 break;
2206 case REG_ERANGE:
2207 vty_out(vty,
2208 "%% Invalid endpoint in range expression\n");
2209 break;
2210 case REG_ESPACE:
2211 vty_out(vty, "%% Failed to compile (out of memory)\n");
2212 break;
2213 }
2214
2215 goto done;
2216 }
2217
2218
a83a5331 2219 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
cf6c83e7 2220 node = vector_slot(cmdvec, i);
a83a5331
QY
2221 if (!node)
2222 continue;
cf6c83e7 2223 clis = node->cmd_vector;
a83a5331 2224 for (unsigned int j = 0; j < vector_active(clis); j++) {
cf6c83e7 2225 cli = vector_slot(clis, j);
68912a20
QY
2226
2227 if (regexec(&exp, cli->string, 0, NULL, 0) == 0)
cf6c83e7 2228 vty_out(vty, " (%s) %s\n",
f4b8291f 2229 node->name, cli->string);
a83a5331
QY
2230 }
2231 }
2232
68912a20
QY
2233done:
2234 regfree(&exp);
a83a5331
QY
2235 return CMD_SUCCESS;
2236}
2237
718e3744 2238/* Set config filename. Called from vty.c */
d62a17ae 2239void host_config_set(const char *filename)
718e3744 2240{
0a22ddfb 2241 XFREE(MTYPE_HOST, host.config);
d62a17ae 2242 host.config = XSTRDUP(MTYPE_HOST, filename);
718e3744 2243}
2244
d62a17ae 2245const char *host_config_get(void)
57387fb2 2246{
d62a17ae 2247 return host.config;
57387fb2
CF
2248}
2249
d62a17ae 2250void install_default(enum node_type node)
718e3744 2251{
d62a17ae 2252 install_element(node, &config_exit_cmd);
2253 install_element(node, &config_quit_cmd);
2254 install_element(node, &config_end_cmd);
2255 install_element(node, &config_help_cmd);
2256 install_element(node, &config_list_cmd);
26fbe472 2257 install_element(node, &show_cli_graph_cmd);
a83a5331 2258 install_element(node, &find_cmd);
718e3744 2259
d62a17ae 2260 install_element(node, &config_write_cmd);
2261 install_element(node, &show_running_config_cmd);
7f059ea6 2262
d62a17ae 2263 install_element(node, &autocomplete_cmd);
1c2facd1
RW
2264
2265 nb_cli_install_default(node);
718e3744 2266}
2267
87f44e2f
DL
2268/* Initialize command interface. Install basic nodes and commands.
2269 *
2270 * terminal = 0 -- vtysh / no logging, no config control
2271 * terminal = 1 -- normal daemon
9473e340 2272 * terminal = -1 -- watchfrr / no logging, but minimal config control */
d62a17ae 2273void cmd_init(int terminal)
2274{
419cd5a0
MK
2275 struct utsname names;
2276
419cd5a0 2277 uname(&names);
d62a17ae 2278 qobj_init();
2279
fe6b47b9
QY
2280 /* register command preprocessors */
2281 hook_register(cmd_execute, handle_pipe_action);
2282 hook_register(cmd_execute_done, handle_pipe_action_done);
2283
d62a17ae 2284 varhandlers = list_new();
2285
2286 /* Allocate initial top vector of commands. */
2287 cmdvec = vector_init(VECTOR_MIN_SIZE);
2288
2289 /* Default host value settings. */
419cd5a0
MK
2290 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2291#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
6b3ee3a0
MK
2292 if ((strcmp(names.domainname, "(none)") == 0))
2293 host.domainname = NULL;
2294 else
2295 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
419cd5a0
MK
2296#else
2297 host.domainname = NULL;
2298#endif
d62a17ae 2299 host.password = NULL;
2300 host.enable = NULL;
d62a17ae 2301 host.config = NULL;
2302 host.noconfig = (terminal < 0);
2303 host.lines = -1;
19d61463 2304 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
d62a17ae 2305 host.motdfile = NULL;
2306
2307 /* Install top nodes. */
612c2c15
DL
2308 install_node(&view_node);
2309 install_node(&enable_node);
2310 install_node(&auth_node);
2311 install_node(&auth_enable_node);
2312 install_node(&config_node);
d62a17ae 2313
2314 /* Each node's basic commands. */
2315 install_element(VIEW_NODE, &show_version_cmd);
a83a5331
QY
2316 install_element(ENABLE_NODE, &show_startup_config_cmd);
2317
d62a17ae 2318 if (terminal) {
85a6806d
MS
2319 install_element(ENABLE_NODE, &debug_memstats_cmd);
2320
d62a17ae 2321 install_element(VIEW_NODE, &config_list_cmd);
2322 install_element(VIEW_NODE, &config_exit_cmd);
2323 install_element(VIEW_NODE, &config_quit_cmd);
2324 install_element(VIEW_NODE, &config_help_cmd);
2325 install_element(VIEW_NODE, &config_enable_cmd);
2326 install_element(VIEW_NODE, &config_terminal_length_cmd);
2327 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
d62a17ae 2328 install_element(VIEW_NODE, &show_commandtree_cmd);
2329 install_element(VIEW_NODE, &echo_cmd);
2330 install_element(VIEW_NODE, &autocomplete_cmd);
a83a5331 2331 install_element(VIEW_NODE, &find_cmd);
d62a17ae 2332
d62a17ae 2333 install_element(ENABLE_NODE, &config_end_cmd);
2334 install_element(ENABLE_NODE, &config_disable_cmd);
2335 install_element(ENABLE_NODE, &config_terminal_cmd);
2336 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2337 install_element(ENABLE_NODE, &config_write_cmd);
2338 install_element(ENABLE_NODE, &show_running_config_cmd);
d62a17ae 2339 install_element(ENABLE_NODE, &config_logmsg_cmd);
a83a5331 2340
d62a17ae 2341 install_default(CONFIG_NODE);
2342
2343 thread_cmd_init();
2344 workqueue_cmd_init();
2345 hash_cmd_init();
2346 }
2347
2348 install_element(CONFIG_NODE, &hostname_cmd);
2349 install_element(CONFIG_NODE, &no_hostname_cmd);
6b3ee3a0
MK
2350 install_element(CONFIG_NODE, &domainname_cmd);
2351 install_element(CONFIG_NODE, &no_domainname_cmd);
d62a17ae 2352
2353 if (terminal > 0) {
0bdeb5e5
DL
2354 full_cli = true;
2355
85a6806d
MS
2356 install_element(CONFIG_NODE, &debug_memstats_cmd);
2357
d62a17ae 2358 install_element(CONFIG_NODE, &password_cmd);
322e2d5c 2359 install_element(CONFIG_NODE, &no_password_cmd);
d62a17ae 2360 install_element(CONFIG_NODE, &enable_password_cmd);
2361 install_element(CONFIG_NODE, &no_enable_password_cmd);
2362
d62a17ae 2363 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2364 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2365 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2366 install_element(CONFIG_NODE, &banner_motd_file_cmd);
19d61463 2367 install_element(CONFIG_NODE, &banner_motd_line_cmd);
d62a17ae 2368 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2369 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2370 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2371
0bdeb5e5 2372 log_cmd_init();
d62a17ae 2373 vrf_install_commands();
2374 }
af2567b6
DL
2375
2376#ifdef DEV_BUILD
d62a17ae 2377 grammar_sandbox_init();
af2567b6 2378#endif
718e3744 2379}
228da428 2380
4d762f26 2381void cmd_terminate(void)
d62a17ae 2382{
2383 struct cmd_node *cmd_node;
2384
5d806ec6
QY
2385 hook_unregister(cmd_execute, handle_pipe_action);
2386 hook_unregister(cmd_execute_done, handle_pipe_action_done);
2387
d62a17ae 2388 if (cmdvec) {
2389 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2390 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2391 // deleting the graph delets the cmd_element as
2392 // well
2393 graph_delete_graph(cmd_node->cmdgraph);
2394 vector_free(cmd_node->cmd_vector);
2395 hash_clean(cmd_node->cmd_hash, NULL);
2396 hash_free(cmd_node->cmd_hash);
2397 cmd_node->cmd_hash = NULL;
2398 }
2399
2400 vector_free(cmdvec);
2401 cmdvec = NULL;
2402 }
2403
0a22ddfb
QY
2404 XFREE(MTYPE_HOST, host.name);
2405 XFREE(MTYPE_HOST, host.domainname);
2406 XFREE(MTYPE_HOST, host.password);
2407 XFREE(MTYPE_HOST, host.password_encrypt);
2408 XFREE(MTYPE_HOST, host.enable);
2409 XFREE(MTYPE_HOST, host.enable_encrypt);
0a22ddfb
QY
2410 XFREE(MTYPE_HOST, host.motdfile);
2411 XFREE(MTYPE_HOST, host.config);
19d61463 2412 XFREE(MTYPE_HOST, host.motd);
d62a17ae 2413
6a154c88 2414 list_delete(&varhandlers);
d62a17ae 2415 qobj_finish();
228da428 2416}