]> git.proxmox.com Git - mirror_frr.git/blame - lib/command.c
lib: add cli preprocessor for `|` actions
[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"
deaa50db 34#include "log_int.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"
d0bfb22c 48
d62a17ae 49DEFINE_MTYPE(LIB, HOST, "Host config")
d62a17ae 50DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
4a1ab8e4 51
26fbe472
QY
52#define item(x) \
53 { \
54 x, #x \
55 }
56
57/* clang-format off */
58const struct message tokennames[] = {
59 item(WORD_TKN),
60 item(VARIABLE_TKN),
61 item(RANGE_TKN),
62 item(IPV4_TKN),
63 item(IPV4_PREFIX_TKN),
64 item(IPV6_TKN),
65 item(IPV6_PREFIX_TKN),
66 item(MAC_TKN),
67 item(MAC_PREFIX_TKN),
68 item(FORK_TKN),
69 item(JOIN_TKN),
70 item(START_TKN),
71 item(END_TKN),
72 {0},
73};
74
a83a5331
QY
75const char *node_names[] = {
76 "auth", // AUTH_NODE,
77 "view", // VIEW_NODE,
78 "auth enable", // AUTH_ENABLE_NODE,
79 "enable", // ENABLE_NODE,
80 "config", // CONFIG_NODE,
a83a5331
QY
81 "debug", // DEBUG_NODE,
82 "vrf debug", // VRF_DEBUG_NODE,
c652f6f6 83 "vnc debug", // DEBUG_VNC_NODE,
a83a5331
QY
84 "aaa", // AAA_NODE,
85 "keychain", // KEYCHAIN_NODE,
9d356c93 86 "keychain key", // KEYCHAIN_KEY_NODE,
996c9314 87 "logical-router", // LOGICALROUTER_NODE,
9f46cc36 88 "static ip", // IP_NODE,
a83a5331
QY
89 "vrf", // VRF_NODE,
90 "interface", // INTERFACE_NODE,
dba32923 91 "nexthop-group", // NH_GROUP_NODE,
a83a5331
QY
92 "zebra", // ZEBRA_NODE,
93 "table", // TABLE_NODE,
94 "rip", // RIP_NODE,
95 "ripng", // RIPNG_NODE,
96 "babel", // BABEL_NODE,
97 "eigrp", // EIGRP_NODE,
98 "bgp", // BGP_NODE,
99 "bgp vpnv4", // BGP_VPNV4_NODE,
c652f6f6 100 "bgp vpnv6", // BGP_VPNV6_NODE,
996c9314 101 "bgp ipv4 unicast", // BGP_IPV4_NODE,
a83a5331
QY
102 "bgp ipv4 multicast", // BGP_IPV4M_NODE,
103 "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE,
104 "bgp ipv6", // BGP_IPV6_NODE,
105 "bgp ipv6 multicast", // BGP_IPV6M_NODE,
106 "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE,
996c9314
LB
107 "bgp vrf policy", // BGP_VRF_POLICY_NODE,
108 "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE,
a83a5331
QY
109 "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE,
110 "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE,
111 "rfp defaults", // RFP_DEFAULTS_NODE,
112 "bgp evpn", // BGP_EVPN_NODE,
113 "ospf", // OSPF_NODE,
114 "ospf6", // OSPF6_NODE,
115 "ldp", // LDP_NODE,
116 "ldp ipv4", // LDP_IPV4_NODE,
117 "ldp ipv6", // LDP_IPV6_NODE,
118 "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE,
119 "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE,
120 "ldp l2vpn", // LDP_L2VPN_NODE,
121 "ldp", // LDP_PSEUDOWIRE_NODE,
122 "isis", // ISIS_NODE,
996c9314
LB
123 "ipv4 access list", // ACCESS_NODE,
124 "ipv4 prefix list", // PREFIX_NODE,
125 "ipv6 access list", // ACCESS_IPV6_NODE,
126 "MAC access list", // ACCESS_MAC_NODE,
127 "ipv6 prefix list", // PREFIX_IPV6_NODE,
c652f6f6 128 "as list", // AS_LIST_NODE,
996c9314 129 "community list", // COMMUNITY_LIST_NODE,
a83a5331 130 "routemap", // RMAP_NODE,
e5c83d9b 131 "pbr-map", // PBRMAP_NODE,
a83a5331
QY
132 "smux", // SMUX_NODE,
133 "dump", // DUMP_NODE,
134 "forwarding", // FORWARDING_NODE,
135 "protocol", // PROTOCOL_NODE,
136 "mpls", // MPLS_NODE,
2dd0d726 137 "pw", // PW_NODE,
a83a5331
QY
138 "vty", // VTY_NODE,
139 "link-params", // LINK_PARAMS_NODE,
140 "bgp evpn vni", // BGP_EVPN_VNI_NODE,
dabecd7c 141 "rpki", // RPKI_NODE
7c40bf39 142 "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE
143 */
144 "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
145 */
a83a5331 146};
26fbe472 147/* clang-format on */
a83a5331 148
718e3744 149/* Command vector which includes some level of command lists. Normally
150 each daemon maintains each own cmdvec. */
eb820afe 151vector cmdvec = NULL;
718e3744 152
153/* Host information structure. */
154struct host host;
155
419cd5a0
MK
156/*
157 * Returns host.name if any, otherwise
158 * it returns the system hostname.
159 */
6b3ee3a0 160const char *cmd_hostname_get(void)
419cd5a0
MK
161{
162 return host.name;
163}
164
165/*
166 * Returns unix domainname
167 */
6b3ee3a0 168const char *cmd_domainname_get(void)
419cd5a0
MK
169{
170 return host.domainname;
171}
172
718e3744 173/* Standard command node structures. */
d62a17ae 174static struct cmd_node auth_node = {
9d303b37 175 AUTH_NODE, "Password: ",
718e3744 176};
177
d62a17ae 178static struct cmd_node view_node = {
9d303b37 179 VIEW_NODE, "%s> ",
718e3744 180};
181
d62a17ae 182static struct cmd_node auth_enable_node = {
9d303b37 183 AUTH_ENABLE_NODE, "Password: ",
718e3744 184};
185
d62a17ae 186static struct cmd_node enable_node = {
9d303b37 187 ENABLE_NODE, "%s# ",
718e3744 188};
189
d62a17ae 190static struct cmd_node config_node = {CONFIG_NODE, "%s(config)# ", 1};
6590f2c3 191
192/* Default motd string. */
b2f36157 193static const char *default_motd = FRR_DEFAULT_MOTD;
274a4a44 194
2d362d10 195static const struct facility_map {
d62a17ae 196 int facility;
197 const char *name;
198 size_t match;
199} syslog_facilities[] = {
200 {LOG_KERN, "kern", 1},
201 {LOG_USER, "user", 2},
202 {LOG_MAIL, "mail", 1},
203 {LOG_DAEMON, "daemon", 1},
204 {LOG_AUTH, "auth", 1},
205 {LOG_SYSLOG, "syslog", 1},
206 {LOG_LPR, "lpr", 2},
207 {LOG_NEWS, "news", 1},
208 {LOG_UUCP, "uucp", 2},
209 {LOG_CRON, "cron", 1},
274a4a44 210#ifdef LOG_FTP
d62a17ae 211 {LOG_FTP, "ftp", 1},
274a4a44 212#endif
d62a17ae 213 {LOG_LOCAL0, "local0", 6},
214 {LOG_LOCAL1, "local1", 6},
215 {LOG_LOCAL2, "local2", 6},
216 {LOG_LOCAL3, "local3", 6},
217 {LOG_LOCAL4, "local4", 6},
218 {LOG_LOCAL5, "local5", 6},
219 {LOG_LOCAL6, "local6", 6},
220 {LOG_LOCAL7, "local7", 6},
221 {0, NULL, 0},
222};
274a4a44 223
d62a17ae 224static const char *facility_name(int facility)
274a4a44 225{
d62a17ae 226 const struct facility_map *fm;
274a4a44 227
d62a17ae 228 for (fm = syslog_facilities; fm->name; fm++)
229 if (fm->facility == facility)
230 return fm->name;
231 return "";
274a4a44 232}
233
d62a17ae 234static int facility_match(const char *str)
274a4a44 235{
d62a17ae 236 const struct facility_map *fm;
274a4a44 237
d62a17ae 238 for (fm = syslog_facilities; fm->name; fm++)
239 if (!strncmp(str, fm->name, fm->match))
240 return fm->facility;
241 return -1;
274a4a44 242}
243
d62a17ae 244static int level_match(const char *s)
274a4a44 245{
d62a17ae 246 int level;
d0bfb22c 247
d62a17ae 248 for (level = 0; zlog_priority[level] != NULL; level++)
249 if (!strncmp(s, zlog_priority[level], 2))
250 return level;
251 return ZLOG_DISABLED;
274a4a44 252}
253
cb585b65 254/* This is called from main when a daemon is invoked with -v or --version. */
d62a17ae 255void print_version(const char *progname)
6590f2c3 256{
d62a17ae 257 printf("%s version %s\n", progname, FRR_VERSION);
258 printf("%s\n", FRR_COPYRIGHT);
259 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
6590f2c3 260}
261
d62a17ae 262char *argv_concat(struct cmd_token **argv, int argc, int shift)
263{
fe011935
QY
264 int cnt = argc - shift;
265 const char *argstr[cnt];
266
267 for (int i = 0; i < cnt; i++)
268 argstr[i] = argv[i + shift]->arg;
269
270 return frrstr_join(argstr, cnt, " ");
271}
272
273vector cmd_make_strvec(const char *string)
274{
275 if (!string)
d62a17ae 276 return NULL;
fe011935
QY
277
278 const char *copy = string;
279
280 /* skip leading whitespace */
281 while (isspace((int)*copy) && *copy != '\0')
282 copy++;
283
284 /* if the entire string was whitespace or a comment, return */
285 if (*copy == '\0' || *copy == '!' || *copy == '#')
286 return NULL;
287
288 vector result = frrstr_split_vec(copy, " \n\r\t");
289
290 for (unsigned int i = 0; i < vector_active(result); i++) {
291 if (strlen(vector_slot(result, i)) == 0) {
292 XFREE(MTYPE_TMP, vector_slot(result, i));
293 vector_unset(result, i);
294 }
d62a17ae 295 }
fe011935
QY
296 return result;
297}
298
299void cmd_free_strvec(vector v)
300{
301 frrstr_strvec_free(v);
718e3744 302}
303
ae19d7dd
QY
304/**
305 * Convenience function for accessing argv data.
306 *
307 * @param argc
308 * @param argv
309 * @param text definition snippet of the desired token
310 * @param index the starting index, and where to store the
311 * index of the found token if it exists
312 * @return 1 if found, 0 otherwise
313 */
d62a17ae 314int argv_find(struct cmd_token **argv, int argc, const char *text, int *index)
ae19d7dd 315{
d62a17ae 316 int found = 0;
317 for (int i = *index; i < argc && found == 0; i++)
318 if ((found = strmatch(text, argv[i]->text)))
319 *index = i;
320 return found;
ae19d7dd
QY
321}
322
d62a17ae 323static unsigned int cmd_hash_key(void *p)
274f29b2 324{
bd74dc61
DS
325 int size = sizeof(p);
326
327 return jhash(p, size, 0);
274f29b2
PJ
328}
329
d62a17ae 330static int cmd_hash_cmp(const void *a, const void *b)
274f29b2 331{
d62a17ae 332 return a == b;
274f29b2
PJ
333}
334
718e3744 335/* Install top node of command vector. */
d62a17ae 336void install_node(struct cmd_node *node, int (*func)(struct vty *))
718e3744 337{
d62a17ae 338 vector_set_index(cmdvec, node->node, node);
339 node->func = func;
340 node->cmdgraph = graph_new();
341 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
342 // add start node
343 struct cmd_token *token =
344 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
345 graph_new_node(node->cmdgraph, token,
346 (void (*)(void *)) & cmd_token_del);
996c9314 347 node->cmd_hash = hash_create_size(16, cmd_hash_key, cmd_hash_cmp,
bd74dc61 348 "Command Hash");
718e3744 349}
350
718e3744 351/* Return prompt character of specified node. */
d62a17ae 352const char *cmd_prompt(enum node_type node)
718e3744 353{
d62a17ae 354 struct cmd_node *cnode;
718e3744 355
d62a17ae 356 cnode = vector_slot(cmdvec, node);
357 return cnode->prompt;
718e3744 358}
359
360/* Install a command into a node. */
d62a17ae 361void install_element(enum node_type ntype, struct cmd_element *cmd)
718e3744 362{
d62a17ae 363 struct cmd_node *cnode;
d0bfb22c 364
d62a17ae 365 /* cmd_init hasn't been called */
366 if (!cmdvec) {
367 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
368 __func__);
369 return;
370 }
ebacb4ed 371
3cbb67f2 372 cnode = vector_lookup(cmdvec, ntype);
718e3744 373
d62a17ae 374 if (cnode == NULL) {
375 fprintf(stderr,
3cbb67f2
DL
376 "%s[%s]:\n"
377 "\tnode %d (%s) does not exist.\n"
378 "\tplease call install_node() before install_element()\n",
379 cmd->name, cmd->string, ntype, node_names[ntype]);
d62a17ae 380 exit(EXIT_FAILURE);
381 }
ebacb4ed 382
d62a17ae 383 if (hash_lookup(cnode->cmd_hash, cmd) != NULL) {
384 fprintf(stderr,
3cbb67f2
DL
385 "%s[%s]:\n"
386 "\tnode %d (%s) already has this command installed.\n"
387 "\tduplicate install_element call?\n",
388 cmd->name, cmd->string, ntype, node_names[ntype]);
d62a17ae 389 return;
390 }
ebacb4ed 391
d62a17ae 392 assert(hash_get(cnode->cmd_hash, cmd, hash_alloc_intern));
ebacb4ed 393
d62a17ae 394 struct graph *graph = graph_new();
395 struct cmd_token *token =
396 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
397 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
de8f7a39 398
d62a17ae 399 cmd_graph_parse(graph, cmd);
400 cmd_graph_names(graph);
401 cmd_graph_merge(cnode->cmdgraph, graph, +1);
402 graph_delete_graph(graph);
de8f7a39 403
d62a17ae 404 vector_set(cnode->cmd_vector, cmd);
735e62a0 405
d62a17ae 406 if (ntype == VIEW_NODE)
407 install_element(ENABLE_NODE, cmd);
718e3744 408}
409
d62a17ae 410void uninstall_element(enum node_type ntype, struct cmd_element *cmd)
de8f7a39 411{
d62a17ae 412 struct cmd_node *cnode;
de8f7a39 413
d62a17ae 414 /* cmd_init hasn't been called */
415 if (!cmdvec) {
416 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
417 __func__);
418 return;
419 }
de8f7a39 420
3cbb67f2 421 cnode = vector_lookup(cmdvec, ntype);
de8f7a39 422
d62a17ae 423 if (cnode == NULL) {
424 fprintf(stderr,
3cbb67f2
DL
425 "%s[%s]:\n"
426 "\tnode %d (%s) does not exist.\n"
427 "\tplease call install_node() before uninstall_element()\n",
428 cmd->name, cmd->string, ntype, node_names[ntype]);
d62a17ae 429 exit(EXIT_FAILURE);
430 }
de8f7a39 431
d62a17ae 432 if (hash_release(cnode->cmd_hash, cmd) == NULL) {
433 fprintf(stderr,
3cbb67f2
DL
434 "%s[%s]:\n"
435 "\tnode %d (%s) does not have this command installed.\n"
436 "\tduplicate uninstall_element call?\n",
437 cmd->name, cmd->string, ntype, node_names[ntype]);
d62a17ae 438 return;
439 }
de8f7a39 440
d62a17ae 441 vector_unset_value(cnode->cmd_vector, cmd);
de8f7a39 442
d62a17ae 443 struct graph *graph = graph_new();
444 struct cmd_token *token =
445 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
446 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
de8f7a39 447
d62a17ae 448 cmd_graph_parse(graph, cmd);
449 cmd_graph_names(graph);
450 cmd_graph_merge(cnode->cmdgraph, graph, -1);
451 graph_delete_graph(graph);
de8f7a39 452
d62a17ae 453 if (ntype == VIEW_NODE)
454 uninstall_element(ENABLE_NODE, cmd);
de8f7a39
DL
455}
456
457
2d362d10 458static const unsigned char itoa64[] =
d62a17ae 459 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
718e3744 460
d62a17ae 461static void to64(char *s, long v, int n)
718e3744 462{
d62a17ae 463 while (--n >= 0) {
464 *s++ = itoa64[v & 0x3f];
465 v >>= 6;
466 }
718e3744 467}
468
d62a17ae 469static char *zencrypt(const char *passwd)
718e3744 470{
d62a17ae 471 char salt[6];
472 struct timeval tv;
473 char *crypt(const char *, const char *);
718e3744 474
d62a17ae 475 gettimeofday(&tv, 0);
d0bfb22c 476
d62a17ae 477 to64(&salt[0], random(), 3);
478 to64(&salt[3], tv.tv_usec, 3);
479 salt[5] = '\0';
718e3744 480
d62a17ae 481 return crypt(passwd, salt);
718e3744 482}
483
484/* This function write configuration of this host. */
d62a17ae 485static int config_write_host(struct vty *vty)
486{
6b3ee3a0
MK
487 if (cmd_hostname_get())
488 vty_out(vty, "hostname %s\n", cmd_hostname_get());
d62a17ae 489
3b103fec
MK
490 if (cmd_domainname_get())
491 vty_out(vty, "domainname %s\n", cmd_domainname_get());
492
0204baa8 493 /* The following are all configuration commands that are not sent to
3518f352
DS
494 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
495 * we would always display 'log syslog informational' in the config
496 * which would cause other daemons to then switch to syslog when they
497 * parse frr.conf.
498 */
0204baa8
DW
499 if (strcmp(zlog_default->protoname, "WATCHFRR")) {
500 if (host.encrypt) {
501 if (host.password_encrypt)
3518f352
DS
502 vty_out(vty, "password 8 %s\n",
503 host.password_encrypt);
0204baa8
DW
504 if (host.enable_encrypt)
505 vty_out(vty, "enable password 8 %s\n",
506 host.enable_encrypt);
507 } else {
508 if (host.password)
509 vty_out(vty, "password %s\n", host.password);
510 if (host.enable)
3518f352
DS
511 vty_out(vty, "enable password %s\n",
512 host.enable);
0204baa8 513 }
d62a17ae 514
0204baa8 515 if (zlog_default->default_lvl != LOG_DEBUG) {
3518f352
DS
516 vty_out(vty,
517 "! N.B. The 'log trap' command is deprecated.\n");
0204baa8
DW
518 vty_out(vty, "log trap %s\n",
519 zlog_priority[zlog_default->default_lvl]);
520 }
d62a17ae 521
0204baa8 522 if (host.logfile
3518f352
DS
523 && (zlog_default->maxlvl[ZLOG_DEST_FILE]
524 != ZLOG_DISABLED)) {
0204baa8
DW
525 vty_out(vty, "log file %s", host.logfile);
526 if (zlog_default->maxlvl[ZLOG_DEST_FILE]
527 != zlog_default->default_lvl)
528 vty_out(vty, " %s",
529 zlog_priority
3518f352
DS
530 [zlog_default->maxlvl
531 [ZLOG_DEST_FILE]]);
0204baa8
DW
532 vty_out(vty, "\n");
533 }
d62a17ae 534
0204baa8
DW
535 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
536 vty_out(vty, "log stdout");
537 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT]
538 != zlog_default->default_lvl)
539 vty_out(vty, " %s",
3518f352
DS
540 zlog_priority
541 [zlog_default->maxlvl
542 [ZLOG_DEST_STDOUT]]);
0204baa8
DW
543 vty_out(vty, "\n");
544 }
d62a17ae 545
0204baa8
DW
546 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
547 vty_out(vty, "no log monitor\n");
548 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
549 != zlog_default->default_lvl)
550 vty_out(vty, "log monitor %s\n",
d62a17ae 551 zlog_priority[zlog_default->maxlvl
3518f352 552 [ZLOG_DEST_MONITOR]]);
0204baa8
DW
553
554 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
555 vty_out(vty, "log syslog");
556 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG]
557 != zlog_default->default_lvl)
558 vty_out(vty, " %s",
559 zlog_priority[zlog_default->maxlvl
560 [ZLOG_DEST_SYSLOG]]);
561 vty_out(vty, "\n");
562 }
d62a17ae 563
0204baa8
DW
564 if (zlog_default->facility != LOG_DAEMON)
565 vty_out(vty, "log facility %s\n",
566 facility_name(zlog_default->facility));
d62a17ae 567
0204baa8
DW
568 if (zlog_default->record_priority == 1)
569 vty_out(vty, "log record-priority\n");
d62a17ae 570
0204baa8
DW
571 if (zlog_default->timestamp_precision > 0)
572 vty_out(vty, "log timestamp precision %d\n",
573 zlog_default->timestamp_precision);
d62a17ae 574
0204baa8
DW
575 if (host.advanced)
576 vty_out(vty, "service advanced-vty\n");
d62a17ae 577
0204baa8
DW
578 if (host.encrypt)
579 vty_out(vty, "service password-encryption\n");
d62a17ae 580
0204baa8 581 if (host.lines >= 0)
3518f352
DS
582 vty_out(vty, "service terminal-length %d\n",
583 host.lines);
d62a17ae 584
0204baa8
DW
585 if (host.motdfile)
586 vty_out(vty, "banner motd file %s\n", host.motdfile);
587 else if (!host.motd)
588 vty_out(vty, "no banner motd\n");
589 }
d62a17ae 590
9eed278b
DL
591 if (debug_memstats_at_exit)
592 vty_out(vty, "!\ndebug memstats-at-exit\n");
593
d62a17ae 594 return 1;
718e3744 595}
596
d0bfb22c 597/* Utility function for getting command graph. */
d62a17ae 598static struct graph *cmd_node_graph(vector v, enum node_type ntype)
718e3744 599{
d62a17ae 600 struct cmd_node *cnode = vector_slot(v, ntype);
601 return cnode->cmdgraph;
718e3744 602}
603
d62a17ae 604static int cmd_try_do_shortcut(enum node_type node, char *first_word)
605{
606 if (first_word != NULL && node != AUTH_NODE && node != VIEW_NODE
607 && node != AUTH_ENABLE_NODE && 0 == strcmp("do", first_word))
608 return 1;
609 return 0;
718e3744 610}
611
d0bfb22c
QY
612/**
613 * Compare function for cmd_token.
614 * Used with qsort to sort command completions.
615 */
d62a17ae 616static int compare_completions(const void *fst, const void *snd)
718e3744 617{
d62a17ae 618 struct cmd_token *first = *(struct cmd_token **)fst,
619 *secnd = *(struct cmd_token **)snd;
620 return strcmp(first->text, secnd->text);
718e3744 621}
622
d0bfb22c
QY
623/**
624 * Takes a list of completions returned by command_complete,
625 * dedeuplicates them based on both text and description,
6011c1b2
QY
626 * sorts them, and returns them as a vector.
627 *
628 * @param completions linked list of cmd_token
629 * @return deduplicated and sorted vector with
d0bfb22c 630 */
d62a17ae 631vector completions_to_vec(struct list *completions)
632{
633 vector comps = vector_init(VECTOR_MIN_SIZE);
634
635 struct listnode *ln;
636 struct cmd_token *token, *cr = NULL;
637 unsigned int i, exists;
638 for (ALL_LIST_ELEMENTS_RO(completions, ln, token)) {
639 if (token->type == END_TKN && (cr = token))
640 continue;
641
642 // linear search for token in completions vector
643 exists = 0;
644 for (i = 0; i < vector_active(comps) && !exists; i++) {
645 struct cmd_token *curr = vector_slot(comps, i);
53205694 646#ifdef VTYSH_DEBUG
d62a17ae 647 exists = !strcmp(curr->text, token->text)
648 && !strcmp(curr->desc, token->desc);
53205694 649#else
d62a17ae 650 exists = !strcmp(curr->text, token->text);
53205694 651#endif /* VTYSH_DEBUG */
d62a17ae 652 }
909a2155 653
d62a17ae 654 if (!exists)
655 vector_set(comps, token);
656 }
cd40b329 657
d62a17ae 658 // sort completions
659 qsort(comps->index, vector_active(comps), sizeof(void *),
660 &compare_completions);
cd40b329 661
d62a17ae 662 // make <cr> the first element, if it is present
663 if (cr) {
664 vector_set_index(comps, vector_active(comps), NULL);
665 memmove(comps->index + 1, comps->index,
666 (comps->alloced - 1) * sizeof(void *));
667 vector_set_index(comps, 0, cr);
668 }
6011c1b2 669
d62a17ae 670 return comps;
cd40b329 671}
d0bfb22c
QY
672/**
673 * Generates a vector of cmd_token representing possible completions
674 * on the current input.
675 *
676 * @param vline the vectorized input line
677 * @param vty the vty with the node to match on
678 * @param status pointer to matcher status code
ebacb4ed 679 * @return vector of struct cmd_token * with possible completions
d0bfb22c 680 */
d62a17ae 681static vector cmd_complete_command_real(vector vline, struct vty *vty,
682 int *status)
cd40b329 683{
d62a17ae 684 struct list *completions;
685 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
cd40b329 686
d62a17ae 687 enum matcher_rv rv = command_complete(cmdgraph, vline, &completions);
cd40b329 688
d62a17ae 689 if (MATCHER_ERROR(rv)) {
690 *status = CMD_ERR_NO_MATCH;
691 return NULL;
692 }
b92938a7 693
d62a17ae 694 vector comps = completions_to_vec(completions);
affe9e99 695 list_delete_and_null(&completions);
b92938a7 696
d62a17ae 697 // set status code appropriately
698 switch (vector_active(comps)) {
699 case 0:
700 *status = CMD_ERR_NO_MATCH;
701 break;
702 case 1:
703 *status = CMD_COMPLETE_FULL_MATCH;
704 break;
705 default:
706 *status = CMD_COMPLETE_LIST_MATCH;
707 }
718e3744 708
d62a17ae 709 return comps;
d0bfb22c 710}
718e3744 711
d62a17ae 712vector cmd_describe_command(vector vline, struct vty *vty, int *status)
d0bfb22c 713{
d62a17ae 714 vector ret;
718e3744 715
d62a17ae 716 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
717 enum node_type onode;
718 vector shifted_vline;
719 unsigned int index;
718e3744 720
d62a17ae 721 onode = vty->node;
722 vty->node = ENABLE_NODE;
723 /* We can try it on enable node, cos' the vty is authenticated
724 */
718e3744 725
d62a17ae 726 shifted_vline = vector_init(vector_count(vline));
727 /* use memcpy? */
728 for (index = 1; index < vector_active(vline); index++) {
729 vector_set_index(shifted_vline, index - 1,
730 vector_lookup(vline, index));
731 }
718e3744 732
d62a17ae 733 ret = cmd_complete_command_real(shifted_vline, vty, status);
d0bfb22c 734
d62a17ae 735 vector_free(shifted_vline);
736 vty->node = onode;
737 return ret;
738 }
718e3744 739
d62a17ae 740 return cmd_complete_command_real(vline, vty, status);
718e3744 741}
742
70d44c5c
DL
743static struct list *varhandlers = NULL;
744
d62a17ae 745void cmd_variable_complete(struct cmd_token *token, const char *arg,
746 vector comps)
747{
748 struct listnode *ln;
749 const struct cmd_variable_handler *cvh;
750 size_t i, argsz;
751 vector tmpcomps;
752
753 tmpcomps = arg ? vector_init(VECTOR_MIN_SIZE) : comps;
754
755 for (ALL_LIST_ELEMENTS_RO(varhandlers, ln, cvh)) {
756 if (cvh->tokenname && strcmp(cvh->tokenname, token->text))
757 continue;
9d303b37
DL
758 if (cvh->varname && (!token->varname
759 || strcmp(cvh->varname, token->varname)))
d62a17ae 760 continue;
761 cvh->completions(tmpcomps, token);
762 break;
763 }
764
765 if (!arg)
766 return;
767
768 argsz = strlen(arg);
769 for (i = vector_active(tmpcomps); i; i--) {
770 char *item = vector_slot(tmpcomps, i - 1);
771 if (strlen(item) >= argsz && !strncmp(item, arg, argsz))
772 vector_set(comps, item);
773 else
774 XFREE(MTYPE_COMPLETION, item);
775 }
776 vector_free(tmpcomps);
70d44c5c
DL
777}
778
1a0f614d
QY
779#define AUTOCOMP_INDENT 5
780
d62a17ae 781char *cmd_variable_comp2str(vector comps, unsigned short cols)
1a0f614d 782{
d62a17ae 783 size_t bsz = 16;
784 char *buf = XCALLOC(MTYPE_TMP, bsz);
785 int lc = AUTOCOMP_INDENT;
786 size_t cs = AUTOCOMP_INDENT;
787 size_t itemlen;
788 snprintf(buf, bsz, "%*s", AUTOCOMP_INDENT, "");
789 for (size_t j = 0; j < vector_active(comps); j++) {
790 char *item = vector_slot(comps, j);
791 itemlen = strlen(item);
1a0f614d 792
d62a17ae 793 if (cs + itemlen + AUTOCOMP_INDENT + 3 >= bsz)
794 buf = XREALLOC(MTYPE_TMP, buf, (bsz *= 2));
1a0f614d 795
d62a17ae 796 if (lc + itemlen + 1 >= cols) {
797 cs += snprintf(&buf[cs], bsz - cs, "\n%*s",
798 AUTOCOMP_INDENT, "");
799 lc = AUTOCOMP_INDENT;
800 }
1a0f614d 801
d62a17ae 802 size_t written = snprintf(&buf[cs], bsz - cs, "%s ", item);
803 lc += written;
804 cs += written;
805 XFREE(MTYPE_COMPLETION, item);
806 vector_set_index(comps, j, NULL);
807 }
808 return buf;
1a0f614d
QY
809}
810
d62a17ae 811void cmd_variable_handler_register(const struct cmd_variable_handler *cvh)
70d44c5c 812{
d62a17ae 813 if (!varhandlers)
814 return;
70d44c5c 815
d62a17ae 816 for (; cvh->completions; cvh++)
817 listnode_add(varhandlers, (void *)cvh);
70d44c5c
DL
818}
819
7f059ea6
DL
820DEFUN_HIDDEN (autocomplete,
821 autocomplete_cmd,
822 "autocomplete TYPE TEXT VARNAME",
823 "Autocompletion handler (internal, for vtysh)\n"
824 "cmd_token->type\n"
825 "cmd_token->text\n"
826 "cmd_token->varname\n")
827{
d62a17ae 828 struct cmd_token tok;
829 vector comps = vector_init(32);
830 size_t i;
7f059ea6 831
d62a17ae 832 memset(&tok, 0, sizeof(tok));
833 tok.type = atoi(argv[1]->arg);
834 tok.text = argv[2]->arg;
835 tok.varname = argv[3]->arg;
836 if (!strcmp(tok.varname, "-"))
837 tok.varname = NULL;
7f059ea6 838
d62a17ae 839 cmd_variable_complete(&tok, NULL, comps);
7f059ea6 840
d62a17ae 841 for (i = 0; i < vector_active(comps); i++) {
842 char *text = vector_slot(comps, i);
843 vty_out(vty, "%s\n", text);
844 XFREE(MTYPE_COMPLETION, text);
845 }
7f059ea6 846
d62a17ae 847 vector_free(comps);
848 return CMD_SUCCESS;
7f059ea6
DL
849}
850
ebacb4ed
QY
851/**
852 * Generate possible tab-completions for the given input. This function only
853 * returns results that would result in a valid command if used as Readline
854 * completions (as is the case in vtysh). For instance, if the passed vline ends
855 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
856 *
857 * @param vline vectorized input line
858 * @param vty the vty
859 * @param status location to store matcher status code in
860 * @return set of valid strings for use with Readline as tab-completions.
861 */
862
d62a17ae 863char **cmd_complete_command(vector vline, struct vty *vty, int *status)
864{
865 char **ret = NULL;
866 int original_node = vty->node;
867 vector input_line = vector_init(vector_count(vline));
868
869 // if the first token is 'do' we'll want to execute the command in the
870 // enable node
871 int do_shortcut = cmd_try_do_shortcut(vty->node, vector_slot(vline, 0));
872 vty->node = do_shortcut ? ENABLE_NODE : original_node;
873
874 // construct the input line we'll be matching on
875 unsigned int offset = (do_shortcut) ? 1 : 0;
876 for (unsigned index = 0; index + offset < vector_active(vline); index++)
877 vector_set_index(input_line, index,
878 vector_lookup(vline, index + offset));
879
880 // get token completions -- this is a copying operation
881 vector comps = NULL, initial_comps;
882 initial_comps = cmd_complete_command_real(input_line, vty, status);
883
884 if (!MATCHER_ERROR(*status)) {
885 assert(initial_comps);
886 // filter out everything that is not suitable for a
887 // tab-completion
888 comps = vector_init(VECTOR_MIN_SIZE);
889 for (unsigned int i = 0; i < vector_active(initial_comps);
890 i++) {
891 struct cmd_token *token = vector_slot(initial_comps, i);
892 if (token->type == WORD_TKN)
893 vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
894 token->text));
895 else if (IS_VARYING_TOKEN(token->type)) {
896 const char *ref = vector_lookup(
897 vline, vector_active(vline) - 1);
898 cmd_variable_complete(token, ref, comps);
899 }
900 }
901 vector_free(initial_comps);
902
903 // since we filtered results, we need to re-set status code
904 switch (vector_active(comps)) {
905 case 0:
906 *status = CMD_ERR_NO_MATCH;
907 break;
908 case 1:
909 *status = CMD_COMPLETE_FULL_MATCH;
910 break;
911 default:
912 *status = CMD_COMPLETE_LIST_MATCH;
913 }
914
915 // copy completions text into an array of char*
916 ret = XMALLOC(MTYPE_TMP,
917 (vector_active(comps) + 1) * sizeof(char *));
918 unsigned int i;
919 for (i = 0; i < vector_active(comps); i++) {
920 ret[i] = vector_slot(comps, i);
921 }
922 // set the last element to NULL, because this array is used in
923 // a Readline completion_generator function which expects NULL
924 // as a sentinel value
925 ret[i] = NULL;
926 vector_free(comps);
927 comps = NULL;
928 } else if (initial_comps)
929 vector_free(initial_comps);
930
931 // comps should always be null here
932 assert(!comps);
933
934 // free the adjusted input line
935 vector_free(input_line);
936
937 // reset vty->node to its original value
938 vty->node = original_node;
939
940 return ret;
cde9f101 941}
b92938a7 942
b92938a7 943/* return parent node */
944/* MUST eventually converge on CONFIG_NODE */
d62a17ae 945enum node_type node_parent(enum node_type node)
946{
947 enum node_type ret;
948
949 assert(node > CONFIG_NODE);
950
951 switch (node) {
952 case BGP_VPNV4_NODE:
953 case BGP_VPNV6_NODE:
7c40bf39 954 case BGP_FLOWSPECV4_NODE:
955 case BGP_FLOWSPECV6_NODE:
d62a17ae 956 case BGP_VRF_POLICY_NODE:
957 case BGP_VNC_DEFAULTS_NODE:
958 case BGP_VNC_NVE_GROUP_NODE:
959 case BGP_VNC_L2_GROUP_NODE:
960 case BGP_IPV4_NODE:
961 case BGP_IPV4M_NODE:
962 case BGP_IPV4L_NODE:
963 case BGP_IPV6_NODE:
964 case BGP_IPV6M_NODE:
965 case BGP_EVPN_NODE:
966 case BGP_IPV6L_NODE:
967 ret = BGP_NODE;
968 break;
969 case BGP_EVPN_VNI_NODE:
970 ret = BGP_EVPN_NODE;
971 break;
972 case KEYCHAIN_KEY_NODE:
973 ret = KEYCHAIN_NODE;
974 break;
975 case LINK_PARAMS_NODE:
976 ret = INTERFACE_NODE;
977 break;
978 case LDP_IPV4_NODE:
979 case LDP_IPV6_NODE:
980 ret = LDP_NODE;
981 break;
982 case LDP_IPV4_IFACE_NODE:
983 ret = LDP_IPV4_NODE;
984 break;
985 case LDP_IPV6_IFACE_NODE:
986 ret = LDP_IPV6_NODE;
987 break;
988 case LDP_PSEUDOWIRE_NODE:
989 ret = LDP_L2VPN_NODE;
990 break;
991 default:
992 ret = CONFIG_NODE;
993 break;
994 }
995
996 return ret;
b92938a7 997}
998
718e3744 999/* Execute command by argument vline vector. */
d62a17ae 1000static int cmd_execute_command_real(vector vline, enum filter_type filter,
1001 struct vty *vty,
1002 const struct cmd_element **cmd)
1003{
1004 struct list *argv_list;
1005 enum matcher_rv status;
1006 const struct cmd_element *matched_element = NULL;
1007
1008 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
1009 status = command_match(cmdgraph, vline, &argv_list, &matched_element);
1010
1011 if (cmd)
1012 *cmd = matched_element;
1013
1014 // if matcher error, return corresponding CMD_ERR
1015 if (MATCHER_ERROR(status)) {
1016 if (argv_list)
affe9e99 1017 list_delete_and_null(&argv_list);
d62a17ae 1018 switch (status) {
1019 case MATCHER_INCOMPLETE:
1020 return CMD_ERR_INCOMPLETE;
1021 case MATCHER_AMBIGUOUS:
1022 return CMD_ERR_AMBIGUOUS;
1023 default:
1024 return CMD_ERR_NO_MATCH;
1025 }
1026 }
1027
1028 // build argv array from argv list
1029 struct cmd_token **argv = XMALLOC(
1030 MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *));
1031 struct listnode *ln;
1032 struct cmd_token *token;
1033 unsigned int i = 0;
1034 for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token))
1035 argv[i++] = token;
1036
1037 int argc = argv_list->count;
1038
1039 int ret;
1040 if (matched_element->daemon)
1041 ret = CMD_SUCCESS_DAEMON;
1042 else
1043 ret = matched_element->func(matched_element, vty, argc, argv);
1044
1045 // delete list and cmd_token's in it
affe9e99 1046 list_delete_and_null(&argv_list);
d62a17ae 1047 XFREE(MTYPE_TMP, argv);
1048
1049 return ret;
718e3744 1050}
1051
cd40b329
CF
1052/**
1053 * Execute a given command, handling things like "do ..." and checking
1054 * whether the given command might apply at a parent node if doesn't
1055 * apply for the current node.
1056 *
1057 * @param vline Command line input, vector of char* where each element is
1058 * one input token.
1059 * @param vty The vty context in which the command should be executed.
1060 * @param cmd Pointer where the struct cmd_element of the matched command
1061 * will be stored, if any. May be set to NULL if this info is
1062 * not needed.
1063 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1064 * @return The status of the command that has been executed or an error code
1065 * as to why no command could be executed.
1066 */
d62a17ae 1067int cmd_execute_command(vector vline, struct vty *vty,
1068 const struct cmd_element **cmd, int vtysh)
17aca20b 1069{
d62a17ae 1070 int ret, saved_ret = 0;
1071 enum node_type onode, try_node;
eda031f6 1072
d62a17ae 1073 onode = try_node = vty->node;
b92938a7 1074
d62a17ae 1075 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1076 vector shifted_vline;
1077 unsigned int index;
b92938a7 1078
d62a17ae 1079 vty->node = ENABLE_NODE;
1080 /* We can try it on enable node, cos' the vty is authenticated
1081 */
b92938a7 1082
d62a17ae 1083 shifted_vline = vector_init(vector_count(vline));
1084 /* use memcpy? */
1085 for (index = 1; index < vector_active(vline); index++)
1086 vector_set_index(shifted_vline, index - 1,
1087 vector_lookup(vline, index));
b92938a7 1088
d62a17ae 1089 ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
1090 vty, cmd);
b92938a7 1091
d62a17ae 1092 vector_free(shifted_vline);
1093 vty->node = onode;
1094 return ret;
1095 }
b92938a7 1096
d62a17ae 1097 saved_ret = ret =
1098 cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd);
b92938a7 1099
d62a17ae 1100 if (vtysh)
1101 return saved_ret;
87d683b0 1102
825d5fbf
CS
1103 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1104 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
d62a17ae 1105 /* This assumes all nodes above CONFIG_NODE are childs of
1106 * CONFIG_NODE */
1107 while (vty->node > CONFIG_NODE) {
1108 try_node = node_parent(try_node);
1109 vty->node = try_node;
1110 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
1111 vty, cmd);
825d5fbf
CS
1112 if (ret == CMD_SUCCESS || ret == CMD_WARNING
1113 || ret == CMD_NOT_MY_INSTANCE
1114 || ret == CMD_WARNING_CONFIG_FAILED)
d62a17ae 1115 return ret;
1116 }
1117 /* no command succeeded, reset the vty to the original node */
1118 vty->node = onode;
1119 }
04e64062 1120
d62a17ae 1121 /* return command status for original node */
1122 return saved_ret;
b92938a7 1123}
1124
cd40b329
CF
1125/**
1126 * Execute a given command, matching it strictly against the current node.
1127 * This mode is used when reading config files.
1128 *
1129 * @param vline Command line input, vector of char* where each element is
1130 * one input token.
1131 * @param vty The vty context in which the command should be executed.
1132 * @param cmd Pointer where the struct cmd_element* of the matched command
1133 * will be stored, if any. May be set to NULL if this info is
1134 * not needed.
1135 * @return The status of the command that has been executed or an error code
1136 * as to why no command could be executed.
1137 */
d62a17ae 1138int cmd_execute_command_strict(vector vline, struct vty *vty,
1139 const struct cmd_element **cmd)
718e3744 1140{
d62a17ae 1141 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
718e3744 1142}
1143
01e24c4a
QY
1144/*
1145 * Hook for preprocessing command string before executing.
1146 *
1147 * All subscribers are called with the raw command string that is to be
1148 * executed. If any changes are to be made, a new string should be allocated
1149 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1150 * is then responsible for freeing this string.
1151 *
1152 * All processing functions must be mutually exclusive in their action, i.e. if
1153 * one subscriber decides to modify the command, all others must not modify it
1154 * when called. Feeding the output of one processing command into a subsequent
1155 * one is not supported.
1156 *
1157 * This hook is intentionally internal to the command processing system.
1158 *
1159 * cmd_in
1160 * The raw command string.
1161 *
1162 * cmd_out
1163 * The result of any processing.
1164 */
1165DECLARE_HOOK(cmd_execute,
1166 (struct vty * vty, const char *cmd_in, char **cmd_out),
1167 (vty, cmd_in, cmd_out));
1168DEFINE_HOOK(cmd_execute, (struct vty * vty, const char *cmd_in, char **cmd_out),
1169 (vty, cmd_in, cmd_out));
1170
1171/* Hook executed after a CLI command. */
1172DECLARE_KOOH(cmd_execute_done, (struct vty * vty, const char *cmd_exec),
1173 (vty, cmd_exec));
1174DEFINE_KOOH(cmd_execute_done, (struct vty * vty, const char *cmd_exec),
1175 (vty, cmd_exec));
1176
fe6b47b9
QY
1177/*
1178 * cmd_execute hook subscriber to handle `|` actions.
1179 */
1180static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1181 char **cmd_out)
1182{
1183 /* look for `|` */
1184 char *orig, *working, *token;
1185 char *pipe = strstr(cmd_in, "| ");
1186
1187 if (!pipe)
1188 return 0;
1189
1190 /* duplicate string for processing purposes, not including pipe */
1191 orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1192
1193 /* retrieve action */
1194 token = strsep(&working, " ");
1195
1196 /* match result to known actions */
1197 if (strmatch(token, "include")) {
1198 /* the remaining text should be a regexp */
1199 char *regexp = working;
1200 bool succ = vty_set_include(vty, regexp);
1201 if (!succ) {
1202 vty_out(vty, "%% Bad regexp '%s'", regexp);
1203 goto fail;
1204 }
1205 cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
1206 *(strstr(cmd_in, "|")) = '\0';
1207 } else {
1208 vty_out(vty, "%% Unknown action '%s'", token);
1209 goto fail;
1210 }
1211
1212fail:
1213 XFREE(MTYPE_TMP, orig);
1214 return 0;
1215}
1216
1217static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1218{
1219 if (vty->filter) {
1220 vty_set_include(vty, NULL);
1221 vty_out(vty, "\n");
1222 }
1223 return 0;
1224}
1225
01e24c4a
QY
1226int cmd_execute(struct vty *vty, const char *cmd,
1227 const struct cmd_element **matched, int vtysh)
1228{
1229 int ret;
1230 char *cmd_out = NULL;
1231 const char *cmd_exec;
1232 vector vline;
1233
1234 hook_call(cmd_execute, vty, cmd, &cmd_out);
1235 cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1236
1237 vline = cmd_make_strvec(cmd_exec);
1238
1239 if (vline) {
1240 ret = cmd_execute_command(vline, vty, matched, vtysh);
1241 cmd_free_strvec(vline);
1242 } else {
1243 ret = CMD_SUCCESS;
1244 }
1245
1246 hook_call(cmd_execute_done, vty, cmd_exec);
1247
1248 if (cmd_out)
1249 XFREE(MTYPE_TMP, cmd_out);
1250
1251 return ret;
1252}
1253
1254
bed578b8 1255/**
d62a17ae 1256 * Parse one line of config, walking up the parse tree attempting to find a
1257 * match
bed578b8
DS
1258 *
1259 * @param vty The vty context in which the command should be executed.
1260 * @param cmd Pointer where the struct cmd_element* of the match command
1261 * will be stored, if any. May be set to NULL if this info is
1262 * not needed.
d62a17ae 1263 * @param use_daemon Boolean to control whether or not we match on
1264 * CMD_SUCCESS_DAEMON
bed578b8
DS
1265 * or not.
1266 * @return The status of the command that has been executed or an error code
1267 * as to why no command could be executed.
1268 */
d62a17ae 1269int command_config_read_one_line(struct vty *vty,
1270 const struct cmd_element **cmd, int use_daemon)
bed578b8 1271{
d62a17ae 1272 vector vline;
1273 int saved_node;
1274 int ret;
bed578b8 1275
d62a17ae 1276 vline = cmd_make_strvec(vty->buf);
bed578b8 1277
d62a17ae 1278 /* In case of comment line */
1279 if (vline == NULL)
1280 return CMD_SUCCESS;
bed578b8 1281
d62a17ae 1282 /* Execute configuration command : this is strict match */
1283 ret = cmd_execute_command_strict(vline, vty, cmd);
bed578b8 1284
d62a17ae 1285 // Climb the tree and try the command again at each node
1286 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1287 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1288 && ret != CMD_SUCCESS && ret != CMD_WARNING
996c9314 1289 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
d62a17ae 1290 && vty->node != CONFIG_NODE) {
bed578b8 1291
d62a17ae 1292 saved_node = vty->node;
bed578b8 1293
d62a17ae 1294 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1295 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1296 && ret != CMD_SUCCESS && ret != CMD_WARNING
1297 && vty->node > CONFIG_NODE) {
1298 vty->node = node_parent(vty->node);
1299 ret = cmd_execute_command_strict(vline, vty, cmd);
1300 }
bed578b8 1301
d62a17ae 1302 // If climbing the tree did not work then ignore the command and
1303 // stay at the same node
1304 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1305 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1306 && ret != CMD_SUCCESS && ret != CMD_WARNING) {
1307 vty->node = saved_node;
1308 }
1309 }
bed578b8 1310
d62a17ae 1311 if (ret != CMD_SUCCESS && ret != CMD_WARNING)
1312 memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ);
cbd7259d 1313
d62a17ae 1314 cmd_free_strvec(vline);
bed578b8 1315
d62a17ae 1316 return ret;
bed578b8
DS
1317}
1318
5689fe5f 1319/* Configuration make from file. */
d62a17ae 1320int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
718e3744 1321{
d62a17ae 1322 int ret, error_ret = 0;
1323 *line_num = 0;
718e3744 1324
d62a17ae 1325 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
1326 if (!error_ret)
1327 ++(*line_num);
13fbc82d 1328
d62a17ae 1329 ret = command_config_read_one_line(vty, NULL, 0);
718e3744 1330
d62a17ae 1331 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1332 && ret != CMD_ERR_NOTHING_TODO)
1333 error_ret = ret;
1334 }
5689fe5f 1335
d62a17ae 1336 if (error_ret) {
1337 return error_ret;
1338 }
5689fe5f 1339
d62a17ae 1340 return CMD_SUCCESS;
718e3744 1341}
1342
5689fe5f 1343/* Configuration from terminal */
718e3744 1344DEFUN (config_terminal,
1345 config_terminal_cmd,
1346 "configure terminal",
1347 "Configuration from vty interface\n"
1348 "Configuration terminal\n")
1349{
d62a17ae 1350 if (vty_config_lock(vty))
1351 vty->node = CONFIG_NODE;
1352 else {
1353 vty_out(vty, "VTY configuration is locked by other VTY\n");
1354 return CMD_WARNING_CONFIG_FAILED;
1355 }
1356 return CMD_SUCCESS;
718e3744 1357}
1358
1359/* Enable command */
d0bfb22c 1360DEFUN (enable,
718e3744 1361 config_enable_cmd,
1362 "enable",
1363 "Turn on privileged mode command\n")
1364{
d62a17ae 1365 /* If enable password is NULL, change to ENABLE_NODE */
1366 if ((host.enable == NULL && host.enable_encrypt == NULL)
1367 || vty->type == VTY_SHELL_SERV)
1368 vty->node = ENABLE_NODE;
1369 else
1370 vty->node = AUTH_ENABLE_NODE;
718e3744 1371
d62a17ae 1372 return CMD_SUCCESS;
718e3744 1373}
1374
1375/* Disable command */
d0bfb22c 1376DEFUN (disable,
718e3744 1377 config_disable_cmd,
1378 "disable",
1379 "Turn off privileged mode command\n")
1380{
d62a17ae 1381 if (vty->node == ENABLE_NODE)
1382 vty->node = VIEW_NODE;
1383 return CMD_SUCCESS;
718e3744 1384}
1385
1386/* Down vty node level. */
1387DEFUN (config_exit,
1388 config_exit_cmd,
1389 "exit",
1390 "Exit current mode and down to previous mode\n")
0b84f294 1391{
d62a17ae 1392 cmd_exit(vty);
1393 return CMD_SUCCESS;
1394}
1395
1396void cmd_exit(struct vty *vty)
1397{
1398 switch (vty->node) {
1399 case VIEW_NODE:
1400 case ENABLE_NODE:
1401 if (vty_shell(vty))
1402 exit(0);
1403 else
1404 vty->status = VTY_CLOSE;
1405 break;
1406 case CONFIG_NODE:
1407 vty->node = ENABLE_NODE;
1408 vty_config_unlock(vty);
1409 break;
1410 case INTERFACE_NODE:
2dd0d726 1411 case PW_NODE:
e26aedbe 1412 case LOGICALROUTER_NODE:
d62a17ae 1413 case VRF_NODE:
dba32923 1414 case NH_GROUP_NODE:
d62a17ae 1415 case ZEBRA_NODE:
1416 case BGP_NODE:
1417 case RIP_NODE:
1418 case EIGRP_NODE:
1419 case BABEL_NODE:
1420 case RIPNG_NODE:
1421 case OSPF_NODE:
1422 case OSPF6_NODE:
1423 case LDP_NODE:
1424 case LDP_L2VPN_NODE:
1425 case ISIS_NODE:
1426 case KEYCHAIN_NODE:
d62a17ae 1427 case RMAP_NODE:
e5c83d9b 1428 case PBRMAP_NODE:
d62a17ae 1429 case VTY_NODE:
1430 vty->node = CONFIG_NODE;
1431 break;
1432 case BGP_IPV4_NODE:
1433 case BGP_IPV4M_NODE:
1434 case BGP_IPV4L_NODE:
1435 case BGP_VPNV4_NODE:
1436 case BGP_VPNV6_NODE:
7c40bf39 1437 case BGP_FLOWSPECV4_NODE:
1438 case BGP_FLOWSPECV6_NODE:
d62a17ae 1439 case BGP_VRF_POLICY_NODE:
1440 case BGP_VNC_DEFAULTS_NODE:
1441 case BGP_VNC_NVE_GROUP_NODE:
1442 case BGP_VNC_L2_GROUP_NODE:
1443 case BGP_IPV6_NODE:
1444 case BGP_IPV6M_NODE:
1445 case BGP_EVPN_NODE:
1446 case BGP_IPV6L_NODE:
1447 vty->node = BGP_NODE;
1448 break;
1449 case BGP_EVPN_VNI_NODE:
1450 vty->node = BGP_EVPN_NODE;
1451 break;
1452 case LDP_IPV4_NODE:
1453 case LDP_IPV6_NODE:
1454 vty->node = LDP_NODE;
1455 break;
1456 case LDP_IPV4_IFACE_NODE:
1457 vty->node = LDP_IPV4_NODE;
1458 break;
1459 case LDP_IPV6_IFACE_NODE:
1460 vty->node = LDP_IPV6_NODE;
1461 break;
1462 case LDP_PSEUDOWIRE_NODE:
1463 vty->node = LDP_L2VPN_NODE;
1464 break;
1465 case KEYCHAIN_KEY_NODE:
1466 vty->node = KEYCHAIN_NODE;
1467 break;
1468 case LINK_PARAMS_NODE:
1469 vty->node = INTERFACE_NODE;
1470 break;
1471 default:
1472 break;
1473 }
718e3744 1474}
1475
f667a580
QY
1476/* ALIAS_FIXME */
1477DEFUN (config_quit,
1478 config_quit_cmd,
1479 "quit",
1480 "Exit current mode and down to previous mode\n")
1481{
d62a17ae 1482 return config_exit(self, vty, argc, argv);
f667a580
QY
1483}
1484
d0bfb22c 1485
718e3744 1486/* End of configuration. */
1487DEFUN (config_end,
1488 config_end_cmd,
1489 "end",
efd7904e 1490 "End current mode and change to enable mode.\n")
718e3744 1491{
d62a17ae 1492 switch (vty->node) {
1493 case VIEW_NODE:
1494 case ENABLE_NODE:
1495 /* Nothing to do. */
1496 break;
1497 case CONFIG_NODE:
1498 case INTERFACE_NODE:
2dd0d726 1499 case PW_NODE:
e26aedbe 1500 case LOGICALROUTER_NODE:
d62a17ae 1501 case VRF_NODE:
dba32923 1502 case NH_GROUP_NODE:
d62a17ae 1503 case ZEBRA_NODE:
1504 case RIP_NODE:
1505 case RIPNG_NODE:
1506 case EIGRP_NODE:
1507 case BABEL_NODE:
1508 case BGP_NODE:
1509 case BGP_VRF_POLICY_NODE:
1510 case BGP_VNC_DEFAULTS_NODE:
1511 case BGP_VNC_NVE_GROUP_NODE:
1512 case BGP_VNC_L2_GROUP_NODE:
1513 case BGP_VPNV4_NODE:
1514 case BGP_VPNV6_NODE:
7c40bf39 1515 case BGP_FLOWSPECV4_NODE:
1516 case BGP_FLOWSPECV6_NODE:
d62a17ae 1517 case BGP_IPV4_NODE:
1518 case BGP_IPV4M_NODE:
1519 case BGP_IPV4L_NODE:
1520 case BGP_IPV6_NODE:
1521 case BGP_IPV6M_NODE:
1522 case BGP_EVPN_NODE:
1523 case BGP_EVPN_VNI_NODE:
1524 case BGP_IPV6L_NODE:
1525 case RMAP_NODE:
e5c83d9b 1526 case PBRMAP_NODE:
d62a17ae 1527 case OSPF_NODE:
1528 case OSPF6_NODE:
1529 case LDP_NODE:
1530 case LDP_IPV4_NODE:
1531 case LDP_IPV6_NODE:
1532 case LDP_IPV4_IFACE_NODE:
1533 case LDP_IPV6_IFACE_NODE:
1534 case LDP_L2VPN_NODE:
1535 case LDP_PSEUDOWIRE_NODE:
1536 case ISIS_NODE:
1537 case KEYCHAIN_NODE:
1538 case KEYCHAIN_KEY_NODE:
d62a17ae 1539 case VTY_NODE:
1540 case LINK_PARAMS_NODE:
1541 vty_config_unlock(vty);
1542 vty->node = ENABLE_NODE;
1543 break;
1544 default:
1545 break;
1546 }
1547 return CMD_SUCCESS;
718e3744 1548}
1549
1550/* Show version. */
1551DEFUN (show_version,
1552 show_version_cmd,
1553 "show version",
1554 SHOW_STR
1555 "Displays zebra version\n")
1556{
d62a17ae 1557 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
6b3ee3a0 1558 cmd_hostname_get() ? cmd_hostname_get() : "");
d62a17ae 1559 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
1560 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
718e3744 1561
d62a17ae 1562 return CMD_SUCCESS;
718e3744 1563}
1564
8efe88ea
DL
1565/* "Set" version ... ignore version tags */
1566DEFUN (frr_version_defaults,
1567 frr_version_defaults_cmd,
3e7c8d04 1568 "frr <version|defaults> LINE...",
447a8fe9 1569 "FRRouting global parameters\n"
8efe88ea
DL
1570 "version configuration was written by\n"
1571 "set of configuration defaults used\n"
1572 "version string\n")
1573{
d62a17ae 1574 return CMD_SUCCESS;
8efe88ea
DL
1575}
1576
718e3744 1577/* Help display function for all node. */
1578DEFUN (config_help,
1579 config_help_cmd,
1580 "help",
1581 "Description of the interactive help system\n")
1582{
d62a17ae 1583 vty_out(vty,
1584 "Quagga VTY provides advanced help feature. When you need help,\n\
61b7d449
DL
1585anytime at the command line please press '?'.\n\
1586\n\
1587If nothing matches, the help list will be empty and you must backup\n\
1588 until entering a '?' shows the available options.\n\
1589Two styles of help are provided:\n\
15901. Full help is available when you are ready to enter a\n\
1591command argument (e.g. 'show ?') and describes each possible\n\
1592argument.\n\
15932. Partial help is provided when an abbreviated argument is entered\n\
1594 and you want to know what arguments match the input\n\
1595 (e.g. 'show me?'.)\n\n");
d62a17ae 1596 return CMD_SUCCESS;
1597}
1598
1599static void permute(struct graph_node *start, struct vty *vty)
1600{
1601 static struct list *position = NULL;
1602 if (!position)
1603 position = list_new();
1604
1605 struct cmd_token *stok = start->data;
1606 struct graph_node *gnn;
1607 struct listnode *ln;
1608
1609 // recursive dfs
1610 listnode_add(position, start);
1611 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1612 struct graph_node *gn = vector_slot(start->to, i);
1613 struct cmd_token *tok = gn->data;
1614 if (tok->attr == CMD_ATTR_HIDDEN
1615 || tok->attr == CMD_ATTR_DEPRECATED)
1616 continue;
1617 else if (tok->type == END_TKN || gn == start) {
1618 vty_out(vty, " ");
1619 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1620 struct cmd_token *tt = gnn->data;
1621 if (tt->type < SPECIAL_TKN)
1622 vty_out(vty, " %s", tt->text);
1623 }
1624 if (gn == start)
1625 vty_out(vty, "...");
1626 vty_out(vty, "\n");
1627 } else {
1628 bool skip = false;
1629 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1630 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1631 if (gnn == gn) {
1632 skip = true;
1633 break;
1634 }
1635 if (!skip)
1636 permute(gn, vty);
1637 }
1638 }
1639 list_delete_node(position, listtail(position));
1640}
1641
1642int cmd_list_cmds(struct vty *vty, int do_permute)
1643{
1644 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1645
1646 if (do_permute)
1647 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1648 else {
1649 /* loop over all commands at this node */
1650 struct cmd_element *element = NULL;
1651 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1652 i++)
1653 if ((element = vector_slot(node->cmd_vector, i))
1654 && element->attr != CMD_ATTR_DEPRECATED
1655 && element->attr != CMD_ATTR_HIDDEN)
1656 vty_out(vty, " %s\n", element->string);
1657 }
1658 return CMD_SUCCESS;
718e3744 1659}
1660
0b84f294
DL
1661/* Help display function for all node. */
1662DEFUN (config_list,
1663 config_list_cmd,
1664 "list [permutations]",
1665 "Print command list\n"
1666 "Print all possible command permutations\n")
1667{
d62a17ae 1668 return cmd_list_cmds(vty, argc == 2);
0b84f294
DL
1669}
1670
a2454870
QY
1671DEFUN (show_commandtree,
1672 show_commandtree_cmd,
1673 "show commandtree [permutations]",
1674 SHOW_STR
5a1945e4
DS
1675 "Show command tree\n"
1676 "Permutations that we are interested in\n")
a2454870 1677{
d62a17ae 1678 return cmd_list_cmds(vty, argc == 3);
a2454870
QY
1679}
1680
26fbe472
QY
1681DEFUN_HIDDEN(show_cli_graph,
1682 show_cli_graph_cmd,
1683 "show cli graph",
1684 SHOW_STR
1685 "CLI reflection\n"
1686 "Dump current command space as DOT graph\n")
1687{
1688 struct cmd_node *cn = vector_slot(cmdvec, vty->node);
1689 char *dot = cmd_graph_dump_dot(cn->cmdgraph);
1690
1691 vty_out(vty, "%s\n", dot);
1692 XFREE(MTYPE_TMP, dot);
1693 return CMD_SUCCESS;
1694}
1695
f806f29c 1696static int vty_write_config(struct vty *vty)
8efe88ea 1697{
d62a17ae 1698 size_t i;
1699 struct cmd_node *node;
8efe88ea 1700
f806f29c 1701 if (host.noconfig)
1702 return CMD_SUCCESS;
1703
d62a17ae 1704 if (vty->type == VTY_TERM) {
1705 vty_out(vty, "\nCurrent configuration:\n");
1706 vty_out(vty, "!\n");
1707 }
8efe88ea 1708
d62a17ae 1709 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
1710 vty_out(vty, "frr defaults %s\n", DFLT_NAME);
1711 vty_out(vty, "!\n");
8efe88ea 1712
d62a17ae 1713 for (i = 0; i < vector_active(cmdvec); i++)
1714 if ((node = vector_slot(cmdvec, i)) && node->func
1715 && (node->vtysh || vty->type != VTY_SHELL)) {
1716 if ((*node->func)(vty))
1717 vty_out(vty, "!\n");
1718 }
8efe88ea 1719
d62a17ae 1720 if (vty->type == VTY_TERM) {
1721 vty_out(vty, "end\n");
1722 }
8efe88ea 1723
f806f29c 1724 return CMD_SUCCESS;
1725}
d862bffb 1726
f806f29c 1727static int file_write_config(struct vty *vty)
718e3744 1728{
d62a17ae 1729 int fd, dirfd;
1730 char *config_file, *slash;
1731 char *config_file_tmp = NULL;
1732 char *config_file_sav = NULL;
1733 int ret = CMD_WARNING;
1734 struct vty *file_vty;
1735 struct stat conf_stat;
1736
d62a17ae 1737 if (host.noconfig)
1738 return CMD_SUCCESS;
1739
1740 /* Check and see if we are operating under vtysh configuration */
1741 if (host.config == NULL) {
1742 vty_out(vty,
1743 "Can't save to configuration file, using vtysh.\n");
1744 return CMD_WARNING;
1745 }
1746
1747 /* Get filename. */
1748 config_file = host.config;
d0bfb22c 1749
056cfe49
DL
1750#ifndef O_DIRECTORY
1751#define O_DIRECTORY 0
1752#endif
d62a17ae 1753 slash = strrchr(config_file, '/');
1754 if (slash) {
1755 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1756 config_dir[slash - config_file] = '\0';
1757 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1758 XFREE(MTYPE_TMP, config_dir);
1759 } else
1760 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1761 /* if dirfd is invalid, directory sync fails, but we're still OK */
1762
1763 config_file_sav = XMALLOC(
1764 MTYPE_TMP, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1);
1765 strcpy(config_file_sav, config_file);
1766 strcat(config_file_sav, CONF_BACKUP_EXT);
1767
1768
1769 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
1770 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
1771
1772 /* Open file to configuration write. */
1773 fd = mkstemp(config_file_tmp);
1774 if (fd < 0) {
1775 vty_out(vty, "Can't open configuration file %s.\n",
1776 config_file_tmp);
1777 goto finished;
1778 }
1779 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1780 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1781 config_file_tmp, safe_strerror(errno), errno);
1782 goto finished;
1783 }
1784
1785 /* Make vty for configuration file. */
1786 file_vty = vty_new();
1787 file_vty->wfd = fd;
1788 file_vty->type = VTY_FILE;
1789
1790 /* Config file header print. */
1791 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1792 vty_time_print(file_vty, 1);
1793 vty_out(file_vty, "!\n");
1794 vty_write_config(file_vty);
1795 vty_close(file_vty);
1796
1797 if (stat(config_file, &conf_stat) >= 0) {
1798 if (unlink(config_file_sav) != 0)
1799 if (errno != ENOENT) {
1800 vty_out(vty,
1801 "Can't unlink backup configuration file %s.\n",
1802 config_file_sav);
1803 goto finished;
1804 }
1805 if (link(config_file, config_file_sav) != 0) {
1806 vty_out(vty,
1807 "Can't backup old configuration file %s.\n",
1808 config_file_sav);
1809 goto finished;
1810 }
1811 if (dirfd >= 0)
1812 fsync(dirfd);
1813 }
1814 if (rename(config_file_tmp, config_file) != 0) {
1815 vty_out(vty, "Can't save configuration file %s.\n",
1816 config_file);
1817 goto finished;
1818 }
1819 if (dirfd >= 0)
1820 fsync(dirfd);
1821
1822 vty_out(vty, "Configuration saved to %s\n", config_file);
1823 ret = CMD_SUCCESS;
05865c90 1824
1825finished:
d62a17ae 1826 if (ret != CMD_SUCCESS)
1827 unlink(config_file_tmp);
1828 if (dirfd >= 0)
1829 close(dirfd);
1830 XFREE(MTYPE_TMP, config_file_tmp);
1831 XFREE(MTYPE_TMP, config_file_sav);
1832 return ret;
718e3744 1833}
1834
f806f29c 1835/* Write current configuration into file. */
1836
1837DEFUN (config_write,
1838 config_write_cmd,
1839 "write [<file|memory|terminal>]",
1840 "Write running configuration to memory, network, or terminal\n"
1841 "Write to configuration file\n"
1842 "Write configuration currently in memory\n"
1843 "Write configuration to terminal\n")
1844{
1845 const int idx_type = 1;
1846
1847 // if command was 'write terminal' or 'write memory'
1848 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1849 return vty_write_config(vty);
1850 }
1851
1852 return file_write_config(vty);
1853}
1854
d862bffb
QY
1855/* ALIAS_FIXME for 'write <terminal|memory>' */
1856DEFUN (show_running_config,
1857 show_running_config_cmd,
1858 "show running-config",
1859 SHOW_STR
f806f29c 1860 "running configuration (same as write terminal)\n")
d862bffb 1861{
f806f29c 1862 return vty_write_config(vty);
d862bffb 1863}
718e3744 1864
d862bffb
QY
1865/* ALIAS_FIXME for 'write file' */
1866DEFUN (copy_runningconf_startupconf,
1867 copy_runningconf_startupconf_cmd,
1868 "copy running-config startup-config",
1869 "Copy configuration\n"
1870 "Copy running config to... \n"
f806f29c 1871 "Copy running config to startup config (same as write file/memory)\n")
d862bffb 1872{
f806f29c 1873 return file_write_config(vty);
d862bffb
QY
1874}
1875/** -- **/
718e3744 1876
1877/* Write startup configuration into the terminal. */
1878DEFUN (show_startup_config,
1879 show_startup_config_cmd,
1880 "show startup-config",
1881 SHOW_STR
d0bfb22c 1882 "Contents of startup configuration\n")
718e3744 1883{
d62a17ae 1884 char buf[BUFSIZ];
1885 FILE *confp;
718e3744 1886
d62a17ae 1887 if (host.noconfig)
1888 return CMD_SUCCESS;
1889 if (host.config == NULL)
1890 return CMD_WARNING;
87f44e2f 1891
d62a17ae 1892 confp = fopen(host.config, "r");
1893 if (confp == NULL) {
1894 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1895 host.config, safe_strerror(errno));
1896 return CMD_WARNING;
1897 }
718e3744 1898
d62a17ae 1899 while (fgets(buf, BUFSIZ, confp)) {
1900 char *cp = buf;
718e3744 1901
d62a17ae 1902 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1903 cp++;
1904 *cp = '\0';
718e3744 1905
d62a17ae 1906 vty_out(vty, "%s\n", buf);
1907 }
718e3744 1908
d62a17ae 1909 fclose(confp);
718e3744 1910
d62a17ae 1911 return CMD_SUCCESS;
718e3744 1912}
1913
6b3ee3a0
MK
1914int cmd_domainname_set(const char *domainname)
1915{
1916 XFREE(MTYPE_HOST, host.domainname);
1917 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1918 return CMD_SUCCESS;
1919}
1920
1921/* Hostname configuration */
60466a63
QY
1922DEFUN(config_domainname,
1923 domainname_cmd,
1924 "domainname WORD",
1925 "Set system's domain name\n"
1926 "This system's domain name\n")
6b3ee3a0
MK
1927{
1928 struct cmd_token *word = argv[1];
1929
1930 if (!isalpha((int)word->arg[0])) {
1931 vty_out(vty, "Please specify string starting with alphabet\n");
1932 return CMD_WARNING_CONFIG_FAILED;
1933 }
1934
1935 return cmd_domainname_set(word->arg);
1936}
1937
60466a63
QY
1938DEFUN(config_no_domainname,
1939 no_domainname_cmd,
1940 "no domainname [DOMAINNAME]",
1941 NO_STR
1942 "Reset system's domain name\n"
1943 "domain name of this router\n")
6b3ee3a0
MK
1944{
1945 return cmd_domainname_set(NULL);
1946}
1947
d62a17ae 1948int cmd_hostname_set(const char *hostname)
bff9c3e9 1949{
d62a17ae 1950 XFREE(MTYPE_HOST, host.name);
1951 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1952 return CMD_SUCCESS;
bff9c3e9
DL
1953}
1954
718e3744 1955/* Hostname configuration */
d0bfb22c 1956DEFUN (config_hostname,
718e3744 1957 hostname_cmd,
1958 "hostname WORD",
1959 "Set system's network name\n"
1960 "This system's network name\n")
1961{
d62a17ae 1962 struct cmd_token *word = argv[1];
d0bfb22c 1963
d4f368e1 1964 if (!isalnum((int)word->arg[0])) {
d62a17ae 1965 vty_out(vty, "Please specify string starting with alphabet\n");
1966 return CMD_WARNING_CONFIG_FAILED;
1967 }
718e3744 1968
d62a17ae 1969 return cmd_hostname_set(word->arg);
718e3744 1970}
1971
d0bfb22c 1972DEFUN (config_no_hostname,
718e3744 1973 no_hostname_cmd,
1974 "no hostname [HOSTNAME]",
1975 NO_STR
1976 "Reset system's network name\n"
1977 "Host name of this router\n")
1978{
d62a17ae 1979 return cmd_hostname_set(NULL);
718e3744 1980}
1981
1982/* VTY interface password set. */
f412b39a
DW
1983DEFUN (config_password,
1984 password_cmd,
98463e0a 1985 "password [(8-8)] WORD",
322e2d5c 1986 "Modify the terminal connection password\n"
718e3744 1987 "Specifies a HIDDEN password will follow\n"
d0bfb22c 1988 "The password string\n")
718e3744 1989{
d62a17ae 1990 int idx_8 = 1;
1991 int idx_word = 2;
1992 if (argc == 3) // '8' was specified
1993 {
1994 if (host.password)
1995 XFREE(MTYPE_HOST, host.password);
1996 host.password = NULL;
1997 if (host.password_encrypt)
1998 XFREE(MTYPE_HOST, host.password_encrypt);
1999 host.password_encrypt =
2000 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
2001 return CMD_SUCCESS;
2002 }
2003
a7ce0ad1 2004 if (!isalnum((int)argv[idx_8]->arg[0])) {
d62a17ae 2005 vty_out(vty,
2006 "Please specify string starting with alphanumeric\n");
2007 return CMD_WARNING_CONFIG_FAILED;
2008 }
2009
2010 if (host.password)
2011 XFREE(MTYPE_HOST, host.password);
2012 host.password = NULL;
2013
2014 if (host.encrypt) {
2015 if (host.password_encrypt)
2016 XFREE(MTYPE_HOST, host.password_encrypt);
2017 host.password_encrypt =
2018 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
2019 } else
2020 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
2021
2022 return CMD_SUCCESS;
718e3744 2023}
2024
322e2d5c
PM
2025/* VTY interface password delete. */
2026DEFUN (no_config_password,
2027 no_password_cmd,
2028 "no password",
2029 NO_STR
2030 "Modify the terminal connection password\n")
2031{
2032 bool warned = false;
2033
2034 if (host.password) {
eb83f7ce 2035 if (!vty_shell_serv(vty)) {
4911ca9c 2036 vty_out(vty, NO_PASSWD_CMD_WARNING);
eb83f7ce
PM
2037 warned = true;
2038 }
322e2d5c
PM
2039 XFREE(MTYPE_HOST, host.password);
2040 }
2041 host.password = NULL;
2042
2043 if (host.password_encrypt) {
eb83f7ce 2044 if (!warned && !vty_shell_serv(vty))
4911ca9c 2045 vty_out(vty, NO_PASSWD_CMD_WARNING);
322e2d5c
PM
2046 XFREE(MTYPE_HOST, host.password_encrypt);
2047 }
2048 host.password_encrypt = NULL;
2049
2050 return CMD_SUCCESS;
2051}
2052
718e3744 2053/* VTY enable password set. */
f412b39a
DW
2054DEFUN (config_enable_password,
2055 enable_password_cmd,
98463e0a 2056 "enable password [(8-8)] WORD",
718e3744 2057 "Modify enable password parameters\n"
2058 "Assign the privileged level password\n"
2059 "Specifies a HIDDEN password will follow\n"
718e3744 2060 "The HIDDEN 'enable' password string\n")
2061{
d62a17ae 2062 int idx_8 = 2;
2063 int idx_word = 3;
2064
2065 /* Crypt type is specified. */
2066 if (argc == 4) {
2067 if (argv[idx_8]->arg[0] == '8') {
2068 if (host.enable)
2069 XFREE(MTYPE_HOST, host.enable);
2070 host.enable = NULL;
2071
2072 if (host.enable_encrypt)
2073 XFREE(MTYPE_HOST, host.enable_encrypt);
2074 host.enable_encrypt =
2075 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
2076
2077 return CMD_SUCCESS;
2078 } else {
2079 vty_out(vty, "Unknown encryption type.\n");
2080 return CMD_WARNING_CONFIG_FAILED;
2081 }
2082 }
2083
a7ce0ad1 2084 if (!isalnum((int)argv[idx_8]->arg[0])) {
d62a17ae 2085 vty_out(vty,
2086 "Please specify string starting with alphanumeric\n");
2087 return CMD_WARNING_CONFIG_FAILED;
2088 }
2089
2090 if (host.enable)
2091 XFREE(MTYPE_HOST, host.enable);
2092 host.enable = NULL;
2093
2094 /* Plain password input. */
2095 if (host.encrypt) {
2096 if (host.enable_encrypt)
2097 XFREE(MTYPE_HOST, host.enable_encrypt);
2098 host.enable_encrypt =
2099 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
2100 } else
2101 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
2102
2103 return CMD_SUCCESS;
718e3744 2104}
2105
718e3744 2106/* VTY enable password delete. */
f412b39a
DW
2107DEFUN (no_config_enable_password,
2108 no_enable_password_cmd,
718e3744 2109 "no enable password",
2110 NO_STR
2111 "Modify enable password parameters\n"
2112 "Assign the privileged level password\n")
2113{
322e2d5c
PM
2114 bool warned = false;
2115
2116 if (host.enable) {
eb83f7ce 2117 if (!vty_shell_serv(vty)) {
4911ca9c 2118 vty_out(vty, NO_PASSWD_CMD_WARNING);
eb83f7ce
PM
2119 warned = true;
2120 }
d62a17ae 2121 XFREE(MTYPE_HOST, host.enable);
322e2d5c 2122 }
d62a17ae 2123 host.enable = NULL;
718e3744 2124
322e2d5c 2125 if (host.enable_encrypt) {
eb83f7ce 2126 if (!warned && !vty_shell_serv(vty))
4911ca9c 2127 vty_out(vty, NO_PASSWD_CMD_WARNING);
d62a17ae 2128 XFREE(MTYPE_HOST, host.enable_encrypt);
322e2d5c 2129 }
d62a17ae 2130 host.enable_encrypt = NULL;
718e3744 2131
d62a17ae 2132 return CMD_SUCCESS;
718e3744 2133}
d0bfb22c 2134
718e3744 2135DEFUN (service_password_encrypt,
2136 service_password_encrypt_cmd,
2137 "service password-encryption",
2138 "Set up miscellaneous service\n"
2139 "Enable encrypted passwords\n")
2140{
d62a17ae 2141 if (host.encrypt)
2142 return CMD_SUCCESS;
718e3744 2143
d62a17ae 2144 host.encrypt = 1;
718e3744 2145
d62a17ae 2146 if (host.password) {
2147 if (host.password_encrypt)
2148 XFREE(MTYPE_HOST, host.password_encrypt);
2149 host.password_encrypt =
2150 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
2151 }
2152 if (host.enable) {
2153 if (host.enable_encrypt)
2154 XFREE(MTYPE_HOST, host.enable_encrypt);
2155 host.enable_encrypt =
2156 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
2157 }
718e3744 2158
d62a17ae 2159 return CMD_SUCCESS;
718e3744 2160}
2161
2162DEFUN (no_service_password_encrypt,
2163 no_service_password_encrypt_cmd,
2164 "no service password-encryption",
2165 NO_STR
2166 "Set up miscellaneous service\n"
2167 "Enable encrypted passwords\n")
2168{
d62a17ae 2169 if (!host.encrypt)
2170 return CMD_SUCCESS;
718e3744 2171
d62a17ae 2172 host.encrypt = 0;
718e3744 2173
d62a17ae 2174 if (host.password_encrypt)
2175 XFREE(MTYPE_HOST, host.password_encrypt);
2176 host.password_encrypt = NULL;
718e3744 2177
d62a17ae 2178 if (host.enable_encrypt)
2179 XFREE(MTYPE_HOST, host.enable_encrypt);
2180 host.enable_encrypt = NULL;
718e3744 2181
d62a17ae 2182 return CMD_SUCCESS;
718e3744 2183}
2184
f412b39a
DW
2185DEFUN (config_terminal_length,
2186 config_terminal_length_cmd,
d0bfb22c 2187 "terminal length (0-512)",
718e3744 2188 "Set terminal line parameters\n"
2189 "Set number of lines on a screen\n"
2190 "Number of lines on screen (0 for no pausing)\n")
2191{
d62a17ae 2192 int idx_number = 2;
718e3744 2193
d62a17ae 2194 vty->lines = atoi(argv[idx_number]->arg);
718e3744 2195
d62a17ae 2196 return CMD_SUCCESS;
718e3744 2197}
2198
f412b39a
DW
2199DEFUN (config_terminal_no_length,
2200 config_terminal_no_length_cmd,
718e3744 2201 "terminal no length",
2202 "Set terminal line parameters\n"
2203 NO_STR
2204 "Set number of lines on a screen\n")
2205{
d62a17ae 2206 vty->lines = -1;
2207 return CMD_SUCCESS;
718e3744 2208}
2209
f412b39a
DW
2210DEFUN (service_terminal_length,
2211 service_terminal_length_cmd,
d0bfb22c 2212 "service terminal-length (0-512)",
718e3744 2213 "Set up miscellaneous service\n"
2214 "System wide terminal length configuration\n"
2215 "Number of lines of VTY (0 means no line control)\n")
2216{
d62a17ae 2217 int idx_number = 2;
718e3744 2218
d62a17ae 2219 host.lines = atoi(argv[idx_number]->arg);
718e3744 2220
d62a17ae 2221 return CMD_SUCCESS;
718e3744 2222}
2223
f412b39a
DW
2224DEFUN (no_service_terminal_length,
2225 no_service_terminal_length_cmd,
d0bfb22c 2226 "no service terminal-length [(0-512)]",
718e3744 2227 NO_STR
2228 "Set up miscellaneous service\n"
2229 "System wide terminal length configuration\n"
2230 "Number of lines of VTY (0 means no line control)\n")
2231{
d62a17ae 2232 host.lines = -1;
2233 return CMD_SUCCESS;
718e3744 2234}
2235
2885f72d 2236DEFUN_HIDDEN (do_echo,
d0bfb22c
QY
2237 echo_cmd,
2238 "echo MESSAGE...",
2239 "Echo a message back to the vty\n"
2240 "The message to echo\n")
2885f72d 2241{
d62a17ae 2242 char *message;
2885f72d 2243
d62a17ae 2244 vty_out(vty, "%s\n",
2245 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2246 if (message)
2247 XFREE(MTYPE_TMP, message);
2248 return CMD_SUCCESS;
2885f72d 2249}
2250
274a4a44 2251DEFUN (config_logmsg,
2252 config_logmsg_cmd,
199d90a1 2253 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
274a4a44 2254 "Send a message to enabled logging destinations\n"
2255 LOG_LEVEL_DESC
2256 "The message to send\n")
2257{
d62a17ae 2258 int idx_log_level = 1;
2259 int idx_message = 2;
2260 int level;
2261 char *message;
274a4a44 2262
d62a17ae 2263 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2264 return CMD_ERR_NO_MATCH;
274a4a44 2265
d62a17ae 2266 zlog(level, "%s",
2267 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2268 if (message)
2269 XFREE(MTYPE_TMP, message);
65efcfce 2270
d62a17ae 2271 return CMD_SUCCESS;
274a4a44 2272}
2273
2274DEFUN (show_logging,
2275 show_logging_cmd,
2276 "show logging",
2277 SHOW_STR
2278 "Show current logging configuration\n")
2279{
d62a17ae 2280 struct zlog *zl = zlog_default;
2281
2282 vty_out(vty, "Syslog logging: ");
2283 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
2284 vty_out(vty, "disabled");
2285 else
2286 vty_out(vty, "level %s, facility %s, ident %s",
2287 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
2288 facility_name(zl->facility), zl->ident);
2289 vty_out(vty, "\n");
2290
2291 vty_out(vty, "Stdout logging: ");
2292 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
2293 vty_out(vty, "disabled");
2294 else
2295 vty_out(vty, "level %s",
2296 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
2297 vty_out(vty, "\n");
2298
2299 vty_out(vty, "Monitor logging: ");
2300 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
2301 vty_out(vty, "disabled");
2302 else
2303 vty_out(vty, "level %s",
2304 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
2305 vty_out(vty, "\n");
2306
2307 vty_out(vty, "File logging: ");
2308 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
2309 vty_out(vty, "disabled");
2310 else
2311 vty_out(vty, "level %s, filename %s",
2312 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
2313 zl->filename);
2314 vty_out(vty, "\n");
2315
2316 vty_out(vty, "Protocol name: %s\n", zl->protoname);
2317 vty_out(vty, "Record priority: %s\n",
2318 (zl->record_priority ? "enabled" : "disabled"));
2319 vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision);
2320
2321 return CMD_SUCCESS;
274a4a44 2322}
2323
718e3744 2324DEFUN (config_log_stdout,
2325 config_log_stdout_cmd,
6de69f83 2326 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
274a4a44 2327 "Logging control\n"
2328 "Set stdout logging level\n"
2329 LOG_LEVEL_DESC)
2330{
d62a17ae 2331 int idx_log_level = 2;
58749582 2332
d62a17ae 2333 if (argc == idx_log_level) {
2334 zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
2335 return CMD_SUCCESS;
2336 }
2337 int level;
274a4a44 2338
d62a17ae 2339 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2340 return CMD_ERR_NO_MATCH;
2341 zlog_set_level(ZLOG_DEST_STDOUT, level);
2342 return CMD_SUCCESS;
718e3744 2343}
2344
2345DEFUN (no_config_log_stdout,
2346 no_config_log_stdout_cmd,
6de69f83 2347 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
718e3744 2348 NO_STR
2349 "Logging control\n"
274a4a44 2350 "Cancel logging to stdout\n"
d0bfb22c 2351 LOG_LEVEL_DESC)
718e3744 2352{
d62a17ae 2353 zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
2354 return CMD_SUCCESS;
718e3744 2355}
2356
274a4a44 2357DEFUN (config_log_monitor,
2358 config_log_monitor_cmd,
6de69f83 2359 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
274a4a44 2360 "Logging control\n"
2361 "Set terminal line (monitor) logging level\n"
2362 LOG_LEVEL_DESC)
2363{
d62a17ae 2364 int idx_log_level = 2;
58749582 2365
d62a17ae 2366 if (argc == idx_log_level) {
2367 zlog_set_level(ZLOG_DEST_MONITOR, zlog_default->default_lvl);
2368 return CMD_SUCCESS;
2369 }
2370 int level;
274a4a44 2371
d62a17ae 2372 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2373 return CMD_ERR_NO_MATCH;
2374 zlog_set_level(ZLOG_DEST_MONITOR, level);
2375 return CMD_SUCCESS;
274a4a44 2376}
2377
2378DEFUN (no_config_log_monitor,
2379 no_config_log_monitor_cmd,
6de69f83 2380 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
274a4a44 2381 NO_STR
2382 "Logging control\n"
2383 "Disable terminal line (monitor) logging\n"
d0bfb22c 2384 LOG_LEVEL_DESC)
274a4a44 2385{
d62a17ae 2386 zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
2387 return CMD_SUCCESS;
274a4a44 2388}
2389
d62a17ae 2390static int set_log_file(struct vty *vty, const char *fname, int loglevel)
718e3744 2391{
d62a17ae 2392 int ret;
2393 char *p = NULL;
2394 const char *fullpath;
d0bfb22c 2395
d62a17ae 2396 /* Path detection. */
2397 if (!IS_DIRECTORY_SEP(*fname)) {
2398 char cwd[MAXPATHLEN + 1];
2399 cwd[MAXPATHLEN] = '\0';
d0bfb22c 2400
d62a17ae 2401 if (getcwd(cwd, MAXPATHLEN) == NULL) {
2402 zlog_err("config_log_file: Unable to alloc mem!");
2403 return CMD_WARNING_CONFIG_FAILED;
2404 }
d0bfb22c 2405
d62a17ae 2406 if ((p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2))
2407 == NULL) {
2408 zlog_err("config_log_file: Unable to alloc mem!");
2409 return CMD_WARNING_CONFIG_FAILED;
2410 }
2411 sprintf(p, "%s/%s", cwd, fname);
2412 fullpath = p;
2413 } else
2414 fullpath = fname;
718e3744 2415
d62a17ae 2416 ret = zlog_set_file(fullpath, loglevel);
718e3744 2417
d62a17ae 2418 if (p)
2419 XFREE(MTYPE_TMP, p);
9035efaa 2420
d62a17ae 2421 if (!ret) {
2422 vty_out(vty, "can't open logfile %s\n", fname);
2423 return CMD_WARNING_CONFIG_FAILED;
2424 }
718e3744 2425
d62a17ae 2426 if (host.logfile)
2427 XFREE(MTYPE_HOST, host.logfile);
718e3744 2428
d62a17ae 2429 host.logfile = XSTRDUP(MTYPE_HOST, fname);
718e3744 2430
c05795b1 2431#if defined(HAVE_CUMULUS)
d62a17ae 2432 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
0204baa8 2433 zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
c05795b1 2434#endif
d62a17ae 2435 return CMD_SUCCESS;
718e3744 2436}
2437
274a4a44 2438DEFUN (config_log_file,
2439 config_log_file_cmd,
6de69f83 2440 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
274a4a44 2441 "Logging control\n"
2442 "Logging to file\n"
2443 "Logging filename\n"
2444 LOG_LEVEL_DESC)
2445{
d62a17ae 2446 int idx_filename = 2;
2447 int idx_log_levels = 3;
2448 if (argc == 4) {
2449 int level;
2450 if ((level = level_match(argv[idx_log_levels]->arg))
2451 == ZLOG_DISABLED)
2452 return CMD_ERR_NO_MATCH;
2453 return set_log_file(vty, argv[idx_filename]->arg, level);
2454 } else
2455 return set_log_file(vty, argv[idx_filename]->arg,
2456 zlog_default->default_lvl);
274a4a44 2457}
2458
914432b5 2459static void disable_log_file(void)
0204baa8
DW
2460{
2461 zlog_reset_file();
2462
2463 if (host.logfile)
2464 XFREE(MTYPE_HOST, host.logfile);
2465
2466 host.logfile = NULL;
2467}
2468
718e3744 2469DEFUN (no_config_log_file,
2470 no_config_log_file_cmd,
d0bfb22c 2471 "no log file [FILENAME [LEVEL]]",
718e3744 2472 NO_STR
2473 "Logging control\n"
2474 "Cancel logging to file\n"
d0bfb22c 2475 "Logging file name\n"
d0bfb22c 2476 "Logging level\n")
718e3744 2477{
0204baa8 2478 disable_log_file();
d62a17ae 2479 return CMD_SUCCESS;
718e3744 2480}
2481
2482DEFUN (config_log_syslog,
2483 config_log_syslog_cmd,
6de69f83 2484 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
12ab19f1 2485 "Logging control\n"
274a4a44 2486 "Set syslog logging level\n"
2487 LOG_LEVEL_DESC)
2488{
d62a17ae 2489 int idx_log_levels = 2;
0204baa8
DW
2490
2491 disable_log_file();
2492
d62a17ae 2493 if (argc == 3) {
2494 int level;
2495 if ((level = level_match(argv[idx_log_levels]->arg))
2496 == ZLOG_DISABLED)
2497 return CMD_ERR_NO_MATCH;
2498 zlog_set_level(ZLOG_DEST_SYSLOG, level);
2499 return CMD_SUCCESS;
2500 } else {
2501 zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
2502 return CMD_SUCCESS;
2503 }
274a4a44 2504}
2505
718e3744 2506DEFUN (no_config_log_syslog,
2507 no_config_log_syslog_cmd,
6de69f83 2508 "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
718e3744 2509 NO_STR
2510 "Logging control\n"
274a4a44 2511 "Cancel logging to syslog\n"
d0bfb22c
QY
2512 LOG_FACILITY_DESC
2513 LOG_LEVEL_DESC)
718e3744 2514{
d62a17ae 2515 zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
2516 return CMD_SUCCESS;
718e3744 2517}
2518
274a4a44 2519DEFUN (config_log_facility,
2520 config_log_facility_cmd,
9ccf14f7 2521 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
718e3744 2522 "Logging control\n"
274a4a44 2523 "Facility parameter for syslog messages\n"
2524 LOG_FACILITY_DESC)
718e3744 2525{
d62a17ae 2526 int idx_target = 2;
2527 int facility = facility_match(argv[idx_target]->arg);
274a4a44 2528
d62a17ae 2529 zlog_default->facility = facility;
2530 return CMD_SUCCESS;
718e3744 2531}
2532
274a4a44 2533DEFUN (no_config_log_facility,
2534 no_config_log_facility_cmd,
9ccf14f7 2535 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
718e3744 2536 NO_STR
2537 "Logging control\n"
274a4a44 2538 "Reset syslog facility to default (daemon)\n"
d0bfb22c 2539 LOG_FACILITY_DESC)
274a4a44 2540{
d62a17ae 2541 zlog_default->facility = LOG_DAEMON;
2542 return CMD_SUCCESS;
274a4a44 2543}
2544
d62a17ae 2545DEFUN_DEPRECATED(
2546 config_log_trap, config_log_trap_cmd,
2547 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2548 "Logging control\n"
2549 "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC)
274a4a44 2550{
d62a17ae 2551 int new_level;
2552 int i;
d0bfb22c 2553
d62a17ae 2554 if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED)
2555 return CMD_ERR_NO_MATCH;
274a4a44 2556
d62a17ae 2557 zlog_default->default_lvl = new_level;
2558 for (i = 0; i < ZLOG_NUM_DESTS; i++)
2559 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
2560 zlog_default->maxlvl[i] = new_level;
2561 return CMD_SUCCESS;
274a4a44 2562}
2563
d62a17ae 2564DEFUN_DEPRECATED(
2565 no_config_log_trap, no_config_log_trap_cmd,
2566 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2567 NO_STR
2568 "Logging control\n"
2569 "Permit all logging information\n" LOG_LEVEL_DESC)
718e3744 2570{
d62a17ae 2571 zlog_default->default_lvl = LOG_DEBUG;
2572 return CMD_SUCCESS;
718e3744 2573}
2574
2575DEFUN (config_log_record_priority,
2576 config_log_record_priority_cmd,
2577 "log record-priority",
2578 "Logging control\n"
2579 "Log the priority of the message within the message\n")
2580{
d62a17ae 2581 zlog_default->record_priority = 1;
2582 return CMD_SUCCESS;
718e3744 2583}
2584
2585DEFUN (no_config_log_record_priority,
2586 no_config_log_record_priority_cmd,
2587 "no log record-priority",
2588 NO_STR
2589 "Logging control\n"
2590 "Do not log the priority of the message within the message\n")
2591{
d62a17ae 2592 zlog_default->record_priority = 0;
2593 return CMD_SUCCESS;
718e3744 2594}
2595
1ed72e0b
AS
2596DEFUN (config_log_timestamp_precision,
2597 config_log_timestamp_precision_cmd,
6147e2c6 2598 "log timestamp precision (0-6)",
1ed72e0b
AS
2599 "Logging control\n"
2600 "Timestamp configuration\n"
2601 "Set the timestamp precision\n"
2602 "Number of subsecond digits\n")
2603{
d62a17ae 2604 int idx_number = 3;
2605 zlog_default->timestamp_precision =
2606 strtoul(argv[idx_number]->arg, NULL, 10);
2607 return CMD_SUCCESS;
1ed72e0b
AS
2608}
2609
2610DEFUN (no_config_log_timestamp_precision,
2611 no_config_log_timestamp_precision_cmd,
2612 "no log timestamp precision",
2613 NO_STR
2614 "Logging control\n"
2615 "Timestamp configuration\n"
2616 "Reset the timestamp precision to the default value of 0\n")
2617{
d62a17ae 2618 zlog_default->timestamp_precision = 0;
2619 return CMD_SUCCESS;
1ed72e0b
AS
2620}
2621
9eed278b
DL
2622DEFUN (debug_memstats,
2623 debug_memstats_cmd,
2624 "[no] debug memstats-at-exit",
2625 NO_STR
2626 DEBUG_STR
2627 "Print memory type statistics at exit\n")
2628{
2629 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2630 return CMD_SUCCESS;
2631}
2632
d62a17ae 2633int cmd_banner_motd_file(const char *file)
7cfc61d3 2634{
d62a17ae 2635 int success = CMD_SUCCESS;
2636 char p[PATH_MAX];
2637 char *rpath;
2638 char *in;
7cfc61d3 2639
d62a17ae 2640 rpath = realpath(file, p);
2641 if (!rpath)
2642 return CMD_ERR_NO_FILE;
2643 in = strstr(rpath, SYSCONFDIR);
2644 if (in == rpath) {
2645 if (host.motdfile)
2646 XFREE(MTYPE_HOST, host.motdfile);
2647 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2648 } else
2649 success = CMD_WARNING_CONFIG_FAILED;
1ee08155 2650
d62a17ae 2651 return success;
7cfc61d3
DS
2652}
2653
3b0c5d9a 2654DEFUN (banner_motd_file,
2655 banner_motd_file_cmd,
4d833e55 2656 "banner motd file FILE",
3b0c5d9a 2657 "Set banner\n"
2658 "Banner for motd\n"
2659 "Banner from a file\n"
2660 "Filename\n")
2661{
d62a17ae 2662 int idx_file = 3;
2663 const char *filename = argv[idx_file]->arg;
2664 int cmd = cmd_banner_motd_file(filename);
1ee08155 2665
d62a17ae 2666 if (cmd == CMD_ERR_NO_FILE)
2667 vty_out(vty, "%s does not exist", filename);
2668 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2669 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
1ee08155 2670
d62a17ae 2671 return cmd;
3b0c5d9a 2672}
718e3744 2673
2674DEFUN (banner_motd_default,
2675 banner_motd_default_cmd,
2676 "banner motd default",
2677 "Set banner string\n"
2678 "Strings for motd\n"
2679 "Default string\n")
2680{
d62a17ae 2681 host.motd = default_motd;
2682 return CMD_SUCCESS;
718e3744 2683}
2684
2685DEFUN (no_banner_motd,
2686 no_banner_motd_cmd,
2687 "no banner motd",
2688 NO_STR
2689 "Set banner string\n"
2690 "Strings for motd\n")
2691{
d62a17ae 2692 host.motd = NULL;
2693 if (host.motdfile)
2694 XFREE(MTYPE_HOST, host.motdfile);
2695 host.motdfile = NULL;
2696 return CMD_SUCCESS;
718e3744 2697}
2698
a83a5331
QY
2699DEFUN(find,
2700 find_cmd,
2701 "find COMMAND...",
2702 "Find CLI command containing text\n"
2703 "Text to search for\n")
2704{
2705 char *text = argv_concat(argv, argc, 1);
cf6c83e7
QY
2706 const struct cmd_node *node;
2707 const struct cmd_element *cli;
2708 vector clis;
a83a5331
QY
2709
2710 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
cf6c83e7 2711 node = vector_slot(cmdvec, i);
a83a5331
QY
2712 if (!node)
2713 continue;
cf6c83e7 2714 clis = node->cmd_vector;
a83a5331 2715 for (unsigned int j = 0; j < vector_active(clis); j++) {
cf6c83e7 2716 cli = vector_slot(clis, j);
a83a5331 2717 if (strcasestr(cli->string, text))
cf6c83e7 2718 vty_out(vty, " (%s) %s\n",
a83a5331
QY
2719 node_names[node->node], cli->string);
2720 }
2721 }
2722
2723 XFREE(MTYPE_TMP, text);
2724
2725 return CMD_SUCCESS;
2726}
2727
718e3744 2728/* Set config filename. Called from vty.c */
d62a17ae 2729void host_config_set(const char *filename)
718e3744 2730{
d62a17ae 2731 if (host.config)
2732 XFREE(MTYPE_HOST, host.config);
2733 host.config = XSTRDUP(MTYPE_HOST, filename);
718e3744 2734}
2735
d62a17ae 2736const char *host_config_get(void)
57387fb2 2737{
d62a17ae 2738 return host.config;
57387fb2
CF
2739}
2740
d62a17ae 2741void install_default(enum node_type node)
718e3744 2742{
d62a17ae 2743 install_element(node, &config_exit_cmd);
2744 install_element(node, &config_quit_cmd);
2745 install_element(node, &config_end_cmd);
2746 install_element(node, &config_help_cmd);
2747 install_element(node, &config_list_cmd);
26fbe472 2748 install_element(node, &show_cli_graph_cmd);
a83a5331 2749 install_element(node, &find_cmd);
718e3744 2750
d62a17ae 2751 install_element(node, &config_write_cmd);
2752 install_element(node, &show_running_config_cmd);
7f059ea6 2753
d62a17ae 2754 install_element(node, &autocomplete_cmd);
718e3744 2755}
2756
87f44e2f
DL
2757/* Initialize command interface. Install basic nodes and commands.
2758 *
2759 * terminal = 0 -- vtysh / no logging, no config control
2760 * terminal = 1 -- normal daemon
9473e340 2761 * terminal = -1 -- watchfrr / no logging, but minimal config control */
d62a17ae 2762void cmd_init(int terminal)
2763{
419cd5a0
MK
2764 struct utsname names;
2765
a83a5331
QY
2766 if (array_size(node_names) != NODE_TYPE_MAX)
2767 assert(!"Update the CLI node description array!");
2768
419cd5a0 2769 uname(&names);
d62a17ae 2770 qobj_init();
2771
fe6b47b9
QY
2772 /* register command preprocessors */
2773 hook_register(cmd_execute, handle_pipe_action);
2774 hook_register(cmd_execute_done, handle_pipe_action_done);
2775
d62a17ae 2776 varhandlers = list_new();
2777
2778 /* Allocate initial top vector of commands. */
2779 cmdvec = vector_init(VECTOR_MIN_SIZE);
2780
2781 /* Default host value settings. */
419cd5a0
MK
2782 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2783#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
6b3ee3a0
MK
2784 if ((strcmp(names.domainname, "(none)") == 0))
2785 host.domainname = NULL;
2786 else
2787 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
419cd5a0
MK
2788#else
2789 host.domainname = NULL;
2790#endif
d62a17ae 2791 host.password = NULL;
2792 host.enable = NULL;
2793 host.logfile = NULL;
2794 host.config = NULL;
2795 host.noconfig = (terminal < 0);
2796 host.lines = -1;
2797 host.motd = default_motd;
2798 host.motdfile = NULL;
2799
2800 /* Install top nodes. */
2801 install_node(&view_node, NULL);
2802 install_node(&enable_node, NULL);
2803 install_node(&auth_node, NULL);
2804 install_node(&auth_enable_node, NULL);
2805 install_node(&config_node, config_write_host);
2806
2807 /* Each node's basic commands. */
2808 install_element(VIEW_NODE, &show_version_cmd);
a83a5331 2809 install_element(ENABLE_NODE, &show_startup_config_cmd);
9eed278b 2810 install_element(ENABLE_NODE, &debug_memstats_cmd);
a83a5331 2811
d62a17ae 2812 if (terminal) {
2813 install_element(VIEW_NODE, &config_list_cmd);
2814 install_element(VIEW_NODE, &config_exit_cmd);
2815 install_element(VIEW_NODE, &config_quit_cmd);
2816 install_element(VIEW_NODE, &config_help_cmd);
2817 install_element(VIEW_NODE, &config_enable_cmd);
2818 install_element(VIEW_NODE, &config_terminal_length_cmd);
2819 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2820 install_element(VIEW_NODE, &show_logging_cmd);
2821 install_element(VIEW_NODE, &show_commandtree_cmd);
2822 install_element(VIEW_NODE, &echo_cmd);
2823 install_element(VIEW_NODE, &autocomplete_cmd);
a83a5331 2824 install_element(VIEW_NODE, &find_cmd);
d62a17ae 2825
d62a17ae 2826 install_element(ENABLE_NODE, &config_end_cmd);
2827 install_element(ENABLE_NODE, &config_disable_cmd);
2828 install_element(ENABLE_NODE, &config_terminal_cmd);
2829 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2830 install_element(ENABLE_NODE, &config_write_cmd);
2831 install_element(ENABLE_NODE, &show_running_config_cmd);
d62a17ae 2832 install_element(ENABLE_NODE, &config_logmsg_cmd);
a83a5331 2833
d62a17ae 2834 install_default(CONFIG_NODE);
2835
2836 thread_cmd_init();
2837 workqueue_cmd_init();
2838 hash_cmd_init();
2839 }
2840
2841 install_element(CONFIG_NODE, &hostname_cmd);
2842 install_element(CONFIG_NODE, &no_hostname_cmd);
6b3ee3a0
MK
2843 install_element(CONFIG_NODE, &domainname_cmd);
2844 install_element(CONFIG_NODE, &no_domainname_cmd);
d62a17ae 2845 install_element(CONFIG_NODE, &frr_version_defaults_cmd);
9eed278b 2846 install_element(CONFIG_NODE, &debug_memstats_cmd);
d62a17ae 2847
2848 if (terminal > 0) {
2849 install_element(CONFIG_NODE, &password_cmd);
322e2d5c 2850 install_element(CONFIG_NODE, &no_password_cmd);
d62a17ae 2851 install_element(CONFIG_NODE, &enable_password_cmd);
2852 install_element(CONFIG_NODE, &no_enable_password_cmd);
2853
2854 install_element(CONFIG_NODE, &config_log_stdout_cmd);
2855 install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
2856 install_element(CONFIG_NODE, &config_log_monitor_cmd);
2857 install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
2858 install_element(CONFIG_NODE, &config_log_file_cmd);
2859 install_element(CONFIG_NODE, &no_config_log_file_cmd);
2860 install_element(CONFIG_NODE, &config_log_syslog_cmd);
2861 install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
2862 install_element(CONFIG_NODE, &config_log_facility_cmd);
2863 install_element(CONFIG_NODE, &no_config_log_facility_cmd);
2864 install_element(CONFIG_NODE, &config_log_trap_cmd);
2865 install_element(CONFIG_NODE, &no_config_log_trap_cmd);
2866 install_element(CONFIG_NODE, &config_log_record_priority_cmd);
2867 install_element(CONFIG_NODE,
2868 &no_config_log_record_priority_cmd);
2869 install_element(CONFIG_NODE,
2870 &config_log_timestamp_precision_cmd);
2871 install_element(CONFIG_NODE,
2872 &no_config_log_timestamp_precision_cmd);
2873 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2874 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2875 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2876 install_element(CONFIG_NODE, &banner_motd_file_cmd);
2877 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2878 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2879 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2880
2881 vrf_install_commands();
2882 }
af2567b6
DL
2883
2884#ifdef DEV_BUILD
d62a17ae 2885 grammar_sandbox_init();
af2567b6 2886#endif
718e3744 2887}
228da428 2888
d62a17ae 2889void cmd_terminate()
2890{
2891 struct cmd_node *cmd_node;
2892
2893 if (cmdvec) {
2894 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2895 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2896 // deleting the graph delets the cmd_element as
2897 // well
2898 graph_delete_graph(cmd_node->cmdgraph);
2899 vector_free(cmd_node->cmd_vector);
2900 hash_clean(cmd_node->cmd_hash, NULL);
2901 hash_free(cmd_node->cmd_hash);
2902 cmd_node->cmd_hash = NULL;
2903 }
2904
2905 vector_free(cmdvec);
2906 cmdvec = NULL;
2907 }
2908
2909 if (host.name)
2910 XFREE(MTYPE_HOST, host.name);
419cd5a0
MK
2911 if (host.domainname)
2912 XFREE(MTYPE_HOST, host.domainname);
d62a17ae 2913 if (host.password)
2914 XFREE(MTYPE_HOST, host.password);
2915 if (host.password_encrypt)
2916 XFREE(MTYPE_HOST, host.password_encrypt);
2917 if (host.enable)
2918 XFREE(MTYPE_HOST, host.enable);
2919 if (host.enable_encrypt)
2920 XFREE(MTYPE_HOST, host.enable_encrypt);
2921 if (host.logfile)
2922 XFREE(MTYPE_HOST, host.logfile);
2923 if (host.motdfile)
2924 XFREE(MTYPE_HOST, host.motdfile);
2925 if (host.config)
2926 XFREE(MTYPE_HOST, host.config);
2927
affe9e99 2928 list_delete_and_null(&varhandlers);
d62a17ae 2929 qobj_finish();
228da428 2930}