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