blob: de084ab3957220fb2a9b509c51a27937e96ca224 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
40#include <sys/time.h>
41#include <sys/stat.h>
42
43#include <osmocom/vty/vector.h>
44#include <osmocom/vty/vty.h>
45#include <osmocom/vty/command.h>
46
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010047#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010048#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020049
Harald Weltee881b1b2011-08-17 18:52:30 +020050/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020051 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020052 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020053 *
54 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020055
Harald Welte3fb0b6f2010-05-19 19:02:52 +020056#define CONFIGFILE_MASK 022
57
58void *tall_vty_cmd_ctx;
59
60/* Command vector which includes some level of command lists. Normally
61 each daemon maintains each own cmdvec. */
62vector cmdvec;
63
64/* Host information structure. */
65struct host host;
66
67/* Standard command node structures. */
68struct cmd_node auth_node = {
69 AUTH_NODE,
70 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010071 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020072};
73
74struct cmd_node view_node = {
75 VIEW_NODE,
76 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node auth_enable_node = {
81 AUTH_ENABLE_NODE,
82 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node enable_node = {
87 ENABLE_NODE,
88 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node config_node = {
93 CONFIG_NODE,
94 "%s(config)# ",
95 1
96};
97
98/* Default motd string. */
99const char *default_motd = "";
100
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200101/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200102 *
103 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200104void print_version(int print_copyright)
105{
Harald Welte237f6242010-05-25 23:00:45 +0200106 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200107 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200108 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200109}
110
111/* Utility function to concatenate argv argument into a single string
112 with inserting ' ' character between each argument. */
113char *argv_concat(const char **argv, int argc, int shift)
114{
115 int i;
116 size_t len;
117 char *str;
118 char *p;
119
120 len = 0;
121 for (i = shift; i < argc; i++)
122 len += strlen(argv[i]) + 1;
123 if (!len)
124 return NULL;
125 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
126 for (i = shift; i < argc; i++) {
127 size_t arglen;
128 memcpy(p, argv[i], (arglen = strlen(argv[i])));
129 p += arglen;
130 *p++ = ' ';
131 }
132 *(p - 1) = '\0';
133 return str;
134}
135
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200136/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
137 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
138 * in turn, this name us used for XML IDs in 'show online-help'. */
139static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
140{
141 const char *pos;
142 int dest = 0;
143
144 if (!prompt || !*prompt)
145 return "";
146
147 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
148 if (pos[0] == '%' && pos[1]) {
149 /* skip "%s"; loop pos++ does the second one. */
150 pos++;
151 continue;
152 }
153 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
154 continue;
155 name_buf[dest] = pos[0];
156 dest++;
157 }
158 name_buf[dest] = '\0';
159 return name_buf;
160}
161
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200162static void install_basic_node_commands(int node);
163
164/*! Install top node of command vector, without adding basic node commands. */
165static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200166{
167 vector_set_index(cmdvec, node->node, node);
168 node->func = func;
169 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200170 if (!*node->name)
171 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172}
173
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200174/*! Install top node of command vector. */
175void install_node(struct cmd_node *node, int (*func) (struct vty *))
176{
177 install_node_bare(node, func);
178 install_basic_node_commands(node->node);
179}
180
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200181/* Compare two command's string. Used in sort_node (). */
182static int cmp_node(const void *p, const void *q)
183{
184 struct cmd_element *a = *(struct cmd_element **)p;
185 struct cmd_element *b = *(struct cmd_element **)q;
186
187 return strcmp(a->string, b->string);
188}
189
190static int cmp_desc(const void *p, const void *q)
191{
192 struct desc *a = *(struct desc **)p;
193 struct desc *b = *(struct desc **)q;
194
195 return strcmp(a->cmd, b->cmd);
196}
197
Jacob Erlbeck2442e092013-09-06 16:51:58 +0200198static int is_config_child(struct vty *vty)
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800199{
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800200 if (vty->node <= CONFIG_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800201 return 0;
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800202 else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800203 return 1;
204 else if (host.app_info->is_config_node)
Holger Hans Peter Freyther8f09f012010-08-25 17:34:56 +0800205 return host.app_info->is_config_node(vty, vty->node);
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800206 else
207 return vty->node > CONFIG_NODE;
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200211void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200212{
213 unsigned int i, j;
214 struct cmd_node *cnode;
215 vector descvec;
216 struct cmd_element *cmd_element;
217
218 for (i = 0; i < vector_active(cmdvec); i++)
219 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
220 vector cmd_vector = cnode->cmd_vector;
221 qsort(cmd_vector->index, vector_active(cmd_vector),
222 sizeof(void *), cmp_node);
223
224 for (j = 0; j < vector_active(cmd_vector); j++)
225 if ((cmd_element =
226 vector_slot(cmd_vector, j)) != NULL
227 && vector_active(cmd_element->strvec)) {
228 descvec =
229 vector_slot(cmd_element->strvec,
230 vector_active
231 (cmd_element->strvec) -
232 1);
233 qsort(descvec->index,
234 vector_active(descvec),
235 sizeof(void *), cmp_desc);
236 }
237 }
238}
239
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200240/*! Break up string in command tokens. Return leading indents.
241 * \param[in] string String to split.
242 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
243 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
244 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
245 *
246 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
247 * so that \a indent can simply return the count of leading spaces.
248 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
249 */
250int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200251{
252 const char *cp, *start;
253 char *token;
254 int strlen;
255 vector strvec;
256
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200257 *strvec_p = NULL;
258 if (indent)
259 *indent = 0;
260
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200262 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200263
264 cp = string;
265
266 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200267 while (isspace((int)*cp) && *cp != '\0') {
268 /* if we're counting indents, we need to be strict about them */
269 if (indent && (*cp != ' ') && (*cp != '\t')) {
270 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
271 if (*cp == '\n' || *cp == '\r') {
272 cp++;
273 string = cp;
274 continue;
275 }
276 return CMD_ERR_INVALID_INDENT;
277 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200278 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200279 }
280
281 if (indent)
282 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200283
284 /* Return if there is only white spaces */
285 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200286 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200287
288 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200289 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200290
291 /* Prepare return vector. */
292 strvec = vector_init(VECTOR_MIN_SIZE);
293
294 /* Copy each command piece and set into vector. */
295 while (1) {
296 start = cp;
297 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
298 *cp != '\0')
299 cp++;
300 strlen = cp - start;
301 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
302 memcpy(token, start, strlen);
303 *(token + strlen) = '\0';
304 vector_set(strvec, token);
305
306 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
307 *cp != '\0')
308 cp++;
309
310 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200311 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200312 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200313
314 *strvec_p = strvec;
315 return CMD_SUCCESS;
316}
317
318/*! Breaking up string into each command piece. I assume given
319 character is separated by a space character. Return value is a
320 vector which includes char ** data element. */
321vector cmd_make_strvec(const char *string)
322{
323 vector strvec;
324 cmd_make_strvec2(string, NULL, &strvec);
325 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200326}
327
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200328/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200329void cmd_free_strvec(vector v)
330{
331 unsigned int i;
332 char *cp;
333
334 if (!v)
335 return;
336
337 for (i = 0; i < vector_active(v); i++)
338 if ((cp = vector_slot(v, i)) != NULL)
339 talloc_free(cp);
340
341 vector_free(v);
342}
343
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200344/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200345static char *cmd_desc_str(const char **string)
346{
347 const char *cp, *start;
348 char *token;
349 int strlen;
350
351 cp = *string;
352
353 if (cp == NULL)
354 return NULL;
355
356 /* Skip white spaces. */
357 while (isspace((int)*cp) && *cp != '\0')
358 cp++;
359
360 /* Return if there is only white spaces */
361 if (*cp == '\0')
362 return NULL;
363
364 start = cp;
365
366 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
367 cp++;
368
369 strlen = cp - start;
370 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
371 memcpy(token, start, strlen);
372 *(token + strlen) = '\0';
373
374 *string = cp;
375
376 return token;
377}
378
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200379/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200380static vector cmd_make_descvec(const char *string, const char *descstr)
381{
382 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100383 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200384 const char *sp;
385 char *token;
386 int len;
387 const char *cp;
388 const char *dp;
389 vector allvec;
390 vector strvec = NULL;
391 struct desc *desc;
392
393 cp = string;
394 dp = descstr;
395
396 if (cp == NULL)
397 return NULL;
398
399 allvec = vector_init(VECTOR_MIN_SIZE);
400
401 while (1) {
402 while (isspace((int)*cp) && *cp != '\0')
403 cp++;
404
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100405 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
406 if (cp[0] == '[' && cp[1] == '(') {
407 optional_brace = 1;
408 cp++;
409 }
410
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200411 if (*cp == '(') {
412 multiple = 1;
413 cp++;
414 }
415 if (*cp == ')') {
416 multiple = 0;
417 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100418 if (*cp == ']')
419 cp++;
420 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200421 }
422 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100423 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200424 cp++;
425 }
426
427 while (isspace((int)*cp) && *cp != '\0')
428 cp++;
429
430 if (*cp == '(') {
431 multiple = 1;
432 cp++;
433 }
434
435 if (*cp == '\0')
436 return allvec;
437
438 sp = cp;
439
440 while (!
441 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
442 || *cp == ')' || *cp == '|') && *cp != '\0')
443 cp++;
444
445 len = cp - sp;
446
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100447 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
448 if (optional_brace) {
449 /* Place each individual multi-choice token in its own square braces */
450 token[0] = '[';
451 memcpy(token + 1, sp, len);
452 token[1 + len] = ']';
453 token[2 + len] = '\0';
454 } else {
455 memcpy(token, sp, len);
456 *(token + len) = '\0';
457 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200458
459 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
460 desc->cmd = token;
461 desc->str = cmd_desc_str(&dp);
462
463 if (multiple) {
464 if (multiple == 1) {
465 strvec = vector_init(VECTOR_MIN_SIZE);
466 vector_set(allvec, strvec);
467 }
468 multiple++;
469 } else {
470 strvec = vector_init(VECTOR_MIN_SIZE);
471 vector_set(allvec, strvec);
472 }
473 vector_set(strvec, desc);
474 }
475}
476
477/* Count mandantory string vector size. This is to determine inputed
478 command has enough command length. */
479static int cmd_cmdsize(vector strvec)
480{
481 unsigned int i;
482 int size = 0;
483 vector descvec;
484 struct desc *desc;
485
486 for (i = 0; i < vector_active(strvec); i++)
487 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100488 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200489 && (desc = vector_slot(descvec, 0)) != NULL) {
490 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
491 return size;
492 else
493 size++;
494 } else
495 size++;
496 }
497 return size;
498}
499
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200500/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200501const char *cmd_prompt(enum node_type node)
502{
503 struct cmd_node *cnode;
504
505 cnode = vector_slot(cmdvec, node);
506 return cnode->prompt;
507}
508
Alexander Couzensad580ba2016-05-16 16:01:45 +0200509/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200510 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200511 * \param unsafe string
512 * \return a new talloc char *
513 */
514char *osmo_asciidoc_escape(const char *inp)
515{
516 int _strlen;
517 char *out, *out_ptr;
518 int len = 0, i, j;
519
520 if (!inp)
521 return NULL;
522 _strlen = strlen(inp);
523
524 for (i = 0; i < _strlen; ++i) {
525 switch (inp[i]) {
526 case '|':
527 len += 2;
528 break;
529 default:
530 len += 1;
531 break;
532 }
533 }
534
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200535 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200536 if (!out)
537 return NULL;
538
539 out_ptr = out;
540
541#define ADD(out, str) \
542 for (j = 0; j < strlen(str); ++j) \
543 *(out++) = str[j];
544
545 for (i = 0; i < _strlen; ++i) {
546 switch (inp[i]) {
547 case '|':
548 ADD(out_ptr, "\\|");
549 break;
550 default:
551 *(out_ptr++) = inp[i];
552 break;
553 }
554 }
555
556#undef ADD
557
558 out_ptr[0] = '\0';
559 return out;
560}
561
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100562static char *xml_escape(const char *inp)
563{
564 int _strlen;
565 char *out, *out_ptr;
566 int len = 0, i, j;
567
568 if (!inp)
569 return NULL;
570 _strlen = strlen(inp);
571
572 for (i = 0; i < _strlen; ++i) {
573 switch (inp[i]) {
574 case '"':
575 len += 6;
576 break;
577 case '\'':
578 len += 6;
579 break;
580 case '<':
581 len += 4;
582 break;
583 case '>':
584 len += 4;
585 break;
586 case '&':
587 len += 5;
588 break;
589 default:
590 len += 1;
591 break;
592 }
593 }
594
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200595 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100596 if (!out)
597 return NULL;
598
599 out_ptr = out;
600
601#define ADD(out, str) \
602 for (j = 0; j < strlen(str); ++j) \
603 *(out++) = str[j];
604
605 for (i = 0; i < _strlen; ++i) {
606 switch (inp[i]) {
607 case '"':
608 ADD(out_ptr, "&quot;");
609 break;
610 case '\'':
611 ADD(out_ptr, "&apos;");
612 break;
613 case '<':
614 ADD(out_ptr, "&lt;");
615 break;
616 case '>':
617 ADD(out_ptr, "&gt;");
618 break;
619 case '&':
620 ADD(out_ptr, "&amp;");
621 break;
622 default:
623 *(out_ptr++) = inp[i];
624 break;
625 }
626 }
627
628#undef ADD
629
630 out_ptr[0] = '\0';
631 return out;
632}
633
634/*
635 * Write one cmd_element as XML to the given VTY.
636 */
637static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
638{
639 char *xml_string = xml_escape(cmd->string);
640
641 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
642 vty_out(vty, " <params>%s", VTY_NEWLINE);
643
644 int j;
645 for (j = 0; j < vector_count(cmd->strvec); ++j) {
646 vector descvec = vector_slot(cmd->strvec, j);
647 int i;
648 for (i = 0; i < vector_active(descvec); ++i) {
649 char *xml_param, *xml_doc;
650 struct desc *desc = vector_slot(descvec, i);
651 if (desc == NULL)
652 continue;
653
654 xml_param = xml_escape(desc->cmd);
655 xml_doc = xml_escape(desc->str);
656 vty_out(vty, " <param name='%s' doc='%s' />%s",
657 xml_param, xml_doc, VTY_NEWLINE);
658 talloc_free(xml_param);
659 talloc_free(xml_doc);
660 }
661 }
662
663 vty_out(vty, " </params>%s", VTY_NEWLINE);
664 vty_out(vty, " </command>%s", VTY_NEWLINE);
665
666 talloc_free(xml_string);
667 return 0;
668}
669
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200670static bool vty_command_is_common(struct cmd_element *cmd);
671
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100672/*
673 * Dump all nodes and commands associated with a given node as XML to the VTY.
674 */
675static int vty_dump_nodes(struct vty *vty)
676{
677 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200678 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100679
680 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
681
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200682 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
683 vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
684 vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
685 vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
686 " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
687 for (i = 0; i < vector_active(cmdvec); ++i) {
688 struct cmd_node *cnode;
689 cnode = vector_slot(cmdvec, i);
690 if (!cnode)
691 continue;
692 if (cnode->node != CONFIG_NODE)
693 continue;
694
695 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
696 struct cmd_element *elem;
697 elem = vector_slot(cnode->cmd_vector, j);
698 if (!vty_command_is_common(elem))
699 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200700 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200701 vty_dump_element(elem, vty);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200702 }
703 }
704 vty_out(vty, " </node>%s", VTY_NEWLINE);
705
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100706 for (i = 0; i < vector_active(cmdvec); ++i) {
707 struct cmd_node *cnode;
708 cnode = vector_slot(cmdvec, i);
709 if (!cnode)
710 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200711 if (vector_active(cnode->cmd_vector) < 1)
712 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100713
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200714 /* De-dup node IDs: how many times has this same name been used before? Count the first
715 * occurence as _1 and omit that first suffix, so that the first occurence is called
716 * 'name', the second becomes 'name_2', then 'name_3', ... */
717 same_name_count = 1;
718 for (j = 0; j < i; ++j) {
719 struct cmd_node *cnode2;
720 cnode2 = vector_slot(cmdvec, j);
721 if (!cnode2)
722 continue;
723 if (strcmp(cnode->name, cnode2->name) == 0)
724 same_name_count ++;
725 }
726
727 vty_out(vty, " <node id='%s", cnode->name);
728 if (same_name_count > 1 || !*cnode->name)
729 vty_out(vty, "_%d", same_name_count);
730 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200731 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100732
733 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
734 struct cmd_element *elem;
735 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200736 if (vty_command_is_common(elem))
737 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200738 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200739 vty_dump_element(elem, vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100740 }
741
742 vty_out(vty, " </node>%s", VTY_NEWLINE);
743 }
744
745 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
746
747 return 0;
748}
749
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200750/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100751static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
752{
753 int i;
754
755 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
756 struct cmd_element *elem;
757 elem = vector_slot(cnode->cmd_vector, i);
758 if (!elem->string)
759 continue;
760 if (!strcmp(elem->string, cmdstring))
761 return 1;
762 }
763 return 0;
764}
765
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200766/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200767 * \param[in] ntype Node Type
768 * \param[cmd] element to be installed
769 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000770void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200771{
772 struct cmd_node *cnode;
773
774 cnode = vector_slot(cmdvec, ntype);
775
Harald Weltea99d45a2015-11-12 13:48:23 +0100776 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100777 /* ensure no _identical_ command has been registered at this
778 * node so far */
779 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200780
781 vector_set(cnode->cmd_vector, cmd);
782
783 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
784 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
785}
786
787/* Install a command into VIEW and ENABLE node */
788void install_element_ve(struct cmd_element *cmd)
789{
790 install_element(VIEW_NODE, cmd);
791 install_element(ENABLE_NODE, cmd);
792}
793
794#ifdef VTY_CRYPT_PW
795static unsigned char itoa64[] =
796 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
797
798static void to64(char *s, long v, int n)
799{
800 while (--n >= 0) {
801 *s++ = itoa64[v & 0x3f];
802 v >>= 6;
803 }
804}
805
806static char *zencrypt(const char *passwd)
807{
808 char salt[6];
809 struct timeval tv;
810 char *crypt(const char *, const char *);
811
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200812 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200813
814 to64(&salt[0], random(), 3);
815 to64(&salt[3], tv.tv_usec, 3);
816 salt[5] = '\0';
817
818 return crypt(passwd, salt);
819}
820#endif
821
822/* This function write configuration of this host. */
823static int config_write_host(struct vty *vty)
824{
825 if (host.name)
826 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
827
828 if (host.encrypt) {
829 if (host.password_encrypt)
830 vty_out(vty, "password 8 %s%s", host.password_encrypt,
831 VTY_NEWLINE);
832 if (host.enable_encrypt)
833 vty_out(vty, "enable password 8 %s%s",
834 host.enable_encrypt, VTY_NEWLINE);
835 } else {
836 if (host.password)
837 vty_out(vty, "password %s%s", host.password,
838 VTY_NEWLINE);
839 if (host.enable)
840 vty_out(vty, "enable password %s%s", host.enable,
841 VTY_NEWLINE);
842 }
843
844 if (host.advanced)
845 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
846
847 if (host.encrypt)
848 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
849
850 if (host.lines >= 0)
851 vty_out(vty, "service terminal-length %d%s", host.lines,
852 VTY_NEWLINE);
853
854 if (host.motdfile)
855 vty_out(vty, "banner motd file %s%s", host.motdfile,
856 VTY_NEWLINE);
857 else if (!host.motd)
858 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
859
860 return 1;
861}
862
863/* Utility function for getting command vector. */
864static vector cmd_node_vector(vector v, enum node_type ntype)
865{
866 struct cmd_node *cnode = vector_slot(v, ntype);
867 return cnode->cmd_vector;
868}
869
870/* Completion match types. */
871enum match_type {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100872 no_match = 0,
873 any_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200874 extend_match,
875 ipv4_prefix_match,
876 ipv4_match,
877 ipv6_prefix_match,
878 ipv6_match,
879 range_match,
880 vararg_match,
881 partly_match,
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100882 exact_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200883};
884
885static enum match_type cmd_ipv4_match(const char *str)
886{
887 const char *sp;
888 int dots = 0, nums = 0;
889 char buf[4];
890
891 if (str == NULL)
892 return partly_match;
893
894 for (;;) {
895 memset(buf, 0, sizeof(buf));
896 sp = str;
897 while (*str != '\0') {
898 if (*str == '.') {
899 if (dots >= 3)
900 return no_match;
901
902 if (*(str + 1) == '.')
903 return no_match;
904
905 if (*(str + 1) == '\0')
906 return partly_match;
907
908 dots++;
909 break;
910 }
911 if (!isdigit((int)*str))
912 return no_match;
913
914 str++;
915 }
916
917 if (str - sp > 3)
918 return no_match;
919
920 strncpy(buf, sp, str - sp);
921 if (atoi(buf) > 255)
922 return no_match;
923
924 nums++;
925
926 if (*str == '\0')
927 break;
928
929 str++;
930 }
931
932 if (nums < 4)
933 return partly_match;
934
935 return exact_match;
936}
937
938static enum match_type cmd_ipv4_prefix_match(const char *str)
939{
940 const char *sp;
941 int dots = 0;
942 char buf[4];
943
944 if (str == NULL)
945 return partly_match;
946
947 for (;;) {
948 memset(buf, 0, sizeof(buf));
949 sp = str;
950 while (*str != '\0' && *str != '/') {
951 if (*str == '.') {
952 if (dots == 3)
953 return no_match;
954
955 if (*(str + 1) == '.' || *(str + 1) == '/')
956 return no_match;
957
958 if (*(str + 1) == '\0')
959 return partly_match;
960
961 dots++;
962 break;
963 }
964
965 if (!isdigit((int)*str))
966 return no_match;
967
968 str++;
969 }
970
971 if (str - sp > 3)
972 return no_match;
973
974 strncpy(buf, sp, str - sp);
975 if (atoi(buf) > 255)
976 return no_match;
977
978 if (dots == 3) {
979 if (*str == '/') {
980 if (*(str + 1) == '\0')
981 return partly_match;
982
983 str++;
984 break;
985 } else if (*str == '\0')
986 return partly_match;
987 }
988
989 if (*str == '\0')
990 return partly_match;
991
992 str++;
993 }
994
995 sp = str;
996 while (*str != '\0') {
997 if (!isdigit((int)*str))
998 return no_match;
999
1000 str++;
1001 }
1002
1003 if (atoi(sp) > 32)
1004 return no_match;
1005
1006 return exact_match;
1007}
1008
1009#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1010#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1011#define STATE_START 1
1012#define STATE_COLON 2
1013#define STATE_DOUBLE 3
1014#define STATE_ADDR 4
1015#define STATE_DOT 5
1016#define STATE_SLASH 6
1017#define STATE_MASK 7
1018
1019#ifdef HAVE_IPV6
1020
1021static enum match_type cmd_ipv6_match(const char *str)
1022{
1023 int state = STATE_START;
1024 int colons = 0, nums = 0, double_colon = 0;
1025 const char *sp = NULL;
1026 struct sockaddr_in6 sin6_dummy;
1027 int ret;
1028
1029 if (str == NULL)
1030 return partly_match;
1031
1032 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
1033 return no_match;
1034
1035 /* use inet_pton that has a better support,
1036 * for example inet_pton can support the automatic addresses:
1037 * ::1.2.3.4
1038 */
1039 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1040
1041 if (ret == 1)
1042 return exact_match;
1043
1044 while (*str != '\0') {
1045 switch (state) {
1046 case STATE_START:
1047 if (*str == ':') {
1048 if (*(str + 1) != ':' && *(str + 1) != '\0')
1049 return no_match;
1050 colons--;
1051 state = STATE_COLON;
1052 } else {
1053 sp = str;
1054 state = STATE_ADDR;
1055 }
1056
1057 continue;
1058 case STATE_COLON:
1059 colons++;
1060 if (*(str + 1) == ':')
1061 state = STATE_DOUBLE;
1062 else {
1063 sp = str + 1;
1064 state = STATE_ADDR;
1065 }
1066 break;
1067 case STATE_DOUBLE:
1068 if (double_colon)
1069 return no_match;
1070
1071 if (*(str + 1) == ':')
1072 return no_match;
1073 else {
1074 if (*(str + 1) != '\0')
1075 colons++;
1076 sp = str + 1;
1077 state = STATE_ADDR;
1078 }
1079
1080 double_colon++;
1081 nums++;
1082 break;
1083 case STATE_ADDR:
1084 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1085 if (str - sp > 3)
1086 return no_match;
1087
1088 nums++;
1089 state = STATE_COLON;
1090 }
1091 if (*(str + 1) == '.')
1092 state = STATE_DOT;
1093 break;
1094 case STATE_DOT:
1095 state = STATE_ADDR;
1096 break;
1097 default:
1098 break;
1099 }
1100
1101 if (nums > 8)
1102 return no_match;
1103
1104 if (colons > 7)
1105 return no_match;
1106
1107 str++;
1108 }
1109
1110#if 0
1111 if (nums < 11)
1112 return partly_match;
1113#endif /* 0 */
1114
1115 return exact_match;
1116}
1117
1118static enum match_type cmd_ipv6_prefix_match(const char *str)
1119{
1120 int state = STATE_START;
1121 int colons = 0, nums = 0, double_colon = 0;
1122 int mask;
1123 const char *sp = NULL;
1124 char *endptr = NULL;
1125
1126 if (str == NULL)
1127 return partly_match;
1128
1129 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
1130 return no_match;
1131
1132 while (*str != '\0' && state != STATE_MASK) {
1133 switch (state) {
1134 case STATE_START:
1135 if (*str == ':') {
1136 if (*(str + 1) != ':' && *(str + 1) != '\0')
1137 return no_match;
1138 colons--;
1139 state = STATE_COLON;
1140 } else {
1141 sp = str;
1142 state = STATE_ADDR;
1143 }
1144
1145 continue;
1146 case STATE_COLON:
1147 colons++;
1148 if (*(str + 1) == '/')
1149 return no_match;
1150 else if (*(str + 1) == ':')
1151 state = STATE_DOUBLE;
1152 else {
1153 sp = str + 1;
1154 state = STATE_ADDR;
1155 }
1156 break;
1157 case STATE_DOUBLE:
1158 if (double_colon)
1159 return no_match;
1160
1161 if (*(str + 1) == ':')
1162 return no_match;
1163 else {
1164 if (*(str + 1) != '\0' && *(str + 1) != '/')
1165 colons++;
1166 sp = str + 1;
1167
1168 if (*(str + 1) == '/')
1169 state = STATE_SLASH;
1170 else
1171 state = STATE_ADDR;
1172 }
1173
1174 double_colon++;
1175 nums += 1;
1176 break;
1177 case STATE_ADDR:
1178 if (*(str + 1) == ':' || *(str + 1) == '.'
1179 || *(str + 1) == '\0' || *(str + 1) == '/') {
1180 if (str - sp > 3)
1181 return no_match;
1182
1183 for (; sp <= str; sp++)
1184 if (*sp == '/')
1185 return no_match;
1186
1187 nums++;
1188
1189 if (*(str + 1) == ':')
1190 state = STATE_COLON;
1191 else if (*(str + 1) == '.')
1192 state = STATE_DOT;
1193 else if (*(str + 1) == '/')
1194 state = STATE_SLASH;
1195 }
1196 break;
1197 case STATE_DOT:
1198 state = STATE_ADDR;
1199 break;
1200 case STATE_SLASH:
1201 if (*(str + 1) == '\0')
1202 return partly_match;
1203
1204 state = STATE_MASK;
1205 break;
1206 default:
1207 break;
1208 }
1209
1210 if (nums > 11)
1211 return no_match;
1212
1213 if (colons > 7)
1214 return no_match;
1215
1216 str++;
1217 }
1218
1219 if (state < STATE_MASK)
1220 return partly_match;
1221
1222 mask = strtol(str, &endptr, 10);
1223 if (*endptr != '\0')
1224 return no_match;
1225
1226 if (mask < 0 || mask > 128)
1227 return no_match;
1228
1229/* I don't know why mask < 13 makes command match partly.
1230 Forgive me to make this comments. I Want to set static default route
1231 because of lack of function to originate default in ospf6d; sorry
1232 yasu
1233 if (mask < 13)
1234 return partly_match;
1235*/
1236
1237 return exact_match;
1238}
1239
1240#endif /* HAVE_IPV6 */
1241
1242#define DECIMAL_STRLEN_MAX 10
1243
1244static int cmd_range_match(const char *range, const char *str)
1245{
1246 char *p;
1247 char buf[DECIMAL_STRLEN_MAX + 1];
1248 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001249
1250 if (str == NULL)
1251 return 1;
1252
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001253 if (range[1] == '-') {
1254 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001255
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001256 val = strtol(str, &endptr, 10);
1257 if (*endptr != '\0')
1258 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001260 range += 2;
1261 p = strchr(range, '-');
1262 if (p == NULL)
1263 return 0;
1264 if (p - range > DECIMAL_STRLEN_MAX)
1265 return 0;
1266 strncpy(buf, range, p - range);
1267 buf[p - range] = '\0';
1268 min = -strtol(buf, &endptr, 10);
1269 if (*endptr != '\0')
1270 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001271
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001272 range = p + 1;
1273 p = strchr(range, '>');
1274 if (p == NULL)
1275 return 0;
1276 if (p - range > DECIMAL_STRLEN_MAX)
1277 return 0;
1278 strncpy(buf, range, p - range);
1279 buf[p - range] = '\0';
1280 max = strtol(buf, &endptr, 10);
1281 if (*endptr != '\0')
1282 return 0;
1283
1284 if (val < min || val > max)
1285 return 0;
1286 } else {
1287 unsigned long min, max, val;
1288
1289 val = strtoul(str, &endptr, 10);
1290 if (*endptr != '\0')
1291 return 0;
1292
1293 range++;
1294 p = strchr(range, '-');
1295 if (p == NULL)
1296 return 0;
1297 if (p - range > DECIMAL_STRLEN_MAX)
1298 return 0;
1299 strncpy(buf, range, p - range);
1300 buf[p - range] = '\0';
1301 min = strtoul(buf, &endptr, 10);
1302 if (*endptr != '\0')
1303 return 0;
1304
1305 range = p + 1;
1306 p = strchr(range, '>');
1307 if (p == NULL)
1308 return 0;
1309 if (p - range > DECIMAL_STRLEN_MAX)
1310 return 0;
1311 strncpy(buf, range, p - range);
1312 buf[p - range] = '\0';
1313 max = strtoul(buf, &endptr, 10);
1314 if (*endptr != '\0')
1315 return 0;
1316
1317 if (val < min || val > max)
1318 return 0;
1319 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001320
1321 return 1;
1322}
1323
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001324/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001325static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001326{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001327 /* we've got "[blah]". We want to strip off the []s and redo the
1328 * match check for "blah"
1329 */
1330 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001332 if (len < 3)
1333 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001334
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001335 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001336}
1337
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001338static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001339cmd_match(const char *str, const char *command,
1340 enum match_type min, bool recur)
1341{
1342
1343 if (recur && CMD_OPTION(str))
1344 {
1345 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001346 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001347
1348 /* this would be a bug in a command, however handle it gracefully
1349 * as it we only discover it if a user tries to run it
1350 */
1351 if (tmp == NULL)
1352 return no_match;
1353
1354 ret = cmd_match(tmp, command, min, false);
1355
1356 talloc_free(tmp);
1357
1358 return ret;
1359 }
1360 else if (CMD_VARARG(str))
1361 return vararg_match;
1362 else if (CMD_RANGE(str))
1363 {
1364 if (cmd_range_match(str, command))
1365 return range_match;
1366 }
1367#ifdef HAVE_IPV6
1368 else if (CMD_IPV6(str))
1369 {
1370 if (cmd_ipv6_match(command) >= min)
1371 return ipv6_match;
1372 }
1373 else if (CMD_IPV6_PREFIX(str))
1374 {
1375 if (cmd_ipv6_prefix_match(command) >= min)
1376 return ipv6_prefix_match;
1377 }
1378#endif /* HAVE_IPV6 */
1379 else if (CMD_IPV4(str))
1380 {
1381 if (cmd_ipv4_match(command) >= min)
1382 return ipv4_match;
1383 }
1384 else if (CMD_IPV4_PREFIX(str))
1385 {
1386 if (cmd_ipv4_prefix_match(command) >= min)
1387 return ipv4_prefix_match;
1388 }
1389 else if (CMD_VARIABLE(str))
1390 return extend_match;
1391 else if (strncmp(command, str, strlen(command)) == 0)
1392 {
1393 if (strcmp(command, str) == 0)
1394 return exact_match;
1395 else if (partly_match >= min)
1396 return partly_match;
1397 }
1398
1399 return no_match;
1400}
1401
1402/* Filter vector at the specified index and by the given command string, to
1403 * the desired matching level (thus allowing part matches), and return match
1404 * type flag.
1405 */
1406static enum match_type
1407cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001408{
1409 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001410 struct cmd_element *cmd_element;
1411 enum match_type match_type;
1412 vector descvec;
1413 struct desc *desc;
1414
1415 match_type = no_match;
1416
1417 /* If command and cmd_element string does not match set NULL to vector */
1418 for (i = 0; i < vector_active(v); i++)
1419 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001420 if (index >= vector_active(cmd_element->strvec))
1421 vector_slot(v, i) = NULL;
1422 else {
1423 unsigned int j;
1424 int matched = 0;
1425
1426 descvec =
1427 vector_slot(cmd_element->strvec, index);
1428
1429 for (j = 0; j < vector_active(descvec); j++)
1430 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001431 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001432
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001433 ret = cmd_match (desc->cmd, command, level, true);
1434
1435 if (ret != no_match)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001436 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001437
1438 if (match_type < ret)
1439 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001440 }
1441 if (!matched)
1442 vector_slot(v, i) = NULL;
1443 }
1444 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001445
1446 if (match_type == no_match)
1447 return no_match;
1448
1449 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1450 * go again and filter out commands whose argument (at this index) is
1451 * 'weaker'. E.g., if we have 2 commands:
1452 *
1453 * foo bar <1-255>
1454 * foo bar BLAH
1455 *
1456 * and the command string is 'foo bar 10', then we will get here with with
1457 * 'range_match' being the strongest match. However, if 'BLAH' came
1458 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1459 *
1460 * If we don't do a 2nd pass and filter it out, the higher-layers will
1461 * consider this to be ambiguous.
1462 */
1463 for (i = 0; i < vector_active(v); i++)
1464 if ((cmd_element = vector_slot(v, i)) != NULL) {
1465 if (index >= vector_active(cmd_element->strvec))
1466 vector_slot(v, i) = NULL;
1467 else {
1468 unsigned int j;
1469 int matched = 0;
1470
1471 descvec =
1472 vector_slot(cmd_element->strvec, index);
1473
1474 for (j = 0; j < vector_active(descvec); j++)
1475 if ((desc = vector_slot(descvec, j))) {
1476 enum match_type ret;
1477
1478 ret = cmd_match(desc->cmd, command, any_match, true);
1479
1480 if (ret >= match_type)
1481 matched++;
1482 }
1483 if (!matched)
1484 vector_slot(v, i) = NULL;
1485 }
1486 }
1487
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001488 return match_type;
1489}
1490
1491/* Check ambiguous match */
1492static int
1493is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1494{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001495 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001496 unsigned int i;
1497 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001498 struct cmd_element *cmd_element;
1499 const char *matched = NULL;
1500 vector descvec;
1501 struct desc *desc;
1502
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001503 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1504 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1505 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1506 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1507 * that case, the string must remain allocated until this function exits or another match comes
1508 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1509 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1510 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1511 void *cmd_deopt_ctx = NULL;
1512
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001513 for (i = 0; i < vector_active(v); i++)
1514 if ((cmd_element = vector_slot(v, i)) != NULL) {
1515 int match = 0;
1516
1517 descvec = vector_slot(cmd_element->strvec, index);
1518
1519 for (j = 0; j < vector_active(descvec); j++)
1520 if ((desc = vector_slot(descvec, j))) {
1521 enum match_type ret;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001522 const char *str = desc->cmd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001523
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001524 if (CMD_OPTION(str)) {
1525 if (!cmd_deopt_ctx)
1526 cmd_deopt_ctx =
1527 talloc_named_const(tall_vty_cmd_ctx, 0,
1528 __func__);
1529 str = cmd_deopt(cmd_deopt_ctx, str);
1530 if (str == NULL)
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001531 continue;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001532 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001533
1534 switch (type) {
1535 case exact_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001536 if (!(CMD_VARIABLE (str))
1537 && strcmp(command, str) == 0)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001538 match++;
1539 break;
1540 case partly_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001541 if (!(CMD_VARIABLE (str))
1542 && strncmp(command, str, strlen (command)) == 0)
1543 {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001544 if (matched
1545 && strcmp(matched,
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001546 str) != 0) {
1547 ret = 1; /* There is ambiguous match. */
1548 goto free_and_return;
1549 } else
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001550 matched = str;
1551 match++;
1552 }
1553 break;
1554 case range_match:
1555 if (cmd_range_match
1556 (str, command)) {
1557 if (matched
1558 && strcmp(matched,
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001559 str) != 0) {
1560 ret = 1;
1561 goto free_and_return;
1562 } else
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001563 matched = str;
1564 match++;
1565 }
1566 break;
1567#ifdef HAVE_IPV6
1568 case ipv6_match:
1569 if (CMD_IPV6(str))
1570 match++;
1571 break;
1572 case ipv6_prefix_match:
1573 if ((ret =
1574 cmd_ipv6_prefix_match
1575 (command)) != no_match) {
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001576 if (ret == partly_match) {
1577 ret = 2; /* There is incomplete match. */
1578 goto free_and_return;
1579 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001580
1581 match++;
1582 }
1583 break;
1584#endif /* HAVE_IPV6 */
1585 case ipv4_match:
1586 if (CMD_IPV4(str))
1587 match++;
1588 break;
1589 case ipv4_prefix_match:
1590 if ((ret =
1591 cmd_ipv4_prefix_match
1592 (command)) != no_match) {
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001593 if (ret == partly_match) {
1594 ret = 2; /* There is incomplete match. */
1595 goto free_and_return;
1596 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001597
1598 match++;
1599 }
1600 break;
1601 case extend_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001602 if (CMD_VARIABLE (str))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001603 match++;
1604 break;
1605 case no_match:
1606 default:
1607 break;
1608 }
1609 }
1610 if (!match)
1611 vector_slot(v, i) = NULL;
1612 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001613
1614free_and_return:
1615 if (cmd_deopt_ctx)
1616 talloc_free(cmd_deopt_ctx);
1617 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001618}
1619
1620/* If src matches dst return dst string, otherwise return NULL */
1621static const char *cmd_entry_function(const char *src, const char *dst)
1622{
1623 /* Skip variable arguments. */
1624 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1625 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1626 return NULL;
1627
1628 /* In case of 'command \t', given src is NULL string. */
1629 if (src == NULL)
1630 return dst;
1631
1632 /* Matched with input string. */
1633 if (strncmp(src, dst, strlen(src)) == 0)
1634 return dst;
1635
1636 return NULL;
1637}
1638
1639/* If src matches dst return dst string, otherwise return NULL */
1640/* This version will return the dst string always if it is
1641 CMD_VARIABLE for '?' key processing */
1642static const char *cmd_entry_function_desc(const char *src, const char *dst)
1643{
1644 if (CMD_VARARG(dst))
1645 return dst;
1646
1647 if (CMD_RANGE(dst)) {
1648 if (cmd_range_match(dst, src))
1649 return dst;
1650 else
1651 return NULL;
1652 }
1653#ifdef HAVE_IPV6
1654 if (CMD_IPV6(dst)) {
1655 if (cmd_ipv6_match(src))
1656 return dst;
1657 else
1658 return NULL;
1659 }
1660
1661 if (CMD_IPV6_PREFIX(dst)) {
1662 if (cmd_ipv6_prefix_match(src))
1663 return dst;
1664 else
1665 return NULL;
1666 }
1667#endif /* HAVE_IPV6 */
1668
1669 if (CMD_IPV4(dst)) {
1670 if (cmd_ipv4_match(src))
1671 return dst;
1672 else
1673 return NULL;
1674 }
1675
1676 if (CMD_IPV4_PREFIX(dst)) {
1677 if (cmd_ipv4_prefix_match(src))
1678 return dst;
1679 else
1680 return NULL;
1681 }
1682
1683 /* Optional or variable commands always match on '?' */
1684 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1685 return dst;
1686
1687 /* In case of 'command \t', given src is NULL string. */
1688 if (src == NULL)
1689 return dst;
1690
1691 if (strncmp(src, dst, strlen(src)) == 0)
1692 return dst;
1693 else
1694 return NULL;
1695}
1696
1697/* Check same string element existence. If it isn't there return
1698 1. */
1699static int cmd_unique_string(vector v, const char *str)
1700{
1701 unsigned int i;
1702 char *match;
1703
1704 for (i = 0; i < vector_active(v); i++)
1705 if ((match = vector_slot(v, i)) != NULL)
1706 if (strcmp(match, str) == 0)
1707 return 0;
1708 return 1;
1709}
1710
1711/* Compare string to description vector. If there is same string
1712 return 1 else return 0. */
1713static int desc_unique_string(vector v, const char *str)
1714{
1715 unsigned int i;
1716 struct desc *desc;
1717
1718 for (i = 0; i < vector_active(v); i++)
1719 if ((desc = vector_slot(v, i)) != NULL)
1720 if (strcmp(desc->cmd, str) == 0)
1721 return 1;
1722 return 0;
1723}
1724
1725static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1726{
1727 if (first_word != NULL &&
1728 node != AUTH_NODE &&
1729 node != VIEW_NODE &&
1730 node != AUTH_ENABLE_NODE &&
1731 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1732 return 1;
1733 return 0;
1734}
1735
1736/* '?' describe command support. */
1737static vector
1738cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1739{
1740 unsigned int i;
1741 vector cmd_vector;
1742#define INIT_MATCHVEC_SIZE 10
1743 vector matchvec;
1744 struct cmd_element *cmd_element;
1745 unsigned int index;
1746 int ret;
1747 enum match_type match;
1748 char *command;
1749 static struct desc desc_cr = { "<cr>", "" };
1750
1751 /* Set index. */
1752 if (vector_active(vline) == 0) {
1753 *status = CMD_ERR_NO_MATCH;
1754 return NULL;
1755 } else
1756 index = vector_active(vline) - 1;
1757
1758 /* Make copy vector of current node's command vector. */
1759 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1760
1761 /* Prepare match vector */
1762 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1763
1764 /* Filter commands. */
1765 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001766 for (i = 0; i < index; i++) {
1767 command = vector_slot(vline, i);
1768 if (!command)
1769 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001770
Harald Welte80d30fe2013-02-12 11:08:57 +01001771 match = cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001772
Harald Welte80d30fe2013-02-12 11:08:57 +01001773 if (match == vararg_match) {
1774 struct cmd_element *cmd_element;
1775 vector descvec;
1776 unsigned int j, k;
1777
1778 for (j = 0; j < vector_active(cmd_vector); j++)
1779 if ((cmd_element =
1780 vector_slot(cmd_vector, j)) != NULL
1781 &&
1782 (vector_active(cmd_element->strvec))) {
1783 descvec =
1784 vector_slot(cmd_element->
1785 strvec,
1786 vector_active
1787 (cmd_element->
1788 strvec) - 1);
1789 for (k = 0;
1790 k < vector_active(descvec);
1791 k++) {
1792 struct desc *desc =
1793 vector_slot(descvec,
1794 k);
1795 vector_set(matchvec,
1796 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001797 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001798 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001799
Harald Welte80d30fe2013-02-12 11:08:57 +01001800 vector_set(matchvec, &desc_cr);
1801 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001802
Harald Welte80d30fe2013-02-12 11:08:57 +01001803 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001804 }
1805
Harald Welte80d30fe2013-02-12 11:08:57 +01001806 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1807 match)) == 1) {
1808 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001809 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001810 *status = CMD_ERR_AMBIGUOUS;
1811 return NULL;
1812 } else if (ret == 2) {
1813 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001814 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001815 *status = CMD_ERR_NO_MATCH;
1816 return NULL;
1817 }
1818 }
1819
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001820 /* Prepare match vector */
1821 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1822
1823 /* Make sure that cmd_vector is filtered based on current word */
1824 command = vector_slot(vline, index);
1825 if (command)
Vadim Yanitskiy49a0dec2017-06-12 03:49:38 +07001826 cmd_filter(command, cmd_vector, index, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001827
1828 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001829 for (i = 0; i < vector_active(cmd_vector); i++) {
1830 const char *string = NULL;
1831 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001832
Harald Welte80d30fe2013-02-12 11:08:57 +01001833 cmd_element = vector_slot(cmd_vector, i);
1834 if (!cmd_element)
1835 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001836
Harald Welted17aa592013-02-12 11:11:34 +01001837 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1838 continue;
1839
Harald Welte80d30fe2013-02-12 11:08:57 +01001840 strvec = cmd_element->strvec;
1841
1842 /* if command is NULL, index may be equal to vector_active */
1843 if (command && index >= vector_active(strvec))
1844 vector_slot(cmd_vector, i) = NULL;
1845 else {
1846 /* Check if command is completed. */
1847 if (command == NULL
1848 && index == vector_active(strvec)) {
1849 string = "<cr>";
1850 if (!desc_unique_string(matchvec, string))
1851 vector_set(matchvec, &desc_cr);
1852 } else {
1853 unsigned int j;
1854 vector descvec = vector_slot(strvec, index);
1855 struct desc *desc;
1856
1857 for (j = 0; j < vector_active(descvec); j++) {
1858 desc = vector_slot(descvec, j);
1859 if (!desc)
1860 continue;
1861 string = cmd_entry_function_desc
1862 (command, desc->cmd);
1863 if (!string)
1864 continue;
1865 /* Uniqueness check */
1866 if (!desc_unique_string(matchvec, string))
1867 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001868 }
1869 }
1870 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001871 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001872 vector_free(cmd_vector);
1873
1874 if (vector_slot(matchvec, 0) == NULL) {
1875 vector_free(matchvec);
1876 *status = CMD_ERR_NO_MATCH;
1877 } else
1878 *status = CMD_SUCCESS;
1879
1880 return matchvec;
1881}
1882
1883vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1884{
1885 vector ret;
1886
1887 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1888 enum node_type onode;
1889 vector shifted_vline;
1890 unsigned int index;
1891
1892 onode = vty->node;
1893 vty->node = ENABLE_NODE;
1894 /* We can try it on enable node, cos' the vty is authenticated */
1895
1896 shifted_vline = vector_init(vector_count(vline));
1897 /* use memcpy? */
1898 for (index = 1; index < vector_active(vline); index++) {
1899 vector_set_index(shifted_vline, index - 1,
1900 vector_lookup(vline, index));
1901 }
1902
1903 ret = cmd_describe_command_real(shifted_vline, vty, status);
1904
1905 vector_free(shifted_vline);
1906 vty->node = onode;
1907 return ret;
1908 }
1909
1910 return cmd_describe_command_real(vline, vty, status);
1911}
1912
1913/* Check LCD of matched command. */
1914static int cmd_lcd(char **matched)
1915{
1916 int i;
1917 int j;
1918 int lcd = -1;
1919 char *s1, *s2;
1920 char c1, c2;
1921
1922 if (matched[0] == NULL || matched[1] == NULL)
1923 return 0;
1924
1925 for (i = 1; matched[i] != NULL; i++) {
1926 s1 = matched[i - 1];
1927 s2 = matched[i];
1928
1929 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1930 if (c1 != c2)
1931 break;
1932
1933 if (lcd < 0)
1934 lcd = j;
1935 else {
1936 if (lcd > j)
1937 lcd = j;
1938 }
1939 }
1940 return lcd;
1941}
1942
1943/* Command line completion support. */
1944static char **cmd_complete_command_real(vector vline, struct vty *vty,
1945 int *status)
1946{
1947 unsigned int i;
1948 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1949#define INIT_MATCHVEC_SIZE 10
1950 vector matchvec;
1951 struct cmd_element *cmd_element;
1952 unsigned int index;
1953 char **match_str;
1954 struct desc *desc;
1955 vector descvec;
1956 char *command;
1957 int lcd;
1958
1959 if (vector_active(vline) == 0) {
1960 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001961 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001962 return NULL;
1963 } else
1964 index = vector_active(vline) - 1;
1965
1966 /* First, filter by preceeding command string */
1967 for (i = 0; i < index; i++)
1968 if ((command = vector_slot(vline, i))) {
1969 enum match_type match;
1970 int ret;
1971
1972 /* First try completion match, if there is exactly match return 1 */
1973 match =
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001974 cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001975
1976 /* If there is exact match then filter ambiguous match else check
1977 ambiguousness. */
1978 if ((ret =
1979 is_cmd_ambiguous(command, cmd_vector, i,
1980 match)) == 1) {
1981 vector_free(cmd_vector);
1982 *status = CMD_ERR_AMBIGUOUS;
1983 return NULL;
1984 }
1985 /*
1986 else if (ret == 2)
1987 {
1988 vector_free (cmd_vector);
1989 *status = CMD_ERR_NO_MATCH;
1990 return NULL;
1991 }
1992 */
1993 }
1994
1995 /* Prepare match vector. */
1996 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1997
1998 /* Now we got into completion */
1999 for (i = 0; i < vector_active(cmd_vector); i++)
2000 if ((cmd_element = vector_slot(cmd_vector, i))) {
2001 const char *string;
2002 vector strvec = cmd_element->strvec;
2003
2004 /* Check field length */
2005 if (index >= vector_active(strvec))
2006 vector_slot(cmd_vector, i) = NULL;
2007 else {
2008 unsigned int j;
2009
2010 descvec = vector_slot(strvec, index);
2011 for (j = 0; j < vector_active(descvec); j++)
2012 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002013 const char *cmd = desc->cmd;
2014 char *tmp = NULL;
2015
2016 if (CMD_OPTION(desc->cmd)) {
2017 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2018 cmd = tmp;
2019 }
2020 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002021 if (cmd_unique_string (matchvec, string))
2022 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002023 if (tmp)
2024 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002025 }
2026 }
2027 }
2028
2029 /* We don't need cmd_vector any more. */
2030 vector_free(cmd_vector);
2031
2032 /* No matched command */
2033 if (vector_slot(matchvec, 0) == NULL) {
2034 vector_free(matchvec);
2035
2036 /* In case of 'command \t' pattern. Do you need '?' command at
2037 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002038 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002039 *status = CMD_ERR_NOTHING_TODO;
2040 else
2041 *status = CMD_ERR_NO_MATCH;
2042 return NULL;
2043 }
2044
2045 /* Only one matched */
2046 if (vector_slot(matchvec, 1) == NULL) {
2047 match_str = (char **)matchvec->index;
2048 vector_only_wrapper_free(matchvec);
2049 *status = CMD_COMPLETE_FULL_MATCH;
2050 return match_str;
2051 }
2052 /* Make it sure last element is NULL. */
2053 vector_set(matchvec, NULL);
2054
2055 /* Check LCD of matched strings. */
2056 if (vector_slot(vline, index) != NULL) {
2057 lcd = cmd_lcd((char **)matchvec->index);
2058
2059 if (lcd) {
2060 int len = strlen(vector_slot(vline, index));
2061
2062 if (len < lcd) {
2063 char *lcdstr;
2064
2065 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2066 "complete-lcdstr");
2067 memcpy(lcdstr, matchvec->index[0], lcd);
2068 lcdstr[lcd] = '\0';
2069
2070 /* match_str = (char **) &lcdstr; */
2071
2072 /* Free matchvec. */
2073 for (i = 0; i < vector_active(matchvec); i++) {
2074 if (vector_slot(matchvec, i))
2075 talloc_free(vector_slot(matchvec, i));
2076 }
2077 vector_free(matchvec);
2078
2079 /* Make new matchvec. */
2080 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2081 vector_set(matchvec, lcdstr);
2082 match_str = (char **)matchvec->index;
2083 vector_only_wrapper_free(matchvec);
2084
2085 *status = CMD_COMPLETE_MATCH;
2086 return match_str;
2087 }
2088 }
2089 }
2090
2091 match_str = (char **)matchvec->index;
2092 vector_only_wrapper_free(matchvec);
2093 *status = CMD_COMPLETE_LIST_MATCH;
2094 return match_str;
2095}
2096
2097char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2098{
2099 char **ret;
2100
2101 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2102 enum node_type onode;
2103 vector shifted_vline;
2104 unsigned int index;
2105
2106 onode = vty->node;
2107 vty->node = ENABLE_NODE;
2108 /* We can try it on enable node, cos' the vty is authenticated */
2109
2110 shifted_vline = vector_init(vector_count(vline));
2111 /* use memcpy? */
2112 for (index = 1; index < vector_active(vline); index++) {
2113 vector_set_index(shifted_vline, index - 1,
2114 vector_lookup(vline, index));
2115 }
2116
2117 ret = cmd_complete_command_real(shifted_vline, vty, status);
2118
2119 vector_free(shifted_vline);
2120 vty->node = onode;
2121 return ret;
2122 }
2123
2124 return cmd_complete_command_real(vline, vty, status);
2125}
2126
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002127static struct vty_parent_node *vty_parent(struct vty *vty)
2128{
2129 return llist_first_entry_or_null(&vty->parent_nodes,
2130 struct vty_parent_node,
2131 entry);
2132}
2133
2134static bool vty_pop_parent(struct vty *vty)
2135{
2136 struct vty_parent_node *parent = vty_parent(vty);
2137 if (!parent)
2138 return false;
2139 llist_del(&parent->entry);
2140 vty->node = parent->node;
2141 vty->priv = parent->priv;
2142 if (vty->indent)
2143 talloc_free(vty->indent);
2144 vty->indent = parent->indent;
2145 talloc_free(parent);
2146 return true;
2147}
2148
2149static void vty_clear_parents(struct vty *vty)
2150{
2151 while (vty_pop_parent(vty));
2152}
2153
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002154/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002155/*
2156 * This function MUST eventually converge on a node when called repeatedly,
2157 * there must not be any cycles.
2158 * All 'config' nodes shall converge on CONFIG_NODE.
2159 * All other 'enable' nodes shall converge on ENABLE_NODE.
2160 * All 'view' only nodes shall converge on VIEW_NODE.
2161 * All other nodes shall converge on themselves or it must be ensured,
2162 * that the user's rights are not extended anyhow by calling this function.
2163 *
2164 * Note that these requirements also apply to all functions that are used
2165 * as go_parent_cb.
2166 * Note also that this function relies on the is_config_child callback to
2167 * recognize non-config nodes if go_parent_cb is not set.
2168 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002169int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002170{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002171 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002172 case AUTH_NODE:
2173 case VIEW_NODE:
2174 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002175 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002176 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002177 break;
2178
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002179 case AUTH_ENABLE_NODE:
2180 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002181 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002182 break;
2183
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002184 case CFG_LOG_NODE:
2185 case VTY_NODE:
2186 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002187 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002188 break;
2189
2190 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002191 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002192 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002193 vty_pop_parent(vty);
2194 }
2195 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002196 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002197 vty_clear_parents(vty);
2198 }
2199 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002200 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002201 vty_clear_parents(vty);
2202 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002203 break;
2204 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002205
2206 return vty->node;
2207}
2208
2209/* Execute command by argument vline vector. */
2210static int
2211cmd_execute_command_real(vector vline, struct vty *vty,
2212 struct cmd_element **cmd)
2213{
2214 unsigned int i;
2215 unsigned int index;
2216 vector cmd_vector;
2217 struct cmd_element *cmd_element;
2218 struct cmd_element *matched_element;
2219 unsigned int matched_count, incomplete_count;
2220 int argc;
2221 const char *argv[CMD_ARGC_MAX];
2222 enum match_type match = 0;
2223 int varflag;
2224 char *command;
2225
2226 /* Make copy of command elements. */
2227 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2228
2229 for (index = 0; index < vector_active(vline); index++)
2230 if ((command = vector_slot(vline, index))) {
2231 int ret;
2232
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002233 match = cmd_filter(command, cmd_vector, index,
2234 any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002235
2236 if (match == vararg_match)
2237 break;
2238
2239 ret =
2240 is_cmd_ambiguous(command, cmd_vector, index, match);
2241
2242 if (ret == 1) {
2243 vector_free(cmd_vector);
2244 return CMD_ERR_AMBIGUOUS;
2245 } else if (ret == 2) {
2246 vector_free(cmd_vector);
2247 return CMD_ERR_NO_MATCH;
2248 }
2249 }
2250
2251 /* Check matched count. */
2252 matched_element = NULL;
2253 matched_count = 0;
2254 incomplete_count = 0;
2255
2256 for (i = 0; i < vector_active(cmd_vector); i++)
2257 if ((cmd_element = vector_slot(cmd_vector, i))) {
2258 if (match == vararg_match
2259 || index >= cmd_element->cmdsize) {
2260 matched_element = cmd_element;
2261#if 0
2262 printf("DEBUG: %s\n", cmd_element->string);
2263#endif
2264 matched_count++;
2265 } else {
2266 incomplete_count++;
2267 }
2268 }
2269
2270 /* Finish of using cmd_vector. */
2271 vector_free(cmd_vector);
2272
2273 /* To execute command, matched_count must be 1. */
2274 if (matched_count == 0) {
2275 if (incomplete_count)
2276 return CMD_ERR_INCOMPLETE;
2277 else
2278 return CMD_ERR_NO_MATCH;
2279 }
2280
2281 if (matched_count > 1)
2282 return CMD_ERR_AMBIGUOUS;
2283
2284 /* Argument treatment */
2285 varflag = 0;
2286 argc = 0;
2287
2288 for (i = 0; i < vector_active(vline); i++) {
2289 if (varflag)
2290 argv[argc++] = vector_slot(vline, i);
2291 else {
2292 vector descvec =
2293 vector_slot(matched_element->strvec, i);
2294
2295 if (vector_active(descvec) == 1) {
2296 struct desc *desc = vector_slot(descvec, 0);
2297
2298 if (CMD_VARARG(desc->cmd))
2299 varflag = 1;
2300
2301 if (varflag || CMD_VARIABLE(desc->cmd)
2302 || CMD_OPTION(desc->cmd))
2303 argv[argc++] = vector_slot(vline, i);
2304 } else
2305 argv[argc++] = vector_slot(vline, i);
2306 }
2307
2308 if (argc >= CMD_ARGC_MAX)
2309 return CMD_ERR_EXEED_ARGC_MAX;
2310 }
2311
2312 /* For vtysh execution. */
2313 if (cmd)
2314 *cmd = matched_element;
2315
2316 if (matched_element->daemon)
2317 return CMD_SUCCESS_DAEMON;
2318
2319 /* Execute matched command. */
2320 return (*matched_element->func) (matched_element, vty, argc, argv);
2321}
2322
2323int
2324cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2325 int vtysh)
2326{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002327 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002328 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002329
2330 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002331
2332 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2333 vector shifted_vline;
2334 unsigned int index;
2335
2336 vty->node = ENABLE_NODE;
2337 /* We can try it on enable node, cos' the vty is authenticated */
2338
2339 shifted_vline = vector_init(vector_count(vline));
2340 /* use memcpy? */
2341 for (index = 1; index < vector_active(vline); index++) {
2342 vector_set_index(shifted_vline, index - 1,
2343 vector_lookup(vline, index));
2344 }
2345
2346 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2347
2348 vector_free(shifted_vline);
2349 vty->node = onode;
2350 return ret;
2351 }
2352
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002353 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002354}
2355
2356/* Execute command by argument readline. */
2357int
2358cmd_execute_command_strict(vector vline, struct vty *vty,
2359 struct cmd_element **cmd)
2360{
2361 unsigned int i;
2362 unsigned int index;
2363 vector cmd_vector;
2364 struct cmd_element *cmd_element;
2365 struct cmd_element *matched_element;
2366 unsigned int matched_count, incomplete_count;
2367 int argc;
2368 const char *argv[CMD_ARGC_MAX];
2369 int varflag;
2370 enum match_type match = 0;
2371 char *command;
2372
2373 /* Make copy of command element */
2374 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2375
2376 for (index = 0; index < vector_active(vline); index++)
2377 if ((command = vector_slot(vline, index))) {
2378 int ret;
2379
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002380 match = cmd_filter(vector_slot(vline, index),
2381 cmd_vector, index, exact_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002382
2383 /* If command meets '.VARARG' then finish matching. */
2384 if (match == vararg_match)
2385 break;
2386
2387 ret =
2388 is_cmd_ambiguous(command, cmd_vector, index, match);
2389 if (ret == 1) {
2390 vector_free(cmd_vector);
2391 return CMD_ERR_AMBIGUOUS;
2392 }
2393 if (ret == 2) {
2394 vector_free(cmd_vector);
2395 return CMD_ERR_NO_MATCH;
2396 }
2397 }
2398
2399 /* Check matched count. */
2400 matched_element = NULL;
2401 matched_count = 0;
2402 incomplete_count = 0;
2403 for (i = 0; i < vector_active(cmd_vector); i++)
2404 if (vector_slot(cmd_vector, i) != NULL) {
2405 cmd_element = vector_slot(cmd_vector, i);
2406
2407 if (match == vararg_match
2408 || index >= cmd_element->cmdsize) {
2409 matched_element = cmd_element;
2410 matched_count++;
2411 } else
2412 incomplete_count++;
2413 }
2414
2415 /* Finish of using cmd_vector. */
2416 vector_free(cmd_vector);
2417
2418 /* To execute command, matched_count must be 1. */
2419 if (matched_count == 0) {
2420 if (incomplete_count)
2421 return CMD_ERR_INCOMPLETE;
2422 else
2423 return CMD_ERR_NO_MATCH;
2424 }
2425
2426 if (matched_count > 1)
2427 return CMD_ERR_AMBIGUOUS;
2428
2429 /* Argument treatment */
2430 varflag = 0;
2431 argc = 0;
2432
2433 for (i = 0; i < vector_active(vline); i++) {
2434 if (varflag)
2435 argv[argc++] = vector_slot(vline, i);
2436 else {
2437 vector descvec =
2438 vector_slot(matched_element->strvec, i);
2439
2440 if (vector_active(descvec) == 1) {
2441 struct desc *desc = vector_slot(descvec, 0);
2442
2443 if (CMD_VARARG(desc->cmd))
2444 varflag = 1;
2445
2446 if (varflag || CMD_VARIABLE(desc->cmd)
2447 || CMD_OPTION(desc->cmd))
2448 argv[argc++] = vector_slot(vline, i);
2449 } else
2450 argv[argc++] = vector_slot(vline, i);
2451 }
2452
2453 if (argc >= CMD_ARGC_MAX)
2454 return CMD_ERR_EXEED_ARGC_MAX;
2455 }
2456
2457 /* For vtysh execution. */
2458 if (cmd)
2459 *cmd = matched_element;
2460
2461 if (matched_element->daemon)
2462 return CMD_SUCCESS_DAEMON;
2463
2464 /* Now execute matched command */
2465 return (*matched_element->func) (matched_element, vty, argc, argv);
2466}
2467
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002468static inline size_t len(const char *str)
2469{
2470 return str? strlen(str) : 0;
2471}
2472
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002473/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2474 * is longer than b, a must start with exactly b, and vice versa.
2475 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2476 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002477static int indent_cmp(const char *a, const char *b)
2478{
2479 size_t al, bl;
2480 al = len(a);
2481 bl = len(b);
2482 if (al > bl) {
2483 if (bl && strncmp(a, b, bl) != 0)
2484 return EINVAL;
2485 return 1;
2486 }
2487 /* al <= bl */
2488 if (al && strncmp(a, b, al) != 0)
2489 return EINVAL;
2490 return (al < bl)? -1 : 0;
2491}
2492
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002493/* Configration make from file. */
2494int config_from_file(struct vty *vty, FILE * fp)
2495{
2496 int ret;
2497 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002498 char *indent;
2499 int cmp;
2500 struct vty_parent_node this_node;
2501 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002502
2503 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002504 indent = NULL;
2505 vline = NULL;
2506 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002507
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002508 if (ret != CMD_SUCCESS)
2509 goto return_invalid_indent;
2510
2511 /* In case of comment or empty line */
2512 if (vline == NULL) {
2513 if (indent) {
2514 talloc_free(indent);
2515 indent = NULL;
2516 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002517 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002518 }
2519
Neels Hofmeyr43063632017-09-19 23:54:01 +02002520 /* We have a nonempty line. */
2521 if (!vty->indent) {
2522 /* We have just entered a node and expecting the first child to come up; but we
2523 * may also skip right back to a parent or ancestor level. */
2524 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002525
Neels Hofmeyr43063632017-09-19 23:54:01 +02002526 /* If there is no parent, record any indentation we encounter. */
2527 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2528
2529 if (cmp == EINVAL)
2530 goto return_invalid_indent;
2531
2532 if (cmp <= 0) {
2533 /* We have gone right back to the parent level or higher, we are skipping
2534 * this child node level entirely. Pop the parent to go back to a node
2535 * that was actually there (to reinstate vty->indent) and re-use below
2536 * go-parent while-loop to find an accurate match of indent in the node
2537 * ancestry. */
2538 vty_go_parent(vty);
2539 } else {
2540 /* The indent is deeper than the just entered parent, record the new
2541 * indentation characters. */
2542 vty->indent = talloc_strdup(vty, indent);
2543 /* This *is* the new indentation. */
2544 cmp = 0;
2545 }
2546 } else {
2547 /* There is a known indentation for this node level, validate and detect node
2548 * exits. */
2549 cmp = indent_cmp(indent, vty->indent);
2550 if (cmp == EINVAL)
2551 goto return_invalid_indent;
2552 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002553
2554 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2555 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2556 while (cmp < 0) {
2557 vty_go_parent(vty);
2558 cmp = indent_cmp(indent, vty->indent);
2559 if (cmp == EINVAL)
2560 goto return_invalid_indent;
2561 }
2562
2563 /* More indent without having entered a child node level? Either the parent node's indent
2564 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2565 * or the indentation increased even though the vty command didn't enter a child. */
2566 if (cmp > 0)
2567 goto return_invalid_indent;
2568
2569 /* Remember the current node before the command possibly changes it. */
2570 this_node = (struct vty_parent_node){
2571 .node = vty->node,
2572 .priv = vty->priv,
2573 .indent = vty->indent,
2574 };
2575
2576 parent = vty_parent(vty);
2577 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002578 cmd_free_strvec(vline);
2579
2580 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002581 && ret != CMD_ERR_NOTHING_TODO) {
2582 if (indent) {
2583 talloc_free(indent);
2584 indent = NULL;
2585 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002586 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002587 }
2588
2589 /* If we have stepped down into a child node, push a parent frame.
2590 * The causality is such: we don't expect every single node entry implementation to push
2591 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2592 * a parent node. Hence if the node changed without the parent node changing, we must
2593 * have stepped into a child node (and now expect a deeper indent). */
2594 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2595 /* Push the parent node. */
2596 parent = talloc_zero(vty, struct vty_parent_node);
2597 *parent = this_node;
2598 llist_add(&parent->entry, &vty->parent_nodes);
2599
2600 /* The current talloc'ed vty->indent string will now be owned by this parent
2601 * struct. Indicate that we don't know what deeper indent characters the user
2602 * will choose. */
2603 vty->indent = NULL;
2604 }
2605
2606 if (indent) {
2607 talloc_free(indent);
2608 indent = NULL;
2609 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002610 }
2611 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002612
2613return_invalid_indent:
2614 if (vline)
2615 cmd_free_strvec(vline);
2616 if (indent) {
2617 talloc_free(indent);
2618 indent = NULL;
2619 }
2620 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002621}
2622
2623/* Configration from terminal */
2624DEFUN(config_terminal,
2625 config_terminal_cmd,
2626 "configure terminal",
2627 "Configuration from vty interface\n" "Configuration terminal\n")
2628{
2629 if (vty_config_lock(vty))
2630 vty->node = CONFIG_NODE;
2631 else {
2632 vty_out(vty, "VTY configuration is locked by other VTY%s",
2633 VTY_NEWLINE);
2634 return CMD_WARNING;
2635 }
2636 return CMD_SUCCESS;
2637}
2638
2639/* Enable command */
2640DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2641{
2642 /* If enable password is NULL, change to ENABLE_NODE */
2643 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2644 vty->type == VTY_SHELL_SERV)
2645 vty->node = ENABLE_NODE;
2646 else
2647 vty->node = AUTH_ENABLE_NODE;
2648
2649 return CMD_SUCCESS;
2650}
2651
2652/* Disable command */
2653DEFUN(disable,
2654 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2655{
2656 if (vty->node == ENABLE_NODE)
2657 vty->node = VIEW_NODE;
2658 return CMD_SUCCESS;
2659}
2660
2661/* Down vty node level. */
2662gDEFUN(config_exit,
2663 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2664{
2665 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002666 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002667 case VIEW_NODE:
2668 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002669 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002670 break;
2671 case CONFIG_NODE:
2672 vty->node = ENABLE_NODE;
2673 vty_config_unlock(vty);
2674 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002675 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002676 if (vty->node > CONFIG_NODE)
2677 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002678 break;
2679 }
2680 return CMD_SUCCESS;
2681}
2682
2683/* End of configuration. */
2684 gDEFUN(config_end,
2685 config_end_cmd, "end", "End current mode and change to enable mode.")
2686{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002687 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002688 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002689
2690 /* Repeatedly call go_parent until a top node is reached. */
2691 while (vty->node > CONFIG_NODE) {
2692 if (vty->node == last_node) {
2693 /* Ensure termination, this shouldn't happen. */
2694 break;
2695 }
2696 last_node = vty->node;
2697 vty_go_parent(vty);
2698 }
2699
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002700 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002701 if (vty->node > ENABLE_NODE)
2702 vty->node = ENABLE_NODE;
2703 vty->index = NULL;
2704 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002705 }
2706 return CMD_SUCCESS;
2707}
2708
2709/* Show version. */
2710DEFUN(show_version,
2711 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2712{
Harald Welte237f6242010-05-25 23:00:45 +02002713 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2714 host.app_info->version,
2715 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2716 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002717
2718 return CMD_SUCCESS;
2719}
2720
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002721DEFUN(show_online_help,
2722 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2723{
2724 vty_dump_nodes(vty);
2725 return CMD_SUCCESS;
2726}
2727
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002728/* Help display function for all node. */
2729gDEFUN(config_help,
2730 config_help_cmd, "help", "Description of the interactive help system\n")
2731{
2732 vty_out(vty,
2733 "This VTY provides advanced help features. When you need help,%s\
2734anytime at the command line please press '?'.%s\
2735%s\
2736If nothing matches, the help list will be empty and you must backup%s\
2737 until entering a '?' shows the available options.%s\
2738Two styles of help are provided:%s\
27391. Full help is available when you are ready to enter a%s\
2740command argument (e.g. 'show ?') and describes each possible%s\
2741argument.%s\
27422. Partial help is provided when an abbreviated argument is entered%s\
2743 and you want to know what arguments match the input%s\
2744 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2745 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2746 return CMD_SUCCESS;
2747}
2748
2749/* Help display function for all node. */
2750gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2751{
2752 unsigned int i;
2753 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2754 struct cmd_element *cmd;
2755
2756 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2757 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002758 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002759 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2760 return CMD_SUCCESS;
2761}
2762
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002763static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002764{
2765 unsigned int i;
2766 int fd;
2767 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002768 char *config_file_tmp = NULL;
2769 char *config_file_sav = NULL;
2770 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002771 struct stat st;
2772
2773 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002774
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002775 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2776 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2777 * manually instead. */
2778
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002779 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002780 config_file_sav =
2781 _talloc_zero(tall_vty_cmd_ctx,
2782 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2783 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002784 if (!config_file_sav)
2785 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002786 strcpy(config_file_sav, config_file);
2787 strcat(config_file_sav, CONF_BACKUP_EXT);
2788
2789 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002790 "config_file_tmp");
2791 if (!config_file_tmp) {
2792 talloc_free(config_file_sav);
2793 return -1;
2794 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002795 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2796
2797 /* Open file to configuration write. */
2798 fd = mkstemp(config_file_tmp);
2799 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002800 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002801 talloc_free(config_file_tmp);
2802 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002803 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002804 }
2805
2806 /* Make vty for configuration file. */
2807 file_vty = vty_new();
2808 file_vty->fd = fd;
2809 file_vty->type = VTY_FILE;
2810
2811 /* Config file header print. */
2812 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002813 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002814 //vty_time_print (file_vty, 1);
2815 vty_out(file_vty, "!\n");
2816
2817 for (i = 0; i < vector_active(cmdvec); i++)
2818 if ((node = vector_slot(cmdvec, i)) && node->func) {
2819 if ((*node->func) (file_vty))
2820 vty_out(file_vty, "!\n");
2821 }
2822 vty_close(file_vty);
2823
2824 if (unlink(config_file_sav) != 0)
2825 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002826 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002827 talloc_free(config_file_sav);
2828 talloc_free(config_file_tmp);
2829 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002830 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002831 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002832
2833 /* Only link the .sav file if the original file exists */
2834 if (stat(config_file, &st) == 0) {
2835 if (link(config_file, config_file_sav) != 0) {
2836 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2837 talloc_free(config_file_sav);
2838 talloc_free(config_file_tmp);
2839 unlink(config_file_tmp);
2840 return -3;
2841 }
2842 sync();
2843 if (unlink(config_file) != 0) {
2844 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2845 talloc_free(config_file_sav);
2846 talloc_free(config_file_tmp);
2847 unlink(config_file_tmp);
2848 return -4;
2849 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002850 }
2851 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002852 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002853 talloc_free(config_file_sav);
2854 talloc_free(config_file_tmp);
2855 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002856 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002857 }
2858 unlink(config_file_tmp);
2859 sync();
2860
2861 talloc_free(config_file_sav);
2862 talloc_free(config_file_tmp);
2863
2864 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002865 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2866 return -6;
2867 }
2868
2869 return 0;
2870}
2871
2872
2873/* Write current configuration into file. */
2874DEFUN(config_write_file,
2875 config_write_file_cmd,
2876 "write file",
2877 "Write running configuration to memory, network, or terminal\n"
2878 "Write to configuration file\n")
2879{
2880 char *failed_file;
2881 int rc;
2882
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002883 if (host.app_info->config_is_consistent) {
2884 rc = host.app_info->config_is_consistent(vty);
2885 if (!rc) {
2886 vty_out(vty, "Configuration is not consistent%s",
2887 VTY_NEWLINE);
2888 return CMD_WARNING;
2889 }
2890 }
2891
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002892 if (host.config == NULL) {
2893 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2894 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002895 return CMD_WARNING;
2896 }
2897
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002898 rc = write_config_file(host.config, &failed_file);
2899 switch (rc) {
2900 case -1:
2901 vty_out(vty, "Can't open configuration file %s.%s",
2902 failed_file, VTY_NEWLINE);
2903 rc = CMD_WARNING;
2904 break;
2905 case -2:
2906 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2907 failed_file, VTY_NEWLINE);
2908 rc = CMD_WARNING;
2909 break;
2910 case -3:
2911 vty_out(vty, "Can't backup old configuration file %s.%s",
2912 failed_file, VTY_NEWLINE);
2913 rc = CMD_WARNING;
2914 break;
2915 case -4:
2916 vty_out(vty, "Can't unlink configuration file %s.%s",
2917 failed_file, VTY_NEWLINE);
2918 rc = CMD_WARNING;
2919 break;
2920 case -5:
2921 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2922 VTY_NEWLINE);
2923 rc = CMD_WARNING;
2924 break;
2925 case -6:
2926 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2927 failed_file, strerror(errno), errno, VTY_NEWLINE);
2928 rc = CMD_WARNING;
2929 break;
2930 default:
2931 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2932 rc = CMD_SUCCESS;
2933 break;
2934 }
2935
2936 talloc_free(failed_file);
2937 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002938}
2939
2940ALIAS(config_write_file,
2941 config_write_cmd,
2942 "write", "Write running configuration to memory, network, or terminal\n")
2943
2944 ALIAS(config_write_file,
2945 config_write_memory_cmd,
2946 "write memory",
2947 "Write running configuration to memory, network, or terminal\n"
2948 "Write configuration to the file (same as write file)\n")
2949
2950 ALIAS(config_write_file,
2951 copy_runningconfig_startupconfig_cmd,
2952 "copy running-config startup-config",
2953 "Copy configuration\n"
2954 "Copy running config to... \n"
2955 "Copy running config to startup config (same as write file)\n")
2956
2957/* Write current configuration into the terminal. */
2958 DEFUN(config_write_terminal,
2959 config_write_terminal_cmd,
2960 "write terminal",
2961 "Write running configuration to memory, network, or terminal\n"
2962 "Write to terminal\n")
2963{
2964 unsigned int i;
2965 struct cmd_node *node;
2966
2967 if (vty->type == VTY_SHELL_SERV) {
2968 for (i = 0; i < vector_active(cmdvec); i++)
2969 if ((node = vector_slot(cmdvec, i)) && node->func
2970 && node->vtysh) {
2971 if ((*node->func) (vty))
2972 vty_out(vty, "!%s", VTY_NEWLINE);
2973 }
2974 } else {
2975 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2976 VTY_NEWLINE);
2977 vty_out(vty, "!%s", VTY_NEWLINE);
2978
2979 for (i = 0; i < vector_active(cmdvec); i++)
2980 if ((node = vector_slot(cmdvec, i)) && node->func) {
2981 if ((*node->func) (vty))
2982 vty_out(vty, "!%s", VTY_NEWLINE);
2983 }
2984 vty_out(vty, "end%s", VTY_NEWLINE);
2985 }
2986 return CMD_SUCCESS;
2987}
2988
2989/* Write current configuration into the terminal. */
2990ALIAS(config_write_terminal,
2991 show_running_config_cmd,
2992 "show running-config", SHOW_STR "running configuration\n")
2993
2994/* Write startup configuration into the terminal. */
2995 DEFUN(show_startup_config,
2996 show_startup_config_cmd,
2997 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2998{
2999 char buf[BUFSIZ];
3000 FILE *confp;
3001
3002 confp = fopen(host.config, "r");
3003 if (confp == NULL) {
3004 vty_out(vty, "Can't open configuration file [%s]%s",
3005 host.config, VTY_NEWLINE);
3006 return CMD_WARNING;
3007 }
3008
3009 while (fgets(buf, BUFSIZ, confp)) {
3010 char *cp = buf;
3011
3012 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3013 cp++;
3014 *cp = '\0';
3015
3016 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3017 }
3018
3019 fclose(confp);
3020
3021 return CMD_SUCCESS;
3022}
3023
3024/* Hostname configuration */
3025DEFUN(config_hostname,
3026 hostname_cmd,
3027 "hostname WORD",
3028 "Set system's network name\n" "This system's network name\n")
3029{
3030 if (!isalpha((int)*argv[0])) {
3031 vty_out(vty, "Please specify string starting with alphabet%s",
3032 VTY_NEWLINE);
3033 return CMD_WARNING;
3034 }
3035
3036 if (host.name)
3037 talloc_free(host.name);
3038
3039 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3040 return CMD_SUCCESS;
3041}
3042
3043DEFUN(config_no_hostname,
3044 no_hostname_cmd,
3045 "no hostname [HOSTNAME]",
3046 NO_STR "Reset system's network name\n" "Host name of this router\n")
3047{
3048 if (host.name)
3049 talloc_free(host.name);
3050 host.name = NULL;
3051 return CMD_SUCCESS;
3052}
3053
3054/* VTY interface password set. */
3055DEFUN(config_password, password_cmd,
3056 "password (8|) WORD",
3057 "Assign the terminal connection password\n"
3058 "Specifies a HIDDEN password will follow\n"
3059 "dummy string \n" "The HIDDEN line password string\n")
3060{
3061 /* Argument check. */
3062 if (argc == 0) {
3063 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3064 return CMD_WARNING;
3065 }
3066
3067 if (argc == 2) {
3068 if (*argv[0] == '8') {
3069 if (host.password)
3070 talloc_free(host.password);
3071 host.password = NULL;
3072 if (host.password_encrypt)
3073 talloc_free(host.password_encrypt);
3074 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3075 return CMD_SUCCESS;
3076 } else {
3077 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3078 return CMD_WARNING;
3079 }
3080 }
3081
3082 if (!isalnum((int)*argv[0])) {
3083 vty_out(vty,
3084 "Please specify string starting with alphanumeric%s",
3085 VTY_NEWLINE);
3086 return CMD_WARNING;
3087 }
3088
3089 if (host.password)
3090 talloc_free(host.password);
3091 host.password = NULL;
3092
3093#ifdef VTY_CRYPT_PW
3094 if (host.encrypt) {
3095 if (host.password_encrypt)
3096 talloc_free(host.password_encrypt);
3097 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3098 } else
3099#endif
3100 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3101
3102 return CMD_SUCCESS;
3103}
3104
3105ALIAS(config_password, password_text_cmd,
3106 "password LINE",
3107 "Assign the terminal connection password\n"
3108 "The UNENCRYPTED (cleartext) line password\n")
3109
3110/* VTY enable password set. */
3111 DEFUN(config_enable_password, enable_password_cmd,
3112 "enable password (8|) WORD",
3113 "Modify enable password parameters\n"
3114 "Assign the privileged level password\n"
3115 "Specifies a HIDDEN password will follow\n"
3116 "dummy string \n" "The HIDDEN 'enable' password string\n")
3117{
3118 /* Argument check. */
3119 if (argc == 0) {
3120 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3121 return CMD_WARNING;
3122 }
3123
3124 /* Crypt type is specified. */
3125 if (argc == 2) {
3126 if (*argv[0] == '8') {
3127 if (host.enable)
3128 talloc_free(host.enable);
3129 host.enable = NULL;
3130
3131 if (host.enable_encrypt)
3132 talloc_free(host.enable_encrypt);
3133 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3134
3135 return CMD_SUCCESS;
3136 } else {
3137 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3138 return CMD_WARNING;
3139 }
3140 }
3141
3142 if (!isalnum((int)*argv[0])) {
3143 vty_out(vty,
3144 "Please specify string starting with alphanumeric%s",
3145 VTY_NEWLINE);
3146 return CMD_WARNING;
3147 }
3148
3149 if (host.enable)
3150 talloc_free(host.enable);
3151 host.enable = NULL;
3152
3153 /* Plain password input. */
3154#ifdef VTY_CRYPT_PW
3155 if (host.encrypt) {
3156 if (host.enable_encrypt)
3157 talloc_free(host.enable_encrypt);
3158 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3159 } else
3160#endif
3161 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3162
3163 return CMD_SUCCESS;
3164}
3165
3166ALIAS(config_enable_password,
3167 enable_password_text_cmd,
3168 "enable password LINE",
3169 "Modify enable password parameters\n"
3170 "Assign the privileged level password\n"
3171 "The UNENCRYPTED (cleartext) 'enable' password\n")
3172
3173/* VTY enable password delete. */
3174 DEFUN(no_config_enable_password, no_enable_password_cmd,
3175 "no enable password",
3176 NO_STR
3177 "Modify enable password parameters\n"
3178 "Assign the privileged level password\n")
3179{
3180 if (host.enable)
3181 talloc_free(host.enable);
3182 host.enable = NULL;
3183
3184 if (host.enable_encrypt)
3185 talloc_free(host.enable_encrypt);
3186 host.enable_encrypt = NULL;
3187
3188 return CMD_SUCCESS;
3189}
3190
3191#ifdef VTY_CRYPT_PW
3192DEFUN(service_password_encrypt,
3193 service_password_encrypt_cmd,
3194 "service password-encryption",
3195 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3196{
3197 if (host.encrypt)
3198 return CMD_SUCCESS;
3199
3200 host.encrypt = 1;
3201
3202 if (host.password) {
3203 if (host.password_encrypt)
3204 talloc_free(host.password_encrypt);
3205 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3206 }
3207 if (host.enable) {
3208 if (host.enable_encrypt)
3209 talloc_free(host.enable_encrypt);
3210 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3211 }
3212
3213 return CMD_SUCCESS;
3214}
3215
3216DEFUN(no_service_password_encrypt,
3217 no_service_password_encrypt_cmd,
3218 "no service password-encryption",
3219 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3220{
3221 if (!host.encrypt)
3222 return CMD_SUCCESS;
3223
3224 host.encrypt = 0;
3225
3226 if (host.password_encrypt)
3227 talloc_free(host.password_encrypt);
3228 host.password_encrypt = NULL;
3229
3230 if (host.enable_encrypt)
3231 talloc_free(host.enable_encrypt);
3232 host.enable_encrypt = NULL;
3233
3234 return CMD_SUCCESS;
3235}
3236#endif
3237
3238DEFUN(config_terminal_length, config_terminal_length_cmd,
3239 "terminal length <0-512>",
3240 "Set terminal line parameters\n"
3241 "Set number of lines on a screen\n"
3242 "Number of lines on screen (0 for no pausing)\n")
3243{
3244 int lines;
3245 char *endptr = NULL;
3246
3247 lines = strtol(argv[0], &endptr, 10);
3248 if (lines < 0 || lines > 512 || *endptr != '\0') {
3249 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3250 return CMD_WARNING;
3251 }
3252 vty->lines = lines;
3253
3254 return CMD_SUCCESS;
3255}
3256
3257DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3258 "terminal no length",
3259 "Set terminal line parameters\n"
3260 NO_STR "Set number of lines on a screen\n")
3261{
3262 vty->lines = -1;
3263 return CMD_SUCCESS;
3264}
3265
3266DEFUN(service_terminal_length, service_terminal_length_cmd,
3267 "service terminal-length <0-512>",
3268 "Set up miscellaneous service\n"
3269 "System wide terminal length configuration\n"
3270 "Number of lines of VTY (0 means no line control)\n")
3271{
3272 int lines;
3273 char *endptr = NULL;
3274
3275 lines = strtol(argv[0], &endptr, 10);
3276 if (lines < 0 || lines > 512 || *endptr != '\0') {
3277 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3278 return CMD_WARNING;
3279 }
3280 host.lines = lines;
3281
3282 return CMD_SUCCESS;
3283}
3284
3285DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3286 "no service terminal-length [<0-512>]",
3287 NO_STR
3288 "Set up miscellaneous service\n"
3289 "System wide terminal length configuration\n"
3290 "Number of lines of VTY (0 means no line control)\n")
3291{
3292 host.lines = -1;
3293 return CMD_SUCCESS;
3294}
3295
3296DEFUN_HIDDEN(do_echo,
3297 echo_cmd,
3298 "echo .MESSAGE",
3299 "Echo a message back to the vty\n" "The message to echo\n")
3300{
3301 char *message;
3302
3303 vty_out(vty, "%s%s",
3304 ((message =
3305 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3306 if (message)
3307 talloc_free(message);
3308 return CMD_SUCCESS;
3309}
3310
3311#if 0
3312DEFUN(config_logmsg,
3313 config_logmsg_cmd,
3314 "logmsg " LOG_LEVELS " .MESSAGE",
3315 "Send a message to enabled logging destinations\n"
3316 LOG_LEVEL_DESC "The message to send\n")
3317{
3318 int level;
3319 char *message;
3320
3321 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3322 return CMD_ERR_NO_MATCH;
3323
3324 zlog(NULL, level,
3325 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3326 if (message)
3327 talloc_free(message);
3328 return CMD_SUCCESS;
3329}
3330
3331DEFUN(show_logging,
3332 show_logging_cmd,
3333 "show logging", SHOW_STR "Show current logging configuration\n")
3334{
3335 struct zlog *zl = zlog_default;
3336
3337 vty_out(vty, "Syslog logging: ");
3338 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3339 vty_out(vty, "disabled");
3340 else
3341 vty_out(vty, "level %s, facility %s, ident %s",
3342 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3343 facility_name(zl->facility), zl->ident);
3344 vty_out(vty, "%s", VTY_NEWLINE);
3345
3346 vty_out(vty, "Stdout logging: ");
3347 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3348 vty_out(vty, "disabled");
3349 else
3350 vty_out(vty, "level %s",
3351 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3352 vty_out(vty, "%s", VTY_NEWLINE);
3353
3354 vty_out(vty, "Monitor logging: ");
3355 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3356 vty_out(vty, "disabled");
3357 else
3358 vty_out(vty, "level %s",
3359 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3360 vty_out(vty, "%s", VTY_NEWLINE);
3361
3362 vty_out(vty, "File logging: ");
3363 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3364 vty_out(vty, "disabled");
3365 else
3366 vty_out(vty, "level %s, filename %s",
3367 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3368 zl->filename);
3369 vty_out(vty, "%s", VTY_NEWLINE);
3370
3371 vty_out(vty, "Protocol name: %s%s",
3372 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3373 vty_out(vty, "Record priority: %s%s",
3374 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3375
3376 return CMD_SUCCESS;
3377}
3378
3379DEFUN(config_log_stdout,
3380 config_log_stdout_cmd,
3381 "log stdout", "Logging control\n" "Set stdout logging level\n")
3382{
3383 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3384 return CMD_SUCCESS;
3385}
3386
3387DEFUN(config_log_stdout_level,
3388 config_log_stdout_level_cmd,
3389 "log stdout " LOG_LEVELS,
3390 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3391{
3392 int level;
3393
3394 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3395 return CMD_ERR_NO_MATCH;
3396 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3397 return CMD_SUCCESS;
3398}
3399
3400DEFUN(no_config_log_stdout,
3401 no_config_log_stdout_cmd,
3402 "no log stdout [LEVEL]",
3403 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3404{
3405 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3406 return CMD_SUCCESS;
3407}
3408
3409DEFUN(config_log_monitor,
3410 config_log_monitor_cmd,
3411 "log monitor",
3412 "Logging control\n" "Set terminal line (monitor) logging level\n")
3413{
3414 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3415 return CMD_SUCCESS;
3416}
3417
3418DEFUN(config_log_monitor_level,
3419 config_log_monitor_level_cmd,
3420 "log monitor " LOG_LEVELS,
3421 "Logging control\n"
3422 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3423{
3424 int level;
3425
3426 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3427 return CMD_ERR_NO_MATCH;
3428 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3429 return CMD_SUCCESS;
3430}
3431
3432DEFUN(no_config_log_monitor,
3433 no_config_log_monitor_cmd,
3434 "no log monitor [LEVEL]",
3435 NO_STR
3436 "Logging control\n"
3437 "Disable terminal line (monitor) logging\n" "Logging level\n")
3438{
3439 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3440 return CMD_SUCCESS;
3441}
3442
3443static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3444{
3445 int ret;
3446 char *p = NULL;
3447 const char *fullpath;
3448
3449 /* Path detection. */
3450 if (!IS_DIRECTORY_SEP(*fname)) {
3451 char cwd[MAXPATHLEN + 1];
3452 cwd[MAXPATHLEN] = '\0';
3453
3454 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3455 zlog_err("config_log_file: Unable to alloc mem!");
3456 return CMD_WARNING;
3457 }
3458
3459 if ((p = _talloc_zero(tall_vcmd_ctx,
3460 strlen(cwd) + strlen(fname) + 2),
3461 "set_log_file")
3462 == NULL) {
3463 zlog_err("config_log_file: Unable to alloc mem!");
3464 return CMD_WARNING;
3465 }
3466 sprintf(p, "%s/%s", cwd, fname);
3467 fullpath = p;
3468 } else
3469 fullpath = fname;
3470
3471 ret = zlog_set_file(NULL, fullpath, loglevel);
3472
3473 if (p)
3474 talloc_free(p);
3475
3476 if (!ret) {
3477 vty_out(vty, "can't open logfile %s\n", fname);
3478 return CMD_WARNING;
3479 }
3480
3481 if (host.logfile)
3482 talloc_free(host.logfile);
3483
3484 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3485
3486 return CMD_SUCCESS;
3487}
3488
3489DEFUN(config_log_file,
3490 config_log_file_cmd,
3491 "log file FILENAME",
3492 "Logging control\n" "Logging to file\n" "Logging filename\n")
3493{
3494 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3495}
3496
3497DEFUN(config_log_file_level,
3498 config_log_file_level_cmd,
3499 "log file FILENAME " LOG_LEVELS,
3500 "Logging control\n"
3501 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3502{
3503 int level;
3504
3505 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3506 return CMD_ERR_NO_MATCH;
3507 return set_log_file(vty, argv[0], level);
3508}
3509
3510DEFUN(no_config_log_file,
3511 no_config_log_file_cmd,
3512 "no log file [FILENAME]",
3513 NO_STR
3514 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3515{
3516 zlog_reset_file(NULL);
3517
3518 if (host.logfile)
3519 talloc_free(host.logfile);
3520
3521 host.logfile = NULL;
3522
3523 return CMD_SUCCESS;
3524}
3525
3526ALIAS(no_config_log_file,
3527 no_config_log_file_level_cmd,
3528 "no log file FILENAME LEVEL",
3529 NO_STR
3530 "Logging control\n"
3531 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3532
3533 DEFUN(config_log_syslog,
3534 config_log_syslog_cmd,
3535 "log syslog", "Logging control\n" "Set syslog logging level\n")
3536{
3537 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3538 return CMD_SUCCESS;
3539}
3540
3541DEFUN(config_log_syslog_level,
3542 config_log_syslog_level_cmd,
3543 "log syslog " LOG_LEVELS,
3544 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3545{
3546 int level;
3547
3548 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3549 return CMD_ERR_NO_MATCH;
3550 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3551 return CMD_SUCCESS;
3552}
3553
3554DEFUN_DEPRECATED(config_log_syslog_facility,
3555 config_log_syslog_facility_cmd,
3556 "log syslog facility " LOG_FACILITIES,
3557 "Logging control\n"
3558 "Logging goes to syslog\n"
3559 "(Deprecated) Facility parameter for syslog messages\n"
3560 LOG_FACILITY_DESC)
3561{
3562 int facility;
3563
3564 if ((facility = facility_match(argv[0])) < 0)
3565 return CMD_ERR_NO_MATCH;
3566
3567 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3568 zlog_default->facility = facility;
3569 return CMD_SUCCESS;
3570}
3571
3572DEFUN(no_config_log_syslog,
3573 no_config_log_syslog_cmd,
3574 "no log syslog [LEVEL]",
3575 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3576{
3577 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3578 return CMD_SUCCESS;
3579}
3580
3581ALIAS(no_config_log_syslog,
3582 no_config_log_syslog_facility_cmd,
3583 "no log syslog facility " LOG_FACILITIES,
3584 NO_STR
3585 "Logging control\n"
3586 "Logging goes to syslog\n"
3587 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3588
3589 DEFUN(config_log_facility,
3590 config_log_facility_cmd,
3591 "log facility " LOG_FACILITIES,
3592 "Logging control\n"
3593 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3594{
3595 int facility;
3596
3597 if ((facility = facility_match(argv[0])) < 0)
3598 return CMD_ERR_NO_MATCH;
3599 zlog_default->facility = facility;
3600 return CMD_SUCCESS;
3601}
3602
3603DEFUN(no_config_log_facility,
3604 no_config_log_facility_cmd,
3605 "no log facility [FACILITY]",
3606 NO_STR
3607 "Logging control\n"
3608 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3609{
3610 zlog_default->facility = LOG_DAEMON;
3611 return CMD_SUCCESS;
3612}
3613
3614DEFUN_DEPRECATED(config_log_trap,
3615 config_log_trap_cmd,
3616 "log trap " LOG_LEVELS,
3617 "Logging control\n"
3618 "(Deprecated) Set logging level and default for all destinations\n"
3619 LOG_LEVEL_DESC)
3620{
3621 int new_level;
3622 int i;
3623
3624 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3625 return CMD_ERR_NO_MATCH;
3626
3627 zlog_default->default_lvl = new_level;
3628 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3629 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3630 zlog_default->maxlvl[i] = new_level;
3631 return CMD_SUCCESS;
3632}
3633
3634DEFUN_DEPRECATED(no_config_log_trap,
3635 no_config_log_trap_cmd,
3636 "no log trap [LEVEL]",
3637 NO_STR
3638 "Logging control\n"
3639 "Permit all logging information\n" "Logging level\n")
3640{
3641 zlog_default->default_lvl = LOG_DEBUG;
3642 return CMD_SUCCESS;
3643}
3644
3645DEFUN(config_log_record_priority,
3646 config_log_record_priority_cmd,
3647 "log record-priority",
3648 "Logging control\n"
3649 "Log the priority of the message within the message\n")
3650{
3651 zlog_default->record_priority = 1;
3652 return CMD_SUCCESS;
3653}
3654
3655DEFUN(no_config_log_record_priority,
3656 no_config_log_record_priority_cmd,
3657 "no log record-priority",
3658 NO_STR
3659 "Logging control\n"
3660 "Do not log the priority of the message within the message\n")
3661{
3662 zlog_default->record_priority = 0;
3663 return CMD_SUCCESS;
3664}
3665#endif
3666
3667DEFUN(banner_motd_file,
3668 banner_motd_file_cmd,
3669 "banner motd file [FILE]",
3670 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3671{
3672 if (host.motdfile)
3673 talloc_free(host.motdfile);
3674 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3675
3676 return CMD_SUCCESS;
3677}
3678
3679DEFUN(banner_motd_default,
3680 banner_motd_default_cmd,
3681 "banner motd default",
3682 "Set banner string\n" "Strings for motd\n" "Default string\n")
3683{
3684 host.motd = default_motd;
3685 return CMD_SUCCESS;
3686}
3687
3688DEFUN(no_banner_motd,
3689 no_banner_motd_cmd,
3690 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3691{
3692 host.motd = NULL;
3693 if (host.motdfile)
3694 talloc_free(host.motdfile);
3695 host.motdfile = NULL;
3696 return CMD_SUCCESS;
3697}
3698
3699/* Set config filename. Called from vty.c */
3700void host_config_set(const char *filename)
3701{
3702 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3703}
3704
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003705/*! Deprecated, now happens implicitly when calling install_node().
3706 * Users of the API may still attempt to call this function, hence
3707 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003708void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003709{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003710}
3711
3712/*! Deprecated, now happens implicitly when calling install_node().
3713 * Users of the API may still attempt to call this function, hence
3714 * leave it here as a no-op. */
3715void vty_install_default(int node)
3716{
3717}
3718
3719/*! Install common commands like 'exit' and 'list'. */
3720static void install_basic_node_commands(int node)
3721{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003722 install_element(node, &config_help_cmd);
3723 install_element(node, &config_list_cmd);
3724
3725 install_element(node, &config_write_terminal_cmd);
3726 install_element(node, &config_write_file_cmd);
3727 install_element(node, &config_write_memory_cmd);
3728 install_element(node, &config_write_cmd);
3729 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003730
3731 install_element(node, &config_exit_cmd);
3732
3733 if (node >= CONFIG_NODE) {
3734 /* It's not a top node. */
3735 install_element(node, &config_end_cmd);
3736 }
3737}
3738
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003739/*! Return true if a node is installed by install_basic_node_commands(), so
3740 * that we can avoid repeating them for each and every node during 'show
3741 * running-config' */
3742static bool vty_command_is_common(struct cmd_element *cmd)
3743{
3744 if (cmd == &config_help_cmd
3745 || cmd == &config_list_cmd
3746 || cmd == &config_write_terminal_cmd
3747 || cmd == &config_write_file_cmd
3748 || cmd == &config_write_memory_cmd
3749 || cmd == &config_write_cmd
3750 || cmd == &show_running_config_cmd
3751 || cmd == &config_exit_cmd
3752 || cmd == &config_end_cmd)
3753 return true;
3754 return false;
3755}
3756
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003757/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003758 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003759 * \param[in] vty the vty of the code
3760 * \param[in] filename where to store the file
3761 * \return 0 in case of success.
3762 *
3763 * If the filename already exists create a filename.sav
3764 * version with the current code.
3765 *
3766 */
3767int osmo_vty_write_config_file(const char *filename)
3768{
3769 char *failed_file;
3770 int rc;
3771
3772 rc = write_config_file(filename, &failed_file);
3773 talloc_free(failed_file);
3774 return rc;
3775}
3776
3777/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003778 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003779 * \return 0 in case of success.
3780 *
3781 * If the filename already exists create a filename.sav
3782 * version with the current code.
3783 *
3784 */
3785int osmo_vty_save_config_file(void)
3786{
3787 char *failed_file;
3788 int rc;
3789
3790 if (host.config == NULL)
3791 return -7;
3792
3793 rc = write_config_file(host.config, &failed_file);
3794 talloc_free(failed_file);
3795 return rc;
3796}
3797
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003798/* Initialize command interface. Install basic nodes and commands. */
3799void cmd_init(int terminal)
3800{
3801 /* Allocate initial top vector of commands. */
3802 cmdvec = vector_init(VECTOR_MIN_SIZE);
3803
3804 /* Default host value settings. */
3805 host.name = NULL;
3806 host.password = NULL;
3807 host.enable = NULL;
3808 host.logfile = NULL;
3809 host.config = NULL;
3810 host.lines = -1;
3811 host.motd = default_motd;
3812 host.motdfile = NULL;
3813
3814 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003815 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003816 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003817 install_node_bare(&auth_node, NULL);
3818 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003819 install_node(&config_node, config_write_host);
3820
3821 /* Each node's basic commands. */
3822 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003823 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003824 if (terminal) {
3825 install_element(VIEW_NODE, &config_list_cmd);
3826 install_element(VIEW_NODE, &config_exit_cmd);
3827 install_element(VIEW_NODE, &config_help_cmd);
3828 install_element(VIEW_NODE, &config_enable_cmd);
3829 install_element(VIEW_NODE, &config_terminal_length_cmd);
3830 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3831 install_element(VIEW_NODE, &echo_cmd);
3832 }
3833
3834 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003835 install_element(ENABLE_NODE, &config_disable_cmd);
3836 install_element(ENABLE_NODE, &config_terminal_cmd);
3837 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3838 }
3839 install_element (ENABLE_NODE, &show_startup_config_cmd);
3840 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003841 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003842
3843 if (terminal) {
3844 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3845 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3846 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003847 }
3848
3849 install_element(CONFIG_NODE, &hostname_cmd);
3850 install_element(CONFIG_NODE, &no_hostname_cmd);
3851
3852 if (terminal) {
3853 install_element(CONFIG_NODE, &password_cmd);
3854 install_element(CONFIG_NODE, &password_text_cmd);
3855 install_element(CONFIG_NODE, &enable_password_cmd);
3856 install_element(CONFIG_NODE, &enable_password_text_cmd);
3857 install_element(CONFIG_NODE, &no_enable_password_cmd);
3858
3859#ifdef VTY_CRYPT_PW
3860 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3861 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3862#endif
3863 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3864 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3865 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3866 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3867 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3868
3869 }
3870 srand(time(NULL));
3871}
Harald Welte7acb30c2011-08-17 17:13:48 +02003872
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003873/*! @} */