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