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