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