blob: 6380487e7f58127879f6165b726a3c6ee6ba0a2c [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
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001519 for (j = 0; j < vector_active(descvec); j++) {
1520 desc = vector_slot(descvec, j);
1521 if (!desc)
1522 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001523
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001524 enum match_type ret;
1525 const char *str = desc->cmd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001526
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001527 if (CMD_OPTION(str)) {
1528 if (!cmd_deopt_ctx)
1529 cmd_deopt_ctx =
1530 talloc_named_const(tall_vty_cmd_ctx, 0,
1531 __func__);
1532 str = cmd_deopt(cmd_deopt_ctx, str);
1533 if (str == NULL)
1534 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001535 }
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001536
1537 switch (type) {
1538 case exact_match:
1539 if (!(CMD_VARIABLE (str))
1540 && strcmp(command, str) == 0)
1541 match++;
1542 break;
1543 case partly_match:
1544 if (!(CMD_VARIABLE (str))
1545 && strncmp(command, str, strlen (command)) == 0)
1546 {
1547 if (matched
1548 && strcmp(matched,
1549 str) != 0) {
1550 ret = 1; /* There is ambiguous match. */
1551 goto free_and_return;
1552 } else
1553 matched = str;
1554 match++;
1555 }
1556 break;
1557 case range_match:
1558 if (cmd_range_match
1559 (str, command)) {
1560 if (matched
1561 && strcmp(matched,
1562 str) != 0) {
1563 ret = 1;
1564 goto free_and_return;
1565 } else
1566 matched = str;
1567 match++;
1568 }
1569 break;
1570#ifdef HAVE_IPV6
1571 case ipv6_match:
1572 if (CMD_IPV6(str))
1573 match++;
1574 break;
1575 case ipv6_prefix_match:
1576 if ((ret =
1577 cmd_ipv6_prefix_match
1578 (command)) != no_match) {
1579 if (ret == partly_match) {
1580 ret = 2; /* There is incomplete match. */
1581 goto free_and_return;
1582 }
1583
1584 match++;
1585 }
1586 break;
1587#endif /* HAVE_IPV6 */
1588 case ipv4_match:
1589 if (CMD_IPV4(str))
1590 match++;
1591 break;
1592 case ipv4_prefix_match:
1593 if ((ret =
1594 cmd_ipv4_prefix_match
1595 (command)) != no_match) {
1596 if (ret == partly_match) {
1597 ret = 2; /* There is incomplete match. */
1598 goto free_and_return;
1599 }
1600
1601 match++;
1602 }
1603 break;
1604 case extend_match:
1605 if (CMD_VARIABLE (str))
1606 match++;
1607 break;
1608 case no_match:
1609 default:
1610 break;
1611 }
1612 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001613 if (!match)
1614 vector_slot(v, i) = NULL;
1615 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001616
1617free_and_return:
1618 if (cmd_deopt_ctx)
1619 talloc_free(cmd_deopt_ctx);
1620 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001621}
1622
1623/* If src matches dst return dst string, otherwise return NULL */
1624static const char *cmd_entry_function(const char *src, const char *dst)
1625{
1626 /* Skip variable arguments. */
1627 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1628 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1629 return NULL;
1630
1631 /* In case of 'command \t', given src is NULL string. */
1632 if (src == NULL)
1633 return dst;
1634
1635 /* Matched with input string. */
1636 if (strncmp(src, dst, strlen(src)) == 0)
1637 return dst;
1638
1639 return NULL;
1640}
1641
1642/* If src matches dst return dst string, otherwise return NULL */
1643/* This version will return the dst string always if it is
1644 CMD_VARIABLE for '?' key processing */
1645static const char *cmd_entry_function_desc(const char *src, const char *dst)
1646{
1647 if (CMD_VARARG(dst))
1648 return dst;
1649
1650 if (CMD_RANGE(dst)) {
1651 if (cmd_range_match(dst, src))
1652 return dst;
1653 else
1654 return NULL;
1655 }
1656#ifdef HAVE_IPV6
1657 if (CMD_IPV6(dst)) {
1658 if (cmd_ipv6_match(src))
1659 return dst;
1660 else
1661 return NULL;
1662 }
1663
1664 if (CMD_IPV6_PREFIX(dst)) {
1665 if (cmd_ipv6_prefix_match(src))
1666 return dst;
1667 else
1668 return NULL;
1669 }
1670#endif /* HAVE_IPV6 */
1671
1672 if (CMD_IPV4(dst)) {
1673 if (cmd_ipv4_match(src))
1674 return dst;
1675 else
1676 return NULL;
1677 }
1678
1679 if (CMD_IPV4_PREFIX(dst)) {
1680 if (cmd_ipv4_prefix_match(src))
1681 return dst;
1682 else
1683 return NULL;
1684 }
1685
1686 /* Optional or variable commands always match on '?' */
1687 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1688 return dst;
1689
1690 /* In case of 'command \t', given src is NULL string. */
1691 if (src == NULL)
1692 return dst;
1693
1694 if (strncmp(src, dst, strlen(src)) == 0)
1695 return dst;
1696 else
1697 return NULL;
1698}
1699
1700/* Check same string element existence. If it isn't there return
1701 1. */
1702static int cmd_unique_string(vector v, const char *str)
1703{
1704 unsigned int i;
1705 char *match;
1706
1707 for (i = 0; i < vector_active(v); i++)
1708 if ((match = vector_slot(v, i)) != NULL)
1709 if (strcmp(match, str) == 0)
1710 return 0;
1711 return 1;
1712}
1713
1714/* Compare string to description vector. If there is same string
1715 return 1 else return 0. */
1716static int desc_unique_string(vector v, const char *str)
1717{
1718 unsigned int i;
1719 struct desc *desc;
1720
1721 for (i = 0; i < vector_active(v); i++)
1722 if ((desc = vector_slot(v, i)) != NULL)
1723 if (strcmp(desc->cmd, str) == 0)
1724 return 1;
1725 return 0;
1726}
1727
1728static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1729{
1730 if (first_word != NULL &&
1731 node != AUTH_NODE &&
1732 node != VIEW_NODE &&
1733 node != AUTH_ENABLE_NODE &&
1734 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1735 return 1;
1736 return 0;
1737}
1738
1739/* '?' describe command support. */
1740static vector
1741cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1742{
1743 unsigned int i;
1744 vector cmd_vector;
1745#define INIT_MATCHVEC_SIZE 10
1746 vector matchvec;
1747 struct cmd_element *cmd_element;
1748 unsigned int index;
1749 int ret;
1750 enum match_type match;
1751 char *command;
1752 static struct desc desc_cr = { "<cr>", "" };
1753
1754 /* Set index. */
1755 if (vector_active(vline) == 0) {
1756 *status = CMD_ERR_NO_MATCH;
1757 return NULL;
1758 } else
1759 index = vector_active(vline) - 1;
1760
1761 /* Make copy vector of current node's command vector. */
1762 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1763
1764 /* Prepare match vector */
1765 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1766
1767 /* Filter commands. */
1768 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001769 for (i = 0; i < index; i++) {
1770 command = vector_slot(vline, i);
1771 if (!command)
1772 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001773
Harald Welte80d30fe2013-02-12 11:08:57 +01001774 match = cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001775
Harald Welte80d30fe2013-02-12 11:08:57 +01001776 if (match == vararg_match) {
1777 struct cmd_element *cmd_element;
1778 vector descvec;
1779 unsigned int j, k;
1780
1781 for (j = 0; j < vector_active(cmd_vector); j++)
1782 if ((cmd_element =
1783 vector_slot(cmd_vector, j)) != NULL
1784 &&
1785 (vector_active(cmd_element->strvec))) {
1786 descvec =
1787 vector_slot(cmd_element->
1788 strvec,
1789 vector_active
1790 (cmd_element->
1791 strvec) - 1);
1792 for (k = 0;
1793 k < vector_active(descvec);
1794 k++) {
1795 struct desc *desc =
1796 vector_slot(descvec,
1797 k);
1798 vector_set(matchvec,
1799 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001800 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001801 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001802
Harald Welte80d30fe2013-02-12 11:08:57 +01001803 vector_set(matchvec, &desc_cr);
1804 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001805
Harald Welte80d30fe2013-02-12 11:08:57 +01001806 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001807 }
1808
Harald Welte80d30fe2013-02-12 11:08:57 +01001809 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1810 match)) == 1) {
1811 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001812 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001813 *status = CMD_ERR_AMBIGUOUS;
1814 return NULL;
1815 } else if (ret == 2) {
1816 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001817 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001818 *status = CMD_ERR_NO_MATCH;
1819 return NULL;
1820 }
1821 }
1822
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001823 /* Prepare match vector */
1824 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1825
1826 /* Make sure that cmd_vector is filtered based on current word */
1827 command = vector_slot(vline, index);
1828 if (command)
Vadim Yanitskiy49a0dec2017-06-12 03:49:38 +07001829 cmd_filter(command, cmd_vector, index, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001830
1831 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001832 for (i = 0; i < vector_active(cmd_vector); i++) {
1833 const char *string = NULL;
1834 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001835
Harald Welte80d30fe2013-02-12 11:08:57 +01001836 cmd_element = vector_slot(cmd_vector, i);
1837 if (!cmd_element)
1838 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001839
Harald Welted17aa592013-02-12 11:11:34 +01001840 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1841 continue;
1842
Harald Welte80d30fe2013-02-12 11:08:57 +01001843 strvec = cmd_element->strvec;
1844
1845 /* if command is NULL, index may be equal to vector_active */
1846 if (command && index >= vector_active(strvec))
1847 vector_slot(cmd_vector, i) = NULL;
1848 else {
1849 /* Check if command is completed. */
1850 if (command == NULL
1851 && index == vector_active(strvec)) {
1852 string = "<cr>";
1853 if (!desc_unique_string(matchvec, string))
1854 vector_set(matchvec, &desc_cr);
1855 } else {
1856 unsigned int j;
1857 vector descvec = vector_slot(strvec, index);
1858 struct desc *desc;
1859
1860 for (j = 0; j < vector_active(descvec); j++) {
1861 desc = vector_slot(descvec, j);
1862 if (!desc)
1863 continue;
1864 string = cmd_entry_function_desc
1865 (command, desc->cmd);
1866 if (!string)
1867 continue;
1868 /* Uniqueness check */
1869 if (!desc_unique_string(matchvec, string))
1870 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001871 }
1872 }
1873 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001874 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001875 vector_free(cmd_vector);
1876
1877 if (vector_slot(matchvec, 0) == NULL) {
1878 vector_free(matchvec);
1879 *status = CMD_ERR_NO_MATCH;
1880 } else
1881 *status = CMD_SUCCESS;
1882
1883 return matchvec;
1884}
1885
1886vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1887{
1888 vector ret;
1889
1890 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1891 enum node_type onode;
1892 vector shifted_vline;
1893 unsigned int index;
1894
1895 onode = vty->node;
1896 vty->node = ENABLE_NODE;
1897 /* We can try it on enable node, cos' the vty is authenticated */
1898
1899 shifted_vline = vector_init(vector_count(vline));
1900 /* use memcpy? */
1901 for (index = 1; index < vector_active(vline); index++) {
1902 vector_set_index(shifted_vline, index - 1,
1903 vector_lookup(vline, index));
1904 }
1905
1906 ret = cmd_describe_command_real(shifted_vline, vty, status);
1907
1908 vector_free(shifted_vline);
1909 vty->node = onode;
1910 return ret;
1911 }
1912
1913 return cmd_describe_command_real(vline, vty, status);
1914}
1915
1916/* Check LCD of matched command. */
1917static int cmd_lcd(char **matched)
1918{
1919 int i;
1920 int j;
1921 int lcd = -1;
1922 char *s1, *s2;
1923 char c1, c2;
1924
1925 if (matched[0] == NULL || matched[1] == NULL)
1926 return 0;
1927
1928 for (i = 1; matched[i] != NULL; i++) {
1929 s1 = matched[i - 1];
1930 s2 = matched[i];
1931
1932 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1933 if (c1 != c2)
1934 break;
1935
1936 if (lcd < 0)
1937 lcd = j;
1938 else {
1939 if (lcd > j)
1940 lcd = j;
1941 }
1942 }
1943 return lcd;
1944}
1945
1946/* Command line completion support. */
1947static char **cmd_complete_command_real(vector vline, struct vty *vty,
1948 int *status)
1949{
1950 unsigned int i;
1951 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1952#define INIT_MATCHVEC_SIZE 10
1953 vector matchvec;
1954 struct cmd_element *cmd_element;
1955 unsigned int index;
1956 char **match_str;
1957 struct desc *desc;
1958 vector descvec;
1959 char *command;
1960 int lcd;
1961
1962 if (vector_active(vline) == 0) {
1963 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001964 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001965 return NULL;
1966 } else
1967 index = vector_active(vline) - 1;
1968
1969 /* First, filter by preceeding command string */
1970 for (i = 0; i < index; i++)
1971 if ((command = vector_slot(vline, i))) {
1972 enum match_type match;
1973 int ret;
1974
1975 /* First try completion match, if there is exactly match return 1 */
1976 match =
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001977 cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001978
1979 /* If there is exact match then filter ambiguous match else check
1980 ambiguousness. */
1981 if ((ret =
1982 is_cmd_ambiguous(command, cmd_vector, i,
1983 match)) == 1) {
1984 vector_free(cmd_vector);
1985 *status = CMD_ERR_AMBIGUOUS;
1986 return NULL;
1987 }
1988 /*
1989 else if (ret == 2)
1990 {
1991 vector_free (cmd_vector);
1992 *status = CMD_ERR_NO_MATCH;
1993 return NULL;
1994 }
1995 */
1996 }
1997
1998 /* Prepare match vector. */
1999 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2000
2001 /* Now we got into completion */
2002 for (i = 0; i < vector_active(cmd_vector); i++)
2003 if ((cmd_element = vector_slot(cmd_vector, i))) {
2004 const char *string;
2005 vector strvec = cmd_element->strvec;
2006
2007 /* Check field length */
2008 if (index >= vector_active(strvec))
2009 vector_slot(cmd_vector, i) = NULL;
2010 else {
2011 unsigned int j;
2012
2013 descvec = vector_slot(strvec, index);
2014 for (j = 0; j < vector_active(descvec); j++)
2015 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002016 const char *cmd = desc->cmd;
2017 char *tmp = NULL;
2018
2019 if (CMD_OPTION(desc->cmd)) {
2020 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2021 cmd = tmp;
2022 }
2023 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002024 if (cmd_unique_string (matchvec, string))
2025 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002026 if (tmp)
2027 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002028 }
2029 }
2030 }
2031
2032 /* We don't need cmd_vector any more. */
2033 vector_free(cmd_vector);
2034
2035 /* No matched command */
2036 if (vector_slot(matchvec, 0) == NULL) {
2037 vector_free(matchvec);
2038
2039 /* In case of 'command \t' pattern. Do you need '?' command at
2040 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002041 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002042 *status = CMD_ERR_NOTHING_TODO;
2043 else
2044 *status = CMD_ERR_NO_MATCH;
2045 return NULL;
2046 }
2047
2048 /* Only one matched */
2049 if (vector_slot(matchvec, 1) == NULL) {
2050 match_str = (char **)matchvec->index;
2051 vector_only_wrapper_free(matchvec);
2052 *status = CMD_COMPLETE_FULL_MATCH;
2053 return match_str;
2054 }
2055 /* Make it sure last element is NULL. */
2056 vector_set(matchvec, NULL);
2057
2058 /* Check LCD of matched strings. */
2059 if (vector_slot(vline, index) != NULL) {
2060 lcd = cmd_lcd((char **)matchvec->index);
2061
2062 if (lcd) {
2063 int len = strlen(vector_slot(vline, index));
2064
2065 if (len < lcd) {
2066 char *lcdstr;
2067
2068 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2069 "complete-lcdstr");
2070 memcpy(lcdstr, matchvec->index[0], lcd);
2071 lcdstr[lcd] = '\0';
2072
2073 /* match_str = (char **) &lcdstr; */
2074
2075 /* Free matchvec. */
2076 for (i = 0; i < vector_active(matchvec); i++) {
2077 if (vector_slot(matchvec, i))
2078 talloc_free(vector_slot(matchvec, i));
2079 }
2080 vector_free(matchvec);
2081
2082 /* Make new matchvec. */
2083 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2084 vector_set(matchvec, lcdstr);
2085 match_str = (char **)matchvec->index;
2086 vector_only_wrapper_free(matchvec);
2087
2088 *status = CMD_COMPLETE_MATCH;
2089 return match_str;
2090 }
2091 }
2092 }
2093
2094 match_str = (char **)matchvec->index;
2095 vector_only_wrapper_free(matchvec);
2096 *status = CMD_COMPLETE_LIST_MATCH;
2097 return match_str;
2098}
2099
2100char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2101{
2102 char **ret;
2103
2104 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2105 enum node_type onode;
2106 vector shifted_vline;
2107 unsigned int index;
2108
2109 onode = vty->node;
2110 vty->node = ENABLE_NODE;
2111 /* We can try it on enable node, cos' the vty is authenticated */
2112
2113 shifted_vline = vector_init(vector_count(vline));
2114 /* use memcpy? */
2115 for (index = 1; index < vector_active(vline); index++) {
2116 vector_set_index(shifted_vline, index - 1,
2117 vector_lookup(vline, index));
2118 }
2119
2120 ret = cmd_complete_command_real(shifted_vline, vty, status);
2121
2122 vector_free(shifted_vline);
2123 vty->node = onode;
2124 return ret;
2125 }
2126
2127 return cmd_complete_command_real(vline, vty, status);
2128}
2129
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002130static struct vty_parent_node *vty_parent(struct vty *vty)
2131{
2132 return llist_first_entry_or_null(&vty->parent_nodes,
2133 struct vty_parent_node,
2134 entry);
2135}
2136
2137static bool vty_pop_parent(struct vty *vty)
2138{
2139 struct vty_parent_node *parent = vty_parent(vty);
2140 if (!parent)
2141 return false;
2142 llist_del(&parent->entry);
2143 vty->node = parent->node;
2144 vty->priv = parent->priv;
2145 if (vty->indent)
2146 talloc_free(vty->indent);
2147 vty->indent = parent->indent;
2148 talloc_free(parent);
2149 return true;
2150}
2151
2152static void vty_clear_parents(struct vty *vty)
2153{
2154 while (vty_pop_parent(vty));
2155}
2156
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002157/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002158/*
2159 * This function MUST eventually converge on a node when called repeatedly,
2160 * there must not be any cycles.
2161 * All 'config' nodes shall converge on CONFIG_NODE.
2162 * All other 'enable' nodes shall converge on ENABLE_NODE.
2163 * All 'view' only nodes shall converge on VIEW_NODE.
2164 * All other nodes shall converge on themselves or it must be ensured,
2165 * that the user's rights are not extended anyhow by calling this function.
2166 *
2167 * Note that these requirements also apply to all functions that are used
2168 * as go_parent_cb.
2169 * Note also that this function relies on the is_config_child callback to
2170 * recognize non-config nodes if go_parent_cb is not set.
2171 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002172int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002173{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002174 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002175 case AUTH_NODE:
2176 case VIEW_NODE:
2177 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002178 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002179 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002180 break;
2181
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002182 case AUTH_ENABLE_NODE:
2183 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002184 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002185 break;
2186
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002187 case CFG_LOG_NODE:
2188 case VTY_NODE:
2189 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002190 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002191 break;
2192
2193 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002194 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002195 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002196 vty_pop_parent(vty);
2197 }
2198 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002199 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002200 vty_clear_parents(vty);
2201 }
2202 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002203 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002204 vty_clear_parents(vty);
2205 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002206 break;
2207 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002208
2209 return vty->node;
2210}
2211
2212/* Execute command by argument vline vector. */
2213static int
2214cmd_execute_command_real(vector vline, struct vty *vty,
2215 struct cmd_element **cmd)
2216{
2217 unsigned int i;
2218 unsigned int index;
2219 vector cmd_vector;
2220 struct cmd_element *cmd_element;
2221 struct cmd_element *matched_element;
2222 unsigned int matched_count, incomplete_count;
2223 int argc;
2224 const char *argv[CMD_ARGC_MAX];
2225 enum match_type match = 0;
2226 int varflag;
2227 char *command;
2228
2229 /* Make copy of command elements. */
2230 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2231
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002232 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002233 if ((command = vector_slot(vline, index))) {
2234 int ret;
2235
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002236 match = cmd_filter(command, cmd_vector, index,
2237 any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002238
2239 if (match == vararg_match)
2240 break;
2241
2242 ret =
2243 is_cmd_ambiguous(command, cmd_vector, index, match);
2244
2245 if (ret == 1) {
2246 vector_free(cmd_vector);
2247 return CMD_ERR_AMBIGUOUS;
2248 } else if (ret == 2) {
2249 vector_free(cmd_vector);
2250 return CMD_ERR_NO_MATCH;
2251 }
2252 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002253 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002254
2255 /* Check matched count. */
2256 matched_element = NULL;
2257 matched_count = 0;
2258 incomplete_count = 0;
2259
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002260 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002261 if ((cmd_element = vector_slot(cmd_vector, i))) {
2262 if (match == vararg_match
2263 || index >= cmd_element->cmdsize) {
2264 matched_element = cmd_element;
2265#if 0
2266 printf("DEBUG: %s\n", cmd_element->string);
2267#endif
2268 matched_count++;
2269 } else {
2270 incomplete_count++;
2271 }
2272 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002273 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002274
2275 /* Finish of using cmd_vector. */
2276 vector_free(cmd_vector);
2277
2278 /* To execute command, matched_count must be 1. */
2279 if (matched_count == 0) {
2280 if (incomplete_count)
2281 return CMD_ERR_INCOMPLETE;
2282 else
2283 return CMD_ERR_NO_MATCH;
2284 }
2285
2286 if (matched_count > 1)
2287 return CMD_ERR_AMBIGUOUS;
2288
2289 /* Argument treatment */
2290 varflag = 0;
2291 argc = 0;
2292
2293 for (i = 0; i < vector_active(vline); i++) {
2294 if (varflag)
2295 argv[argc++] = vector_slot(vline, i);
2296 else {
2297 vector descvec =
2298 vector_slot(matched_element->strvec, i);
2299
2300 if (vector_active(descvec) == 1) {
2301 struct desc *desc = vector_slot(descvec, 0);
2302
2303 if (CMD_VARARG(desc->cmd))
2304 varflag = 1;
2305
2306 if (varflag || CMD_VARIABLE(desc->cmd)
2307 || CMD_OPTION(desc->cmd))
2308 argv[argc++] = vector_slot(vline, i);
2309 } else
2310 argv[argc++] = vector_slot(vline, i);
2311 }
2312
2313 if (argc >= CMD_ARGC_MAX)
2314 return CMD_ERR_EXEED_ARGC_MAX;
2315 }
2316
2317 /* For vtysh execution. */
2318 if (cmd)
2319 *cmd = matched_element;
2320
2321 if (matched_element->daemon)
2322 return CMD_SUCCESS_DAEMON;
2323
2324 /* Execute matched command. */
2325 return (*matched_element->func) (matched_element, vty, argc, argv);
2326}
2327
2328int
2329cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2330 int vtysh)
2331{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002332 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002333 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002334
2335 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002336
2337 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2338 vector shifted_vline;
2339 unsigned int index;
2340
2341 vty->node = ENABLE_NODE;
2342 /* We can try it on enable node, cos' the vty is authenticated */
2343
2344 shifted_vline = vector_init(vector_count(vline));
2345 /* use memcpy? */
2346 for (index = 1; index < vector_active(vline); index++) {
2347 vector_set_index(shifted_vline, index - 1,
2348 vector_lookup(vline, index));
2349 }
2350
2351 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2352
2353 vector_free(shifted_vline);
2354 vty->node = onode;
2355 return ret;
2356 }
2357
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002358 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002359}
2360
2361/* Execute command by argument readline. */
2362int
2363cmd_execute_command_strict(vector vline, struct vty *vty,
2364 struct cmd_element **cmd)
2365{
2366 unsigned int i;
2367 unsigned int index;
2368 vector cmd_vector;
2369 struct cmd_element *cmd_element;
2370 struct cmd_element *matched_element;
2371 unsigned int matched_count, incomplete_count;
2372 int argc;
2373 const char *argv[CMD_ARGC_MAX];
2374 int varflag;
2375 enum match_type match = 0;
2376 char *command;
2377
2378 /* Make copy of command element */
2379 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2380
2381 for (index = 0; index < vector_active(vline); index++)
2382 if ((command = vector_slot(vline, index))) {
2383 int ret;
2384
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002385 match = cmd_filter(vector_slot(vline, index),
2386 cmd_vector, index, exact_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002387
2388 /* If command meets '.VARARG' then finish matching. */
2389 if (match == vararg_match)
2390 break;
2391
2392 ret =
2393 is_cmd_ambiguous(command, cmd_vector, index, match);
2394 if (ret == 1) {
2395 vector_free(cmd_vector);
2396 return CMD_ERR_AMBIGUOUS;
2397 }
2398 if (ret == 2) {
2399 vector_free(cmd_vector);
2400 return CMD_ERR_NO_MATCH;
2401 }
2402 }
2403
2404 /* Check matched count. */
2405 matched_element = NULL;
2406 matched_count = 0;
2407 incomplete_count = 0;
2408 for (i = 0; i < vector_active(cmd_vector); i++)
2409 if (vector_slot(cmd_vector, i) != NULL) {
2410 cmd_element = vector_slot(cmd_vector, i);
2411
2412 if (match == vararg_match
2413 || index >= cmd_element->cmdsize) {
2414 matched_element = cmd_element;
2415 matched_count++;
2416 } else
2417 incomplete_count++;
2418 }
2419
2420 /* Finish of using cmd_vector. */
2421 vector_free(cmd_vector);
2422
2423 /* To execute command, matched_count must be 1. */
2424 if (matched_count == 0) {
2425 if (incomplete_count)
2426 return CMD_ERR_INCOMPLETE;
2427 else
2428 return CMD_ERR_NO_MATCH;
2429 }
2430
2431 if (matched_count > 1)
2432 return CMD_ERR_AMBIGUOUS;
2433
2434 /* Argument treatment */
2435 varflag = 0;
2436 argc = 0;
2437
2438 for (i = 0; i < vector_active(vline); i++) {
2439 if (varflag)
2440 argv[argc++] = vector_slot(vline, i);
2441 else {
2442 vector descvec =
2443 vector_slot(matched_element->strvec, i);
2444
2445 if (vector_active(descvec) == 1) {
2446 struct desc *desc = vector_slot(descvec, 0);
2447
2448 if (CMD_VARARG(desc->cmd))
2449 varflag = 1;
2450
2451 if (varflag || CMD_VARIABLE(desc->cmd)
2452 || CMD_OPTION(desc->cmd))
2453 argv[argc++] = vector_slot(vline, i);
2454 } else
2455 argv[argc++] = vector_slot(vline, i);
2456 }
2457
2458 if (argc >= CMD_ARGC_MAX)
2459 return CMD_ERR_EXEED_ARGC_MAX;
2460 }
2461
2462 /* For vtysh execution. */
2463 if (cmd)
2464 *cmd = matched_element;
2465
2466 if (matched_element->daemon)
2467 return CMD_SUCCESS_DAEMON;
2468
2469 /* Now execute matched command */
2470 return (*matched_element->func) (matched_element, vty, argc, argv);
2471}
2472
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002473static inline size_t len(const char *str)
2474{
2475 return str? strlen(str) : 0;
2476}
2477
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002478/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2479 * is longer than b, a must start with exactly b, and vice versa.
2480 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2481 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002482static int indent_cmp(const char *a, const char *b)
2483{
2484 size_t al, bl;
2485 al = len(a);
2486 bl = len(b);
2487 if (al > bl) {
2488 if (bl && strncmp(a, b, bl) != 0)
2489 return EINVAL;
2490 return 1;
2491 }
2492 /* al <= bl */
2493 if (al && strncmp(a, b, al) != 0)
2494 return EINVAL;
2495 return (al < bl)? -1 : 0;
2496}
2497
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002498/* Configration make from file. */
2499int config_from_file(struct vty *vty, FILE * fp)
2500{
2501 int ret;
2502 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002503 char *indent;
2504 int cmp;
2505 struct vty_parent_node this_node;
2506 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002507
2508 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002509 indent = NULL;
2510 vline = NULL;
2511 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002512
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002513 if (ret != CMD_SUCCESS)
2514 goto return_invalid_indent;
2515
2516 /* In case of comment or empty line */
2517 if (vline == NULL) {
2518 if (indent) {
2519 talloc_free(indent);
2520 indent = NULL;
2521 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002522 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002523 }
2524
Neels Hofmeyr43063632017-09-19 23:54:01 +02002525 /* We have a nonempty line. */
2526 if (!vty->indent) {
2527 /* We have just entered a node and expecting the first child to come up; but we
2528 * may also skip right back to a parent or ancestor level. */
2529 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002530
Neels Hofmeyr43063632017-09-19 23:54:01 +02002531 /* If there is no parent, record any indentation we encounter. */
2532 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2533
2534 if (cmp == EINVAL)
2535 goto return_invalid_indent;
2536
2537 if (cmp <= 0) {
2538 /* We have gone right back to the parent level or higher, we are skipping
2539 * this child node level entirely. Pop the parent to go back to a node
2540 * that was actually there (to reinstate vty->indent) and re-use below
2541 * go-parent while-loop to find an accurate match of indent in the node
2542 * ancestry. */
2543 vty_go_parent(vty);
2544 } else {
2545 /* The indent is deeper than the just entered parent, record the new
2546 * indentation characters. */
2547 vty->indent = talloc_strdup(vty, indent);
2548 /* This *is* the new indentation. */
2549 cmp = 0;
2550 }
2551 } else {
2552 /* There is a known indentation for this node level, validate and detect node
2553 * exits. */
2554 cmp = indent_cmp(indent, vty->indent);
2555 if (cmp == EINVAL)
2556 goto return_invalid_indent;
2557 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002558
2559 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2560 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2561 while (cmp < 0) {
2562 vty_go_parent(vty);
2563 cmp = indent_cmp(indent, vty->indent);
2564 if (cmp == EINVAL)
2565 goto return_invalid_indent;
2566 }
2567
2568 /* More indent without having entered a child node level? Either the parent node's indent
2569 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2570 * or the indentation increased even though the vty command didn't enter a child. */
2571 if (cmp > 0)
2572 goto return_invalid_indent;
2573
2574 /* Remember the current node before the command possibly changes it. */
2575 this_node = (struct vty_parent_node){
2576 .node = vty->node,
2577 .priv = vty->priv,
2578 .indent = vty->indent,
2579 };
2580
2581 parent = vty_parent(vty);
2582 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002583 cmd_free_strvec(vline);
2584
2585 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002586 && ret != CMD_ERR_NOTHING_TODO) {
2587 if (indent) {
2588 talloc_free(indent);
2589 indent = NULL;
2590 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002591 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002592 }
2593
2594 /* If we have stepped down into a child node, push a parent frame.
2595 * The causality is such: we don't expect every single node entry implementation to push
2596 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2597 * a parent node. Hence if the node changed without the parent node changing, we must
2598 * have stepped into a child node (and now expect a deeper indent). */
2599 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2600 /* Push the parent node. */
2601 parent = talloc_zero(vty, struct vty_parent_node);
2602 *parent = this_node;
2603 llist_add(&parent->entry, &vty->parent_nodes);
2604
2605 /* The current talloc'ed vty->indent string will now be owned by this parent
2606 * struct. Indicate that we don't know what deeper indent characters the user
2607 * will choose. */
2608 vty->indent = NULL;
2609 }
2610
2611 if (indent) {
2612 talloc_free(indent);
2613 indent = NULL;
2614 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002615 }
2616 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002617
2618return_invalid_indent:
2619 if (vline)
2620 cmd_free_strvec(vline);
2621 if (indent) {
2622 talloc_free(indent);
2623 indent = NULL;
2624 }
2625 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002626}
2627
2628/* Configration from terminal */
2629DEFUN(config_terminal,
2630 config_terminal_cmd,
2631 "configure terminal",
2632 "Configuration from vty interface\n" "Configuration terminal\n")
2633{
2634 if (vty_config_lock(vty))
2635 vty->node = CONFIG_NODE;
2636 else {
2637 vty_out(vty, "VTY configuration is locked by other VTY%s",
2638 VTY_NEWLINE);
2639 return CMD_WARNING;
2640 }
2641 return CMD_SUCCESS;
2642}
2643
2644/* Enable command */
2645DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2646{
2647 /* If enable password is NULL, change to ENABLE_NODE */
2648 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2649 vty->type == VTY_SHELL_SERV)
2650 vty->node = ENABLE_NODE;
2651 else
2652 vty->node = AUTH_ENABLE_NODE;
2653
2654 return CMD_SUCCESS;
2655}
2656
2657/* Disable command */
2658DEFUN(disable,
2659 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2660{
2661 if (vty->node == ENABLE_NODE)
2662 vty->node = VIEW_NODE;
2663 return CMD_SUCCESS;
2664}
2665
2666/* Down vty node level. */
2667gDEFUN(config_exit,
2668 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2669{
2670 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002671 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002672 case VIEW_NODE:
2673 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002674 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002675 break;
2676 case CONFIG_NODE:
2677 vty->node = ENABLE_NODE;
2678 vty_config_unlock(vty);
2679 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002680 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002681 if (vty->node > CONFIG_NODE)
2682 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002683 break;
2684 }
2685 return CMD_SUCCESS;
2686}
2687
2688/* End of configuration. */
2689 gDEFUN(config_end,
2690 config_end_cmd, "end", "End current mode and change to enable mode.")
2691{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002692 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002693 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002694
2695 /* Repeatedly call go_parent until a top node is reached. */
2696 while (vty->node > CONFIG_NODE) {
2697 if (vty->node == last_node) {
2698 /* Ensure termination, this shouldn't happen. */
2699 break;
2700 }
2701 last_node = vty->node;
2702 vty_go_parent(vty);
2703 }
2704
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002705 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002706 if (vty->node > ENABLE_NODE)
2707 vty->node = ENABLE_NODE;
2708 vty->index = NULL;
2709 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002710 }
2711 return CMD_SUCCESS;
2712}
2713
2714/* Show version. */
2715DEFUN(show_version,
2716 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2717{
Harald Welte237f6242010-05-25 23:00:45 +02002718 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2719 host.app_info->version,
2720 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2721 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002722
2723 return CMD_SUCCESS;
2724}
2725
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002726DEFUN(show_online_help,
2727 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2728{
2729 vty_dump_nodes(vty);
2730 return CMD_SUCCESS;
2731}
2732
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002733/* Help display function for all node. */
2734gDEFUN(config_help,
2735 config_help_cmd, "help", "Description of the interactive help system\n")
2736{
2737 vty_out(vty,
2738 "This VTY provides advanced help features. When you need help,%s\
2739anytime at the command line please press '?'.%s\
2740%s\
2741If nothing matches, the help list will be empty and you must backup%s\
2742 until entering a '?' shows the available options.%s\
2743Two styles of help are provided:%s\
27441. Full help is available when you are ready to enter a%s\
2745command argument (e.g. 'show ?') and describes each possible%s\
2746argument.%s\
27472. Partial help is provided when an abbreviated argument is entered%s\
2748 and you want to know what arguments match the input%s\
2749 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2750 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2751 return CMD_SUCCESS;
2752}
2753
2754/* Help display function for all node. */
2755gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2756{
2757 unsigned int i;
2758 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2759 struct cmd_element *cmd;
2760
2761 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2762 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002763 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002764 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2765 return CMD_SUCCESS;
2766}
2767
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002768static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002769{
2770 unsigned int i;
2771 int fd;
2772 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002773 char *config_file_tmp = NULL;
2774 char *config_file_sav = NULL;
2775 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002776 struct stat st;
2777
2778 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002779
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002780 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2781 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2782 * manually instead. */
2783
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002784 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002785 config_file_sav =
2786 _talloc_zero(tall_vty_cmd_ctx,
2787 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2788 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002789 if (!config_file_sav)
2790 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002791 strcpy(config_file_sav, config_file);
2792 strcat(config_file_sav, CONF_BACKUP_EXT);
2793
2794 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002795 "config_file_tmp");
2796 if (!config_file_tmp) {
2797 talloc_free(config_file_sav);
2798 return -1;
2799 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002800 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2801
2802 /* Open file to configuration write. */
2803 fd = mkstemp(config_file_tmp);
2804 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002805 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002806 talloc_free(config_file_tmp);
2807 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002808 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002809 }
2810
2811 /* Make vty for configuration file. */
2812 file_vty = vty_new();
2813 file_vty->fd = fd;
2814 file_vty->type = VTY_FILE;
2815
2816 /* Config file header print. */
2817 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002818 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002819 //vty_time_print (file_vty, 1);
2820 vty_out(file_vty, "!\n");
2821
2822 for (i = 0; i < vector_active(cmdvec); i++)
2823 if ((node = vector_slot(cmdvec, i)) && node->func) {
2824 if ((*node->func) (file_vty))
2825 vty_out(file_vty, "!\n");
2826 }
2827 vty_close(file_vty);
2828
2829 if (unlink(config_file_sav) != 0)
2830 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002831 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002832 talloc_free(config_file_sav);
2833 talloc_free(config_file_tmp);
2834 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002835 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002836 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002837
2838 /* Only link the .sav file if the original file exists */
2839 if (stat(config_file, &st) == 0) {
2840 if (link(config_file, config_file_sav) != 0) {
2841 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2842 talloc_free(config_file_sav);
2843 talloc_free(config_file_tmp);
2844 unlink(config_file_tmp);
2845 return -3;
2846 }
2847 sync();
2848 if (unlink(config_file) != 0) {
2849 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2850 talloc_free(config_file_sav);
2851 talloc_free(config_file_tmp);
2852 unlink(config_file_tmp);
2853 return -4;
2854 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002855 }
2856 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002857 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002858 talloc_free(config_file_sav);
2859 talloc_free(config_file_tmp);
2860 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002861 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002862 }
2863 unlink(config_file_tmp);
2864 sync();
2865
2866 talloc_free(config_file_sav);
2867 talloc_free(config_file_tmp);
2868
2869 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002870 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2871 return -6;
2872 }
2873
2874 return 0;
2875}
2876
2877
2878/* Write current configuration into file. */
2879DEFUN(config_write_file,
2880 config_write_file_cmd,
2881 "write file",
2882 "Write running configuration to memory, network, or terminal\n"
2883 "Write to configuration file\n")
2884{
2885 char *failed_file;
2886 int rc;
2887
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002888 if (host.app_info->config_is_consistent) {
2889 rc = host.app_info->config_is_consistent(vty);
2890 if (!rc) {
2891 vty_out(vty, "Configuration is not consistent%s",
2892 VTY_NEWLINE);
2893 return CMD_WARNING;
2894 }
2895 }
2896
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002897 if (host.config == NULL) {
2898 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2899 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002900 return CMD_WARNING;
2901 }
2902
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002903 rc = write_config_file(host.config, &failed_file);
2904 switch (rc) {
2905 case -1:
2906 vty_out(vty, "Can't open configuration file %s.%s",
2907 failed_file, VTY_NEWLINE);
2908 rc = CMD_WARNING;
2909 break;
2910 case -2:
2911 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2912 failed_file, VTY_NEWLINE);
2913 rc = CMD_WARNING;
2914 break;
2915 case -3:
2916 vty_out(vty, "Can't backup old configuration file %s.%s",
2917 failed_file, VTY_NEWLINE);
2918 rc = CMD_WARNING;
2919 break;
2920 case -4:
2921 vty_out(vty, "Can't unlink configuration file %s.%s",
2922 failed_file, VTY_NEWLINE);
2923 rc = CMD_WARNING;
2924 break;
2925 case -5:
2926 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2927 VTY_NEWLINE);
2928 rc = CMD_WARNING;
2929 break;
2930 case -6:
2931 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2932 failed_file, strerror(errno), errno, VTY_NEWLINE);
2933 rc = CMD_WARNING;
2934 break;
2935 default:
2936 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2937 rc = CMD_SUCCESS;
2938 break;
2939 }
2940
2941 talloc_free(failed_file);
2942 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002943}
2944
2945ALIAS(config_write_file,
2946 config_write_cmd,
2947 "write", "Write running configuration to memory, network, or terminal\n")
2948
2949 ALIAS(config_write_file,
2950 config_write_memory_cmd,
2951 "write memory",
2952 "Write running configuration to memory, network, or terminal\n"
2953 "Write configuration to the file (same as write file)\n")
2954
2955 ALIAS(config_write_file,
2956 copy_runningconfig_startupconfig_cmd,
2957 "copy running-config startup-config",
2958 "Copy configuration\n"
2959 "Copy running config to... \n"
2960 "Copy running config to startup config (same as write file)\n")
2961
2962/* Write current configuration into the terminal. */
2963 DEFUN(config_write_terminal,
2964 config_write_terminal_cmd,
2965 "write terminal",
2966 "Write running configuration to memory, network, or terminal\n"
2967 "Write to terminal\n")
2968{
2969 unsigned int i;
2970 struct cmd_node *node;
2971
2972 if (vty->type == VTY_SHELL_SERV) {
2973 for (i = 0; i < vector_active(cmdvec); i++)
2974 if ((node = vector_slot(cmdvec, i)) && node->func
2975 && node->vtysh) {
2976 if ((*node->func) (vty))
2977 vty_out(vty, "!%s", VTY_NEWLINE);
2978 }
2979 } else {
2980 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2981 VTY_NEWLINE);
2982 vty_out(vty, "!%s", VTY_NEWLINE);
2983
2984 for (i = 0; i < vector_active(cmdvec); i++)
2985 if ((node = vector_slot(cmdvec, i)) && node->func) {
2986 if ((*node->func) (vty))
2987 vty_out(vty, "!%s", VTY_NEWLINE);
2988 }
2989 vty_out(vty, "end%s", VTY_NEWLINE);
2990 }
2991 return CMD_SUCCESS;
2992}
2993
2994/* Write current configuration into the terminal. */
2995ALIAS(config_write_terminal,
2996 show_running_config_cmd,
2997 "show running-config", SHOW_STR "running configuration\n")
2998
2999/* Write startup configuration into the terminal. */
3000 DEFUN(show_startup_config,
3001 show_startup_config_cmd,
3002 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3003{
3004 char buf[BUFSIZ];
3005 FILE *confp;
3006
3007 confp = fopen(host.config, "r");
3008 if (confp == NULL) {
3009 vty_out(vty, "Can't open configuration file [%s]%s",
3010 host.config, VTY_NEWLINE);
3011 return CMD_WARNING;
3012 }
3013
3014 while (fgets(buf, BUFSIZ, confp)) {
3015 char *cp = buf;
3016
3017 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3018 cp++;
3019 *cp = '\0';
3020
3021 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3022 }
3023
3024 fclose(confp);
3025
3026 return CMD_SUCCESS;
3027}
3028
3029/* Hostname configuration */
3030DEFUN(config_hostname,
3031 hostname_cmd,
3032 "hostname WORD",
3033 "Set system's network name\n" "This system's network name\n")
3034{
3035 if (!isalpha((int)*argv[0])) {
3036 vty_out(vty, "Please specify string starting with alphabet%s",
3037 VTY_NEWLINE);
3038 return CMD_WARNING;
3039 }
3040
3041 if (host.name)
3042 talloc_free(host.name);
3043
3044 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3045 return CMD_SUCCESS;
3046}
3047
3048DEFUN(config_no_hostname,
3049 no_hostname_cmd,
3050 "no hostname [HOSTNAME]",
3051 NO_STR "Reset system's network name\n" "Host name of this router\n")
3052{
3053 if (host.name)
3054 talloc_free(host.name);
3055 host.name = NULL;
3056 return CMD_SUCCESS;
3057}
3058
3059/* VTY interface password set. */
3060DEFUN(config_password, password_cmd,
3061 "password (8|) WORD",
3062 "Assign the terminal connection password\n"
3063 "Specifies a HIDDEN password will follow\n"
3064 "dummy string \n" "The HIDDEN line password string\n")
3065{
3066 /* Argument check. */
3067 if (argc == 0) {
3068 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3069 return CMD_WARNING;
3070 }
3071
3072 if (argc == 2) {
3073 if (*argv[0] == '8') {
3074 if (host.password)
3075 talloc_free(host.password);
3076 host.password = NULL;
3077 if (host.password_encrypt)
3078 talloc_free(host.password_encrypt);
3079 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3080 return CMD_SUCCESS;
3081 } else {
3082 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3083 return CMD_WARNING;
3084 }
3085 }
3086
3087 if (!isalnum((int)*argv[0])) {
3088 vty_out(vty,
3089 "Please specify string starting with alphanumeric%s",
3090 VTY_NEWLINE);
3091 return CMD_WARNING;
3092 }
3093
3094 if (host.password)
3095 talloc_free(host.password);
3096 host.password = NULL;
3097
3098#ifdef VTY_CRYPT_PW
3099 if (host.encrypt) {
3100 if (host.password_encrypt)
3101 talloc_free(host.password_encrypt);
3102 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3103 } else
3104#endif
3105 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3106
3107 return CMD_SUCCESS;
3108}
3109
3110ALIAS(config_password, password_text_cmd,
3111 "password LINE",
3112 "Assign the terminal connection password\n"
3113 "The UNENCRYPTED (cleartext) line password\n")
3114
3115/* VTY enable password set. */
3116 DEFUN(config_enable_password, enable_password_cmd,
3117 "enable password (8|) WORD",
3118 "Modify enable password parameters\n"
3119 "Assign the privileged level password\n"
3120 "Specifies a HIDDEN password will follow\n"
3121 "dummy string \n" "The HIDDEN 'enable' password string\n")
3122{
3123 /* Argument check. */
3124 if (argc == 0) {
3125 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3126 return CMD_WARNING;
3127 }
3128
3129 /* Crypt type is specified. */
3130 if (argc == 2) {
3131 if (*argv[0] == '8') {
3132 if (host.enable)
3133 talloc_free(host.enable);
3134 host.enable = NULL;
3135
3136 if (host.enable_encrypt)
3137 talloc_free(host.enable_encrypt);
3138 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3139
3140 return CMD_SUCCESS;
3141 } else {
3142 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3143 return CMD_WARNING;
3144 }
3145 }
3146
3147 if (!isalnum((int)*argv[0])) {
3148 vty_out(vty,
3149 "Please specify string starting with alphanumeric%s",
3150 VTY_NEWLINE);
3151 return CMD_WARNING;
3152 }
3153
3154 if (host.enable)
3155 talloc_free(host.enable);
3156 host.enable = NULL;
3157
3158 /* Plain password input. */
3159#ifdef VTY_CRYPT_PW
3160 if (host.encrypt) {
3161 if (host.enable_encrypt)
3162 talloc_free(host.enable_encrypt);
3163 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3164 } else
3165#endif
3166 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3167
3168 return CMD_SUCCESS;
3169}
3170
3171ALIAS(config_enable_password,
3172 enable_password_text_cmd,
3173 "enable password LINE",
3174 "Modify enable password parameters\n"
3175 "Assign the privileged level password\n"
3176 "The UNENCRYPTED (cleartext) 'enable' password\n")
3177
3178/* VTY enable password delete. */
3179 DEFUN(no_config_enable_password, no_enable_password_cmd,
3180 "no enable password",
3181 NO_STR
3182 "Modify enable password parameters\n"
3183 "Assign the privileged level password\n")
3184{
3185 if (host.enable)
3186 talloc_free(host.enable);
3187 host.enable = NULL;
3188
3189 if (host.enable_encrypt)
3190 talloc_free(host.enable_encrypt);
3191 host.enable_encrypt = NULL;
3192
3193 return CMD_SUCCESS;
3194}
3195
3196#ifdef VTY_CRYPT_PW
3197DEFUN(service_password_encrypt,
3198 service_password_encrypt_cmd,
3199 "service password-encryption",
3200 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3201{
3202 if (host.encrypt)
3203 return CMD_SUCCESS;
3204
3205 host.encrypt = 1;
3206
3207 if (host.password) {
3208 if (host.password_encrypt)
3209 talloc_free(host.password_encrypt);
3210 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3211 }
3212 if (host.enable) {
3213 if (host.enable_encrypt)
3214 talloc_free(host.enable_encrypt);
3215 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3216 }
3217
3218 return CMD_SUCCESS;
3219}
3220
3221DEFUN(no_service_password_encrypt,
3222 no_service_password_encrypt_cmd,
3223 "no service password-encryption",
3224 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3225{
3226 if (!host.encrypt)
3227 return CMD_SUCCESS;
3228
3229 host.encrypt = 0;
3230
3231 if (host.password_encrypt)
3232 talloc_free(host.password_encrypt);
3233 host.password_encrypt = NULL;
3234
3235 if (host.enable_encrypt)
3236 talloc_free(host.enable_encrypt);
3237 host.enable_encrypt = NULL;
3238
3239 return CMD_SUCCESS;
3240}
3241#endif
3242
3243DEFUN(config_terminal_length, config_terminal_length_cmd,
3244 "terminal length <0-512>",
3245 "Set terminal line parameters\n"
3246 "Set number of lines on a screen\n"
3247 "Number of lines on screen (0 for no pausing)\n")
3248{
3249 int lines;
3250 char *endptr = NULL;
3251
3252 lines = strtol(argv[0], &endptr, 10);
3253 if (lines < 0 || lines > 512 || *endptr != '\0') {
3254 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3255 return CMD_WARNING;
3256 }
3257 vty->lines = lines;
3258
3259 return CMD_SUCCESS;
3260}
3261
3262DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3263 "terminal no length",
3264 "Set terminal line parameters\n"
3265 NO_STR "Set number of lines on a screen\n")
3266{
3267 vty->lines = -1;
3268 return CMD_SUCCESS;
3269}
3270
3271DEFUN(service_terminal_length, service_terminal_length_cmd,
3272 "service terminal-length <0-512>",
3273 "Set up miscellaneous service\n"
3274 "System wide terminal length configuration\n"
3275 "Number of lines of VTY (0 means no line control)\n")
3276{
3277 int lines;
3278 char *endptr = NULL;
3279
3280 lines = strtol(argv[0], &endptr, 10);
3281 if (lines < 0 || lines > 512 || *endptr != '\0') {
3282 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3283 return CMD_WARNING;
3284 }
3285 host.lines = lines;
3286
3287 return CMD_SUCCESS;
3288}
3289
3290DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3291 "no service terminal-length [<0-512>]",
3292 NO_STR
3293 "Set up miscellaneous service\n"
3294 "System wide terminal length configuration\n"
3295 "Number of lines of VTY (0 means no line control)\n")
3296{
3297 host.lines = -1;
3298 return CMD_SUCCESS;
3299}
3300
3301DEFUN_HIDDEN(do_echo,
3302 echo_cmd,
3303 "echo .MESSAGE",
3304 "Echo a message back to the vty\n" "The message to echo\n")
3305{
3306 char *message;
3307
3308 vty_out(vty, "%s%s",
3309 ((message =
3310 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3311 if (message)
3312 talloc_free(message);
3313 return CMD_SUCCESS;
3314}
3315
3316#if 0
3317DEFUN(config_logmsg,
3318 config_logmsg_cmd,
3319 "logmsg " LOG_LEVELS " .MESSAGE",
3320 "Send a message to enabled logging destinations\n"
3321 LOG_LEVEL_DESC "The message to send\n")
3322{
3323 int level;
3324 char *message;
3325
3326 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3327 return CMD_ERR_NO_MATCH;
3328
3329 zlog(NULL, level,
3330 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3331 if (message)
3332 talloc_free(message);
3333 return CMD_SUCCESS;
3334}
3335
3336DEFUN(show_logging,
3337 show_logging_cmd,
3338 "show logging", SHOW_STR "Show current logging configuration\n")
3339{
3340 struct zlog *zl = zlog_default;
3341
3342 vty_out(vty, "Syslog logging: ");
3343 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3344 vty_out(vty, "disabled");
3345 else
3346 vty_out(vty, "level %s, facility %s, ident %s",
3347 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3348 facility_name(zl->facility), zl->ident);
3349 vty_out(vty, "%s", VTY_NEWLINE);
3350
3351 vty_out(vty, "Stdout logging: ");
3352 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3353 vty_out(vty, "disabled");
3354 else
3355 vty_out(vty, "level %s",
3356 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3357 vty_out(vty, "%s", VTY_NEWLINE);
3358
3359 vty_out(vty, "Monitor logging: ");
3360 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3361 vty_out(vty, "disabled");
3362 else
3363 vty_out(vty, "level %s",
3364 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3365 vty_out(vty, "%s", VTY_NEWLINE);
3366
3367 vty_out(vty, "File logging: ");
3368 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3369 vty_out(vty, "disabled");
3370 else
3371 vty_out(vty, "level %s, filename %s",
3372 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3373 zl->filename);
3374 vty_out(vty, "%s", VTY_NEWLINE);
3375
3376 vty_out(vty, "Protocol name: %s%s",
3377 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3378 vty_out(vty, "Record priority: %s%s",
3379 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3380
3381 return CMD_SUCCESS;
3382}
3383
3384DEFUN(config_log_stdout,
3385 config_log_stdout_cmd,
3386 "log stdout", "Logging control\n" "Set stdout logging level\n")
3387{
3388 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3389 return CMD_SUCCESS;
3390}
3391
3392DEFUN(config_log_stdout_level,
3393 config_log_stdout_level_cmd,
3394 "log stdout " LOG_LEVELS,
3395 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3396{
3397 int level;
3398
3399 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3400 return CMD_ERR_NO_MATCH;
3401 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3402 return CMD_SUCCESS;
3403}
3404
3405DEFUN(no_config_log_stdout,
3406 no_config_log_stdout_cmd,
3407 "no log stdout [LEVEL]",
3408 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3409{
3410 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3411 return CMD_SUCCESS;
3412}
3413
3414DEFUN(config_log_monitor,
3415 config_log_monitor_cmd,
3416 "log monitor",
3417 "Logging control\n" "Set terminal line (monitor) logging level\n")
3418{
3419 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3420 return CMD_SUCCESS;
3421}
3422
3423DEFUN(config_log_monitor_level,
3424 config_log_monitor_level_cmd,
3425 "log monitor " LOG_LEVELS,
3426 "Logging control\n"
3427 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3428{
3429 int level;
3430
3431 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3432 return CMD_ERR_NO_MATCH;
3433 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3434 return CMD_SUCCESS;
3435}
3436
3437DEFUN(no_config_log_monitor,
3438 no_config_log_monitor_cmd,
3439 "no log monitor [LEVEL]",
3440 NO_STR
3441 "Logging control\n"
3442 "Disable terminal line (monitor) logging\n" "Logging level\n")
3443{
3444 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3445 return CMD_SUCCESS;
3446}
3447
3448static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3449{
3450 int ret;
3451 char *p = NULL;
3452 const char *fullpath;
3453
3454 /* Path detection. */
3455 if (!IS_DIRECTORY_SEP(*fname)) {
3456 char cwd[MAXPATHLEN + 1];
3457 cwd[MAXPATHLEN] = '\0';
3458
3459 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3460 zlog_err("config_log_file: Unable to alloc mem!");
3461 return CMD_WARNING;
3462 }
3463
3464 if ((p = _talloc_zero(tall_vcmd_ctx,
3465 strlen(cwd) + strlen(fname) + 2),
3466 "set_log_file")
3467 == NULL) {
3468 zlog_err("config_log_file: Unable to alloc mem!");
3469 return CMD_WARNING;
3470 }
3471 sprintf(p, "%s/%s", cwd, fname);
3472 fullpath = p;
3473 } else
3474 fullpath = fname;
3475
3476 ret = zlog_set_file(NULL, fullpath, loglevel);
3477
3478 if (p)
3479 talloc_free(p);
3480
3481 if (!ret) {
3482 vty_out(vty, "can't open logfile %s\n", fname);
3483 return CMD_WARNING;
3484 }
3485
3486 if (host.logfile)
3487 talloc_free(host.logfile);
3488
3489 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3490
3491 return CMD_SUCCESS;
3492}
3493
3494DEFUN(config_log_file,
3495 config_log_file_cmd,
3496 "log file FILENAME",
3497 "Logging control\n" "Logging to file\n" "Logging filename\n")
3498{
3499 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3500}
3501
3502DEFUN(config_log_file_level,
3503 config_log_file_level_cmd,
3504 "log file FILENAME " LOG_LEVELS,
3505 "Logging control\n"
3506 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3507{
3508 int level;
3509
3510 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3511 return CMD_ERR_NO_MATCH;
3512 return set_log_file(vty, argv[0], level);
3513}
3514
3515DEFUN(no_config_log_file,
3516 no_config_log_file_cmd,
3517 "no log file [FILENAME]",
3518 NO_STR
3519 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3520{
3521 zlog_reset_file(NULL);
3522
3523 if (host.logfile)
3524 talloc_free(host.logfile);
3525
3526 host.logfile = NULL;
3527
3528 return CMD_SUCCESS;
3529}
3530
3531ALIAS(no_config_log_file,
3532 no_config_log_file_level_cmd,
3533 "no log file FILENAME LEVEL",
3534 NO_STR
3535 "Logging control\n"
3536 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3537
3538 DEFUN(config_log_syslog,
3539 config_log_syslog_cmd,
3540 "log syslog", "Logging control\n" "Set syslog logging level\n")
3541{
3542 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3543 return CMD_SUCCESS;
3544}
3545
3546DEFUN(config_log_syslog_level,
3547 config_log_syslog_level_cmd,
3548 "log syslog " LOG_LEVELS,
3549 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3550{
3551 int level;
3552
3553 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3554 return CMD_ERR_NO_MATCH;
3555 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3556 return CMD_SUCCESS;
3557}
3558
3559DEFUN_DEPRECATED(config_log_syslog_facility,
3560 config_log_syslog_facility_cmd,
3561 "log syslog facility " LOG_FACILITIES,
3562 "Logging control\n"
3563 "Logging goes to syslog\n"
3564 "(Deprecated) Facility parameter for syslog messages\n"
3565 LOG_FACILITY_DESC)
3566{
3567 int facility;
3568
3569 if ((facility = facility_match(argv[0])) < 0)
3570 return CMD_ERR_NO_MATCH;
3571
3572 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3573 zlog_default->facility = facility;
3574 return CMD_SUCCESS;
3575}
3576
3577DEFUN(no_config_log_syslog,
3578 no_config_log_syslog_cmd,
3579 "no log syslog [LEVEL]",
3580 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3581{
3582 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3583 return CMD_SUCCESS;
3584}
3585
3586ALIAS(no_config_log_syslog,
3587 no_config_log_syslog_facility_cmd,
3588 "no log syslog facility " LOG_FACILITIES,
3589 NO_STR
3590 "Logging control\n"
3591 "Logging goes to syslog\n"
3592 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3593
3594 DEFUN(config_log_facility,
3595 config_log_facility_cmd,
3596 "log facility " LOG_FACILITIES,
3597 "Logging control\n"
3598 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3599{
3600 int facility;
3601
3602 if ((facility = facility_match(argv[0])) < 0)
3603 return CMD_ERR_NO_MATCH;
3604 zlog_default->facility = facility;
3605 return CMD_SUCCESS;
3606}
3607
3608DEFUN(no_config_log_facility,
3609 no_config_log_facility_cmd,
3610 "no log facility [FACILITY]",
3611 NO_STR
3612 "Logging control\n"
3613 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3614{
3615 zlog_default->facility = LOG_DAEMON;
3616 return CMD_SUCCESS;
3617}
3618
3619DEFUN_DEPRECATED(config_log_trap,
3620 config_log_trap_cmd,
3621 "log trap " LOG_LEVELS,
3622 "Logging control\n"
3623 "(Deprecated) Set logging level and default for all destinations\n"
3624 LOG_LEVEL_DESC)
3625{
3626 int new_level;
3627 int i;
3628
3629 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3630 return CMD_ERR_NO_MATCH;
3631
3632 zlog_default->default_lvl = new_level;
3633 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3634 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3635 zlog_default->maxlvl[i] = new_level;
3636 return CMD_SUCCESS;
3637}
3638
3639DEFUN_DEPRECATED(no_config_log_trap,
3640 no_config_log_trap_cmd,
3641 "no log trap [LEVEL]",
3642 NO_STR
3643 "Logging control\n"
3644 "Permit all logging information\n" "Logging level\n")
3645{
3646 zlog_default->default_lvl = LOG_DEBUG;
3647 return CMD_SUCCESS;
3648}
3649
3650DEFUN(config_log_record_priority,
3651 config_log_record_priority_cmd,
3652 "log record-priority",
3653 "Logging control\n"
3654 "Log the priority of the message within the message\n")
3655{
3656 zlog_default->record_priority = 1;
3657 return CMD_SUCCESS;
3658}
3659
3660DEFUN(no_config_log_record_priority,
3661 no_config_log_record_priority_cmd,
3662 "no log record-priority",
3663 NO_STR
3664 "Logging control\n"
3665 "Do not log the priority of the message within the message\n")
3666{
3667 zlog_default->record_priority = 0;
3668 return CMD_SUCCESS;
3669}
3670#endif
3671
3672DEFUN(banner_motd_file,
3673 banner_motd_file_cmd,
3674 "banner motd file [FILE]",
3675 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3676{
3677 if (host.motdfile)
3678 talloc_free(host.motdfile);
3679 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3680
3681 return CMD_SUCCESS;
3682}
3683
3684DEFUN(banner_motd_default,
3685 banner_motd_default_cmd,
3686 "banner motd default",
3687 "Set banner string\n" "Strings for motd\n" "Default string\n")
3688{
3689 host.motd = default_motd;
3690 return CMD_SUCCESS;
3691}
3692
3693DEFUN(no_banner_motd,
3694 no_banner_motd_cmd,
3695 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3696{
3697 host.motd = NULL;
3698 if (host.motdfile)
3699 talloc_free(host.motdfile);
3700 host.motdfile = NULL;
3701 return CMD_SUCCESS;
3702}
3703
3704/* Set config filename. Called from vty.c */
3705void host_config_set(const char *filename)
3706{
3707 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3708}
3709
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003710/*! Deprecated, now happens implicitly when calling install_node().
3711 * Users of the API may still attempt to call this function, hence
3712 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003713void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003714{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003715}
3716
3717/*! Deprecated, now happens implicitly when calling install_node().
3718 * Users of the API may still attempt to call this function, hence
3719 * leave it here as a no-op. */
3720void vty_install_default(int node)
3721{
3722}
3723
3724/*! Install common commands like 'exit' and 'list'. */
3725static void install_basic_node_commands(int node)
3726{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003727 install_element(node, &config_help_cmd);
3728 install_element(node, &config_list_cmd);
3729
3730 install_element(node, &config_write_terminal_cmd);
3731 install_element(node, &config_write_file_cmd);
3732 install_element(node, &config_write_memory_cmd);
3733 install_element(node, &config_write_cmd);
3734 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003735
3736 install_element(node, &config_exit_cmd);
3737
3738 if (node >= CONFIG_NODE) {
3739 /* It's not a top node. */
3740 install_element(node, &config_end_cmd);
3741 }
3742}
3743
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003744/*! Return true if a node is installed by install_basic_node_commands(), so
3745 * that we can avoid repeating them for each and every node during 'show
3746 * running-config' */
3747static bool vty_command_is_common(struct cmd_element *cmd)
3748{
3749 if (cmd == &config_help_cmd
3750 || cmd == &config_list_cmd
3751 || cmd == &config_write_terminal_cmd
3752 || cmd == &config_write_file_cmd
3753 || cmd == &config_write_memory_cmd
3754 || cmd == &config_write_cmd
3755 || cmd == &show_running_config_cmd
3756 || cmd == &config_exit_cmd
3757 || cmd == &config_end_cmd)
3758 return true;
3759 return false;
3760}
3761
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003762/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003763 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003764 * \param[in] vty the vty of the code
3765 * \param[in] filename where to store the file
3766 * \return 0 in case of success.
3767 *
3768 * If the filename already exists create a filename.sav
3769 * version with the current code.
3770 *
3771 */
3772int osmo_vty_write_config_file(const char *filename)
3773{
3774 char *failed_file;
3775 int rc;
3776
3777 rc = write_config_file(filename, &failed_file);
3778 talloc_free(failed_file);
3779 return rc;
3780}
3781
3782/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003783 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003784 * \return 0 in case of success.
3785 *
3786 * If the filename already exists create a filename.sav
3787 * version with the current code.
3788 *
3789 */
3790int osmo_vty_save_config_file(void)
3791{
3792 char *failed_file;
3793 int rc;
3794
3795 if (host.config == NULL)
3796 return -7;
3797
3798 rc = write_config_file(host.config, &failed_file);
3799 talloc_free(failed_file);
3800 return rc;
3801}
3802
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003803/* Initialize command interface. Install basic nodes and commands. */
3804void cmd_init(int terminal)
3805{
3806 /* Allocate initial top vector of commands. */
3807 cmdvec = vector_init(VECTOR_MIN_SIZE);
3808
3809 /* Default host value settings. */
3810 host.name = NULL;
3811 host.password = NULL;
3812 host.enable = NULL;
3813 host.logfile = NULL;
3814 host.config = NULL;
3815 host.lines = -1;
3816 host.motd = default_motd;
3817 host.motdfile = NULL;
3818
3819 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003820 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003821 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003822 install_node_bare(&auth_node, NULL);
3823 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003824 install_node(&config_node, config_write_host);
3825
3826 /* Each node's basic commands. */
3827 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003828 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003829 if (terminal) {
3830 install_element(VIEW_NODE, &config_list_cmd);
3831 install_element(VIEW_NODE, &config_exit_cmd);
3832 install_element(VIEW_NODE, &config_help_cmd);
3833 install_element(VIEW_NODE, &config_enable_cmd);
3834 install_element(VIEW_NODE, &config_terminal_length_cmd);
3835 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3836 install_element(VIEW_NODE, &echo_cmd);
3837 }
3838
3839 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003840 install_element(ENABLE_NODE, &config_disable_cmd);
3841 install_element(ENABLE_NODE, &config_terminal_cmd);
3842 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3843 }
3844 install_element (ENABLE_NODE, &show_startup_config_cmd);
3845 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003846 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003847
3848 if (terminal) {
3849 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3850 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3851 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003852 }
3853
3854 install_element(CONFIG_NODE, &hostname_cmd);
3855 install_element(CONFIG_NODE, &no_hostname_cmd);
3856
3857 if (terminal) {
3858 install_element(CONFIG_NODE, &password_cmd);
3859 install_element(CONFIG_NODE, &password_text_cmd);
3860 install_element(CONFIG_NODE, &enable_password_cmd);
3861 install_element(CONFIG_NODE, &enable_password_text_cmd);
3862 install_element(CONFIG_NODE, &no_enable_password_cmd);
3863
3864#ifdef VTY_CRYPT_PW
3865 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3866 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3867#endif
3868 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3869 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3870 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3871 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3872 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3873
3874 }
3875 srand(time(NULL));
3876}
Harald Welte7acb30c2011-08-17 17:13:48 +02003877
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003878/*! @} */