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