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