blob: cee590b9ec6f9e6dc6b89e6c8568eac0648d5782 [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>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020041#include <sys/time.h>
42#include <sys/stat.h>
43
44#include <osmocom/vty/vector.h>
45#include <osmocom/vty/vty.h>
46#include <osmocom/vty/command.h>
47
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010048#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010049#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050
Ruben Undheim766f77c2018-11-18 13:02:47 +010051#ifndef MAXPATHLEN
52 #define MAXPATHLEN 4096
53#endif
54
55
Harald Weltee881b1b2011-08-17 18:52:30 +020056/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020057 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020059 *
60 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020061
Harald Welte3fb0b6f2010-05-19 19:02:52 +020062#define CONFIGFILE_MASK 022
63
64void *tall_vty_cmd_ctx;
65
66/* Command vector which includes some level of command lists. Normally
67 each daemon maintains each own cmdvec. */
68vector cmdvec;
69
70/* Host information structure. */
71struct host host;
72
73/* Standard command node structures. */
74struct cmd_node auth_node = {
75 AUTH_NODE,
76 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node view_node = {
81 VIEW_NODE,
82 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node auth_enable_node = {
87 AUTH_ENABLE_NODE,
88 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node enable_node = {
93 ENABLE_NODE,
94 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010095 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096};
97
98struct cmd_node config_node = {
99 CONFIG_NODE,
100 "%s(config)# ",
101 1
102};
103
104/* Default motd string. */
105const char *default_motd = "";
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200108 *
109 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110void print_version(int print_copyright)
111{
Harald Welte237f6242010-05-25 23:00:45 +0200112 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200114 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115}
116
117/* Utility function to concatenate argv argument into a single string
118 with inserting ' ' character between each argument. */
119char *argv_concat(const char **argv, int argc, int shift)
120{
121 int i;
122 size_t len;
123 char *str;
124 char *p;
125
126 len = 0;
127 for (i = shift; i < argc; i++)
128 len += strlen(argv[i]) + 1;
129 if (!len)
130 return NULL;
131 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
132 for (i = shift; i < argc; i++) {
133 size_t arglen;
134 memcpy(p, argv[i], (arglen = strlen(argv[i])));
135 p += arglen;
136 *p++ = ' ';
137 }
138 *(p - 1) = '\0';
139 return str;
140}
141
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200142/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
143 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
144 * in turn, this name us used for XML IDs in 'show online-help'. */
145static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
146{
147 const char *pos;
148 int dest = 0;
149
150 if (!prompt || !*prompt)
151 return "";
152
153 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
154 if (pos[0] == '%' && pos[1]) {
155 /* skip "%s"; loop pos++ does the second one. */
156 pos++;
157 continue;
158 }
159 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
160 continue;
161 name_buf[dest] = pos[0];
162 dest++;
163 }
164 name_buf[dest] = '\0';
165 return name_buf;
166}
167
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200168static void install_basic_node_commands(int node);
169
170/*! Install top node of command vector, without adding basic node commands. */
171static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172{
173 vector_set_index(cmdvec, node->node, node);
174 node->func = func;
175 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200176 if (!*node->name)
177 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200178}
179
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200180/*! Install top node of command vector. */
181void install_node(struct cmd_node *node, int (*func) (struct vty *))
182{
183 install_node_bare(node, func);
184 install_basic_node_commands(node->node);
185}
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187/* Compare two command's string. Used in sort_node (). */
188static int cmp_node(const void *p, const void *q)
189{
190 struct cmd_element *a = *(struct cmd_element **)p;
191 struct cmd_element *b = *(struct cmd_element **)q;
192
193 return strcmp(a->string, b->string);
194}
195
196static int cmp_desc(const void *p, const void *q)
197{
198 struct desc *a = *(struct desc **)p;
199 struct desc *b = *(struct desc **)q;
200
201 return strcmp(a->cmd, b->cmd);
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200205void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200206{
207 unsigned int i, j;
208 struct cmd_node *cnode;
209 vector descvec;
210 struct cmd_element *cmd_element;
211
212 for (i = 0; i < vector_active(cmdvec); i++)
213 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
214 vector cmd_vector = cnode->cmd_vector;
215 qsort(cmd_vector->index, vector_active(cmd_vector),
216 sizeof(void *), cmp_node);
217
218 for (j = 0; j < vector_active(cmd_vector); j++)
219 if ((cmd_element =
220 vector_slot(cmd_vector, j)) != NULL
221 && vector_active(cmd_element->strvec)) {
222 descvec =
223 vector_slot(cmd_element->strvec,
224 vector_active
225 (cmd_element->strvec) -
226 1);
227 qsort(descvec->index,
228 vector_active(descvec),
229 sizeof(void *), cmp_desc);
230 }
231 }
232}
233
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200234/*! Break up string in command tokens. Return leading indents.
235 * \param[in] string String to split.
236 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
237 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
238 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
239 *
240 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
241 * so that \a indent can simply return the count of leading spaces.
242 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
243 */
244int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245{
246 const char *cp, *start;
247 char *token;
248 int strlen;
249 vector strvec;
250
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200251 *strvec_p = NULL;
252 if (indent)
253 *indent = 0;
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200257
258 cp = string;
259
260 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200261 while (isspace((int)*cp) && *cp != '\0') {
262 /* if we're counting indents, we need to be strict about them */
263 if (indent && (*cp != ' ') && (*cp != '\t')) {
264 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
265 if (*cp == '\n' || *cp == '\r') {
266 cp++;
267 string = cp;
268 continue;
269 }
270 return CMD_ERR_INVALID_INDENT;
271 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 }
274
275 if (indent)
276 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277
278 /* Return if there is only white spaces */
279 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Prepare return vector. */
286 strvec = vector_init(VECTOR_MIN_SIZE);
287
288 /* Copy each command piece and set into vector. */
289 while (1) {
290 start = cp;
291 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
292 *cp != '\0')
293 cp++;
294 strlen = cp - start;
295 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
296 memcpy(token, start, strlen);
297 *(token + strlen) = '\0';
298 vector_set(strvec, token);
299
300 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
301 *cp != '\0')
302 cp++;
303
304 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200307
308 *strvec_p = strvec;
309 return CMD_SUCCESS;
310}
311
312/*! Breaking up string into each command piece. I assume given
313 character is separated by a space character. Return value is a
314 vector which includes char ** data element. */
315vector cmd_make_strvec(const char *string)
316{
317 vector strvec;
318 cmd_make_strvec2(string, NULL, &strvec);
319 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200320}
321
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200322/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200323void cmd_free_strvec(vector v)
324{
325 unsigned int i;
326 char *cp;
327
328 if (!v)
329 return;
330
331 for (i = 0; i < vector_active(v); i++)
332 if ((cp = vector_slot(v, i)) != NULL)
333 talloc_free(cp);
334
335 vector_free(v);
336}
337
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200338/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200339static char *cmd_desc_str(const char **string)
340{
341 const char *cp, *start;
342 char *token;
343 int strlen;
344
345 cp = *string;
346
347 if (cp == NULL)
348 return NULL;
349
350 /* Skip white spaces. */
351 while (isspace((int)*cp) && *cp != '\0')
352 cp++;
353
354 /* Return if there is only white spaces */
355 if (*cp == '\0')
356 return NULL;
357
358 start = cp;
359
360 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
361 cp++;
362
363 strlen = cp - start;
364 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
365 memcpy(token, start, strlen);
366 *(token + strlen) = '\0';
367
368 *string = cp;
369
370 return token;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200374static vector cmd_make_descvec(const char *string, const char *descstr)
375{
376 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100377 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378 const char *sp;
379 char *token;
380 int len;
381 const char *cp;
382 const char *dp;
383 vector allvec;
384 vector strvec = NULL;
385 struct desc *desc;
386
387 cp = string;
388 dp = descstr;
389
390 if (cp == NULL)
391 return NULL;
392
393 allvec = vector_init(VECTOR_MIN_SIZE);
394
395 while (1) {
396 while (isspace((int)*cp) && *cp != '\0')
397 cp++;
398
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
400 if (cp[0] == '[' && cp[1] == '(') {
401 optional_brace = 1;
402 cp++;
403 }
404
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200405 if (*cp == '(') {
406 multiple = 1;
407 cp++;
408 }
409 if (*cp == ')') {
410 multiple = 0;
411 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100412 if (*cp == ']')
413 cp++;
414 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 }
416 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100417 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 cp++;
419 }
420
421 while (isspace((int)*cp) && *cp != '\0')
422 cp++;
423
424 if (*cp == '(') {
425 multiple = 1;
426 cp++;
427 }
428
429 if (*cp == '\0')
430 return allvec;
431
432 sp = cp;
433
434 while (!
435 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
436 || *cp == ')' || *cp == '|') && *cp != '\0')
437 cp++;
438
439 len = cp - sp;
440
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100441 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
442 if (optional_brace) {
443 /* Place each individual multi-choice token in its own square braces */
444 token[0] = '[';
445 memcpy(token + 1, sp, len);
446 token[1 + len] = ']';
447 token[2 + len] = '\0';
448 } else {
449 memcpy(token, sp, len);
450 *(token + len) = '\0';
451 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200452
453 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
454 desc->cmd = token;
455 desc->str = cmd_desc_str(&dp);
456
457 if (multiple) {
458 if (multiple == 1) {
459 strvec = vector_init(VECTOR_MIN_SIZE);
460 vector_set(allvec, strvec);
461 }
462 multiple++;
463 } else {
464 strvec = vector_init(VECTOR_MIN_SIZE);
465 vector_set(allvec, strvec);
466 }
467 vector_set(strvec, desc);
468 }
469}
470
471/* Count mandantory string vector size. This is to determine inputed
472 command has enough command length. */
473static int cmd_cmdsize(vector strvec)
474{
475 unsigned int i;
476 int size = 0;
477 vector descvec;
478 struct desc *desc;
479
480 for (i = 0; i < vector_active(strvec); i++)
481 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100482 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483 && (desc = vector_slot(descvec, 0)) != NULL) {
484 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
485 return size;
486 else
487 size++;
488 } else
489 size++;
490 }
491 return size;
492}
493
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200494/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200495const char *cmd_prompt(enum node_type node)
496{
497 struct cmd_node *cnode;
498
499 cnode = vector_slot(cmdvec, node);
500 return cnode->prompt;
501}
502
Alexander Couzensad580ba2016-05-16 16:01:45 +0200503/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200505 * \param unsafe string
506 * \return a new talloc char *
507 */
508char *osmo_asciidoc_escape(const char *inp)
509{
510 int _strlen;
511 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200512 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200513
514 if (!inp)
515 return NULL;
516 _strlen = strlen(inp);
517
518 for (i = 0; i < _strlen; ++i) {
519 switch (inp[i]) {
520 case '|':
521 len += 2;
522 break;
523 default:
524 len += 1;
525 break;
526 }
527 }
528
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200529 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200530 if (!out)
531 return NULL;
532
533 out_ptr = out;
534
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200538 /* Prepend escape character "\": */
539 *(out_ptr++) = '\\';
540 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 default:
542 *(out_ptr++) = inp[i];
543 break;
544 }
545 }
546
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 out_ptr[0] = '\0';
548 return out;
549}
550
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100551static char *xml_escape(const char *inp)
552{
553 int _strlen;
554 char *out, *out_ptr;
555 int len = 0, i, j;
556
557 if (!inp)
558 return NULL;
559 _strlen = strlen(inp);
560
561 for (i = 0; i < _strlen; ++i) {
562 switch (inp[i]) {
563 case '"':
564 len += 6;
565 break;
566 case '\'':
567 len += 6;
568 break;
569 case '<':
570 len += 4;
571 break;
572 case '>':
573 len += 4;
574 break;
575 case '&':
576 len += 5;
577 break;
578 default:
579 len += 1;
580 break;
581 }
582 }
583
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200584 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100585 if (!out)
586 return NULL;
587
588 out_ptr = out;
589
590#define ADD(out, str) \
591 for (j = 0; j < strlen(str); ++j) \
592 *(out++) = str[j];
593
594 for (i = 0; i < _strlen; ++i) {
595 switch (inp[i]) {
596 case '"':
597 ADD(out_ptr, "&quot;");
598 break;
599 case '\'':
600 ADD(out_ptr, "&apos;");
601 break;
602 case '<':
603 ADD(out_ptr, "&lt;");
604 break;
605 case '>':
606 ADD(out_ptr, "&gt;");
607 break;
608 case '&':
609 ADD(out_ptr, "&amp;");
610 break;
611 default:
612 *(out_ptr++) = inp[i];
613 break;
614 }
615 }
616
617#undef ADD
618
619 out_ptr[0] = '\0';
620 return out;
621}
622
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200623typedef int (*print_func_t)(void *data, const char *fmt, ...);
624
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700625static const struct value_string cmd_attr_desc[] = {
626 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
627 { CMD_ATTR_HIDDEN, "This command is hidden" },
628 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700629 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700630 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700631 { 0, NULL }
632};
633
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100634/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200635 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100636 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200637static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100638{
639 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700640 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100641
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200642 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700643
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700644 /* Print global attributes and their description */
645 if (cmd->attr != 0x00) { /* ... if at least one flag is set */
646 print_func(data, " <attributes scope='global'>%s", newline);
647
648 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
649 char *xml_att_desc;
650
651 if (~cmd->attr & cmd_attr_desc[i].value)
652 continue;
653
654 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
655 print_func(data, " <attribute doc='%s' />%s",
656 xml_att_desc, newline);
657 talloc_free(xml_att_desc);
658 }
659
660 print_func(data, " </attributes>%s", newline);
661 }
662
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700663 /* Print application specific attributes and their description */
664 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
665 print_func(data, " <attributes scope='application'>%s", newline);
666
667 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
668 char *xml_att_desc;
669 char flag;
670
671 /* Skip attribute if *not* set */
672 if (~cmd->usrattr & (1 << i))
673 continue;
674
675 xml_att_desc = xml_escape(host.app_info->usr_attr_desc[i]);
676 print_func(data, " <attribute doc='%s'", xml_att_desc);
677 talloc_free(xml_att_desc);
678
679 if ((flag = host.app_info->usr_attr_letters[i]) != '\0')
680 print_func(data, " flag='%c'", flag);
681 print_func(data, " />%s", newline);
682 }
683
684 print_func(data, " </attributes>%s", newline);
685 }
686
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200687 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100688
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700689 for (i = 0; i < vector_count(cmd->strvec); ++i) {
690 vector descvec = vector_slot(cmd->strvec, i);
691 int j;
692 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100693 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700694 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100695 if (desc == NULL)
696 continue;
697
698 xml_param = xml_escape(desc->cmd);
699 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200700 print_func(data, " <param name='%s' doc='%s' />%s",
701 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100702 talloc_free(xml_param);
703 talloc_free(xml_doc);
704 }
705 }
706
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200707 print_func(data, " </params>%s", newline);
708 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100709
710 talloc_free(xml_string);
711 return 0;
712}
713
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200714static bool vty_command_is_common(struct cmd_element *cmd);
715
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100716/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200717 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100718 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200719static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100720{
721 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200722 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100723
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200724 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100725
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200726 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200727 print_func(data, " <node id='_common_cmds_'>%s", newline);
728 print_func(data, " <name>Common Commands</name>%s", newline);
729 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
730 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200731 for (i = 0; i < vector_active(cmdvec); ++i) {
732 struct cmd_node *cnode;
733 cnode = vector_slot(cmdvec, i);
734 if (!cnode)
735 continue;
736 if (cnode->node != CONFIG_NODE)
737 continue;
738
739 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
740 struct cmd_element *elem;
741 elem = vector_slot(cnode->cmd_vector, j);
742 if (!vty_command_is_common(elem))
743 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200744 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200745 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200746 }
747 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200748 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200749
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100750 for (i = 0; i < vector_active(cmdvec); ++i) {
751 struct cmd_node *cnode;
752 cnode = vector_slot(cmdvec, i);
753 if (!cnode)
754 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200755 if (vector_active(cnode->cmd_vector) < 1)
756 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100757
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200758 /* De-dup node IDs: how many times has this same name been used before? Count the first
759 * occurence as _1 and omit that first suffix, so that the first occurence is called
760 * 'name', the second becomes 'name_2', then 'name_3', ... */
761 same_name_count = 1;
762 for (j = 0; j < i; ++j) {
763 struct cmd_node *cnode2;
764 cnode2 = vector_slot(cmdvec, j);
765 if (!cnode2)
766 continue;
767 if (strcmp(cnode->name, cnode2->name) == 0)
768 same_name_count ++;
769 }
770
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200771 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200772 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200773 print_func(data, "_%d", same_name_count);
774 print_func(data, "'>%s", newline);
775 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100776
777 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
778 struct cmd_element *elem;
779 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200780 if (vty_command_is_common(elem))
781 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200782 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200783 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100784 }
785
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200786 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100787 }
788
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200789 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100790
791 return 0;
792}
793
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200794static int print_func_vty(void *data, const char *format, ...)
795{
796 struct vty *vty = data;
797 va_list args;
798 int rc;
799 va_start(args, format);
800 rc = vty_out_va(vty, format, args);
801 va_end(args);
802 return rc;
803}
804
805static int vty_dump_xml_ref_to_vty(struct vty *vty)
806{
807 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
808}
809
810static int print_func_stream(void *data, const char *format, ...)
811{
812 va_list args;
813 int rc;
814 va_start(args, format);
815 rc = vfprintf((FILE*)data, format, args);
816 va_end(args);
817 return rc;
818}
819
820/*! Print the XML reference of all VTY nodes to the given stream.
821 */
822int vty_dump_xml_ref(FILE *stream)
823{
824 return vty_dump_nodes(print_func_stream, stream, "\n");
825}
826
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200827/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100828static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
829{
830 int i;
831
832 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
833 struct cmd_element *elem;
834 elem = vector_slot(cnode->cmd_vector, i);
835 if (!elem->string)
836 continue;
837 if (!strcmp(elem->string, cmdstring))
838 return 1;
839 }
840 return 0;
841}
842
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200843/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200844 * \param[in] ntype Node Type
845 * \param[cmd] element to be installed
846 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000847void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200848{
849 struct cmd_node *cnode;
850
851 cnode = vector_slot(cmdvec, ntype);
852
Harald Weltea99d45a2015-11-12 13:48:23 +0100853 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100854 /* ensure no _identical_ command has been registered at this
855 * node so far */
856 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200857
858 vector_set(cnode->cmd_vector, cmd);
859
860 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
861 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
862}
863
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700864/*! Install a library command into a node
865 * \param[in] ntype Node Type
866 * \param[in] cmd element to be installed
867 */
868void install_lib_element(int ntype, struct cmd_element *cmd)
869{
870 cmd->attr |= CMD_ATTR_LIB_COMMAND;
871 install_element(ntype, cmd);
872}
873
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200874/* Install a command into VIEW and ENABLE node */
875void install_element_ve(struct cmd_element *cmd)
876{
877 install_element(VIEW_NODE, cmd);
878 install_element(ENABLE_NODE, cmd);
879}
880
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700881/* Install a library command into VIEW and ENABLE node */
882void install_lib_element_ve(struct cmd_element *cmd)
883{
884 cmd->attr |= CMD_ATTR_LIB_COMMAND;
885 install_element_ve(cmd);
886}
887
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200888#ifdef VTY_CRYPT_PW
889static unsigned char itoa64[] =
890 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
891
892static void to64(char *s, long v, int n)
893{
894 while (--n >= 0) {
895 *s++ = itoa64[v & 0x3f];
896 v >>= 6;
897 }
898}
899
900static char *zencrypt(const char *passwd)
901{
902 char salt[6];
903 struct timeval tv;
904 char *crypt(const char *, const char *);
905
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200906 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200907
908 to64(&salt[0], random(), 3);
909 to64(&salt[3], tv.tv_usec, 3);
910 salt[5] = '\0';
911
912 return crypt(passwd, salt);
913}
914#endif
915
916/* This function write configuration of this host. */
917static int config_write_host(struct vty *vty)
918{
919 if (host.name)
920 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
921
922 if (host.encrypt) {
923 if (host.password_encrypt)
924 vty_out(vty, "password 8 %s%s", host.password_encrypt,
925 VTY_NEWLINE);
926 if (host.enable_encrypt)
927 vty_out(vty, "enable password 8 %s%s",
928 host.enable_encrypt, VTY_NEWLINE);
929 } else {
930 if (host.password)
931 vty_out(vty, "password %s%s", host.password,
932 VTY_NEWLINE);
933 if (host.enable)
934 vty_out(vty, "enable password %s%s", host.enable,
935 VTY_NEWLINE);
936 }
937
938 if (host.advanced)
939 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
940
941 if (host.encrypt)
942 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
943
944 if (host.lines >= 0)
945 vty_out(vty, "service terminal-length %d%s", host.lines,
946 VTY_NEWLINE);
947
948 if (host.motdfile)
949 vty_out(vty, "banner motd file %s%s", host.motdfile,
950 VTY_NEWLINE);
951 else if (!host.motd)
952 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
953
954 return 1;
955}
956
957/* Utility function for getting command vector. */
958static vector cmd_node_vector(vector v, enum node_type ntype)
959{
960 struct cmd_node *cnode = vector_slot(v, ntype);
961 return cnode->cmd_vector;
962}
963
964/* Completion match types. */
965enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200966 NO_MATCH = 0,
967 ANY_MATCH,
968 EXTEND_MATCH,
969 IPV4_PREFIX_MATCH,
970 IPV4_MATCH,
971 IPV6_PREFIX_MATCH,
972 IPV6_MATCH,
973 RANGE_MATCH,
974 VARARG_MATCH,
975 PARTLY_MATCH,
976 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200977};
978
979static enum match_type cmd_ipv4_match(const char *str)
980{
981 const char *sp;
982 int dots = 0, nums = 0;
983 char buf[4];
984
985 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200986 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200987
988 for (;;) {
989 memset(buf, 0, sizeof(buf));
990 sp = str;
991 while (*str != '\0') {
992 if (*str == '.') {
993 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200994 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200995
996 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200997 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200998
999 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001000 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001001
1002 dots++;
1003 break;
1004 }
1005 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001006 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001007
1008 str++;
1009 }
1010
1011 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001012 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001013
1014 strncpy(buf, sp, str - sp);
1015 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001016 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001017
1018 nums++;
1019
1020 if (*str == '\0')
1021 break;
1022
1023 str++;
1024 }
1025
1026 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001027 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001028
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001029 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001030}
1031
1032static enum match_type cmd_ipv4_prefix_match(const char *str)
1033{
1034 const char *sp;
1035 int dots = 0;
1036 char buf[4];
1037
1038 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001039 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001040
1041 for (;;) {
1042 memset(buf, 0, sizeof(buf));
1043 sp = str;
1044 while (*str != '\0' && *str != '/') {
1045 if (*str == '.') {
1046 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001047 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001048
1049 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001050 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001051
1052 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001053 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001054
1055 dots++;
1056 break;
1057 }
1058
1059 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001060 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001061
1062 str++;
1063 }
1064
1065 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001066 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001067
1068 strncpy(buf, sp, str - sp);
1069 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001070 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001071
1072 if (dots == 3) {
1073 if (*str == '/') {
1074 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001075 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001076
1077 str++;
1078 break;
1079 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001080 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001081 }
1082
1083 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001084 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001085
1086 str++;
1087 }
1088
1089 sp = str;
1090 while (*str != '\0') {
1091 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001092 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001093
1094 str++;
1095 }
1096
1097 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001098 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001099
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001100 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001101}
1102
1103#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1104#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1105#define STATE_START 1
1106#define STATE_COLON 2
1107#define STATE_DOUBLE 3
1108#define STATE_ADDR 4
1109#define STATE_DOT 5
1110#define STATE_SLASH 6
1111#define STATE_MASK 7
1112
1113#ifdef HAVE_IPV6
1114
1115static enum match_type cmd_ipv6_match(const char *str)
1116{
1117 int state = STATE_START;
1118 int colons = 0, nums = 0, double_colon = 0;
1119 const char *sp = NULL;
1120 struct sockaddr_in6 sin6_dummy;
1121 int ret;
1122
1123 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001124 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125
1126 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001127 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001128
1129 /* use inet_pton that has a better support,
1130 * for example inet_pton can support the automatic addresses:
1131 * ::1.2.3.4
1132 */
1133 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1134
1135 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001136 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001137
1138 while (*str != '\0') {
1139 switch (state) {
1140 case STATE_START:
1141 if (*str == ':') {
1142 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001143 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001144 colons--;
1145 state = STATE_COLON;
1146 } else {
1147 sp = str;
1148 state = STATE_ADDR;
1149 }
1150
1151 continue;
1152 case STATE_COLON:
1153 colons++;
1154 if (*(str + 1) == ':')
1155 state = STATE_DOUBLE;
1156 else {
1157 sp = str + 1;
1158 state = STATE_ADDR;
1159 }
1160 break;
1161 case STATE_DOUBLE:
1162 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001163 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001164
1165 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001166 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001167 else {
1168 if (*(str + 1) != '\0')
1169 colons++;
1170 sp = str + 1;
1171 state = STATE_ADDR;
1172 }
1173
1174 double_colon++;
1175 nums++;
1176 break;
1177 case STATE_ADDR:
1178 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1179 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001180 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001181
1182 nums++;
1183 state = STATE_COLON;
1184 }
1185 if (*(str + 1) == '.')
1186 state = STATE_DOT;
1187 break;
1188 case STATE_DOT:
1189 state = STATE_ADDR;
1190 break;
1191 default:
1192 break;
1193 }
1194
1195 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001196 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001197
1198 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001199 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001200
1201 str++;
1202 }
1203
1204#if 0
1205 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001206 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207#endif /* 0 */
1208
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001209 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001210}
1211
1212static enum match_type cmd_ipv6_prefix_match(const char *str)
1213{
1214 int state = STATE_START;
1215 int colons = 0, nums = 0, double_colon = 0;
1216 int mask;
1217 const char *sp = NULL;
1218 char *endptr = NULL;
1219
1220 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001221 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001222
1223 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001224 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001225
1226 while (*str != '\0' && state != STATE_MASK) {
1227 switch (state) {
1228 case STATE_START:
1229 if (*str == ':') {
1230 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001231 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001232 colons--;
1233 state = STATE_COLON;
1234 } else {
1235 sp = str;
1236 state = STATE_ADDR;
1237 }
1238
1239 continue;
1240 case STATE_COLON:
1241 colons++;
1242 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001243 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001244 else if (*(str + 1) == ':')
1245 state = STATE_DOUBLE;
1246 else {
1247 sp = str + 1;
1248 state = STATE_ADDR;
1249 }
1250 break;
1251 case STATE_DOUBLE:
1252 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001253 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001254
1255 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001256 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001257 else {
1258 if (*(str + 1) != '\0' && *(str + 1) != '/')
1259 colons++;
1260 sp = str + 1;
1261
1262 if (*(str + 1) == '/')
1263 state = STATE_SLASH;
1264 else
1265 state = STATE_ADDR;
1266 }
1267
1268 double_colon++;
1269 nums += 1;
1270 break;
1271 case STATE_ADDR:
1272 if (*(str + 1) == ':' || *(str + 1) == '.'
1273 || *(str + 1) == '\0' || *(str + 1) == '/') {
1274 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001275 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001276
1277 for (; sp <= str; sp++)
1278 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001279 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001280
1281 nums++;
1282
1283 if (*(str + 1) == ':')
1284 state = STATE_COLON;
1285 else if (*(str + 1) == '.')
1286 state = STATE_DOT;
1287 else if (*(str + 1) == '/')
1288 state = STATE_SLASH;
1289 }
1290 break;
1291 case STATE_DOT:
1292 state = STATE_ADDR;
1293 break;
1294 case STATE_SLASH:
1295 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001296 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001297
1298 state = STATE_MASK;
1299 break;
1300 default:
1301 break;
1302 }
1303
1304 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001305 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001306
1307 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001308 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001309
1310 str++;
1311 }
1312
1313 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001314 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001315
1316 mask = strtol(str, &endptr, 10);
1317 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001318 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001319
1320 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001321 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001322
1323/* I don't know why mask < 13 makes command match partly.
1324 Forgive me to make this comments. I Want to set static default route
1325 because of lack of function to originate default in ospf6d; sorry
1326 yasu
1327 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001328 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001329*/
1330
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001331 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001332}
1333
1334#endif /* HAVE_IPV6 */
1335
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001336
1337#if ULONG_MAX == 18446744073709551615UL
1338#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1339#elif ULONG_MAX == 4294967295UL
1340#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1341#else
1342#error "ULONG_MAX not defined!"
1343#endif
1344
1345#if LONG_MAX == 9223372036854775807L
1346#define DECIMAL_STRLEN_MAX_SIGNED 19
1347#elif LONG_MAX == 2147483647L
1348#define DECIMAL_STRLEN_MAX_SIGNED 10
1349#else
1350#error "LONG_MAX not defined!"
1351#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001352
1353static int cmd_range_match(const char *range, const char *str)
1354{
1355 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001356 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001357 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001358
1359 if (str == NULL)
1360 return 1;
1361
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001362 if (range[1] == '-') {
1363 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001364
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001365 val = strtol(str, &endptr, 10);
1366 if (*endptr != '\0')
1367 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001368
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001369 range += 2;
1370 p = strchr(range, '-');
1371 if (p == NULL)
1372 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001373 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001374 return 0;
1375 strncpy(buf, range, p - range);
1376 buf[p - range] = '\0';
1377 min = -strtol(buf, &endptr, 10);
1378 if (*endptr != '\0')
1379 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001380
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001381 range = p + 1;
1382 p = strchr(range, '>');
1383 if (p == NULL)
1384 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001385 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001386 return 0;
1387 strncpy(buf, range, p - range);
1388 buf[p - range] = '\0';
1389 max = strtol(buf, &endptr, 10);
1390 if (*endptr != '\0')
1391 return 0;
1392
1393 if (val < min || val > max)
1394 return 0;
1395 } else {
1396 unsigned long min, max, val;
1397
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001398 if (str[0] == '-')
1399 return 0;
1400
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001401 val = strtoul(str, &endptr, 10);
1402 if (*endptr != '\0')
1403 return 0;
1404
1405 range++;
1406 p = strchr(range, '-');
1407 if (p == NULL)
1408 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001409 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001410 return 0;
1411 strncpy(buf, range, p - range);
1412 buf[p - range] = '\0';
1413 min = strtoul(buf, &endptr, 10);
1414 if (*endptr != '\0')
1415 return 0;
1416
1417 range = p + 1;
1418 p = strchr(range, '>');
1419 if (p == NULL)
1420 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001421 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001422 return 0;
1423 strncpy(buf, range, p - range);
1424 buf[p - range] = '\0';
1425 max = strtoul(buf, &endptr, 10);
1426 if (*endptr != '\0')
1427 return 0;
1428
1429 if (val < min || val > max)
1430 return 0;
1431 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001432
1433 return 1;
1434}
1435
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001436/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001437static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001438{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001439 /* we've got "[blah]". We want to strip off the []s and redo the
1440 * match check for "blah"
1441 */
1442 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001443
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001444 if (len < 3)
1445 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001446
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001447 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001448}
1449
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001450static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001451cmd_match(const char *str, const char *command,
1452 enum match_type min, bool recur)
1453{
1454
1455 if (recur && CMD_OPTION(str))
1456 {
1457 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001458 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001459
1460 /* this would be a bug in a command, however handle it gracefully
1461 * as it we only discover it if a user tries to run it
1462 */
1463 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001464 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001465
1466 ret = cmd_match(tmp, command, min, false);
1467
1468 talloc_free(tmp);
1469
1470 return ret;
1471 }
1472 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001473 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001474 else if (CMD_RANGE(str))
1475 {
1476 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001477 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001478 }
1479#ifdef HAVE_IPV6
1480 else if (CMD_IPV6(str))
1481 {
1482 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001483 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001484 }
1485 else if (CMD_IPV6_PREFIX(str))
1486 {
1487 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001488 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001489 }
1490#endif /* HAVE_IPV6 */
1491 else if (CMD_IPV4(str))
1492 {
1493 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001494 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001495 }
1496 else if (CMD_IPV4_PREFIX(str))
1497 {
1498 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001499 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001500 }
1501 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001502 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001503 else if (strncmp(command, str, strlen(command)) == 0)
1504 {
1505 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001506 return EXACT_MATCH;
1507 else if (PARTLY_MATCH >= min)
1508 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001509 }
1510
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001511 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001512}
1513
1514/* Filter vector at the specified index and by the given command string, to
1515 * the desired matching level (thus allowing part matches), and return match
1516 * type flag.
1517 */
1518static enum match_type
1519cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001520{
1521 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001522 struct cmd_element *cmd_element;
1523 enum match_type match_type;
1524 vector descvec;
1525 struct desc *desc;
1526
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001527 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001528
1529 /* If command and cmd_element string does not match set NULL to vector */
1530 for (i = 0; i < vector_active(v); i++)
1531 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001532 if (index >= vector_active(cmd_element->strvec))
1533 vector_slot(v, i) = NULL;
1534 else {
1535 unsigned int j;
1536 int matched = 0;
1537
1538 descvec =
1539 vector_slot(cmd_element->strvec, index);
1540
1541 for (j = 0; j < vector_active(descvec); j++)
1542 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001543 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001544
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001545 ret = cmd_match (desc->cmd, command, level, true);
1546
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001547 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001548 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001549
1550 if (match_type < ret)
1551 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001552 }
1553 if (!matched)
1554 vector_slot(v, i) = NULL;
1555 }
1556 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001557
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001558 if (match_type == NO_MATCH)
1559 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001560
1561 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1562 * go again and filter out commands whose argument (at this index) is
1563 * 'weaker'. E.g., if we have 2 commands:
1564 *
1565 * foo bar <1-255>
1566 * foo bar BLAH
1567 *
1568 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001569 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001570 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1571 *
1572 * If we don't do a 2nd pass and filter it out, the higher-layers will
1573 * consider this to be ambiguous.
1574 */
1575 for (i = 0; i < vector_active(v); i++)
1576 if ((cmd_element = vector_slot(v, i)) != NULL) {
1577 if (index >= vector_active(cmd_element->strvec))
1578 vector_slot(v, i) = NULL;
1579 else {
1580 unsigned int j;
1581 int matched = 0;
1582
1583 descvec =
1584 vector_slot(cmd_element->strvec, index);
1585
1586 for (j = 0; j < vector_active(descvec); j++)
1587 if ((desc = vector_slot(descvec, j))) {
1588 enum match_type ret;
1589
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001590 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001591
1592 if (ret >= match_type)
1593 matched++;
1594 }
1595 if (!matched)
1596 vector_slot(v, i) = NULL;
1597 }
1598 }
1599
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001600 return match_type;
1601}
1602
1603/* Check ambiguous match */
1604static int
1605is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1606{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001607 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001608 unsigned int i;
1609 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001610 struct cmd_element *cmd_element;
1611 const char *matched = NULL;
1612 vector descvec;
1613 struct desc *desc;
1614
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001615 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1616 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1617 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1618 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1619 * that case, the string must remain allocated until this function exits or another match comes
1620 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1621 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1622 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1623 void *cmd_deopt_ctx = NULL;
1624
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001625 for (i = 0; i < vector_active(v); i++) {
1626 cmd_element = vector_slot(v, i);
1627 if (!cmd_element)
1628 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001629
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001630 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001631
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001632 descvec = vector_slot(cmd_element->strvec, index);
1633
1634 for (j = 0; j < vector_active(descvec); j++) {
1635 desc = vector_slot(descvec, j);
1636 if (!desc)
1637 continue;
1638
1639 enum match_type mtype;
1640 const char *str = desc->cmd;
1641
1642 if (CMD_OPTION(str)) {
1643 if (!cmd_deopt_ctx)
1644 cmd_deopt_ctx =
1645 talloc_named_const(tall_vty_cmd_ctx, 0,
1646 __func__);
1647 str = cmd_deopt(cmd_deopt_ctx, str);
1648 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001649 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001650 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001651
1652 switch (type) {
1653 case EXACT_MATCH:
1654 if (!(CMD_VARIABLE (str))
1655 && strcmp(command, str) == 0)
1656 match++;
1657 break;
1658 case PARTLY_MATCH:
1659 if (!(CMD_VARIABLE (str))
1660 && strncmp(command, str, strlen (command)) == 0)
1661 {
1662 if (matched
1663 && strcmp(matched,
1664 str) != 0) {
1665 ret = 1; /* There is ambiguous match. */
1666 goto free_and_return;
1667 } else
1668 matched = str;
1669 match++;
1670 }
1671 break;
1672 case RANGE_MATCH:
1673 if (cmd_range_match
1674 (str, command)) {
1675 if (matched
1676 && strcmp(matched,
1677 str) != 0) {
1678 ret = 1;
1679 goto free_and_return;
1680 } else
1681 matched = str;
1682 match++;
1683 }
1684 break;
1685#ifdef HAVE_IPV6
1686 case IPV6_MATCH:
1687 if (CMD_IPV6(str))
1688 match++;
1689 break;
1690 case IPV6_PREFIX_MATCH:
1691 if ((mtype =
1692 cmd_ipv6_prefix_match
1693 (command)) != NO_MATCH) {
1694 if (mtype == PARTLY_MATCH) {
1695 ret = 2; /* There is incomplete match. */
1696 goto free_and_return;
1697 }
1698
1699 match++;
1700 }
1701 break;
1702#endif /* HAVE_IPV6 */
1703 case IPV4_MATCH:
1704 if (CMD_IPV4(str))
1705 match++;
1706 break;
1707 case IPV4_PREFIX_MATCH:
1708 if ((mtype =
1709 cmd_ipv4_prefix_match
1710 (command)) != NO_MATCH) {
1711 if (mtype == PARTLY_MATCH) {
1712 ret = 2; /* There is incomplete match. */
1713 goto free_and_return;
1714 }
1715
1716 match++;
1717 }
1718 break;
1719 case EXTEND_MATCH:
1720 if (CMD_VARIABLE (str))
1721 match++;
1722 break;
1723 case NO_MATCH:
1724 default:
1725 break;
1726 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001727 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001728 if (!match)
1729 vector_slot(v, i) = NULL;
1730 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001731
1732free_and_return:
1733 if (cmd_deopt_ctx)
1734 talloc_free(cmd_deopt_ctx);
1735 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001736}
1737
1738/* If src matches dst return dst string, otherwise return NULL */
1739static const char *cmd_entry_function(const char *src, const char *dst)
1740{
1741 /* Skip variable arguments. */
1742 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1743 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1744 return NULL;
1745
1746 /* In case of 'command \t', given src is NULL string. */
1747 if (src == NULL)
1748 return dst;
1749
1750 /* Matched with input string. */
1751 if (strncmp(src, dst, strlen(src)) == 0)
1752 return dst;
1753
1754 return NULL;
1755}
1756
1757/* If src matches dst return dst string, otherwise return NULL */
1758/* This version will return the dst string always if it is
1759 CMD_VARIABLE for '?' key processing */
1760static const char *cmd_entry_function_desc(const char *src, const char *dst)
1761{
1762 if (CMD_VARARG(dst))
1763 return dst;
1764
1765 if (CMD_RANGE(dst)) {
1766 if (cmd_range_match(dst, src))
1767 return dst;
1768 else
1769 return NULL;
1770 }
1771#ifdef HAVE_IPV6
1772 if (CMD_IPV6(dst)) {
1773 if (cmd_ipv6_match(src))
1774 return dst;
1775 else
1776 return NULL;
1777 }
1778
1779 if (CMD_IPV6_PREFIX(dst)) {
1780 if (cmd_ipv6_prefix_match(src))
1781 return dst;
1782 else
1783 return NULL;
1784 }
1785#endif /* HAVE_IPV6 */
1786
1787 if (CMD_IPV4(dst)) {
1788 if (cmd_ipv4_match(src))
1789 return dst;
1790 else
1791 return NULL;
1792 }
1793
1794 if (CMD_IPV4_PREFIX(dst)) {
1795 if (cmd_ipv4_prefix_match(src))
1796 return dst;
1797 else
1798 return NULL;
1799 }
1800
1801 /* Optional or variable commands always match on '?' */
1802 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1803 return dst;
1804
1805 /* In case of 'command \t', given src is NULL string. */
1806 if (src == NULL)
1807 return dst;
1808
1809 if (strncmp(src, dst, strlen(src)) == 0)
1810 return dst;
1811 else
1812 return NULL;
1813}
1814
1815/* Check same string element existence. If it isn't there return
1816 1. */
1817static int cmd_unique_string(vector v, const char *str)
1818{
1819 unsigned int i;
1820 char *match;
1821
1822 for (i = 0; i < vector_active(v); i++)
1823 if ((match = vector_slot(v, i)) != NULL)
1824 if (strcmp(match, str) == 0)
1825 return 0;
1826 return 1;
1827}
1828
1829/* Compare string to description vector. If there is same string
1830 return 1 else return 0. */
1831static int desc_unique_string(vector v, const char *str)
1832{
1833 unsigned int i;
1834 struct desc *desc;
1835
1836 for (i = 0; i < vector_active(v); i++)
1837 if ((desc = vector_slot(v, i)) != NULL)
1838 if (strcmp(desc->cmd, str) == 0)
1839 return 1;
1840 return 0;
1841}
1842
1843static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1844{
1845 if (first_word != NULL &&
1846 node != AUTH_NODE &&
1847 node != VIEW_NODE &&
1848 node != AUTH_ENABLE_NODE &&
1849 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1850 return 1;
1851 return 0;
1852}
1853
1854/* '?' describe command support. */
1855static vector
1856cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1857{
1858 unsigned int i;
1859 vector cmd_vector;
1860#define INIT_MATCHVEC_SIZE 10
1861 vector matchvec;
1862 struct cmd_element *cmd_element;
1863 unsigned int index;
1864 int ret;
1865 enum match_type match;
1866 char *command;
1867 static struct desc desc_cr = { "<cr>", "" };
1868
1869 /* Set index. */
1870 if (vector_active(vline) == 0) {
1871 *status = CMD_ERR_NO_MATCH;
1872 return NULL;
1873 } else
1874 index = vector_active(vline) - 1;
1875
1876 /* Make copy vector of current node's command vector. */
1877 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1878
1879 /* Prepare match vector */
1880 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1881
1882 /* Filter commands. */
1883 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001884 for (i = 0; i < index; i++) {
1885 command = vector_slot(vline, i);
1886 if (!command)
1887 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001888
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001889 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001890
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001891 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001892 struct cmd_element *cmd_element;
1893 vector descvec;
1894 unsigned int j, k;
1895
1896 for (j = 0; j < vector_active(cmd_vector); j++)
1897 if ((cmd_element =
1898 vector_slot(cmd_vector, j)) != NULL
1899 &&
1900 (vector_active(cmd_element->strvec))) {
1901 descvec =
1902 vector_slot(cmd_element->
1903 strvec,
1904 vector_active
1905 (cmd_element->
1906 strvec) - 1);
1907 for (k = 0;
1908 k < vector_active(descvec);
1909 k++) {
1910 struct desc *desc =
1911 vector_slot(descvec,
1912 k);
1913 vector_set(matchvec,
1914 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001915 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001916 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001917
Harald Welte80d30fe2013-02-12 11:08:57 +01001918 vector_set(matchvec, &desc_cr);
1919 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001920
Harald Welte80d30fe2013-02-12 11:08:57 +01001921 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001922 }
1923
Harald Welte80d30fe2013-02-12 11:08:57 +01001924 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1925 match)) == 1) {
1926 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001927 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001928 *status = CMD_ERR_AMBIGUOUS;
1929 return NULL;
1930 } else if (ret == 2) {
1931 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001932 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001933 *status = CMD_ERR_NO_MATCH;
1934 return NULL;
1935 }
1936 }
1937
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001938 /* Prepare match vector */
1939 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1940
1941 /* Make sure that cmd_vector is filtered based on current word */
1942 command = vector_slot(vline, index);
1943 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001944 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001945
1946 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001947 for (i = 0; i < vector_active(cmd_vector); i++) {
1948 const char *string = NULL;
1949 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001950
Harald Welte80d30fe2013-02-12 11:08:57 +01001951 cmd_element = vector_slot(cmd_vector, i);
1952 if (!cmd_element)
1953 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001954
Harald Welted17aa592013-02-12 11:11:34 +01001955 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1956 continue;
1957
Harald Welte80d30fe2013-02-12 11:08:57 +01001958 strvec = cmd_element->strvec;
1959
1960 /* if command is NULL, index may be equal to vector_active */
1961 if (command && index >= vector_active(strvec))
1962 vector_slot(cmd_vector, i) = NULL;
1963 else {
1964 /* Check if command is completed. */
1965 if (command == NULL
1966 && index == vector_active(strvec)) {
1967 string = "<cr>";
1968 if (!desc_unique_string(matchvec, string))
1969 vector_set(matchvec, &desc_cr);
1970 } else {
1971 unsigned int j;
1972 vector descvec = vector_slot(strvec, index);
1973 struct desc *desc;
1974
1975 for (j = 0; j < vector_active(descvec); j++) {
1976 desc = vector_slot(descvec, j);
1977 if (!desc)
1978 continue;
1979 string = cmd_entry_function_desc
1980 (command, desc->cmd);
1981 if (!string)
1982 continue;
1983 /* Uniqueness check */
1984 if (!desc_unique_string(matchvec, string))
1985 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001986 }
1987 }
1988 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001989 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001990 vector_free(cmd_vector);
1991
1992 if (vector_slot(matchvec, 0) == NULL) {
1993 vector_free(matchvec);
1994 *status = CMD_ERR_NO_MATCH;
1995 } else
1996 *status = CMD_SUCCESS;
1997
1998 return matchvec;
1999}
2000
2001vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2002{
2003 vector ret;
2004
2005 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2006 enum node_type onode;
2007 vector shifted_vline;
2008 unsigned int index;
2009
2010 onode = vty->node;
2011 vty->node = ENABLE_NODE;
2012 /* We can try it on enable node, cos' the vty is authenticated */
2013
2014 shifted_vline = vector_init(vector_count(vline));
2015 /* use memcpy? */
2016 for (index = 1; index < vector_active(vline); index++) {
2017 vector_set_index(shifted_vline, index - 1,
2018 vector_lookup(vline, index));
2019 }
2020
2021 ret = cmd_describe_command_real(shifted_vline, vty, status);
2022
2023 vector_free(shifted_vline);
2024 vty->node = onode;
2025 return ret;
2026 }
2027
2028 return cmd_describe_command_real(vline, vty, status);
2029}
2030
2031/* Check LCD of matched command. */
2032static int cmd_lcd(char **matched)
2033{
2034 int i;
2035 int j;
2036 int lcd = -1;
2037 char *s1, *s2;
2038 char c1, c2;
2039
2040 if (matched[0] == NULL || matched[1] == NULL)
2041 return 0;
2042
2043 for (i = 1; matched[i] != NULL; i++) {
2044 s1 = matched[i - 1];
2045 s2 = matched[i];
2046
2047 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2048 if (c1 != c2)
2049 break;
2050
2051 if (lcd < 0)
2052 lcd = j;
2053 else {
2054 if (lcd > j)
2055 lcd = j;
2056 }
2057 }
2058 return lcd;
2059}
2060
2061/* Command line completion support. */
2062static char **cmd_complete_command_real(vector vline, struct vty *vty,
2063 int *status)
2064{
2065 unsigned int i;
2066 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2067#define INIT_MATCHVEC_SIZE 10
2068 vector matchvec;
2069 struct cmd_element *cmd_element;
2070 unsigned int index;
2071 char **match_str;
2072 struct desc *desc;
2073 vector descvec;
2074 char *command;
2075 int lcd;
2076
2077 if (vector_active(vline) == 0) {
2078 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002079 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002080 return NULL;
2081 } else
2082 index = vector_active(vline) - 1;
2083
2084 /* First, filter by preceeding command string */
2085 for (i = 0; i < index; i++)
2086 if ((command = vector_slot(vline, i))) {
2087 enum match_type match;
2088 int ret;
2089
2090 /* First try completion match, if there is exactly match return 1 */
2091 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002092 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002093
2094 /* If there is exact match then filter ambiguous match else check
2095 ambiguousness. */
2096 if ((ret =
2097 is_cmd_ambiguous(command, cmd_vector, i,
2098 match)) == 1) {
2099 vector_free(cmd_vector);
2100 *status = CMD_ERR_AMBIGUOUS;
2101 return NULL;
2102 }
2103 /*
2104 else if (ret == 2)
2105 {
2106 vector_free (cmd_vector);
2107 *status = CMD_ERR_NO_MATCH;
2108 return NULL;
2109 }
2110 */
2111 }
2112
2113 /* Prepare match vector. */
2114 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2115
2116 /* Now we got into completion */
2117 for (i = 0; i < vector_active(cmd_vector); i++)
2118 if ((cmd_element = vector_slot(cmd_vector, i))) {
2119 const char *string;
2120 vector strvec = cmd_element->strvec;
2121
2122 /* Check field length */
2123 if (index >= vector_active(strvec))
2124 vector_slot(cmd_vector, i) = NULL;
2125 else {
2126 unsigned int j;
2127
2128 descvec = vector_slot(strvec, index);
2129 for (j = 0; j < vector_active(descvec); j++)
2130 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002131 const char *cmd = desc->cmd;
2132 char *tmp = NULL;
2133
2134 if (CMD_OPTION(desc->cmd)) {
2135 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2136 cmd = tmp;
2137 }
2138 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002139 if (cmd_unique_string (matchvec, string))
2140 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002141 if (tmp)
2142 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002143 }
2144 }
2145 }
2146
2147 /* We don't need cmd_vector any more. */
2148 vector_free(cmd_vector);
2149
2150 /* No matched command */
2151 if (vector_slot(matchvec, 0) == NULL) {
2152 vector_free(matchvec);
2153
2154 /* In case of 'command \t' pattern. Do you need '?' command at
2155 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002156 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002157 *status = CMD_ERR_NOTHING_TODO;
2158 else
2159 *status = CMD_ERR_NO_MATCH;
2160 return NULL;
2161 }
2162
2163 /* Only one matched */
2164 if (vector_slot(matchvec, 1) == NULL) {
2165 match_str = (char **)matchvec->index;
2166 vector_only_wrapper_free(matchvec);
2167 *status = CMD_COMPLETE_FULL_MATCH;
2168 return match_str;
2169 }
2170 /* Make it sure last element is NULL. */
2171 vector_set(matchvec, NULL);
2172
2173 /* Check LCD of matched strings. */
2174 if (vector_slot(vline, index) != NULL) {
2175 lcd = cmd_lcd((char **)matchvec->index);
2176
2177 if (lcd) {
2178 int len = strlen(vector_slot(vline, index));
2179
2180 if (len < lcd) {
2181 char *lcdstr;
2182
2183 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2184 "complete-lcdstr");
2185 memcpy(lcdstr, matchvec->index[0], lcd);
2186 lcdstr[lcd] = '\0';
2187
2188 /* match_str = (char **) &lcdstr; */
2189
2190 /* Free matchvec. */
2191 for (i = 0; i < vector_active(matchvec); i++) {
2192 if (vector_slot(matchvec, i))
2193 talloc_free(vector_slot(matchvec, i));
2194 }
2195 vector_free(matchvec);
2196
2197 /* Make new matchvec. */
2198 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2199 vector_set(matchvec, lcdstr);
2200 match_str = (char **)matchvec->index;
2201 vector_only_wrapper_free(matchvec);
2202
2203 *status = CMD_COMPLETE_MATCH;
2204 return match_str;
2205 }
2206 }
2207 }
2208
2209 match_str = (char **)matchvec->index;
2210 vector_only_wrapper_free(matchvec);
2211 *status = CMD_COMPLETE_LIST_MATCH;
2212 return match_str;
2213}
2214
2215char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2216{
2217 char **ret;
2218
2219 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2220 enum node_type onode;
2221 vector shifted_vline;
2222 unsigned int index;
2223
2224 onode = vty->node;
2225 vty->node = ENABLE_NODE;
2226 /* We can try it on enable node, cos' the vty is authenticated */
2227
2228 shifted_vline = vector_init(vector_count(vline));
2229 /* use memcpy? */
2230 for (index = 1; index < vector_active(vline); index++) {
2231 vector_set_index(shifted_vline, index - 1,
2232 vector_lookup(vline, index));
2233 }
2234
2235 ret = cmd_complete_command_real(shifted_vline, vty, status);
2236
2237 vector_free(shifted_vline);
2238 vty->node = onode;
2239 return ret;
2240 }
2241
2242 return cmd_complete_command_real(vline, vty, status);
2243}
2244
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002245static struct vty_parent_node *vty_parent(struct vty *vty)
2246{
2247 return llist_first_entry_or_null(&vty->parent_nodes,
2248 struct vty_parent_node,
2249 entry);
2250}
2251
2252static bool vty_pop_parent(struct vty *vty)
2253{
2254 struct vty_parent_node *parent = vty_parent(vty);
2255 if (!parent)
2256 return false;
2257 llist_del(&parent->entry);
2258 vty->node = parent->node;
2259 vty->priv = parent->priv;
2260 if (vty->indent)
2261 talloc_free(vty->indent);
2262 vty->indent = parent->indent;
2263 talloc_free(parent);
2264 return true;
2265}
2266
2267static void vty_clear_parents(struct vty *vty)
2268{
2269 while (vty_pop_parent(vty));
2270}
2271
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002272/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002273/*
2274 * This function MUST eventually converge on a node when called repeatedly,
2275 * there must not be any cycles.
2276 * All 'config' nodes shall converge on CONFIG_NODE.
2277 * All other 'enable' nodes shall converge on ENABLE_NODE.
2278 * All 'view' only nodes shall converge on VIEW_NODE.
2279 * All other nodes shall converge on themselves or it must be ensured,
2280 * that the user's rights are not extended anyhow by calling this function.
2281 *
2282 * Note that these requirements also apply to all functions that are used
2283 * as go_parent_cb.
2284 * Note also that this function relies on the is_config_child callback to
2285 * recognize non-config nodes if go_parent_cb is not set.
2286 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002287int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002288{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002289 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002290 case AUTH_NODE:
2291 case VIEW_NODE:
2292 case ENABLE_NODE:
2293 case CONFIG_NODE:
2294 vty_clear_parents(vty);
2295 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002296
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002297 case AUTH_ENABLE_NODE:
2298 vty->node = VIEW_NODE;
2299 vty_clear_parents(vty);
2300 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002301
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002302 default:
2303 if (host.app_info->go_parent_cb)
2304 host.app_info->go_parent_cb(vty);
2305 vty_pop_parent(vty);
2306 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002307 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002308
2309 return vty->node;
2310}
2311
2312/* Execute command by argument vline vector. */
2313static int
2314cmd_execute_command_real(vector vline, struct vty *vty,
2315 struct cmd_element **cmd)
2316{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002317 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002318 unsigned int index;
2319 vector cmd_vector;
2320 struct cmd_element *cmd_element;
2321 struct cmd_element *matched_element;
2322 unsigned int matched_count, incomplete_count;
2323 int argc;
2324 const char *argv[CMD_ARGC_MAX];
2325 enum match_type match = 0;
2326 int varflag;
2327 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002328 int rc;
2329 /* Used for temporary storage of cmd_deopt() allocated arguments during
2330 argv[] generation */
2331 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002332
2333 /* Make copy of command elements. */
2334 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2335
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002336 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002337 if ((command = vector_slot(vline, index))) {
2338 int ret;
2339
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002340 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002341 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002342
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002343 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002344 break;
2345
2346 ret =
2347 is_cmd_ambiguous(command, cmd_vector, index, match);
2348
2349 if (ret == 1) {
2350 vector_free(cmd_vector);
2351 return CMD_ERR_AMBIGUOUS;
2352 } else if (ret == 2) {
2353 vector_free(cmd_vector);
2354 return CMD_ERR_NO_MATCH;
2355 }
2356 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002357 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002358
2359 /* Check matched count. */
2360 matched_element = NULL;
2361 matched_count = 0;
2362 incomplete_count = 0;
2363
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002364 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002365 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002366 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002367 || index >= cmd_element->cmdsize) {
2368 matched_element = cmd_element;
2369#if 0
2370 printf("DEBUG: %s\n", cmd_element->string);
2371#endif
2372 matched_count++;
2373 } else {
2374 incomplete_count++;
2375 }
2376 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002377 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002378
2379 /* Finish of using cmd_vector. */
2380 vector_free(cmd_vector);
2381
2382 /* To execute command, matched_count must be 1. */
2383 if (matched_count == 0) {
2384 if (incomplete_count)
2385 return CMD_ERR_INCOMPLETE;
2386 else
2387 return CMD_ERR_NO_MATCH;
2388 }
2389
2390 if (matched_count > 1)
2391 return CMD_ERR_AMBIGUOUS;
2392
2393 /* Argument treatment */
2394 varflag = 0;
2395 argc = 0;
2396
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002397 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2398
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002399 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002400 if (argc == CMD_ARGC_MAX) {
2401 rc = CMD_ERR_EXEED_ARGC_MAX;
2402 goto rc_free_deopt_ctx;
2403 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002404 if (varflag) {
2405 argv[argc++] = vector_slot(vline, i);
2406 continue;
2407 }
2408
2409 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002410 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002411
2412 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002413 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002414 struct desc *desc = vector_slot(descvec, 0);
2415
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002416 if (CMD_OPTION(desc->cmd)) {
2417 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2418 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2419 } else {
2420 tmp_cmd = desc->cmd;
2421 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002422
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002423 if (CMD_VARARG(tmp_cmd))
2424 varflag = 1;
2425 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002426 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002427 else if (CMD_OPTION(desc->cmd))
2428 argv[argc++] = tmp_cmd;
2429 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002430 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002431 /* multi choice argument. look up which choice
2432 the user meant (can only be one after
2433 filtering and checking for ambigous). For instance,
2434 if user typed "th" for "(two|three)" arg, we
2435 want to pass "three" in argv[]. */
2436 for (j = 0; j < vector_active(descvec); j++) {
2437 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002438 if (!desc)
2439 continue;
2440 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2441 continue;
2442 if (CMD_OPTION(desc->cmd)) {
2443 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2444 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2445 } else {
2446 tmp_cmd = desc->cmd;
2447 }
2448
2449 if(CMD_VARIABLE(tmp_cmd)) {
2450 argv[argc++] = vector_slot(vline, i);
2451 } else {
2452 argv[argc++] = tmp_cmd;
2453 }
2454 break;
2455 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002456 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002457 }
2458
2459 /* For vtysh execution. */
2460 if (cmd)
2461 *cmd = matched_element;
2462
2463 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002464 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002465 else {
2466 /* Execute matched command. */
2467 struct vty_parent_node this_node = {
2468 .node = vty->node,
2469 .priv = vty->priv,
2470 .indent = vty->indent,
2471 };
2472 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002473 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002474
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002475 /* If we have stepped down into a child node, push a parent frame.
2476 * The causality is such: we don't expect every single node entry implementation to push
2477 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2478 * a parent node. Hence if the node changed without the parent node changing, we must
2479 * have stepped into a child node. */
2480 if (vty->node != this_node.node && parent == vty_parent(vty)
2481 && vty->node > CONFIG_NODE) {
2482 /* Push the parent node. */
2483 parent = talloc_zero(vty, struct vty_parent_node);
2484 *parent = this_node;
2485 llist_add(&parent->entry, &vty->parent_nodes);
2486 }
2487 }
2488
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002489rc_free_deopt_ctx:
2490 /* Now after we called the command func, we can free temporary strings */
2491 talloc_free(cmd_deopt_ctx);
2492 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002493}
2494
2495int
2496cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2497 int vtysh)
2498{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002499 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002500 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002501
2502 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002503
2504 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2505 vector shifted_vline;
2506 unsigned int index;
2507
2508 vty->node = ENABLE_NODE;
2509 /* We can try it on enable node, cos' the vty is authenticated */
2510
2511 shifted_vline = vector_init(vector_count(vline));
2512 /* use memcpy? */
2513 for (index = 1; index < vector_active(vline); index++) {
2514 vector_set_index(shifted_vline, index - 1,
2515 vector_lookup(vline, index));
2516 }
2517
2518 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2519
2520 vector_free(shifted_vline);
2521 vty->node = onode;
2522 return ret;
2523 }
2524
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002525 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002526}
2527
2528/* Execute command by argument readline. */
2529int
2530cmd_execute_command_strict(vector vline, struct vty *vty,
2531 struct cmd_element **cmd)
2532{
2533 unsigned int i;
2534 unsigned int index;
2535 vector cmd_vector;
2536 struct cmd_element *cmd_element;
2537 struct cmd_element *matched_element;
2538 unsigned int matched_count, incomplete_count;
2539 int argc;
2540 const char *argv[CMD_ARGC_MAX];
2541 int varflag;
2542 enum match_type match = 0;
2543 char *command;
2544
2545 /* Make copy of command element */
2546 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2547
2548 for (index = 0; index < vector_active(vline); index++)
2549 if ((command = vector_slot(vline, index))) {
2550 int ret;
2551
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002552 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002553 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002554
2555 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002556 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002557 break;
2558
2559 ret =
2560 is_cmd_ambiguous(command, cmd_vector, index, match);
2561 if (ret == 1) {
2562 vector_free(cmd_vector);
2563 return CMD_ERR_AMBIGUOUS;
2564 }
2565 if (ret == 2) {
2566 vector_free(cmd_vector);
2567 return CMD_ERR_NO_MATCH;
2568 }
2569 }
2570
2571 /* Check matched count. */
2572 matched_element = NULL;
2573 matched_count = 0;
2574 incomplete_count = 0;
2575 for (i = 0; i < vector_active(cmd_vector); i++)
2576 if (vector_slot(cmd_vector, i) != NULL) {
2577 cmd_element = vector_slot(cmd_vector, i);
2578
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002579 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002580 || index >= cmd_element->cmdsize) {
2581 matched_element = cmd_element;
2582 matched_count++;
2583 } else
2584 incomplete_count++;
2585 }
2586
2587 /* Finish of using cmd_vector. */
2588 vector_free(cmd_vector);
2589
2590 /* To execute command, matched_count must be 1. */
2591 if (matched_count == 0) {
2592 if (incomplete_count)
2593 return CMD_ERR_INCOMPLETE;
2594 else
2595 return CMD_ERR_NO_MATCH;
2596 }
2597
2598 if (matched_count > 1)
2599 return CMD_ERR_AMBIGUOUS;
2600
2601 /* Argument treatment */
2602 varflag = 0;
2603 argc = 0;
2604
2605 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002606 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002607 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002608 if (varflag) {
2609 argv[argc++] = vector_slot(vline, i);
2610 continue;
2611 }
2612
2613 vector descvec = vector_slot(matched_element->strvec, i);
2614
2615 if (vector_active(descvec) == 1) {
2616 struct desc *desc = vector_slot(descvec, 0);
2617
2618 if (CMD_VARARG(desc->cmd))
2619 varflag = 1;
2620
2621 if (varflag || CMD_VARIABLE(desc->cmd)
2622 || CMD_OPTION(desc->cmd))
2623 argv[argc++] = vector_slot(vline, i);
2624 } else {
2625 argv[argc++] = vector_slot(vline, i);
2626 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002627 }
2628
2629 /* For vtysh execution. */
2630 if (cmd)
2631 *cmd = matched_element;
2632
2633 if (matched_element->daemon)
2634 return CMD_SUCCESS_DAEMON;
2635
2636 /* Now execute matched command */
2637 return (*matched_element->func) (matched_element, vty, argc, argv);
2638}
2639
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002640static inline size_t len(const char *str)
2641{
2642 return str? strlen(str) : 0;
2643}
2644
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002645/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2646 * is longer than b, a must start with exactly b, and vice versa.
2647 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2648 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002649static int indent_cmp(const char *a, const char *b)
2650{
2651 size_t al, bl;
2652 al = len(a);
2653 bl = len(b);
2654 if (al > bl) {
2655 if (bl && strncmp(a, b, bl) != 0)
2656 return EINVAL;
2657 return 1;
2658 }
2659 /* al <= bl */
2660 if (al && strncmp(a, b, al) != 0)
2661 return EINVAL;
2662 return (al < bl)? -1 : 0;
2663}
2664
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002665/* Configration make from file. */
2666int config_from_file(struct vty *vty, FILE * fp)
2667{
2668 int ret;
2669 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002670 char *indent;
2671 int cmp;
2672 struct vty_parent_node this_node;
2673 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002674
2675 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002676 indent = NULL;
2677 vline = NULL;
2678 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002679
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002680 if (ret != CMD_SUCCESS)
2681 goto return_invalid_indent;
2682
2683 /* In case of comment or empty line */
2684 if (vline == NULL) {
2685 if (indent) {
2686 talloc_free(indent);
2687 indent = NULL;
2688 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002689 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002690 }
2691
Neels Hofmeyr43063632017-09-19 23:54:01 +02002692 /* We have a nonempty line. */
2693 if (!vty->indent) {
2694 /* We have just entered a node and expecting the first child to come up; but we
2695 * may also skip right back to a parent or ancestor level. */
2696 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002697
Neels Hofmeyr43063632017-09-19 23:54:01 +02002698 /* If there is no parent, record any indentation we encounter. */
2699 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2700
2701 if (cmp == EINVAL)
2702 goto return_invalid_indent;
2703
2704 if (cmp <= 0) {
2705 /* We have gone right back to the parent level or higher, we are skipping
2706 * this child node level entirely. Pop the parent to go back to a node
2707 * that was actually there (to reinstate vty->indent) and re-use below
2708 * go-parent while-loop to find an accurate match of indent in the node
2709 * ancestry. */
2710 vty_go_parent(vty);
2711 } else {
2712 /* The indent is deeper than the just entered parent, record the new
2713 * indentation characters. */
2714 vty->indent = talloc_strdup(vty, indent);
2715 /* This *is* the new indentation. */
2716 cmp = 0;
2717 }
2718 } else {
2719 /* There is a known indentation for this node level, validate and detect node
2720 * exits. */
2721 cmp = indent_cmp(indent, vty->indent);
2722 if (cmp == EINVAL)
2723 goto return_invalid_indent;
2724 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002725
2726 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2727 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2728 while (cmp < 0) {
2729 vty_go_parent(vty);
2730 cmp = indent_cmp(indent, vty->indent);
2731 if (cmp == EINVAL)
2732 goto return_invalid_indent;
2733 }
2734
2735 /* More indent without having entered a child node level? Either the parent node's indent
2736 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2737 * or the indentation increased even though the vty command didn't enter a child. */
2738 if (cmp > 0)
2739 goto return_invalid_indent;
2740
2741 /* Remember the current node before the command possibly changes it. */
2742 this_node = (struct vty_parent_node){
2743 .node = vty->node,
2744 .priv = vty->priv,
2745 .indent = vty->indent,
2746 };
2747
2748 parent = vty_parent(vty);
2749 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002750 cmd_free_strvec(vline);
2751
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002752 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002753 if (indent) {
2754 talloc_free(indent);
2755 indent = NULL;
2756 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002757 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002758 }
2759
2760 /* If we have stepped down into a child node, push a parent frame.
2761 * The causality is such: we don't expect every single node entry implementation to push
2762 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2763 * a parent node. Hence if the node changed without the parent node changing, we must
2764 * have stepped into a child node (and now expect a deeper indent). */
2765 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2766 /* Push the parent node. */
2767 parent = talloc_zero(vty, struct vty_parent_node);
2768 *parent = this_node;
2769 llist_add(&parent->entry, &vty->parent_nodes);
2770
2771 /* The current talloc'ed vty->indent string will now be owned by this parent
2772 * struct. Indicate that we don't know what deeper indent characters the user
2773 * will choose. */
2774 vty->indent = NULL;
2775 }
2776
2777 if (indent) {
2778 talloc_free(indent);
2779 indent = NULL;
2780 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002781 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002782 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2783 while (vty_parent(vty))
2784 vty_go_parent(vty);
2785
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002786 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002787
2788return_invalid_indent:
2789 if (vline)
2790 cmd_free_strvec(vline);
2791 if (indent) {
2792 talloc_free(indent);
2793 indent = NULL;
2794 }
2795 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002796}
2797
2798/* Configration from terminal */
2799DEFUN(config_terminal,
2800 config_terminal_cmd,
2801 "configure terminal",
2802 "Configuration from vty interface\n" "Configuration terminal\n")
2803{
2804 if (vty_config_lock(vty))
2805 vty->node = CONFIG_NODE;
2806 else {
2807 vty_out(vty, "VTY configuration is locked by other VTY%s",
2808 VTY_NEWLINE);
2809 return CMD_WARNING;
2810 }
2811 return CMD_SUCCESS;
2812}
2813
2814/* Enable command */
2815DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2816{
2817 /* If enable password is NULL, change to ENABLE_NODE */
2818 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2819 vty->type == VTY_SHELL_SERV)
2820 vty->node = ENABLE_NODE;
2821 else
2822 vty->node = AUTH_ENABLE_NODE;
2823
2824 return CMD_SUCCESS;
2825}
2826
2827/* Disable command */
2828DEFUN(disable,
2829 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2830{
2831 if (vty->node == ENABLE_NODE)
2832 vty->node = VIEW_NODE;
2833 return CMD_SUCCESS;
2834}
2835
2836/* Down vty node level. */
2837gDEFUN(config_exit,
2838 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2839{
2840 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002841 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002842 case VIEW_NODE:
2843 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002844 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002845 break;
2846 case CONFIG_NODE:
2847 vty->node = ENABLE_NODE;
2848 vty_config_unlock(vty);
2849 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002850 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002851 if (vty->node > CONFIG_NODE)
2852 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002853 break;
2854 }
2855 return CMD_SUCCESS;
2856}
2857
2858/* End of configuration. */
2859 gDEFUN(config_end,
2860 config_end_cmd, "end", "End current mode and change to enable mode.")
2861{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002862 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002863 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002864
2865 /* Repeatedly call go_parent until a top node is reached. */
2866 while (vty->node > CONFIG_NODE) {
2867 if (vty->node == last_node) {
2868 /* Ensure termination, this shouldn't happen. */
2869 break;
2870 }
2871 last_node = vty->node;
2872 vty_go_parent(vty);
2873 }
2874
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002875 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002876 if (vty->node > ENABLE_NODE)
2877 vty->node = ENABLE_NODE;
2878 vty->index = NULL;
2879 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002880 }
2881 return CMD_SUCCESS;
2882}
2883
2884/* Show version. */
2885DEFUN(show_version,
2886 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2887{
Harald Welte237f6242010-05-25 23:00:45 +02002888 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2889 host.app_info->version,
2890 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2891 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002892
2893 return CMD_SUCCESS;
2894}
2895
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002896DEFUN(show_online_help,
2897 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2898{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002899 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002900 return CMD_SUCCESS;
2901}
2902
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002903/* Help display function for all node. */
2904gDEFUN(config_help,
2905 config_help_cmd, "help", "Description of the interactive help system\n")
2906{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002907 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2908 "anytime at the command line please press '?'.%s%s"
2909 "If nothing matches, the help list will be empty and you must backup%s"
2910 " until entering a '?' shows the available options.%s"
2911 "Two styles of help are provided:%s"
2912 "1. Full help is available when you are ready to enter a%s"
2913 "command argument (e.g. 'show ?') and describes each possible%s"
2914 "argument.%s"
2915 "2. Partial help is provided when an abbreviated argument is entered%s"
2916 " and you want to know what arguments match the input%s"
2917 " (e.g. 'show me?'.)%s%s",
2918 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2919 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2920 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2921 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002922 return CMD_SUCCESS;
2923}
2924
2925/* Help display function for all node. */
2926gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2927{
2928 unsigned int i;
2929 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2930 struct cmd_element *cmd;
2931
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002932 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2933 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2934 continue;
2935 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2936 continue;
2937 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2938 }
2939
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002940 return CMD_SUCCESS;
2941}
2942
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002943static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002944{
2945 unsigned int i;
2946 int fd;
2947 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002948 char *config_file_tmp = NULL;
2949 char *config_file_sav = NULL;
2950 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002951 struct stat st;
2952
2953 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002954
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002955 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2956 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2957 * manually instead. */
2958
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002959 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002960 config_file_sav =
2961 _talloc_zero(tall_vty_cmd_ctx,
2962 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2963 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002964 if (!config_file_sav)
2965 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002966 strcpy(config_file_sav, config_file);
2967 strcat(config_file_sav, CONF_BACKUP_EXT);
2968
2969 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002970 "config_file_tmp");
2971 if (!config_file_tmp) {
2972 talloc_free(config_file_sav);
2973 return -1;
2974 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002975 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2976
2977 /* Open file to configuration write. */
2978 fd = mkstemp(config_file_tmp);
2979 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002980 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002981 talloc_free(config_file_tmp);
2982 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002983 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002984 }
2985
2986 /* Make vty for configuration file. */
2987 file_vty = vty_new();
2988 file_vty->fd = fd;
2989 file_vty->type = VTY_FILE;
2990
2991 /* Config file header print. */
2992 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002993 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002994 //vty_time_print (file_vty, 1);
2995 vty_out(file_vty, "!\n");
2996
2997 for (i = 0; i < vector_active(cmdvec); i++)
2998 if ((node = vector_slot(cmdvec, i)) && node->func) {
2999 if ((*node->func) (file_vty))
3000 vty_out(file_vty, "!\n");
3001 }
3002 vty_close(file_vty);
3003
3004 if (unlink(config_file_sav) != 0)
3005 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003006 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003007 talloc_free(config_file_sav);
3008 talloc_free(config_file_tmp);
3009 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003010 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003011 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003012
3013 /* Only link the .sav file if the original file exists */
3014 if (stat(config_file, &st) == 0) {
3015 if (link(config_file, config_file_sav) != 0) {
3016 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3017 talloc_free(config_file_sav);
3018 talloc_free(config_file_tmp);
3019 unlink(config_file_tmp);
3020 return -3;
3021 }
3022 sync();
3023 if (unlink(config_file) != 0) {
3024 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3025 talloc_free(config_file_sav);
3026 talloc_free(config_file_tmp);
3027 unlink(config_file_tmp);
3028 return -4;
3029 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003030 }
3031 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003032 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003033 talloc_free(config_file_sav);
3034 talloc_free(config_file_tmp);
3035 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003036 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003037 }
3038 unlink(config_file_tmp);
3039 sync();
3040
3041 talloc_free(config_file_sav);
3042 talloc_free(config_file_tmp);
3043
3044 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003045 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3046 return -6;
3047 }
3048
3049 return 0;
3050}
3051
3052
3053/* Write current configuration into file. */
3054DEFUN(config_write_file,
3055 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003056 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003057 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003058 "Write to configuration file\n"
3059 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003060{
3061 char *failed_file;
3062 int rc;
3063
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003064 if (host.app_info->config_is_consistent) {
3065 rc = host.app_info->config_is_consistent(vty);
3066 if (!rc) {
3067 vty_out(vty, "Configuration is not consistent%s",
3068 VTY_NEWLINE);
3069 return CMD_WARNING;
3070 }
3071 }
3072
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003073 if (argc == 1)
3074 host_config_set(argv[0]);
3075
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003076 if (host.config == NULL) {
3077 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3078 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003079 return CMD_WARNING;
3080 }
3081
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003082 rc = write_config_file(host.config, &failed_file);
3083 switch (rc) {
3084 case -1:
3085 vty_out(vty, "Can't open configuration file %s.%s",
3086 failed_file, VTY_NEWLINE);
3087 rc = CMD_WARNING;
3088 break;
3089 case -2:
3090 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3091 failed_file, VTY_NEWLINE);
3092 rc = CMD_WARNING;
3093 break;
3094 case -3:
3095 vty_out(vty, "Can't backup old configuration file %s.%s",
3096 failed_file, VTY_NEWLINE);
3097 rc = CMD_WARNING;
3098 break;
3099 case -4:
3100 vty_out(vty, "Can't unlink configuration file %s.%s",
3101 failed_file, VTY_NEWLINE);
3102 rc = CMD_WARNING;
3103 break;
3104 case -5:
3105 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3106 VTY_NEWLINE);
3107 rc = CMD_WARNING;
3108 break;
3109 case -6:
3110 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3111 failed_file, strerror(errno), errno, VTY_NEWLINE);
3112 rc = CMD_WARNING;
3113 break;
3114 default:
3115 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3116 rc = CMD_SUCCESS;
3117 break;
3118 }
3119
3120 talloc_free(failed_file);
3121 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003122}
3123
3124ALIAS(config_write_file,
3125 config_write_cmd,
3126 "write", "Write running configuration to memory, network, or terminal\n")
3127
3128 ALIAS(config_write_file,
3129 config_write_memory_cmd,
3130 "write memory",
3131 "Write running configuration to memory, network, or terminal\n"
3132 "Write configuration to the file (same as write file)\n")
3133
3134 ALIAS(config_write_file,
3135 copy_runningconfig_startupconfig_cmd,
3136 "copy running-config startup-config",
3137 "Copy configuration\n"
3138 "Copy running config to... \n"
3139 "Copy running config to startup config (same as write file)\n")
3140
3141/* Write current configuration into the terminal. */
3142 DEFUN(config_write_terminal,
3143 config_write_terminal_cmd,
3144 "write terminal",
3145 "Write running configuration to memory, network, or terminal\n"
3146 "Write to terminal\n")
3147{
3148 unsigned int i;
3149 struct cmd_node *node;
3150
3151 if (vty->type == VTY_SHELL_SERV) {
3152 for (i = 0; i < vector_active(cmdvec); i++)
3153 if ((node = vector_slot(cmdvec, i)) && node->func
3154 && node->vtysh) {
3155 if ((*node->func) (vty))
3156 vty_out(vty, "!%s", VTY_NEWLINE);
3157 }
3158 } else {
3159 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3160 VTY_NEWLINE);
3161 vty_out(vty, "!%s", VTY_NEWLINE);
3162
3163 for (i = 0; i < vector_active(cmdvec); i++)
3164 if ((node = vector_slot(cmdvec, i)) && node->func) {
3165 if ((*node->func) (vty))
3166 vty_out(vty, "!%s", VTY_NEWLINE);
3167 }
3168 vty_out(vty, "end%s", VTY_NEWLINE);
3169 }
3170 return CMD_SUCCESS;
3171}
3172
3173/* Write current configuration into the terminal. */
3174ALIAS(config_write_terminal,
3175 show_running_config_cmd,
3176 "show running-config", SHOW_STR "running configuration\n")
3177
3178/* Write startup configuration into the terminal. */
3179 DEFUN(show_startup_config,
3180 show_startup_config_cmd,
3181 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3182{
3183 char buf[BUFSIZ];
3184 FILE *confp;
3185
3186 confp = fopen(host.config, "r");
3187 if (confp == NULL) {
3188 vty_out(vty, "Can't open configuration file [%s]%s",
3189 host.config, VTY_NEWLINE);
3190 return CMD_WARNING;
3191 }
3192
3193 while (fgets(buf, BUFSIZ, confp)) {
3194 char *cp = buf;
3195
3196 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3197 cp++;
3198 *cp = '\0';
3199
3200 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3201 }
3202
3203 fclose(confp);
3204
3205 return CMD_SUCCESS;
3206}
3207
3208/* Hostname configuration */
3209DEFUN(config_hostname,
3210 hostname_cmd,
3211 "hostname WORD",
3212 "Set system's network name\n" "This system's network name\n")
3213{
3214 if (!isalpha((int)*argv[0])) {
3215 vty_out(vty, "Please specify string starting with alphabet%s",
3216 VTY_NEWLINE);
3217 return CMD_WARNING;
3218 }
3219
3220 if (host.name)
3221 talloc_free(host.name);
3222
3223 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3224 return CMD_SUCCESS;
3225}
3226
3227DEFUN(config_no_hostname,
3228 no_hostname_cmd,
3229 "no hostname [HOSTNAME]",
3230 NO_STR "Reset system's network name\n" "Host name of this router\n")
3231{
3232 if (host.name)
3233 talloc_free(host.name);
3234 host.name = NULL;
3235 return CMD_SUCCESS;
3236}
3237
3238/* VTY interface password set. */
3239DEFUN(config_password, password_cmd,
3240 "password (8|) WORD",
3241 "Assign the terminal connection password\n"
3242 "Specifies a HIDDEN password will follow\n"
3243 "dummy string \n" "The HIDDEN line password string\n")
3244{
3245 /* Argument check. */
3246 if (argc == 0) {
3247 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3248 return CMD_WARNING;
3249 }
3250
3251 if (argc == 2) {
3252 if (*argv[0] == '8') {
3253 if (host.password)
3254 talloc_free(host.password);
3255 host.password = NULL;
3256 if (host.password_encrypt)
3257 talloc_free(host.password_encrypt);
3258 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3259 return CMD_SUCCESS;
3260 } else {
3261 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3262 return CMD_WARNING;
3263 }
3264 }
3265
3266 if (!isalnum((int)*argv[0])) {
3267 vty_out(vty,
3268 "Please specify string starting with alphanumeric%s",
3269 VTY_NEWLINE);
3270 return CMD_WARNING;
3271 }
3272
3273 if (host.password)
3274 talloc_free(host.password);
3275 host.password = NULL;
3276
3277#ifdef VTY_CRYPT_PW
3278 if (host.encrypt) {
3279 if (host.password_encrypt)
3280 talloc_free(host.password_encrypt);
3281 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3282 } else
3283#endif
3284 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3285
3286 return CMD_SUCCESS;
3287}
3288
3289ALIAS(config_password, password_text_cmd,
3290 "password LINE",
3291 "Assign the terminal connection password\n"
3292 "The UNENCRYPTED (cleartext) line password\n")
3293
3294/* VTY enable password set. */
3295 DEFUN(config_enable_password, enable_password_cmd,
3296 "enable password (8|) WORD",
3297 "Modify enable password parameters\n"
3298 "Assign the privileged level password\n"
3299 "Specifies a HIDDEN password will follow\n"
3300 "dummy string \n" "The HIDDEN 'enable' password string\n")
3301{
3302 /* Argument check. */
3303 if (argc == 0) {
3304 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3305 return CMD_WARNING;
3306 }
3307
3308 /* Crypt type is specified. */
3309 if (argc == 2) {
3310 if (*argv[0] == '8') {
3311 if (host.enable)
3312 talloc_free(host.enable);
3313 host.enable = NULL;
3314
3315 if (host.enable_encrypt)
3316 talloc_free(host.enable_encrypt);
3317 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3318
3319 return CMD_SUCCESS;
3320 } else {
3321 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3322 return CMD_WARNING;
3323 }
3324 }
3325
3326 if (!isalnum((int)*argv[0])) {
3327 vty_out(vty,
3328 "Please specify string starting with alphanumeric%s",
3329 VTY_NEWLINE);
3330 return CMD_WARNING;
3331 }
3332
3333 if (host.enable)
3334 talloc_free(host.enable);
3335 host.enable = NULL;
3336
3337 /* Plain password input. */
3338#ifdef VTY_CRYPT_PW
3339 if (host.encrypt) {
3340 if (host.enable_encrypt)
3341 talloc_free(host.enable_encrypt);
3342 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3343 } else
3344#endif
3345 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3346
3347 return CMD_SUCCESS;
3348}
3349
3350ALIAS(config_enable_password,
3351 enable_password_text_cmd,
3352 "enable password LINE",
3353 "Modify enable password parameters\n"
3354 "Assign the privileged level password\n"
3355 "The UNENCRYPTED (cleartext) 'enable' password\n")
3356
3357/* VTY enable password delete. */
3358 DEFUN(no_config_enable_password, no_enable_password_cmd,
3359 "no enable password",
3360 NO_STR
3361 "Modify enable password parameters\n"
3362 "Assign the privileged level password\n")
3363{
3364 if (host.enable)
3365 talloc_free(host.enable);
3366 host.enable = NULL;
3367
3368 if (host.enable_encrypt)
3369 talloc_free(host.enable_encrypt);
3370 host.enable_encrypt = NULL;
3371
3372 return CMD_SUCCESS;
3373}
3374
3375#ifdef VTY_CRYPT_PW
3376DEFUN(service_password_encrypt,
3377 service_password_encrypt_cmd,
3378 "service password-encryption",
3379 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3380{
3381 if (host.encrypt)
3382 return CMD_SUCCESS;
3383
3384 host.encrypt = 1;
3385
3386 if (host.password) {
3387 if (host.password_encrypt)
3388 talloc_free(host.password_encrypt);
3389 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3390 }
3391 if (host.enable) {
3392 if (host.enable_encrypt)
3393 talloc_free(host.enable_encrypt);
3394 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3395 }
3396
3397 return CMD_SUCCESS;
3398}
3399
3400DEFUN(no_service_password_encrypt,
3401 no_service_password_encrypt_cmd,
3402 "no service password-encryption",
3403 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3404{
3405 if (!host.encrypt)
3406 return CMD_SUCCESS;
3407
3408 host.encrypt = 0;
3409
3410 if (host.password_encrypt)
3411 talloc_free(host.password_encrypt);
3412 host.password_encrypt = NULL;
3413
3414 if (host.enable_encrypt)
3415 talloc_free(host.enable_encrypt);
3416 host.enable_encrypt = NULL;
3417
3418 return CMD_SUCCESS;
3419}
3420#endif
3421
3422DEFUN(config_terminal_length, config_terminal_length_cmd,
3423 "terminal length <0-512>",
3424 "Set terminal line parameters\n"
3425 "Set number of lines on a screen\n"
3426 "Number of lines on screen (0 for no pausing)\n")
3427{
3428 int lines;
3429 char *endptr = NULL;
3430
3431 lines = strtol(argv[0], &endptr, 10);
3432 if (lines < 0 || lines > 512 || *endptr != '\0') {
3433 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3434 return CMD_WARNING;
3435 }
3436 vty->lines = lines;
3437
3438 return CMD_SUCCESS;
3439}
3440
3441DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3442 "terminal no length",
3443 "Set terminal line parameters\n"
3444 NO_STR "Set number of lines on a screen\n")
3445{
3446 vty->lines = -1;
3447 return CMD_SUCCESS;
3448}
3449
3450DEFUN(service_terminal_length, service_terminal_length_cmd,
3451 "service terminal-length <0-512>",
3452 "Set up miscellaneous service\n"
3453 "System wide terminal length configuration\n"
3454 "Number of lines of VTY (0 means no line control)\n")
3455{
3456 int lines;
3457 char *endptr = NULL;
3458
3459 lines = strtol(argv[0], &endptr, 10);
3460 if (lines < 0 || lines > 512 || *endptr != '\0') {
3461 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3462 return CMD_WARNING;
3463 }
3464 host.lines = lines;
3465
3466 return CMD_SUCCESS;
3467}
3468
3469DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3470 "no service terminal-length [<0-512>]",
3471 NO_STR
3472 "Set up miscellaneous service\n"
3473 "System wide terminal length configuration\n"
3474 "Number of lines of VTY (0 means no line control)\n")
3475{
3476 host.lines = -1;
3477 return CMD_SUCCESS;
3478}
3479
3480DEFUN_HIDDEN(do_echo,
3481 echo_cmd,
3482 "echo .MESSAGE",
3483 "Echo a message back to the vty\n" "The message to echo\n")
3484{
3485 char *message;
3486
3487 vty_out(vty, "%s%s",
3488 ((message =
3489 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3490 if (message)
3491 talloc_free(message);
3492 return CMD_SUCCESS;
3493}
3494
3495#if 0
3496DEFUN(config_logmsg,
3497 config_logmsg_cmd,
3498 "logmsg " LOG_LEVELS " .MESSAGE",
3499 "Send a message to enabled logging destinations\n"
3500 LOG_LEVEL_DESC "The message to send\n")
3501{
3502 int level;
3503 char *message;
3504
3505 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3506 return CMD_ERR_NO_MATCH;
3507
3508 zlog(NULL, level,
3509 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3510 if (message)
3511 talloc_free(message);
3512 return CMD_SUCCESS;
3513}
3514
3515DEFUN(show_logging,
3516 show_logging_cmd,
3517 "show logging", SHOW_STR "Show current logging configuration\n")
3518{
3519 struct zlog *zl = zlog_default;
3520
3521 vty_out(vty, "Syslog logging: ");
3522 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3523 vty_out(vty, "disabled");
3524 else
3525 vty_out(vty, "level %s, facility %s, ident %s",
3526 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3527 facility_name(zl->facility), zl->ident);
3528 vty_out(vty, "%s", VTY_NEWLINE);
3529
3530 vty_out(vty, "Stdout logging: ");
3531 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3532 vty_out(vty, "disabled");
3533 else
3534 vty_out(vty, "level %s",
3535 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3536 vty_out(vty, "%s", VTY_NEWLINE);
3537
3538 vty_out(vty, "Monitor logging: ");
3539 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3540 vty_out(vty, "disabled");
3541 else
3542 vty_out(vty, "level %s",
3543 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3544 vty_out(vty, "%s", VTY_NEWLINE);
3545
3546 vty_out(vty, "File logging: ");
3547 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3548 vty_out(vty, "disabled");
3549 else
3550 vty_out(vty, "level %s, filename %s",
3551 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3552 zl->filename);
3553 vty_out(vty, "%s", VTY_NEWLINE);
3554
3555 vty_out(vty, "Protocol name: %s%s",
3556 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3557 vty_out(vty, "Record priority: %s%s",
3558 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3559
3560 return CMD_SUCCESS;
3561}
3562
3563DEFUN(config_log_stdout,
3564 config_log_stdout_cmd,
3565 "log stdout", "Logging control\n" "Set stdout logging level\n")
3566{
3567 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3568 return CMD_SUCCESS;
3569}
3570
3571DEFUN(config_log_stdout_level,
3572 config_log_stdout_level_cmd,
3573 "log stdout " LOG_LEVELS,
3574 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3575{
3576 int level;
3577
3578 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3579 return CMD_ERR_NO_MATCH;
3580 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3581 return CMD_SUCCESS;
3582}
3583
3584DEFUN(no_config_log_stdout,
3585 no_config_log_stdout_cmd,
3586 "no log stdout [LEVEL]",
3587 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3588{
3589 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3590 return CMD_SUCCESS;
3591}
3592
3593DEFUN(config_log_monitor,
3594 config_log_monitor_cmd,
3595 "log monitor",
3596 "Logging control\n" "Set terminal line (monitor) logging level\n")
3597{
3598 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3599 return CMD_SUCCESS;
3600}
3601
3602DEFUN(config_log_monitor_level,
3603 config_log_monitor_level_cmd,
3604 "log monitor " LOG_LEVELS,
3605 "Logging control\n"
3606 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3607{
3608 int level;
3609
3610 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3611 return CMD_ERR_NO_MATCH;
3612 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3613 return CMD_SUCCESS;
3614}
3615
3616DEFUN(no_config_log_monitor,
3617 no_config_log_monitor_cmd,
3618 "no log monitor [LEVEL]",
3619 NO_STR
3620 "Logging control\n"
3621 "Disable terminal line (monitor) logging\n" "Logging level\n")
3622{
3623 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3624 return CMD_SUCCESS;
3625}
3626
3627static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3628{
3629 int ret;
3630 char *p = NULL;
3631 const char *fullpath;
3632
3633 /* Path detection. */
3634 if (!IS_DIRECTORY_SEP(*fname)) {
3635 char cwd[MAXPATHLEN + 1];
3636 cwd[MAXPATHLEN] = '\0';
3637
3638 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3639 zlog_err("config_log_file: Unable to alloc mem!");
3640 return CMD_WARNING;
3641 }
3642
3643 if ((p = _talloc_zero(tall_vcmd_ctx,
3644 strlen(cwd) + strlen(fname) + 2),
3645 "set_log_file")
3646 == NULL) {
3647 zlog_err("config_log_file: Unable to alloc mem!");
3648 return CMD_WARNING;
3649 }
3650 sprintf(p, "%s/%s", cwd, fname);
3651 fullpath = p;
3652 } else
3653 fullpath = fname;
3654
3655 ret = zlog_set_file(NULL, fullpath, loglevel);
3656
3657 if (p)
3658 talloc_free(p);
3659
3660 if (!ret) {
3661 vty_out(vty, "can't open logfile %s\n", fname);
3662 return CMD_WARNING;
3663 }
3664
3665 if (host.logfile)
3666 talloc_free(host.logfile);
3667
3668 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3669
3670 return CMD_SUCCESS;
3671}
3672
3673DEFUN(config_log_file,
3674 config_log_file_cmd,
3675 "log file FILENAME",
3676 "Logging control\n" "Logging to file\n" "Logging filename\n")
3677{
3678 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3679}
3680
3681DEFUN(config_log_file_level,
3682 config_log_file_level_cmd,
3683 "log file FILENAME " LOG_LEVELS,
3684 "Logging control\n"
3685 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3686{
3687 int level;
3688
3689 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3690 return CMD_ERR_NO_MATCH;
3691 return set_log_file(vty, argv[0], level);
3692}
3693
3694DEFUN(no_config_log_file,
3695 no_config_log_file_cmd,
3696 "no log file [FILENAME]",
3697 NO_STR
3698 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3699{
3700 zlog_reset_file(NULL);
3701
3702 if (host.logfile)
3703 talloc_free(host.logfile);
3704
3705 host.logfile = NULL;
3706
3707 return CMD_SUCCESS;
3708}
3709
3710ALIAS(no_config_log_file,
3711 no_config_log_file_level_cmd,
3712 "no log file FILENAME LEVEL",
3713 NO_STR
3714 "Logging control\n"
3715 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3716
3717 DEFUN(config_log_syslog,
3718 config_log_syslog_cmd,
3719 "log syslog", "Logging control\n" "Set syslog logging level\n")
3720{
3721 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3722 return CMD_SUCCESS;
3723}
3724
3725DEFUN(config_log_syslog_level,
3726 config_log_syslog_level_cmd,
3727 "log syslog " LOG_LEVELS,
3728 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3729{
3730 int level;
3731
3732 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3733 return CMD_ERR_NO_MATCH;
3734 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3735 return CMD_SUCCESS;
3736}
3737
3738DEFUN_DEPRECATED(config_log_syslog_facility,
3739 config_log_syslog_facility_cmd,
3740 "log syslog facility " LOG_FACILITIES,
3741 "Logging control\n"
3742 "Logging goes to syslog\n"
3743 "(Deprecated) Facility parameter for syslog messages\n"
3744 LOG_FACILITY_DESC)
3745{
3746 int facility;
3747
3748 if ((facility = facility_match(argv[0])) < 0)
3749 return CMD_ERR_NO_MATCH;
3750
3751 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3752 zlog_default->facility = facility;
3753 return CMD_SUCCESS;
3754}
3755
3756DEFUN(no_config_log_syslog,
3757 no_config_log_syslog_cmd,
3758 "no log syslog [LEVEL]",
3759 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3760{
3761 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3762 return CMD_SUCCESS;
3763}
3764
3765ALIAS(no_config_log_syslog,
3766 no_config_log_syslog_facility_cmd,
3767 "no log syslog facility " LOG_FACILITIES,
3768 NO_STR
3769 "Logging control\n"
3770 "Logging goes to syslog\n"
3771 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3772
3773 DEFUN(config_log_facility,
3774 config_log_facility_cmd,
3775 "log facility " LOG_FACILITIES,
3776 "Logging control\n"
3777 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3778{
3779 int facility;
3780
3781 if ((facility = facility_match(argv[0])) < 0)
3782 return CMD_ERR_NO_MATCH;
3783 zlog_default->facility = facility;
3784 return CMD_SUCCESS;
3785}
3786
3787DEFUN(no_config_log_facility,
3788 no_config_log_facility_cmd,
3789 "no log facility [FACILITY]",
3790 NO_STR
3791 "Logging control\n"
3792 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3793{
3794 zlog_default->facility = LOG_DAEMON;
3795 return CMD_SUCCESS;
3796}
3797
3798DEFUN_DEPRECATED(config_log_trap,
3799 config_log_trap_cmd,
3800 "log trap " LOG_LEVELS,
3801 "Logging control\n"
3802 "(Deprecated) Set logging level and default for all destinations\n"
3803 LOG_LEVEL_DESC)
3804{
3805 int new_level;
3806 int i;
3807
3808 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3809 return CMD_ERR_NO_MATCH;
3810
3811 zlog_default->default_lvl = new_level;
3812 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3813 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3814 zlog_default->maxlvl[i] = new_level;
3815 return CMD_SUCCESS;
3816}
3817
3818DEFUN_DEPRECATED(no_config_log_trap,
3819 no_config_log_trap_cmd,
3820 "no log trap [LEVEL]",
3821 NO_STR
3822 "Logging control\n"
3823 "Permit all logging information\n" "Logging level\n")
3824{
3825 zlog_default->default_lvl = LOG_DEBUG;
3826 return CMD_SUCCESS;
3827}
3828
3829DEFUN(config_log_record_priority,
3830 config_log_record_priority_cmd,
3831 "log record-priority",
3832 "Logging control\n"
3833 "Log the priority of the message within the message\n")
3834{
3835 zlog_default->record_priority = 1;
3836 return CMD_SUCCESS;
3837}
3838
3839DEFUN(no_config_log_record_priority,
3840 no_config_log_record_priority_cmd,
3841 "no log record-priority",
3842 NO_STR
3843 "Logging control\n"
3844 "Do not log the priority of the message within the message\n")
3845{
3846 zlog_default->record_priority = 0;
3847 return CMD_SUCCESS;
3848}
3849#endif
3850
3851DEFUN(banner_motd_file,
3852 banner_motd_file_cmd,
3853 "banner motd file [FILE]",
3854 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3855{
3856 if (host.motdfile)
3857 talloc_free(host.motdfile);
3858 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3859
3860 return CMD_SUCCESS;
3861}
3862
3863DEFUN(banner_motd_default,
3864 banner_motd_default_cmd,
3865 "banner motd default",
3866 "Set banner string\n" "Strings for motd\n" "Default string\n")
3867{
3868 host.motd = default_motd;
3869 return CMD_SUCCESS;
3870}
3871
3872DEFUN(no_banner_motd,
3873 no_banner_motd_cmd,
3874 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3875{
3876 host.motd = NULL;
3877 if (host.motdfile)
3878 talloc_free(host.motdfile);
3879 host.motdfile = NULL;
3880 return CMD_SUCCESS;
3881}
3882
3883/* Set config filename. Called from vty.c */
3884void host_config_set(const char *filename)
3885{
3886 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3887}
3888
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003889/*! Deprecated, now happens implicitly when calling install_node().
3890 * Users of the API may still attempt to call this function, hence
3891 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003892void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003893{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003894}
3895
3896/*! Deprecated, now happens implicitly when calling install_node().
3897 * Users of the API may still attempt to call this function, hence
3898 * leave it here as a no-op. */
3899void vty_install_default(int node)
3900{
3901}
3902
3903/*! Install common commands like 'exit' and 'list'. */
3904static void install_basic_node_commands(int node)
3905{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003906 install_lib_element(node, &config_help_cmd);
3907 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003908
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003909 install_lib_element(node, &config_write_terminal_cmd);
3910 install_lib_element(node, &config_write_file_cmd);
3911 install_lib_element(node, &config_write_memory_cmd);
3912 install_lib_element(node, &config_write_cmd);
3913 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003914
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003915 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003916
3917 if (node >= CONFIG_NODE) {
3918 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003919 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003920 }
3921}
3922
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003923/*! Return true if a node is installed by install_basic_node_commands(), so
3924 * that we can avoid repeating them for each and every node during 'show
3925 * running-config' */
3926static bool vty_command_is_common(struct cmd_element *cmd)
3927{
3928 if (cmd == &config_help_cmd
3929 || cmd == &config_list_cmd
3930 || cmd == &config_write_terminal_cmd
3931 || cmd == &config_write_file_cmd
3932 || cmd == &config_write_memory_cmd
3933 || cmd == &config_write_cmd
3934 || cmd == &show_running_config_cmd
3935 || cmd == &config_exit_cmd
3936 || cmd == &config_end_cmd)
3937 return true;
3938 return false;
3939}
3940
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003941/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003942 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003943 * \param[in] vty the vty of the code
3944 * \param[in] filename where to store the file
3945 * \return 0 in case of success.
3946 *
3947 * If the filename already exists create a filename.sav
3948 * version with the current code.
3949 *
3950 */
3951int osmo_vty_write_config_file(const char *filename)
3952{
3953 char *failed_file;
3954 int rc;
3955
3956 rc = write_config_file(filename, &failed_file);
3957 talloc_free(failed_file);
3958 return rc;
3959}
3960
3961/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003962 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003963 * \return 0 in case of success.
3964 *
3965 * If the filename already exists create a filename.sav
3966 * version with the current code.
3967 *
3968 */
3969int osmo_vty_save_config_file(void)
3970{
3971 char *failed_file;
3972 int rc;
3973
3974 if (host.config == NULL)
3975 return -7;
3976
3977 rc = write_config_file(host.config, &failed_file);
3978 talloc_free(failed_file);
3979 return rc;
3980}
3981
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003982/* Initialize command interface. Install basic nodes and commands. */
3983void cmd_init(int terminal)
3984{
3985 /* Allocate initial top vector of commands. */
3986 cmdvec = vector_init(VECTOR_MIN_SIZE);
3987
3988 /* Default host value settings. */
3989 host.name = NULL;
3990 host.password = NULL;
3991 host.enable = NULL;
3992 host.logfile = NULL;
3993 host.config = NULL;
3994 host.lines = -1;
3995 host.motd = default_motd;
3996 host.motdfile = NULL;
3997
3998 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003999 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004000 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004001 install_node_bare(&auth_node, NULL);
4002 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004003 install_node(&config_node, config_write_host);
4004
4005 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004006 install_lib_element(VIEW_NODE, &show_version_cmd);
4007 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004008 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004009 install_lib_element(VIEW_NODE, &config_list_cmd);
4010 install_lib_element(VIEW_NODE, &config_exit_cmd);
4011 install_lib_element(VIEW_NODE, &config_help_cmd);
4012 install_lib_element(VIEW_NODE, &config_enable_cmd);
4013 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4014 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4015 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004016 }
4017
4018 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004019 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4020 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4021 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004022 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004023 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4024 install_lib_element(ENABLE_NODE, &show_version_cmd);
4025 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004026
4027 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004028 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4029 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4030 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004031 }
4032
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004033 install_lib_element(CONFIG_NODE, &hostname_cmd);
4034 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004035
4036 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004037 install_lib_element(CONFIG_NODE, &password_cmd);
4038 install_lib_element(CONFIG_NODE, &password_text_cmd);
4039 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4040 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4041 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004042
4043#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004044 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4045 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004046#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004047 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4048 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4049 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4050 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4051 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004052
4053 }
4054 srand(time(NULL));
4055}
Harald Welte7acb30c2011-08-17 17:13:48 +02004056
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004057/*! @} */