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