blob: b472f6d03a7d8ca7607ab01dafa99649f3a123cf [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 Yanitskiy7f6c87d2020-09-28 13:03:49 +0700630 { 0, NULL }
631};
632
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100633/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200634 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100635 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200636static 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 +0100637{
638 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700639 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100640
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200641 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700642
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700643 /* Print global attributes and their description */
644 if (cmd->attr != 0x00) { /* ... if at least one flag is set */
645 print_func(data, " <attributes scope='global'>%s", newline);
646
647 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
648 char *xml_att_desc;
649
650 if (~cmd->attr & cmd_attr_desc[i].value)
651 continue;
652
653 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
654 print_func(data, " <attribute doc='%s' />%s",
655 xml_att_desc, newline);
656 talloc_free(xml_att_desc);
657 }
658
659 print_func(data, " </attributes>%s", newline);
660 }
661
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700662 /* Print application specific attributes and their description */
663 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
664 print_func(data, " <attributes scope='application'>%s", newline);
665
666 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
667 char *xml_att_desc;
668 char flag;
669
670 /* Skip attribute if *not* set */
671 if (~cmd->usrattr & (1 << i))
672 continue;
673
674 xml_att_desc = xml_escape(host.app_info->usr_attr_desc[i]);
675 print_func(data, " <attribute doc='%s'", xml_att_desc);
676 talloc_free(xml_att_desc);
677
678 if ((flag = host.app_info->usr_attr_letters[i]) != '\0')
679 print_func(data, " flag='%c'", flag);
680 print_func(data, " />%s", newline);
681 }
682
683 print_func(data, " </attributes>%s", newline);
684 }
685
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200686 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100687
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700688 for (i = 0; i < vector_count(cmd->strvec); ++i) {
689 vector descvec = vector_slot(cmd->strvec, i);
690 int j;
691 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100692 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700693 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100694 if (desc == NULL)
695 continue;
696
697 xml_param = xml_escape(desc->cmd);
698 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200699 print_func(data, " <param name='%s' doc='%s' />%s",
700 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100701 talloc_free(xml_param);
702 talloc_free(xml_doc);
703 }
704 }
705
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200706 print_func(data, " </params>%s", newline);
707 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100708
709 talloc_free(xml_string);
710 return 0;
711}
712
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200713static bool vty_command_is_common(struct cmd_element *cmd);
714
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100715/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200716 * 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 +0100717 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200718static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100719{
720 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200721 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100722
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200723 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100724
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200725 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200726 print_func(data, " <node id='_common_cmds_'>%s", newline);
727 print_func(data, " <name>Common Commands</name>%s", newline);
728 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
729 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200730 for (i = 0; i < vector_active(cmdvec); ++i) {
731 struct cmd_node *cnode;
732 cnode = vector_slot(cmdvec, i);
733 if (!cnode)
734 continue;
735 if (cnode->node != CONFIG_NODE)
736 continue;
737
738 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
739 struct cmd_element *elem;
740 elem = vector_slot(cnode->cmd_vector, j);
741 if (!vty_command_is_common(elem))
742 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200743 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200744 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200745 }
746 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200747 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200748
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100749 for (i = 0; i < vector_active(cmdvec); ++i) {
750 struct cmd_node *cnode;
751 cnode = vector_slot(cmdvec, i);
752 if (!cnode)
753 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200754 if (vector_active(cnode->cmd_vector) < 1)
755 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100756
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200757 /* De-dup node IDs: how many times has this same name been used before? Count the first
758 * occurence as _1 and omit that first suffix, so that the first occurence is called
759 * 'name', the second becomes 'name_2', then 'name_3', ... */
760 same_name_count = 1;
761 for (j = 0; j < i; ++j) {
762 struct cmd_node *cnode2;
763 cnode2 = vector_slot(cmdvec, j);
764 if (!cnode2)
765 continue;
766 if (strcmp(cnode->name, cnode2->name) == 0)
767 same_name_count ++;
768 }
769
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200770 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200771 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200772 print_func(data, "_%d", same_name_count);
773 print_func(data, "'>%s", newline);
774 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100775
776 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
777 struct cmd_element *elem;
778 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200779 if (vty_command_is_common(elem))
780 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200781 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200782 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100783 }
784
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200785 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100786 }
787
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200788 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100789
790 return 0;
791}
792
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200793static int print_func_vty(void *data, const char *format, ...)
794{
795 struct vty *vty = data;
796 va_list args;
797 int rc;
798 va_start(args, format);
799 rc = vty_out_va(vty, format, args);
800 va_end(args);
801 return rc;
802}
803
804static int vty_dump_xml_ref_to_vty(struct vty *vty)
805{
806 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
807}
808
809static int print_func_stream(void *data, const char *format, ...)
810{
811 va_list args;
812 int rc;
813 va_start(args, format);
814 rc = vfprintf((FILE*)data, format, args);
815 va_end(args);
816 return rc;
817}
818
819/*! Print the XML reference of all VTY nodes to the given stream.
820 */
821int vty_dump_xml_ref(FILE *stream)
822{
823 return vty_dump_nodes(print_func_stream, stream, "\n");
824}
825
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200826/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100827static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
828{
829 int i;
830
831 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
832 struct cmd_element *elem;
833 elem = vector_slot(cnode->cmd_vector, i);
834 if (!elem->string)
835 continue;
836 if (!strcmp(elem->string, cmdstring))
837 return 1;
838 }
839 return 0;
840}
841
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200842/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200843 * \param[in] ntype Node Type
844 * \param[cmd] element to be installed
845 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000846void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200847{
848 struct cmd_node *cnode;
849
850 cnode = vector_slot(cmdvec, ntype);
851
Harald Weltea99d45a2015-11-12 13:48:23 +0100852 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100853 /* ensure no _identical_ command has been registered at this
854 * node so far */
855 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200856
857 vector_set(cnode->cmd_vector, cmd);
858
859 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
860 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
861}
862
863/* Install a command into VIEW and ENABLE node */
864void install_element_ve(struct cmd_element *cmd)
865{
866 install_element(VIEW_NODE, cmd);
867 install_element(ENABLE_NODE, cmd);
868}
869
870#ifdef VTY_CRYPT_PW
871static unsigned char itoa64[] =
872 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
873
874static void to64(char *s, long v, int n)
875{
876 while (--n >= 0) {
877 *s++ = itoa64[v & 0x3f];
878 v >>= 6;
879 }
880}
881
882static char *zencrypt(const char *passwd)
883{
884 char salt[6];
885 struct timeval tv;
886 char *crypt(const char *, const char *);
887
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200888 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200889
890 to64(&salt[0], random(), 3);
891 to64(&salt[3], tv.tv_usec, 3);
892 salt[5] = '\0';
893
894 return crypt(passwd, salt);
895}
896#endif
897
898/* This function write configuration of this host. */
899static int config_write_host(struct vty *vty)
900{
901 if (host.name)
902 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
903
904 if (host.encrypt) {
905 if (host.password_encrypt)
906 vty_out(vty, "password 8 %s%s", host.password_encrypt,
907 VTY_NEWLINE);
908 if (host.enable_encrypt)
909 vty_out(vty, "enable password 8 %s%s",
910 host.enable_encrypt, VTY_NEWLINE);
911 } else {
912 if (host.password)
913 vty_out(vty, "password %s%s", host.password,
914 VTY_NEWLINE);
915 if (host.enable)
916 vty_out(vty, "enable password %s%s", host.enable,
917 VTY_NEWLINE);
918 }
919
920 if (host.advanced)
921 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
922
923 if (host.encrypt)
924 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
925
926 if (host.lines >= 0)
927 vty_out(vty, "service terminal-length %d%s", host.lines,
928 VTY_NEWLINE);
929
930 if (host.motdfile)
931 vty_out(vty, "banner motd file %s%s", host.motdfile,
932 VTY_NEWLINE);
933 else if (!host.motd)
934 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
935
936 return 1;
937}
938
939/* Utility function for getting command vector. */
940static vector cmd_node_vector(vector v, enum node_type ntype)
941{
942 struct cmd_node *cnode = vector_slot(v, ntype);
943 return cnode->cmd_vector;
944}
945
946/* Completion match types. */
947enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200948 NO_MATCH = 0,
949 ANY_MATCH,
950 EXTEND_MATCH,
951 IPV4_PREFIX_MATCH,
952 IPV4_MATCH,
953 IPV6_PREFIX_MATCH,
954 IPV6_MATCH,
955 RANGE_MATCH,
956 VARARG_MATCH,
957 PARTLY_MATCH,
958 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200959};
960
961static enum match_type cmd_ipv4_match(const char *str)
962{
963 const char *sp;
964 int dots = 0, nums = 0;
965 char buf[4];
966
967 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200968 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200969
970 for (;;) {
971 memset(buf, 0, sizeof(buf));
972 sp = str;
973 while (*str != '\0') {
974 if (*str == '.') {
975 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200976 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200977
978 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200979 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200980
981 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200982 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200983
984 dots++;
985 break;
986 }
987 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200988 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200989
990 str++;
991 }
992
993 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200994 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200995
996 strncpy(buf, sp, str - sp);
997 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200998 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200999
1000 nums++;
1001
1002 if (*str == '\0')
1003 break;
1004
1005 str++;
1006 }
1007
1008 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001009 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001010
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001011 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001012}
1013
1014static enum match_type cmd_ipv4_prefix_match(const char *str)
1015{
1016 const char *sp;
1017 int dots = 0;
1018 char buf[4];
1019
1020 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001021 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022
1023 for (;;) {
1024 memset(buf, 0, sizeof(buf));
1025 sp = str;
1026 while (*str != '\0' && *str != '/') {
1027 if (*str == '.') {
1028 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001029 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001030
1031 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001032 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001033
1034 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001035 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001036
1037 dots++;
1038 break;
1039 }
1040
1041 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001042 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001043
1044 str++;
1045 }
1046
1047 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001048 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001049
1050 strncpy(buf, sp, str - sp);
1051 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001052 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001053
1054 if (dots == 3) {
1055 if (*str == '/') {
1056 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001057 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001058
1059 str++;
1060 break;
1061 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001062 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001063 }
1064
1065 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001066 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001067
1068 str++;
1069 }
1070
1071 sp = str;
1072 while (*str != '\0') {
1073 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001074 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001075
1076 str++;
1077 }
1078
1079 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001080 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001081
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001082 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001083}
1084
1085#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1086#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1087#define STATE_START 1
1088#define STATE_COLON 2
1089#define STATE_DOUBLE 3
1090#define STATE_ADDR 4
1091#define STATE_DOT 5
1092#define STATE_SLASH 6
1093#define STATE_MASK 7
1094
1095#ifdef HAVE_IPV6
1096
1097static enum match_type cmd_ipv6_match(const char *str)
1098{
1099 int state = STATE_START;
1100 int colons = 0, nums = 0, double_colon = 0;
1101 const char *sp = NULL;
1102 struct sockaddr_in6 sin6_dummy;
1103 int ret;
1104
1105 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001106 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001107
1108 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001109 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001110
1111 /* use inet_pton that has a better support,
1112 * for example inet_pton can support the automatic addresses:
1113 * ::1.2.3.4
1114 */
1115 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1116
1117 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001118 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001119
1120 while (*str != '\0') {
1121 switch (state) {
1122 case STATE_START:
1123 if (*str == ':') {
1124 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126 colons--;
1127 state = STATE_COLON;
1128 } else {
1129 sp = str;
1130 state = STATE_ADDR;
1131 }
1132
1133 continue;
1134 case STATE_COLON:
1135 colons++;
1136 if (*(str + 1) == ':')
1137 state = STATE_DOUBLE;
1138 else {
1139 sp = str + 1;
1140 state = STATE_ADDR;
1141 }
1142 break;
1143 case STATE_DOUBLE:
1144 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001145 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001146
1147 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001148 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001149 else {
1150 if (*(str + 1) != '\0')
1151 colons++;
1152 sp = str + 1;
1153 state = STATE_ADDR;
1154 }
1155
1156 double_colon++;
1157 nums++;
1158 break;
1159 case STATE_ADDR:
1160 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1161 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001162 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001163
1164 nums++;
1165 state = STATE_COLON;
1166 }
1167 if (*(str + 1) == '.')
1168 state = STATE_DOT;
1169 break;
1170 case STATE_DOT:
1171 state = STATE_ADDR;
1172 break;
1173 default:
1174 break;
1175 }
1176
1177 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001178 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001179
1180 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001181 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001182
1183 str++;
1184 }
1185
1186#if 0
1187 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001188 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001189#endif /* 0 */
1190
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001191 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001192}
1193
1194static enum match_type cmd_ipv6_prefix_match(const char *str)
1195{
1196 int state = STATE_START;
1197 int colons = 0, nums = 0, double_colon = 0;
1198 int mask;
1199 const char *sp = NULL;
1200 char *endptr = NULL;
1201
1202 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001203 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001204
1205 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001206 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207
1208 while (*str != '\0' && state != STATE_MASK) {
1209 switch (state) {
1210 case STATE_START:
1211 if (*str == ':') {
1212 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001213 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001214 colons--;
1215 state = STATE_COLON;
1216 } else {
1217 sp = str;
1218 state = STATE_ADDR;
1219 }
1220
1221 continue;
1222 case STATE_COLON:
1223 colons++;
1224 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226 else if (*(str + 1) == ':')
1227 state = STATE_DOUBLE;
1228 else {
1229 sp = str + 1;
1230 state = STATE_ADDR;
1231 }
1232 break;
1233 case STATE_DOUBLE:
1234 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001235 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001236
1237 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001238 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001239 else {
1240 if (*(str + 1) != '\0' && *(str + 1) != '/')
1241 colons++;
1242 sp = str + 1;
1243
1244 if (*(str + 1) == '/')
1245 state = STATE_SLASH;
1246 else
1247 state = STATE_ADDR;
1248 }
1249
1250 double_colon++;
1251 nums += 1;
1252 break;
1253 case STATE_ADDR:
1254 if (*(str + 1) == ':' || *(str + 1) == '.'
1255 || *(str + 1) == '\0' || *(str + 1) == '/') {
1256 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001257 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001258
1259 for (; sp <= str; sp++)
1260 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001261 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001262
1263 nums++;
1264
1265 if (*(str + 1) == ':')
1266 state = STATE_COLON;
1267 else if (*(str + 1) == '.')
1268 state = STATE_DOT;
1269 else if (*(str + 1) == '/')
1270 state = STATE_SLASH;
1271 }
1272 break;
1273 case STATE_DOT:
1274 state = STATE_ADDR;
1275 break;
1276 case STATE_SLASH:
1277 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001278 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001279
1280 state = STATE_MASK;
1281 break;
1282 default:
1283 break;
1284 }
1285
1286 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001287 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001288
1289 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001290 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001291
1292 str++;
1293 }
1294
1295 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001296 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001297
1298 mask = strtol(str, &endptr, 10);
1299 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001300 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001301
1302 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001303 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001304
1305/* I don't know why mask < 13 makes command match partly.
1306 Forgive me to make this comments. I Want to set static default route
1307 because of lack of function to originate default in ospf6d; sorry
1308 yasu
1309 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001310 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001311*/
1312
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001313 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001314}
1315
1316#endif /* HAVE_IPV6 */
1317
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001318
1319#if ULONG_MAX == 18446744073709551615UL
1320#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1321#elif ULONG_MAX == 4294967295UL
1322#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1323#else
1324#error "ULONG_MAX not defined!"
1325#endif
1326
1327#if LONG_MAX == 9223372036854775807L
1328#define DECIMAL_STRLEN_MAX_SIGNED 19
1329#elif LONG_MAX == 2147483647L
1330#define DECIMAL_STRLEN_MAX_SIGNED 10
1331#else
1332#error "LONG_MAX not defined!"
1333#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001334
1335static int cmd_range_match(const char *range, const char *str)
1336{
1337 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001338 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001339 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001340
1341 if (str == NULL)
1342 return 1;
1343
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001344 if (range[1] == '-') {
1345 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001346
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001347 val = strtol(str, &endptr, 10);
1348 if (*endptr != '\0')
1349 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001350
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001351 range += 2;
1352 p = strchr(range, '-');
1353 if (p == NULL)
1354 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001355 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001356 return 0;
1357 strncpy(buf, range, p - range);
1358 buf[p - range] = '\0';
1359 min = -strtol(buf, &endptr, 10);
1360 if (*endptr != '\0')
1361 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001362
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001363 range = p + 1;
1364 p = strchr(range, '>');
1365 if (p == NULL)
1366 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001367 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001368 return 0;
1369 strncpy(buf, range, p - range);
1370 buf[p - range] = '\0';
1371 max = strtol(buf, &endptr, 10);
1372 if (*endptr != '\0')
1373 return 0;
1374
1375 if (val < min || val > max)
1376 return 0;
1377 } else {
1378 unsigned long min, max, val;
1379
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001380 if (str[0] == '-')
1381 return 0;
1382
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001383 val = strtoul(str, &endptr, 10);
1384 if (*endptr != '\0')
1385 return 0;
1386
1387 range++;
1388 p = strchr(range, '-');
1389 if (p == NULL)
1390 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001391 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001392 return 0;
1393 strncpy(buf, range, p - range);
1394 buf[p - range] = '\0';
1395 min = strtoul(buf, &endptr, 10);
1396 if (*endptr != '\0')
1397 return 0;
1398
1399 range = p + 1;
1400 p = strchr(range, '>');
1401 if (p == NULL)
1402 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001403 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001404 return 0;
1405 strncpy(buf, range, p - range);
1406 buf[p - range] = '\0';
1407 max = strtoul(buf, &endptr, 10);
1408 if (*endptr != '\0')
1409 return 0;
1410
1411 if (val < min || val > max)
1412 return 0;
1413 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001414
1415 return 1;
1416}
1417
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001418/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001419static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001420{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001421 /* we've got "[blah]". We want to strip off the []s and redo the
1422 * match check for "blah"
1423 */
1424 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001425
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001426 if (len < 3)
1427 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001429 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001430}
1431
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001432static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001433cmd_match(const char *str, const char *command,
1434 enum match_type min, bool recur)
1435{
1436
1437 if (recur && CMD_OPTION(str))
1438 {
1439 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001440 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001441
1442 /* this would be a bug in a command, however handle it gracefully
1443 * as it we only discover it if a user tries to run it
1444 */
1445 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001446 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001447
1448 ret = cmd_match(tmp, command, min, false);
1449
1450 talloc_free(tmp);
1451
1452 return ret;
1453 }
1454 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001455 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001456 else if (CMD_RANGE(str))
1457 {
1458 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001459 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001460 }
1461#ifdef HAVE_IPV6
1462 else if (CMD_IPV6(str))
1463 {
1464 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001465 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001466 }
1467 else if (CMD_IPV6_PREFIX(str))
1468 {
1469 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001470 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001471 }
1472#endif /* HAVE_IPV6 */
1473 else if (CMD_IPV4(str))
1474 {
1475 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001476 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001477 }
1478 else if (CMD_IPV4_PREFIX(str))
1479 {
1480 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001481 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001482 }
1483 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001484 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001485 else if (strncmp(command, str, strlen(command)) == 0)
1486 {
1487 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001488 return EXACT_MATCH;
1489 else if (PARTLY_MATCH >= min)
1490 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001491 }
1492
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001493 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001494}
1495
1496/* Filter vector at the specified index and by the given command string, to
1497 * the desired matching level (thus allowing part matches), and return match
1498 * type flag.
1499 */
1500static enum match_type
1501cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001502{
1503 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001504 struct cmd_element *cmd_element;
1505 enum match_type match_type;
1506 vector descvec;
1507 struct desc *desc;
1508
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001509 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001510
1511 /* If command and cmd_element string does not match set NULL to vector */
1512 for (i = 0; i < vector_active(v); i++)
1513 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001514 if (index >= vector_active(cmd_element->strvec))
1515 vector_slot(v, i) = NULL;
1516 else {
1517 unsigned int j;
1518 int matched = 0;
1519
1520 descvec =
1521 vector_slot(cmd_element->strvec, index);
1522
1523 for (j = 0; j < vector_active(descvec); j++)
1524 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001525 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001526
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001527 ret = cmd_match (desc->cmd, command, level, true);
1528
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001529 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001530 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001531
1532 if (match_type < ret)
1533 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001534 }
1535 if (!matched)
1536 vector_slot(v, i) = NULL;
1537 }
1538 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001539
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001540 if (match_type == NO_MATCH)
1541 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001542
1543 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1544 * go again and filter out commands whose argument (at this index) is
1545 * 'weaker'. E.g., if we have 2 commands:
1546 *
1547 * foo bar <1-255>
1548 * foo bar BLAH
1549 *
1550 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001551 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001552 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1553 *
1554 * If we don't do a 2nd pass and filter it out, the higher-layers will
1555 * consider this to be ambiguous.
1556 */
1557 for (i = 0; i < vector_active(v); i++)
1558 if ((cmd_element = vector_slot(v, i)) != NULL) {
1559 if (index >= vector_active(cmd_element->strvec))
1560 vector_slot(v, i) = NULL;
1561 else {
1562 unsigned int j;
1563 int matched = 0;
1564
1565 descvec =
1566 vector_slot(cmd_element->strvec, index);
1567
1568 for (j = 0; j < vector_active(descvec); j++)
1569 if ((desc = vector_slot(descvec, j))) {
1570 enum match_type ret;
1571
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001572 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001573
1574 if (ret >= match_type)
1575 matched++;
1576 }
1577 if (!matched)
1578 vector_slot(v, i) = NULL;
1579 }
1580 }
1581
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001582 return match_type;
1583}
1584
1585/* Check ambiguous match */
1586static int
1587is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1588{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001589 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001590 unsigned int i;
1591 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001592 struct cmd_element *cmd_element;
1593 const char *matched = NULL;
1594 vector descvec;
1595 struct desc *desc;
1596
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001597 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1598 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1599 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1600 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1601 * that case, the string must remain allocated until this function exits or another match comes
1602 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1603 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1604 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1605 void *cmd_deopt_ctx = NULL;
1606
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001607 for (i = 0; i < vector_active(v); i++) {
1608 cmd_element = vector_slot(v, i);
1609 if (!cmd_element)
1610 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001611
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001612 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001613
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001614 descvec = vector_slot(cmd_element->strvec, index);
1615
1616 for (j = 0; j < vector_active(descvec); j++) {
1617 desc = vector_slot(descvec, j);
1618 if (!desc)
1619 continue;
1620
1621 enum match_type mtype;
1622 const char *str = desc->cmd;
1623
1624 if (CMD_OPTION(str)) {
1625 if (!cmd_deopt_ctx)
1626 cmd_deopt_ctx =
1627 talloc_named_const(tall_vty_cmd_ctx, 0,
1628 __func__);
1629 str = cmd_deopt(cmd_deopt_ctx, str);
1630 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001631 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001632 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001633
1634 switch (type) {
1635 case EXACT_MATCH:
1636 if (!(CMD_VARIABLE (str))
1637 && strcmp(command, str) == 0)
1638 match++;
1639 break;
1640 case PARTLY_MATCH:
1641 if (!(CMD_VARIABLE (str))
1642 && strncmp(command, str, strlen (command)) == 0)
1643 {
1644 if (matched
1645 && strcmp(matched,
1646 str) != 0) {
1647 ret = 1; /* There is ambiguous match. */
1648 goto free_and_return;
1649 } else
1650 matched = str;
1651 match++;
1652 }
1653 break;
1654 case RANGE_MATCH:
1655 if (cmd_range_match
1656 (str, command)) {
1657 if (matched
1658 && strcmp(matched,
1659 str) != 0) {
1660 ret = 1;
1661 goto free_and_return;
1662 } else
1663 matched = str;
1664 match++;
1665 }
1666 break;
1667#ifdef HAVE_IPV6
1668 case IPV6_MATCH:
1669 if (CMD_IPV6(str))
1670 match++;
1671 break;
1672 case IPV6_PREFIX_MATCH:
1673 if ((mtype =
1674 cmd_ipv6_prefix_match
1675 (command)) != NO_MATCH) {
1676 if (mtype == PARTLY_MATCH) {
1677 ret = 2; /* There is incomplete match. */
1678 goto free_and_return;
1679 }
1680
1681 match++;
1682 }
1683 break;
1684#endif /* HAVE_IPV6 */
1685 case IPV4_MATCH:
1686 if (CMD_IPV4(str))
1687 match++;
1688 break;
1689 case IPV4_PREFIX_MATCH:
1690 if ((mtype =
1691 cmd_ipv4_prefix_match
1692 (command)) != NO_MATCH) {
1693 if (mtype == PARTLY_MATCH) {
1694 ret = 2; /* There is incomplete match. */
1695 goto free_and_return;
1696 }
1697
1698 match++;
1699 }
1700 break;
1701 case EXTEND_MATCH:
1702 if (CMD_VARIABLE (str))
1703 match++;
1704 break;
1705 case NO_MATCH:
1706 default:
1707 break;
1708 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001709 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001710 if (!match)
1711 vector_slot(v, i) = NULL;
1712 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001713
1714free_and_return:
1715 if (cmd_deopt_ctx)
1716 talloc_free(cmd_deopt_ctx);
1717 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001718}
1719
1720/* If src matches dst return dst string, otherwise return NULL */
1721static const char *cmd_entry_function(const char *src, const char *dst)
1722{
1723 /* Skip variable arguments. */
1724 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1725 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1726 return NULL;
1727
1728 /* In case of 'command \t', given src is NULL string. */
1729 if (src == NULL)
1730 return dst;
1731
1732 /* Matched with input string. */
1733 if (strncmp(src, dst, strlen(src)) == 0)
1734 return dst;
1735
1736 return NULL;
1737}
1738
1739/* If src matches dst return dst string, otherwise return NULL */
1740/* This version will return the dst string always if it is
1741 CMD_VARIABLE for '?' key processing */
1742static const char *cmd_entry_function_desc(const char *src, const char *dst)
1743{
1744 if (CMD_VARARG(dst))
1745 return dst;
1746
1747 if (CMD_RANGE(dst)) {
1748 if (cmd_range_match(dst, src))
1749 return dst;
1750 else
1751 return NULL;
1752 }
1753#ifdef HAVE_IPV6
1754 if (CMD_IPV6(dst)) {
1755 if (cmd_ipv6_match(src))
1756 return dst;
1757 else
1758 return NULL;
1759 }
1760
1761 if (CMD_IPV6_PREFIX(dst)) {
1762 if (cmd_ipv6_prefix_match(src))
1763 return dst;
1764 else
1765 return NULL;
1766 }
1767#endif /* HAVE_IPV6 */
1768
1769 if (CMD_IPV4(dst)) {
1770 if (cmd_ipv4_match(src))
1771 return dst;
1772 else
1773 return NULL;
1774 }
1775
1776 if (CMD_IPV4_PREFIX(dst)) {
1777 if (cmd_ipv4_prefix_match(src))
1778 return dst;
1779 else
1780 return NULL;
1781 }
1782
1783 /* Optional or variable commands always match on '?' */
1784 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1785 return dst;
1786
1787 /* In case of 'command \t', given src is NULL string. */
1788 if (src == NULL)
1789 return dst;
1790
1791 if (strncmp(src, dst, strlen(src)) == 0)
1792 return dst;
1793 else
1794 return NULL;
1795}
1796
1797/* Check same string element existence. If it isn't there return
1798 1. */
1799static int cmd_unique_string(vector v, const char *str)
1800{
1801 unsigned int i;
1802 char *match;
1803
1804 for (i = 0; i < vector_active(v); i++)
1805 if ((match = vector_slot(v, i)) != NULL)
1806 if (strcmp(match, str) == 0)
1807 return 0;
1808 return 1;
1809}
1810
1811/* Compare string to description vector. If there is same string
1812 return 1 else return 0. */
1813static int desc_unique_string(vector v, const char *str)
1814{
1815 unsigned int i;
1816 struct desc *desc;
1817
1818 for (i = 0; i < vector_active(v); i++)
1819 if ((desc = vector_slot(v, i)) != NULL)
1820 if (strcmp(desc->cmd, str) == 0)
1821 return 1;
1822 return 0;
1823}
1824
1825static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1826{
1827 if (first_word != NULL &&
1828 node != AUTH_NODE &&
1829 node != VIEW_NODE &&
1830 node != AUTH_ENABLE_NODE &&
1831 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1832 return 1;
1833 return 0;
1834}
1835
1836/* '?' describe command support. */
1837static vector
1838cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1839{
1840 unsigned int i;
1841 vector cmd_vector;
1842#define INIT_MATCHVEC_SIZE 10
1843 vector matchvec;
1844 struct cmd_element *cmd_element;
1845 unsigned int index;
1846 int ret;
1847 enum match_type match;
1848 char *command;
1849 static struct desc desc_cr = { "<cr>", "" };
1850
1851 /* Set index. */
1852 if (vector_active(vline) == 0) {
1853 *status = CMD_ERR_NO_MATCH;
1854 return NULL;
1855 } else
1856 index = vector_active(vline) - 1;
1857
1858 /* Make copy vector of current node's command vector. */
1859 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1860
1861 /* Prepare match vector */
1862 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1863
1864 /* Filter commands. */
1865 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001866 for (i = 0; i < index; i++) {
1867 command = vector_slot(vline, i);
1868 if (!command)
1869 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001870
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001871 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001872
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001873 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001874 struct cmd_element *cmd_element;
1875 vector descvec;
1876 unsigned int j, k;
1877
1878 for (j = 0; j < vector_active(cmd_vector); j++)
1879 if ((cmd_element =
1880 vector_slot(cmd_vector, j)) != NULL
1881 &&
1882 (vector_active(cmd_element->strvec))) {
1883 descvec =
1884 vector_slot(cmd_element->
1885 strvec,
1886 vector_active
1887 (cmd_element->
1888 strvec) - 1);
1889 for (k = 0;
1890 k < vector_active(descvec);
1891 k++) {
1892 struct desc *desc =
1893 vector_slot(descvec,
1894 k);
1895 vector_set(matchvec,
1896 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001897 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001898 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001899
Harald Welte80d30fe2013-02-12 11:08:57 +01001900 vector_set(matchvec, &desc_cr);
1901 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001902
Harald Welte80d30fe2013-02-12 11:08:57 +01001903 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001904 }
1905
Harald Welte80d30fe2013-02-12 11:08:57 +01001906 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1907 match)) == 1) {
1908 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001909 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001910 *status = CMD_ERR_AMBIGUOUS;
1911 return NULL;
1912 } else if (ret == 2) {
1913 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001914 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001915 *status = CMD_ERR_NO_MATCH;
1916 return NULL;
1917 }
1918 }
1919
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001920 /* Prepare match vector */
1921 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1922
1923 /* Make sure that cmd_vector is filtered based on current word */
1924 command = vector_slot(vline, index);
1925 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001926 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001927
1928 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001929 for (i = 0; i < vector_active(cmd_vector); i++) {
1930 const char *string = NULL;
1931 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001932
Harald Welte80d30fe2013-02-12 11:08:57 +01001933 cmd_element = vector_slot(cmd_vector, i);
1934 if (!cmd_element)
1935 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001936
Harald Welted17aa592013-02-12 11:11:34 +01001937 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1938 continue;
1939
Harald Welte80d30fe2013-02-12 11:08:57 +01001940 strvec = cmd_element->strvec;
1941
1942 /* if command is NULL, index may be equal to vector_active */
1943 if (command && index >= vector_active(strvec))
1944 vector_slot(cmd_vector, i) = NULL;
1945 else {
1946 /* Check if command is completed. */
1947 if (command == NULL
1948 && index == vector_active(strvec)) {
1949 string = "<cr>";
1950 if (!desc_unique_string(matchvec, string))
1951 vector_set(matchvec, &desc_cr);
1952 } else {
1953 unsigned int j;
1954 vector descvec = vector_slot(strvec, index);
1955 struct desc *desc;
1956
1957 for (j = 0; j < vector_active(descvec); j++) {
1958 desc = vector_slot(descvec, j);
1959 if (!desc)
1960 continue;
1961 string = cmd_entry_function_desc
1962 (command, desc->cmd);
1963 if (!string)
1964 continue;
1965 /* Uniqueness check */
1966 if (!desc_unique_string(matchvec, string))
1967 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001968 }
1969 }
1970 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001971 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001972 vector_free(cmd_vector);
1973
1974 if (vector_slot(matchvec, 0) == NULL) {
1975 vector_free(matchvec);
1976 *status = CMD_ERR_NO_MATCH;
1977 } else
1978 *status = CMD_SUCCESS;
1979
1980 return matchvec;
1981}
1982
1983vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1984{
1985 vector ret;
1986
1987 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1988 enum node_type onode;
1989 vector shifted_vline;
1990 unsigned int index;
1991
1992 onode = vty->node;
1993 vty->node = ENABLE_NODE;
1994 /* We can try it on enable node, cos' the vty is authenticated */
1995
1996 shifted_vline = vector_init(vector_count(vline));
1997 /* use memcpy? */
1998 for (index = 1; index < vector_active(vline); index++) {
1999 vector_set_index(shifted_vline, index - 1,
2000 vector_lookup(vline, index));
2001 }
2002
2003 ret = cmd_describe_command_real(shifted_vline, vty, status);
2004
2005 vector_free(shifted_vline);
2006 vty->node = onode;
2007 return ret;
2008 }
2009
2010 return cmd_describe_command_real(vline, vty, status);
2011}
2012
2013/* Check LCD of matched command. */
2014static int cmd_lcd(char **matched)
2015{
2016 int i;
2017 int j;
2018 int lcd = -1;
2019 char *s1, *s2;
2020 char c1, c2;
2021
2022 if (matched[0] == NULL || matched[1] == NULL)
2023 return 0;
2024
2025 for (i = 1; matched[i] != NULL; i++) {
2026 s1 = matched[i - 1];
2027 s2 = matched[i];
2028
2029 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2030 if (c1 != c2)
2031 break;
2032
2033 if (lcd < 0)
2034 lcd = j;
2035 else {
2036 if (lcd > j)
2037 lcd = j;
2038 }
2039 }
2040 return lcd;
2041}
2042
2043/* Command line completion support. */
2044static char **cmd_complete_command_real(vector vline, struct vty *vty,
2045 int *status)
2046{
2047 unsigned int i;
2048 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2049#define INIT_MATCHVEC_SIZE 10
2050 vector matchvec;
2051 struct cmd_element *cmd_element;
2052 unsigned int index;
2053 char **match_str;
2054 struct desc *desc;
2055 vector descvec;
2056 char *command;
2057 int lcd;
2058
2059 if (vector_active(vline) == 0) {
2060 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002061 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002062 return NULL;
2063 } else
2064 index = vector_active(vline) - 1;
2065
2066 /* First, filter by preceeding command string */
2067 for (i = 0; i < index; i++)
2068 if ((command = vector_slot(vline, i))) {
2069 enum match_type match;
2070 int ret;
2071
2072 /* First try completion match, if there is exactly match return 1 */
2073 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002074 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002075
2076 /* If there is exact match then filter ambiguous match else check
2077 ambiguousness. */
2078 if ((ret =
2079 is_cmd_ambiguous(command, cmd_vector, i,
2080 match)) == 1) {
2081 vector_free(cmd_vector);
2082 *status = CMD_ERR_AMBIGUOUS;
2083 return NULL;
2084 }
2085 /*
2086 else if (ret == 2)
2087 {
2088 vector_free (cmd_vector);
2089 *status = CMD_ERR_NO_MATCH;
2090 return NULL;
2091 }
2092 */
2093 }
2094
2095 /* Prepare match vector. */
2096 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2097
2098 /* Now we got into completion */
2099 for (i = 0; i < vector_active(cmd_vector); i++)
2100 if ((cmd_element = vector_slot(cmd_vector, i))) {
2101 const char *string;
2102 vector strvec = cmd_element->strvec;
2103
2104 /* Check field length */
2105 if (index >= vector_active(strvec))
2106 vector_slot(cmd_vector, i) = NULL;
2107 else {
2108 unsigned int j;
2109
2110 descvec = vector_slot(strvec, index);
2111 for (j = 0; j < vector_active(descvec); j++)
2112 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002113 const char *cmd = desc->cmd;
2114 char *tmp = NULL;
2115
2116 if (CMD_OPTION(desc->cmd)) {
2117 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2118 cmd = tmp;
2119 }
2120 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002121 if (cmd_unique_string (matchvec, string))
2122 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002123 if (tmp)
2124 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002125 }
2126 }
2127 }
2128
2129 /* We don't need cmd_vector any more. */
2130 vector_free(cmd_vector);
2131
2132 /* No matched command */
2133 if (vector_slot(matchvec, 0) == NULL) {
2134 vector_free(matchvec);
2135
2136 /* In case of 'command \t' pattern. Do you need '?' command at
2137 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002138 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002139 *status = CMD_ERR_NOTHING_TODO;
2140 else
2141 *status = CMD_ERR_NO_MATCH;
2142 return NULL;
2143 }
2144
2145 /* Only one matched */
2146 if (vector_slot(matchvec, 1) == NULL) {
2147 match_str = (char **)matchvec->index;
2148 vector_only_wrapper_free(matchvec);
2149 *status = CMD_COMPLETE_FULL_MATCH;
2150 return match_str;
2151 }
2152 /* Make it sure last element is NULL. */
2153 vector_set(matchvec, NULL);
2154
2155 /* Check LCD of matched strings. */
2156 if (vector_slot(vline, index) != NULL) {
2157 lcd = cmd_lcd((char **)matchvec->index);
2158
2159 if (lcd) {
2160 int len = strlen(vector_slot(vline, index));
2161
2162 if (len < lcd) {
2163 char *lcdstr;
2164
2165 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2166 "complete-lcdstr");
2167 memcpy(lcdstr, matchvec->index[0], lcd);
2168 lcdstr[lcd] = '\0';
2169
2170 /* match_str = (char **) &lcdstr; */
2171
2172 /* Free matchvec. */
2173 for (i = 0; i < vector_active(matchvec); i++) {
2174 if (vector_slot(matchvec, i))
2175 talloc_free(vector_slot(matchvec, i));
2176 }
2177 vector_free(matchvec);
2178
2179 /* Make new matchvec. */
2180 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2181 vector_set(matchvec, lcdstr);
2182 match_str = (char **)matchvec->index;
2183 vector_only_wrapper_free(matchvec);
2184
2185 *status = CMD_COMPLETE_MATCH;
2186 return match_str;
2187 }
2188 }
2189 }
2190
2191 match_str = (char **)matchvec->index;
2192 vector_only_wrapper_free(matchvec);
2193 *status = CMD_COMPLETE_LIST_MATCH;
2194 return match_str;
2195}
2196
2197char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2198{
2199 char **ret;
2200
2201 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2202 enum node_type onode;
2203 vector shifted_vline;
2204 unsigned int index;
2205
2206 onode = vty->node;
2207 vty->node = ENABLE_NODE;
2208 /* We can try it on enable node, cos' the vty is authenticated */
2209
2210 shifted_vline = vector_init(vector_count(vline));
2211 /* use memcpy? */
2212 for (index = 1; index < vector_active(vline); index++) {
2213 vector_set_index(shifted_vline, index - 1,
2214 vector_lookup(vline, index));
2215 }
2216
2217 ret = cmd_complete_command_real(shifted_vline, vty, status);
2218
2219 vector_free(shifted_vline);
2220 vty->node = onode;
2221 return ret;
2222 }
2223
2224 return cmd_complete_command_real(vline, vty, status);
2225}
2226
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002227static struct vty_parent_node *vty_parent(struct vty *vty)
2228{
2229 return llist_first_entry_or_null(&vty->parent_nodes,
2230 struct vty_parent_node,
2231 entry);
2232}
2233
2234static bool vty_pop_parent(struct vty *vty)
2235{
2236 struct vty_parent_node *parent = vty_parent(vty);
2237 if (!parent)
2238 return false;
2239 llist_del(&parent->entry);
2240 vty->node = parent->node;
2241 vty->priv = parent->priv;
2242 if (vty->indent)
2243 talloc_free(vty->indent);
2244 vty->indent = parent->indent;
2245 talloc_free(parent);
2246 return true;
2247}
2248
2249static void vty_clear_parents(struct vty *vty)
2250{
2251 while (vty_pop_parent(vty));
2252}
2253
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002254/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002255/*
2256 * This function MUST eventually converge on a node when called repeatedly,
2257 * there must not be any cycles.
2258 * All 'config' nodes shall converge on CONFIG_NODE.
2259 * All other 'enable' nodes shall converge on ENABLE_NODE.
2260 * All 'view' only nodes shall converge on VIEW_NODE.
2261 * All other nodes shall converge on themselves or it must be ensured,
2262 * that the user's rights are not extended anyhow by calling this function.
2263 *
2264 * Note that these requirements also apply to all functions that are used
2265 * as go_parent_cb.
2266 * Note also that this function relies on the is_config_child callback to
2267 * recognize non-config nodes if go_parent_cb is not set.
2268 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002269int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002270{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002271 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002272 case AUTH_NODE:
2273 case VIEW_NODE:
2274 case ENABLE_NODE:
2275 case CONFIG_NODE:
2276 vty_clear_parents(vty);
2277 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002278
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002279 case AUTH_ENABLE_NODE:
2280 vty->node = VIEW_NODE;
2281 vty_clear_parents(vty);
2282 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002283
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002284 default:
2285 if (host.app_info->go_parent_cb)
2286 host.app_info->go_parent_cb(vty);
2287 vty_pop_parent(vty);
2288 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002289 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002290
2291 return vty->node;
2292}
2293
2294/* Execute command by argument vline vector. */
2295static int
2296cmd_execute_command_real(vector vline, struct vty *vty,
2297 struct cmd_element **cmd)
2298{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002299 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002300 unsigned int index;
2301 vector cmd_vector;
2302 struct cmd_element *cmd_element;
2303 struct cmd_element *matched_element;
2304 unsigned int matched_count, incomplete_count;
2305 int argc;
2306 const char *argv[CMD_ARGC_MAX];
2307 enum match_type match = 0;
2308 int varflag;
2309 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002310 int rc;
2311 /* Used for temporary storage of cmd_deopt() allocated arguments during
2312 argv[] generation */
2313 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002314
2315 /* Make copy of command elements. */
2316 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2317
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002318 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002319 if ((command = vector_slot(vline, index))) {
2320 int ret;
2321
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002322 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002323 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002324
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002325 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002326 break;
2327
2328 ret =
2329 is_cmd_ambiguous(command, cmd_vector, index, match);
2330
2331 if (ret == 1) {
2332 vector_free(cmd_vector);
2333 return CMD_ERR_AMBIGUOUS;
2334 } else if (ret == 2) {
2335 vector_free(cmd_vector);
2336 return CMD_ERR_NO_MATCH;
2337 }
2338 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002339 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002340
2341 /* Check matched count. */
2342 matched_element = NULL;
2343 matched_count = 0;
2344 incomplete_count = 0;
2345
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002346 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002347 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002348 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002349 || index >= cmd_element->cmdsize) {
2350 matched_element = cmd_element;
2351#if 0
2352 printf("DEBUG: %s\n", cmd_element->string);
2353#endif
2354 matched_count++;
2355 } else {
2356 incomplete_count++;
2357 }
2358 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002359 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002360
2361 /* Finish of using cmd_vector. */
2362 vector_free(cmd_vector);
2363
2364 /* To execute command, matched_count must be 1. */
2365 if (matched_count == 0) {
2366 if (incomplete_count)
2367 return CMD_ERR_INCOMPLETE;
2368 else
2369 return CMD_ERR_NO_MATCH;
2370 }
2371
2372 if (matched_count > 1)
2373 return CMD_ERR_AMBIGUOUS;
2374
2375 /* Argument treatment */
2376 varflag = 0;
2377 argc = 0;
2378
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002379 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2380
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002381 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002382 if (argc == CMD_ARGC_MAX) {
2383 rc = CMD_ERR_EXEED_ARGC_MAX;
2384 goto rc_free_deopt_ctx;
2385 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002386 if (varflag) {
2387 argv[argc++] = vector_slot(vline, i);
2388 continue;
2389 }
2390
2391 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002392 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002393
2394 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002395 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002396 struct desc *desc = vector_slot(descvec, 0);
2397
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002398 if (CMD_OPTION(desc->cmd)) {
2399 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2400 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2401 } else {
2402 tmp_cmd = desc->cmd;
2403 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002404
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002405 if (CMD_VARARG(tmp_cmd))
2406 varflag = 1;
2407 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002408 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002409 else if (CMD_OPTION(desc->cmd))
2410 argv[argc++] = tmp_cmd;
2411 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002412 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002413 /* multi choice argument. look up which choice
2414 the user meant (can only be one after
2415 filtering and checking for ambigous). For instance,
2416 if user typed "th" for "(two|three)" arg, we
2417 want to pass "three" in argv[]. */
2418 for (j = 0; j < vector_active(descvec); j++) {
2419 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002420 if (!desc)
2421 continue;
2422 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2423 continue;
2424 if (CMD_OPTION(desc->cmd)) {
2425 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2426 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2427 } else {
2428 tmp_cmd = desc->cmd;
2429 }
2430
2431 if(CMD_VARIABLE(tmp_cmd)) {
2432 argv[argc++] = vector_slot(vline, i);
2433 } else {
2434 argv[argc++] = tmp_cmd;
2435 }
2436 break;
2437 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002438 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002439 }
2440
2441 /* For vtysh execution. */
2442 if (cmd)
2443 *cmd = matched_element;
2444
2445 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002446 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002447 else {
2448 /* Execute matched command. */
2449 struct vty_parent_node this_node = {
2450 .node = vty->node,
2451 .priv = vty->priv,
2452 .indent = vty->indent,
2453 };
2454 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002455 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002456
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002457 /* If we have stepped down into a child node, push a parent frame.
2458 * The causality is such: we don't expect every single node entry implementation to push
2459 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2460 * a parent node. Hence if the node changed without the parent node changing, we must
2461 * have stepped into a child node. */
2462 if (vty->node != this_node.node && parent == vty_parent(vty)
2463 && vty->node > CONFIG_NODE) {
2464 /* Push the parent node. */
2465 parent = talloc_zero(vty, struct vty_parent_node);
2466 *parent = this_node;
2467 llist_add(&parent->entry, &vty->parent_nodes);
2468 }
2469 }
2470
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002471rc_free_deopt_ctx:
2472 /* Now after we called the command func, we can free temporary strings */
2473 talloc_free(cmd_deopt_ctx);
2474 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002475}
2476
2477int
2478cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2479 int vtysh)
2480{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002481 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002482 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002483
2484 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002485
2486 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2487 vector shifted_vline;
2488 unsigned int index;
2489
2490 vty->node = ENABLE_NODE;
2491 /* We can try it on enable node, cos' the vty is authenticated */
2492
2493 shifted_vline = vector_init(vector_count(vline));
2494 /* use memcpy? */
2495 for (index = 1; index < vector_active(vline); index++) {
2496 vector_set_index(shifted_vline, index - 1,
2497 vector_lookup(vline, index));
2498 }
2499
2500 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2501
2502 vector_free(shifted_vline);
2503 vty->node = onode;
2504 return ret;
2505 }
2506
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002507 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002508}
2509
2510/* Execute command by argument readline. */
2511int
2512cmd_execute_command_strict(vector vline, struct vty *vty,
2513 struct cmd_element **cmd)
2514{
2515 unsigned int i;
2516 unsigned int index;
2517 vector cmd_vector;
2518 struct cmd_element *cmd_element;
2519 struct cmd_element *matched_element;
2520 unsigned int matched_count, incomplete_count;
2521 int argc;
2522 const char *argv[CMD_ARGC_MAX];
2523 int varflag;
2524 enum match_type match = 0;
2525 char *command;
2526
2527 /* Make copy of command element */
2528 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2529
2530 for (index = 0; index < vector_active(vline); index++)
2531 if ((command = vector_slot(vline, index))) {
2532 int ret;
2533
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002534 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002535 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002536
2537 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002538 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002539 break;
2540
2541 ret =
2542 is_cmd_ambiguous(command, cmd_vector, index, match);
2543 if (ret == 1) {
2544 vector_free(cmd_vector);
2545 return CMD_ERR_AMBIGUOUS;
2546 }
2547 if (ret == 2) {
2548 vector_free(cmd_vector);
2549 return CMD_ERR_NO_MATCH;
2550 }
2551 }
2552
2553 /* Check matched count. */
2554 matched_element = NULL;
2555 matched_count = 0;
2556 incomplete_count = 0;
2557 for (i = 0; i < vector_active(cmd_vector); i++)
2558 if (vector_slot(cmd_vector, i) != NULL) {
2559 cmd_element = vector_slot(cmd_vector, i);
2560
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002561 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002562 || index >= cmd_element->cmdsize) {
2563 matched_element = cmd_element;
2564 matched_count++;
2565 } else
2566 incomplete_count++;
2567 }
2568
2569 /* Finish of using cmd_vector. */
2570 vector_free(cmd_vector);
2571
2572 /* To execute command, matched_count must be 1. */
2573 if (matched_count == 0) {
2574 if (incomplete_count)
2575 return CMD_ERR_INCOMPLETE;
2576 else
2577 return CMD_ERR_NO_MATCH;
2578 }
2579
2580 if (matched_count > 1)
2581 return CMD_ERR_AMBIGUOUS;
2582
2583 /* Argument treatment */
2584 varflag = 0;
2585 argc = 0;
2586
2587 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002588 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002589 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002590 if (varflag) {
2591 argv[argc++] = vector_slot(vline, i);
2592 continue;
2593 }
2594
2595 vector descvec = vector_slot(matched_element->strvec, i);
2596
2597 if (vector_active(descvec) == 1) {
2598 struct desc *desc = vector_slot(descvec, 0);
2599
2600 if (CMD_VARARG(desc->cmd))
2601 varflag = 1;
2602
2603 if (varflag || CMD_VARIABLE(desc->cmd)
2604 || CMD_OPTION(desc->cmd))
2605 argv[argc++] = vector_slot(vline, i);
2606 } else {
2607 argv[argc++] = vector_slot(vline, i);
2608 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002609 }
2610
2611 /* For vtysh execution. */
2612 if (cmd)
2613 *cmd = matched_element;
2614
2615 if (matched_element->daemon)
2616 return CMD_SUCCESS_DAEMON;
2617
2618 /* Now execute matched command */
2619 return (*matched_element->func) (matched_element, vty, argc, argv);
2620}
2621
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002622static inline size_t len(const char *str)
2623{
2624 return str? strlen(str) : 0;
2625}
2626
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002627/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2628 * is longer than b, a must start with exactly b, and vice versa.
2629 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2630 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002631static int indent_cmp(const char *a, const char *b)
2632{
2633 size_t al, bl;
2634 al = len(a);
2635 bl = len(b);
2636 if (al > bl) {
2637 if (bl && strncmp(a, b, bl) != 0)
2638 return EINVAL;
2639 return 1;
2640 }
2641 /* al <= bl */
2642 if (al && strncmp(a, b, al) != 0)
2643 return EINVAL;
2644 return (al < bl)? -1 : 0;
2645}
2646
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002647/* Configration make from file. */
2648int config_from_file(struct vty *vty, FILE * fp)
2649{
2650 int ret;
2651 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002652 char *indent;
2653 int cmp;
2654 struct vty_parent_node this_node;
2655 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002656
2657 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002658 indent = NULL;
2659 vline = NULL;
2660 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002661
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002662 if (ret != CMD_SUCCESS)
2663 goto return_invalid_indent;
2664
2665 /* In case of comment or empty line */
2666 if (vline == NULL) {
2667 if (indent) {
2668 talloc_free(indent);
2669 indent = NULL;
2670 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002671 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002672 }
2673
Neels Hofmeyr43063632017-09-19 23:54:01 +02002674 /* We have a nonempty line. */
2675 if (!vty->indent) {
2676 /* We have just entered a node and expecting the first child to come up; but we
2677 * may also skip right back to a parent or ancestor level. */
2678 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002679
Neels Hofmeyr43063632017-09-19 23:54:01 +02002680 /* If there is no parent, record any indentation we encounter. */
2681 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2682
2683 if (cmp == EINVAL)
2684 goto return_invalid_indent;
2685
2686 if (cmp <= 0) {
2687 /* We have gone right back to the parent level or higher, we are skipping
2688 * this child node level entirely. Pop the parent to go back to a node
2689 * that was actually there (to reinstate vty->indent) and re-use below
2690 * go-parent while-loop to find an accurate match of indent in the node
2691 * ancestry. */
2692 vty_go_parent(vty);
2693 } else {
2694 /* The indent is deeper than the just entered parent, record the new
2695 * indentation characters. */
2696 vty->indent = talloc_strdup(vty, indent);
2697 /* This *is* the new indentation. */
2698 cmp = 0;
2699 }
2700 } else {
2701 /* There is a known indentation for this node level, validate and detect node
2702 * exits. */
2703 cmp = indent_cmp(indent, vty->indent);
2704 if (cmp == EINVAL)
2705 goto return_invalid_indent;
2706 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002707
2708 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2709 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2710 while (cmp < 0) {
2711 vty_go_parent(vty);
2712 cmp = indent_cmp(indent, vty->indent);
2713 if (cmp == EINVAL)
2714 goto return_invalid_indent;
2715 }
2716
2717 /* More indent without having entered a child node level? Either the parent node's indent
2718 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2719 * or the indentation increased even though the vty command didn't enter a child. */
2720 if (cmp > 0)
2721 goto return_invalid_indent;
2722
2723 /* Remember the current node before the command possibly changes it. */
2724 this_node = (struct vty_parent_node){
2725 .node = vty->node,
2726 .priv = vty->priv,
2727 .indent = vty->indent,
2728 };
2729
2730 parent = vty_parent(vty);
2731 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002732 cmd_free_strvec(vline);
2733
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002734 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002735 if (indent) {
2736 talloc_free(indent);
2737 indent = NULL;
2738 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002739 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002740 }
2741
2742 /* If we have stepped down into a child node, push a parent frame.
2743 * The causality is such: we don't expect every single node entry implementation to push
2744 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2745 * a parent node. Hence if the node changed without the parent node changing, we must
2746 * have stepped into a child node (and now expect a deeper indent). */
2747 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2748 /* Push the parent node. */
2749 parent = talloc_zero(vty, struct vty_parent_node);
2750 *parent = this_node;
2751 llist_add(&parent->entry, &vty->parent_nodes);
2752
2753 /* The current talloc'ed vty->indent string will now be owned by this parent
2754 * struct. Indicate that we don't know what deeper indent characters the user
2755 * will choose. */
2756 vty->indent = NULL;
2757 }
2758
2759 if (indent) {
2760 talloc_free(indent);
2761 indent = NULL;
2762 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002763 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002764 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2765 while (vty_parent(vty))
2766 vty_go_parent(vty);
2767
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002768 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002769
2770return_invalid_indent:
2771 if (vline)
2772 cmd_free_strvec(vline);
2773 if (indent) {
2774 talloc_free(indent);
2775 indent = NULL;
2776 }
2777 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002778}
2779
2780/* Configration from terminal */
2781DEFUN(config_terminal,
2782 config_terminal_cmd,
2783 "configure terminal",
2784 "Configuration from vty interface\n" "Configuration terminal\n")
2785{
2786 if (vty_config_lock(vty))
2787 vty->node = CONFIG_NODE;
2788 else {
2789 vty_out(vty, "VTY configuration is locked by other VTY%s",
2790 VTY_NEWLINE);
2791 return CMD_WARNING;
2792 }
2793 return CMD_SUCCESS;
2794}
2795
2796/* Enable command */
2797DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2798{
2799 /* If enable password is NULL, change to ENABLE_NODE */
2800 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2801 vty->type == VTY_SHELL_SERV)
2802 vty->node = ENABLE_NODE;
2803 else
2804 vty->node = AUTH_ENABLE_NODE;
2805
2806 return CMD_SUCCESS;
2807}
2808
2809/* Disable command */
2810DEFUN(disable,
2811 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2812{
2813 if (vty->node == ENABLE_NODE)
2814 vty->node = VIEW_NODE;
2815 return CMD_SUCCESS;
2816}
2817
2818/* Down vty node level. */
2819gDEFUN(config_exit,
2820 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2821{
2822 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002823 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002824 case VIEW_NODE:
2825 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002826 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002827 break;
2828 case CONFIG_NODE:
2829 vty->node = ENABLE_NODE;
2830 vty_config_unlock(vty);
2831 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002832 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002833 if (vty->node > CONFIG_NODE)
2834 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002835 break;
2836 }
2837 return CMD_SUCCESS;
2838}
2839
2840/* End of configuration. */
2841 gDEFUN(config_end,
2842 config_end_cmd, "end", "End current mode and change to enable mode.")
2843{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002844 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002845 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002846
2847 /* Repeatedly call go_parent until a top node is reached. */
2848 while (vty->node > CONFIG_NODE) {
2849 if (vty->node == last_node) {
2850 /* Ensure termination, this shouldn't happen. */
2851 break;
2852 }
2853 last_node = vty->node;
2854 vty_go_parent(vty);
2855 }
2856
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002857 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002858 if (vty->node > ENABLE_NODE)
2859 vty->node = ENABLE_NODE;
2860 vty->index = NULL;
2861 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002862 }
2863 return CMD_SUCCESS;
2864}
2865
2866/* Show version. */
2867DEFUN(show_version,
2868 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2869{
Harald Welte237f6242010-05-25 23:00:45 +02002870 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2871 host.app_info->version,
2872 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2873 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002874
2875 return CMD_SUCCESS;
2876}
2877
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002878DEFUN(show_online_help,
2879 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2880{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002881 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002882 return CMD_SUCCESS;
2883}
2884
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002885/* Help display function for all node. */
2886gDEFUN(config_help,
2887 config_help_cmd, "help", "Description of the interactive help system\n")
2888{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002889 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2890 "anytime at the command line please press '?'.%s%s"
2891 "If nothing matches, the help list will be empty and you must backup%s"
2892 " until entering a '?' shows the available options.%s"
2893 "Two styles of help are provided:%s"
2894 "1. Full help is available when you are ready to enter a%s"
2895 "command argument (e.g. 'show ?') and describes each possible%s"
2896 "argument.%s"
2897 "2. Partial help is provided when an abbreviated argument is entered%s"
2898 " and you want to know what arguments match the input%s"
2899 " (e.g. 'show me?'.)%s%s",
2900 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2901 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2902 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2903 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002904 return CMD_SUCCESS;
2905}
2906
2907/* Help display function for all node. */
2908gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2909{
2910 unsigned int i;
2911 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2912 struct cmd_element *cmd;
2913
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002914 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2915 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2916 continue;
2917 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2918 continue;
2919 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2920 }
2921
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002922 return CMD_SUCCESS;
2923}
2924
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002925static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002926{
2927 unsigned int i;
2928 int fd;
2929 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002930 char *config_file_tmp = NULL;
2931 char *config_file_sav = NULL;
2932 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002933 struct stat st;
2934
2935 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002936
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002937 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2938 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2939 * manually instead. */
2940
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002941 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002942 config_file_sav =
2943 _talloc_zero(tall_vty_cmd_ctx,
2944 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2945 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002946 if (!config_file_sav)
2947 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002948 strcpy(config_file_sav, config_file);
2949 strcat(config_file_sav, CONF_BACKUP_EXT);
2950
2951 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002952 "config_file_tmp");
2953 if (!config_file_tmp) {
2954 talloc_free(config_file_sav);
2955 return -1;
2956 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002957 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2958
2959 /* Open file to configuration write. */
2960 fd = mkstemp(config_file_tmp);
2961 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002962 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002963 talloc_free(config_file_tmp);
2964 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002965 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002966 }
2967
2968 /* Make vty for configuration file. */
2969 file_vty = vty_new();
2970 file_vty->fd = fd;
2971 file_vty->type = VTY_FILE;
2972
2973 /* Config file header print. */
2974 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002975 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002976 //vty_time_print (file_vty, 1);
2977 vty_out(file_vty, "!\n");
2978
2979 for (i = 0; i < vector_active(cmdvec); i++)
2980 if ((node = vector_slot(cmdvec, i)) && node->func) {
2981 if ((*node->func) (file_vty))
2982 vty_out(file_vty, "!\n");
2983 }
2984 vty_close(file_vty);
2985
2986 if (unlink(config_file_sav) != 0)
2987 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002988 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002989 talloc_free(config_file_sav);
2990 talloc_free(config_file_tmp);
2991 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002992 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002993 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002994
2995 /* Only link the .sav file if the original file exists */
2996 if (stat(config_file, &st) == 0) {
2997 if (link(config_file, config_file_sav) != 0) {
2998 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2999 talloc_free(config_file_sav);
3000 talloc_free(config_file_tmp);
3001 unlink(config_file_tmp);
3002 return -3;
3003 }
3004 sync();
3005 if (unlink(config_file) != 0) {
3006 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3007 talloc_free(config_file_sav);
3008 talloc_free(config_file_tmp);
3009 unlink(config_file_tmp);
3010 return -4;
3011 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003012 }
3013 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003014 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003015 talloc_free(config_file_sav);
3016 talloc_free(config_file_tmp);
3017 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003018 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003019 }
3020 unlink(config_file_tmp);
3021 sync();
3022
3023 talloc_free(config_file_sav);
3024 talloc_free(config_file_tmp);
3025
3026 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003027 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3028 return -6;
3029 }
3030
3031 return 0;
3032}
3033
3034
3035/* Write current configuration into file. */
3036DEFUN(config_write_file,
3037 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003038 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003039 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003040 "Write to configuration file\n"
3041 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003042{
3043 char *failed_file;
3044 int rc;
3045
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003046 if (host.app_info->config_is_consistent) {
3047 rc = host.app_info->config_is_consistent(vty);
3048 if (!rc) {
3049 vty_out(vty, "Configuration is not consistent%s",
3050 VTY_NEWLINE);
3051 return CMD_WARNING;
3052 }
3053 }
3054
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003055 if (argc == 1)
3056 host_config_set(argv[0]);
3057
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003058 if (host.config == NULL) {
3059 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3060 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003061 return CMD_WARNING;
3062 }
3063
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003064 rc = write_config_file(host.config, &failed_file);
3065 switch (rc) {
3066 case -1:
3067 vty_out(vty, "Can't open configuration file %s.%s",
3068 failed_file, VTY_NEWLINE);
3069 rc = CMD_WARNING;
3070 break;
3071 case -2:
3072 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3073 failed_file, VTY_NEWLINE);
3074 rc = CMD_WARNING;
3075 break;
3076 case -3:
3077 vty_out(vty, "Can't backup old configuration file %s.%s",
3078 failed_file, VTY_NEWLINE);
3079 rc = CMD_WARNING;
3080 break;
3081 case -4:
3082 vty_out(vty, "Can't unlink configuration file %s.%s",
3083 failed_file, VTY_NEWLINE);
3084 rc = CMD_WARNING;
3085 break;
3086 case -5:
3087 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3088 VTY_NEWLINE);
3089 rc = CMD_WARNING;
3090 break;
3091 case -6:
3092 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3093 failed_file, strerror(errno), errno, VTY_NEWLINE);
3094 rc = CMD_WARNING;
3095 break;
3096 default:
3097 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3098 rc = CMD_SUCCESS;
3099 break;
3100 }
3101
3102 talloc_free(failed_file);
3103 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003104}
3105
3106ALIAS(config_write_file,
3107 config_write_cmd,
3108 "write", "Write running configuration to memory, network, or terminal\n")
3109
3110 ALIAS(config_write_file,
3111 config_write_memory_cmd,
3112 "write memory",
3113 "Write running configuration to memory, network, or terminal\n"
3114 "Write configuration to the file (same as write file)\n")
3115
3116 ALIAS(config_write_file,
3117 copy_runningconfig_startupconfig_cmd,
3118 "copy running-config startup-config",
3119 "Copy configuration\n"
3120 "Copy running config to... \n"
3121 "Copy running config to startup config (same as write file)\n")
3122
3123/* Write current configuration into the terminal. */
3124 DEFUN(config_write_terminal,
3125 config_write_terminal_cmd,
3126 "write terminal",
3127 "Write running configuration to memory, network, or terminal\n"
3128 "Write to terminal\n")
3129{
3130 unsigned int i;
3131 struct cmd_node *node;
3132
3133 if (vty->type == VTY_SHELL_SERV) {
3134 for (i = 0; i < vector_active(cmdvec); i++)
3135 if ((node = vector_slot(cmdvec, i)) && node->func
3136 && node->vtysh) {
3137 if ((*node->func) (vty))
3138 vty_out(vty, "!%s", VTY_NEWLINE);
3139 }
3140 } else {
3141 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3142 VTY_NEWLINE);
3143 vty_out(vty, "!%s", VTY_NEWLINE);
3144
3145 for (i = 0; i < vector_active(cmdvec); i++)
3146 if ((node = vector_slot(cmdvec, i)) && node->func) {
3147 if ((*node->func) (vty))
3148 vty_out(vty, "!%s", VTY_NEWLINE);
3149 }
3150 vty_out(vty, "end%s", VTY_NEWLINE);
3151 }
3152 return CMD_SUCCESS;
3153}
3154
3155/* Write current configuration into the terminal. */
3156ALIAS(config_write_terminal,
3157 show_running_config_cmd,
3158 "show running-config", SHOW_STR "running configuration\n")
3159
3160/* Write startup configuration into the terminal. */
3161 DEFUN(show_startup_config,
3162 show_startup_config_cmd,
3163 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3164{
3165 char buf[BUFSIZ];
3166 FILE *confp;
3167
3168 confp = fopen(host.config, "r");
3169 if (confp == NULL) {
3170 vty_out(vty, "Can't open configuration file [%s]%s",
3171 host.config, VTY_NEWLINE);
3172 return CMD_WARNING;
3173 }
3174
3175 while (fgets(buf, BUFSIZ, confp)) {
3176 char *cp = buf;
3177
3178 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3179 cp++;
3180 *cp = '\0';
3181
3182 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3183 }
3184
3185 fclose(confp);
3186
3187 return CMD_SUCCESS;
3188}
3189
3190/* Hostname configuration */
3191DEFUN(config_hostname,
3192 hostname_cmd,
3193 "hostname WORD",
3194 "Set system's network name\n" "This system's network name\n")
3195{
3196 if (!isalpha((int)*argv[0])) {
3197 vty_out(vty, "Please specify string starting with alphabet%s",
3198 VTY_NEWLINE);
3199 return CMD_WARNING;
3200 }
3201
3202 if (host.name)
3203 talloc_free(host.name);
3204
3205 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3206 return CMD_SUCCESS;
3207}
3208
3209DEFUN(config_no_hostname,
3210 no_hostname_cmd,
3211 "no hostname [HOSTNAME]",
3212 NO_STR "Reset system's network name\n" "Host name of this router\n")
3213{
3214 if (host.name)
3215 talloc_free(host.name);
3216 host.name = NULL;
3217 return CMD_SUCCESS;
3218}
3219
3220/* VTY interface password set. */
3221DEFUN(config_password, password_cmd,
3222 "password (8|) WORD",
3223 "Assign the terminal connection password\n"
3224 "Specifies a HIDDEN password will follow\n"
3225 "dummy string \n" "The HIDDEN line password string\n")
3226{
3227 /* Argument check. */
3228 if (argc == 0) {
3229 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3230 return CMD_WARNING;
3231 }
3232
3233 if (argc == 2) {
3234 if (*argv[0] == '8') {
3235 if (host.password)
3236 talloc_free(host.password);
3237 host.password = NULL;
3238 if (host.password_encrypt)
3239 talloc_free(host.password_encrypt);
3240 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3241 return CMD_SUCCESS;
3242 } else {
3243 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3244 return CMD_WARNING;
3245 }
3246 }
3247
3248 if (!isalnum((int)*argv[0])) {
3249 vty_out(vty,
3250 "Please specify string starting with alphanumeric%s",
3251 VTY_NEWLINE);
3252 return CMD_WARNING;
3253 }
3254
3255 if (host.password)
3256 talloc_free(host.password);
3257 host.password = NULL;
3258
3259#ifdef VTY_CRYPT_PW
3260 if (host.encrypt) {
3261 if (host.password_encrypt)
3262 talloc_free(host.password_encrypt);
3263 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3264 } else
3265#endif
3266 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3267
3268 return CMD_SUCCESS;
3269}
3270
3271ALIAS(config_password, password_text_cmd,
3272 "password LINE",
3273 "Assign the terminal connection password\n"
3274 "The UNENCRYPTED (cleartext) line password\n")
3275
3276/* VTY enable password set. */
3277 DEFUN(config_enable_password, enable_password_cmd,
3278 "enable password (8|) WORD",
3279 "Modify enable password parameters\n"
3280 "Assign the privileged level password\n"
3281 "Specifies a HIDDEN password will follow\n"
3282 "dummy string \n" "The HIDDEN 'enable' password string\n")
3283{
3284 /* Argument check. */
3285 if (argc == 0) {
3286 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3287 return CMD_WARNING;
3288 }
3289
3290 /* Crypt type is specified. */
3291 if (argc == 2) {
3292 if (*argv[0] == '8') {
3293 if (host.enable)
3294 talloc_free(host.enable);
3295 host.enable = NULL;
3296
3297 if (host.enable_encrypt)
3298 talloc_free(host.enable_encrypt);
3299 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3300
3301 return CMD_SUCCESS;
3302 } else {
3303 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3304 return CMD_WARNING;
3305 }
3306 }
3307
3308 if (!isalnum((int)*argv[0])) {
3309 vty_out(vty,
3310 "Please specify string starting with alphanumeric%s",
3311 VTY_NEWLINE);
3312 return CMD_WARNING;
3313 }
3314
3315 if (host.enable)
3316 talloc_free(host.enable);
3317 host.enable = NULL;
3318
3319 /* Plain password input. */
3320#ifdef VTY_CRYPT_PW
3321 if (host.encrypt) {
3322 if (host.enable_encrypt)
3323 talloc_free(host.enable_encrypt);
3324 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3325 } else
3326#endif
3327 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3328
3329 return CMD_SUCCESS;
3330}
3331
3332ALIAS(config_enable_password,
3333 enable_password_text_cmd,
3334 "enable password LINE",
3335 "Modify enable password parameters\n"
3336 "Assign the privileged level password\n"
3337 "The UNENCRYPTED (cleartext) 'enable' password\n")
3338
3339/* VTY enable password delete. */
3340 DEFUN(no_config_enable_password, no_enable_password_cmd,
3341 "no enable password",
3342 NO_STR
3343 "Modify enable password parameters\n"
3344 "Assign the privileged level password\n")
3345{
3346 if (host.enable)
3347 talloc_free(host.enable);
3348 host.enable = NULL;
3349
3350 if (host.enable_encrypt)
3351 talloc_free(host.enable_encrypt);
3352 host.enable_encrypt = NULL;
3353
3354 return CMD_SUCCESS;
3355}
3356
3357#ifdef VTY_CRYPT_PW
3358DEFUN(service_password_encrypt,
3359 service_password_encrypt_cmd,
3360 "service password-encryption",
3361 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3362{
3363 if (host.encrypt)
3364 return CMD_SUCCESS;
3365
3366 host.encrypt = 1;
3367
3368 if (host.password) {
3369 if (host.password_encrypt)
3370 talloc_free(host.password_encrypt);
3371 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3372 }
3373 if (host.enable) {
3374 if (host.enable_encrypt)
3375 talloc_free(host.enable_encrypt);
3376 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3377 }
3378
3379 return CMD_SUCCESS;
3380}
3381
3382DEFUN(no_service_password_encrypt,
3383 no_service_password_encrypt_cmd,
3384 "no service password-encryption",
3385 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3386{
3387 if (!host.encrypt)
3388 return CMD_SUCCESS;
3389
3390 host.encrypt = 0;
3391
3392 if (host.password_encrypt)
3393 talloc_free(host.password_encrypt);
3394 host.password_encrypt = NULL;
3395
3396 if (host.enable_encrypt)
3397 talloc_free(host.enable_encrypt);
3398 host.enable_encrypt = NULL;
3399
3400 return CMD_SUCCESS;
3401}
3402#endif
3403
3404DEFUN(config_terminal_length, config_terminal_length_cmd,
3405 "terminal length <0-512>",
3406 "Set terminal line parameters\n"
3407 "Set number of lines on a screen\n"
3408 "Number of lines on screen (0 for no pausing)\n")
3409{
3410 int lines;
3411 char *endptr = NULL;
3412
3413 lines = strtol(argv[0], &endptr, 10);
3414 if (lines < 0 || lines > 512 || *endptr != '\0') {
3415 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3416 return CMD_WARNING;
3417 }
3418 vty->lines = lines;
3419
3420 return CMD_SUCCESS;
3421}
3422
3423DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3424 "terminal no length",
3425 "Set terminal line parameters\n"
3426 NO_STR "Set number of lines on a screen\n")
3427{
3428 vty->lines = -1;
3429 return CMD_SUCCESS;
3430}
3431
3432DEFUN(service_terminal_length, service_terminal_length_cmd,
3433 "service terminal-length <0-512>",
3434 "Set up miscellaneous service\n"
3435 "System wide terminal length configuration\n"
3436 "Number of lines of VTY (0 means no line control)\n")
3437{
3438 int lines;
3439 char *endptr = NULL;
3440
3441 lines = strtol(argv[0], &endptr, 10);
3442 if (lines < 0 || lines > 512 || *endptr != '\0') {
3443 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3444 return CMD_WARNING;
3445 }
3446 host.lines = lines;
3447
3448 return CMD_SUCCESS;
3449}
3450
3451DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3452 "no service terminal-length [<0-512>]",
3453 NO_STR
3454 "Set up miscellaneous service\n"
3455 "System wide terminal length configuration\n"
3456 "Number of lines of VTY (0 means no line control)\n")
3457{
3458 host.lines = -1;
3459 return CMD_SUCCESS;
3460}
3461
3462DEFUN_HIDDEN(do_echo,
3463 echo_cmd,
3464 "echo .MESSAGE",
3465 "Echo a message back to the vty\n" "The message to echo\n")
3466{
3467 char *message;
3468
3469 vty_out(vty, "%s%s",
3470 ((message =
3471 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3472 if (message)
3473 talloc_free(message);
3474 return CMD_SUCCESS;
3475}
3476
3477#if 0
3478DEFUN(config_logmsg,
3479 config_logmsg_cmd,
3480 "logmsg " LOG_LEVELS " .MESSAGE",
3481 "Send a message to enabled logging destinations\n"
3482 LOG_LEVEL_DESC "The message to send\n")
3483{
3484 int level;
3485 char *message;
3486
3487 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3488 return CMD_ERR_NO_MATCH;
3489
3490 zlog(NULL, level,
3491 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3492 if (message)
3493 talloc_free(message);
3494 return CMD_SUCCESS;
3495}
3496
3497DEFUN(show_logging,
3498 show_logging_cmd,
3499 "show logging", SHOW_STR "Show current logging configuration\n")
3500{
3501 struct zlog *zl = zlog_default;
3502
3503 vty_out(vty, "Syslog logging: ");
3504 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3505 vty_out(vty, "disabled");
3506 else
3507 vty_out(vty, "level %s, facility %s, ident %s",
3508 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3509 facility_name(zl->facility), zl->ident);
3510 vty_out(vty, "%s", VTY_NEWLINE);
3511
3512 vty_out(vty, "Stdout logging: ");
3513 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3514 vty_out(vty, "disabled");
3515 else
3516 vty_out(vty, "level %s",
3517 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3518 vty_out(vty, "%s", VTY_NEWLINE);
3519
3520 vty_out(vty, "Monitor logging: ");
3521 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3522 vty_out(vty, "disabled");
3523 else
3524 vty_out(vty, "level %s",
3525 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3526 vty_out(vty, "%s", VTY_NEWLINE);
3527
3528 vty_out(vty, "File logging: ");
3529 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3530 vty_out(vty, "disabled");
3531 else
3532 vty_out(vty, "level %s, filename %s",
3533 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3534 zl->filename);
3535 vty_out(vty, "%s", VTY_NEWLINE);
3536
3537 vty_out(vty, "Protocol name: %s%s",
3538 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3539 vty_out(vty, "Record priority: %s%s",
3540 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3541
3542 return CMD_SUCCESS;
3543}
3544
3545DEFUN(config_log_stdout,
3546 config_log_stdout_cmd,
3547 "log stdout", "Logging control\n" "Set stdout logging level\n")
3548{
3549 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3550 return CMD_SUCCESS;
3551}
3552
3553DEFUN(config_log_stdout_level,
3554 config_log_stdout_level_cmd,
3555 "log stdout " LOG_LEVELS,
3556 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3557{
3558 int level;
3559
3560 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3561 return CMD_ERR_NO_MATCH;
3562 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3563 return CMD_SUCCESS;
3564}
3565
3566DEFUN(no_config_log_stdout,
3567 no_config_log_stdout_cmd,
3568 "no log stdout [LEVEL]",
3569 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3570{
3571 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3572 return CMD_SUCCESS;
3573}
3574
3575DEFUN(config_log_monitor,
3576 config_log_monitor_cmd,
3577 "log monitor",
3578 "Logging control\n" "Set terminal line (monitor) logging level\n")
3579{
3580 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3581 return CMD_SUCCESS;
3582}
3583
3584DEFUN(config_log_monitor_level,
3585 config_log_monitor_level_cmd,
3586 "log monitor " LOG_LEVELS,
3587 "Logging control\n"
3588 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3589{
3590 int level;
3591
3592 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3593 return CMD_ERR_NO_MATCH;
3594 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3595 return CMD_SUCCESS;
3596}
3597
3598DEFUN(no_config_log_monitor,
3599 no_config_log_monitor_cmd,
3600 "no log monitor [LEVEL]",
3601 NO_STR
3602 "Logging control\n"
3603 "Disable terminal line (monitor) logging\n" "Logging level\n")
3604{
3605 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3606 return CMD_SUCCESS;
3607}
3608
3609static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3610{
3611 int ret;
3612 char *p = NULL;
3613 const char *fullpath;
3614
3615 /* Path detection. */
3616 if (!IS_DIRECTORY_SEP(*fname)) {
3617 char cwd[MAXPATHLEN + 1];
3618 cwd[MAXPATHLEN] = '\0';
3619
3620 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3621 zlog_err("config_log_file: Unable to alloc mem!");
3622 return CMD_WARNING;
3623 }
3624
3625 if ((p = _talloc_zero(tall_vcmd_ctx,
3626 strlen(cwd) + strlen(fname) + 2),
3627 "set_log_file")
3628 == NULL) {
3629 zlog_err("config_log_file: Unable to alloc mem!");
3630 return CMD_WARNING;
3631 }
3632 sprintf(p, "%s/%s", cwd, fname);
3633 fullpath = p;
3634 } else
3635 fullpath = fname;
3636
3637 ret = zlog_set_file(NULL, fullpath, loglevel);
3638
3639 if (p)
3640 talloc_free(p);
3641
3642 if (!ret) {
3643 vty_out(vty, "can't open logfile %s\n", fname);
3644 return CMD_WARNING;
3645 }
3646
3647 if (host.logfile)
3648 talloc_free(host.logfile);
3649
3650 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3651
3652 return CMD_SUCCESS;
3653}
3654
3655DEFUN(config_log_file,
3656 config_log_file_cmd,
3657 "log file FILENAME",
3658 "Logging control\n" "Logging to file\n" "Logging filename\n")
3659{
3660 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3661}
3662
3663DEFUN(config_log_file_level,
3664 config_log_file_level_cmd,
3665 "log file FILENAME " LOG_LEVELS,
3666 "Logging control\n"
3667 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3668{
3669 int level;
3670
3671 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3672 return CMD_ERR_NO_MATCH;
3673 return set_log_file(vty, argv[0], level);
3674}
3675
3676DEFUN(no_config_log_file,
3677 no_config_log_file_cmd,
3678 "no log file [FILENAME]",
3679 NO_STR
3680 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3681{
3682 zlog_reset_file(NULL);
3683
3684 if (host.logfile)
3685 talloc_free(host.logfile);
3686
3687 host.logfile = NULL;
3688
3689 return CMD_SUCCESS;
3690}
3691
3692ALIAS(no_config_log_file,
3693 no_config_log_file_level_cmd,
3694 "no log file FILENAME LEVEL",
3695 NO_STR
3696 "Logging control\n"
3697 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3698
3699 DEFUN(config_log_syslog,
3700 config_log_syslog_cmd,
3701 "log syslog", "Logging control\n" "Set syslog logging level\n")
3702{
3703 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3704 return CMD_SUCCESS;
3705}
3706
3707DEFUN(config_log_syslog_level,
3708 config_log_syslog_level_cmd,
3709 "log syslog " LOG_LEVELS,
3710 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3711{
3712 int level;
3713
3714 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3715 return CMD_ERR_NO_MATCH;
3716 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3717 return CMD_SUCCESS;
3718}
3719
3720DEFUN_DEPRECATED(config_log_syslog_facility,
3721 config_log_syslog_facility_cmd,
3722 "log syslog facility " LOG_FACILITIES,
3723 "Logging control\n"
3724 "Logging goes to syslog\n"
3725 "(Deprecated) Facility parameter for syslog messages\n"
3726 LOG_FACILITY_DESC)
3727{
3728 int facility;
3729
3730 if ((facility = facility_match(argv[0])) < 0)
3731 return CMD_ERR_NO_MATCH;
3732
3733 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3734 zlog_default->facility = facility;
3735 return CMD_SUCCESS;
3736}
3737
3738DEFUN(no_config_log_syslog,
3739 no_config_log_syslog_cmd,
3740 "no log syslog [LEVEL]",
3741 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3742{
3743 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3744 return CMD_SUCCESS;
3745}
3746
3747ALIAS(no_config_log_syslog,
3748 no_config_log_syslog_facility_cmd,
3749 "no log syslog facility " LOG_FACILITIES,
3750 NO_STR
3751 "Logging control\n"
3752 "Logging goes to syslog\n"
3753 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3754
3755 DEFUN(config_log_facility,
3756 config_log_facility_cmd,
3757 "log facility " LOG_FACILITIES,
3758 "Logging control\n"
3759 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3760{
3761 int facility;
3762
3763 if ((facility = facility_match(argv[0])) < 0)
3764 return CMD_ERR_NO_MATCH;
3765 zlog_default->facility = facility;
3766 return CMD_SUCCESS;
3767}
3768
3769DEFUN(no_config_log_facility,
3770 no_config_log_facility_cmd,
3771 "no log facility [FACILITY]",
3772 NO_STR
3773 "Logging control\n"
3774 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3775{
3776 zlog_default->facility = LOG_DAEMON;
3777 return CMD_SUCCESS;
3778}
3779
3780DEFUN_DEPRECATED(config_log_trap,
3781 config_log_trap_cmd,
3782 "log trap " LOG_LEVELS,
3783 "Logging control\n"
3784 "(Deprecated) Set logging level and default for all destinations\n"
3785 LOG_LEVEL_DESC)
3786{
3787 int new_level;
3788 int i;
3789
3790 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3791 return CMD_ERR_NO_MATCH;
3792
3793 zlog_default->default_lvl = new_level;
3794 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3795 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3796 zlog_default->maxlvl[i] = new_level;
3797 return CMD_SUCCESS;
3798}
3799
3800DEFUN_DEPRECATED(no_config_log_trap,
3801 no_config_log_trap_cmd,
3802 "no log trap [LEVEL]",
3803 NO_STR
3804 "Logging control\n"
3805 "Permit all logging information\n" "Logging level\n")
3806{
3807 zlog_default->default_lvl = LOG_DEBUG;
3808 return CMD_SUCCESS;
3809}
3810
3811DEFUN(config_log_record_priority,
3812 config_log_record_priority_cmd,
3813 "log record-priority",
3814 "Logging control\n"
3815 "Log the priority of the message within the message\n")
3816{
3817 zlog_default->record_priority = 1;
3818 return CMD_SUCCESS;
3819}
3820
3821DEFUN(no_config_log_record_priority,
3822 no_config_log_record_priority_cmd,
3823 "no log record-priority",
3824 NO_STR
3825 "Logging control\n"
3826 "Do not log the priority of the message within the message\n")
3827{
3828 zlog_default->record_priority = 0;
3829 return CMD_SUCCESS;
3830}
3831#endif
3832
3833DEFUN(banner_motd_file,
3834 banner_motd_file_cmd,
3835 "banner motd file [FILE]",
3836 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3837{
3838 if (host.motdfile)
3839 talloc_free(host.motdfile);
3840 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3841
3842 return CMD_SUCCESS;
3843}
3844
3845DEFUN(banner_motd_default,
3846 banner_motd_default_cmd,
3847 "banner motd default",
3848 "Set banner string\n" "Strings for motd\n" "Default string\n")
3849{
3850 host.motd = default_motd;
3851 return CMD_SUCCESS;
3852}
3853
3854DEFUN(no_banner_motd,
3855 no_banner_motd_cmd,
3856 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3857{
3858 host.motd = NULL;
3859 if (host.motdfile)
3860 talloc_free(host.motdfile);
3861 host.motdfile = NULL;
3862 return CMD_SUCCESS;
3863}
3864
3865/* Set config filename. Called from vty.c */
3866void host_config_set(const char *filename)
3867{
3868 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3869}
3870
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003871/*! Deprecated, now happens implicitly when calling install_node().
3872 * Users of the API may still attempt to call this function, hence
3873 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003874void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003875{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003876}
3877
3878/*! Deprecated, now happens implicitly when calling install_node().
3879 * Users of the API may still attempt to call this function, hence
3880 * leave it here as a no-op. */
3881void vty_install_default(int node)
3882{
3883}
3884
3885/*! Install common commands like 'exit' and 'list'. */
3886static void install_basic_node_commands(int node)
3887{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003888 install_element(node, &config_help_cmd);
3889 install_element(node, &config_list_cmd);
3890
3891 install_element(node, &config_write_terminal_cmd);
3892 install_element(node, &config_write_file_cmd);
3893 install_element(node, &config_write_memory_cmd);
3894 install_element(node, &config_write_cmd);
3895 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003896
3897 install_element(node, &config_exit_cmd);
3898
3899 if (node >= CONFIG_NODE) {
3900 /* It's not a top node. */
3901 install_element(node, &config_end_cmd);
3902 }
3903}
3904
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003905/*! Return true if a node is installed by install_basic_node_commands(), so
3906 * that we can avoid repeating them for each and every node during 'show
3907 * running-config' */
3908static bool vty_command_is_common(struct cmd_element *cmd)
3909{
3910 if (cmd == &config_help_cmd
3911 || cmd == &config_list_cmd
3912 || cmd == &config_write_terminal_cmd
3913 || cmd == &config_write_file_cmd
3914 || cmd == &config_write_memory_cmd
3915 || cmd == &config_write_cmd
3916 || cmd == &show_running_config_cmd
3917 || cmd == &config_exit_cmd
3918 || cmd == &config_end_cmd)
3919 return true;
3920 return false;
3921}
3922
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003923/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003924 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003925 * \param[in] vty the vty of the code
3926 * \param[in] filename where to store the file
3927 * \return 0 in case of success.
3928 *
3929 * If the filename already exists create a filename.sav
3930 * version with the current code.
3931 *
3932 */
3933int osmo_vty_write_config_file(const char *filename)
3934{
3935 char *failed_file;
3936 int rc;
3937
3938 rc = write_config_file(filename, &failed_file);
3939 talloc_free(failed_file);
3940 return rc;
3941}
3942
3943/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003944 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003945 * \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_save_config_file(void)
3952{
3953 char *failed_file;
3954 int rc;
3955
3956 if (host.config == NULL)
3957 return -7;
3958
3959 rc = write_config_file(host.config, &failed_file);
3960 talloc_free(failed_file);
3961 return rc;
3962}
3963
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003964/* Initialize command interface. Install basic nodes and commands. */
3965void cmd_init(int terminal)
3966{
3967 /* Allocate initial top vector of commands. */
3968 cmdvec = vector_init(VECTOR_MIN_SIZE);
3969
3970 /* Default host value settings. */
3971 host.name = NULL;
3972 host.password = NULL;
3973 host.enable = NULL;
3974 host.logfile = NULL;
3975 host.config = NULL;
3976 host.lines = -1;
3977 host.motd = default_motd;
3978 host.motdfile = NULL;
3979
3980 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003981 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003982 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003983 install_node_bare(&auth_node, NULL);
3984 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003985 install_node(&config_node, config_write_host);
3986
3987 /* Each node's basic commands. */
3988 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003989 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003990 if (terminal) {
3991 install_element(VIEW_NODE, &config_list_cmd);
3992 install_element(VIEW_NODE, &config_exit_cmd);
3993 install_element(VIEW_NODE, &config_help_cmd);
3994 install_element(VIEW_NODE, &config_enable_cmd);
3995 install_element(VIEW_NODE, &config_terminal_length_cmd);
3996 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3997 install_element(VIEW_NODE, &echo_cmd);
3998 }
3999
4000 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004001 install_element(ENABLE_NODE, &config_disable_cmd);
4002 install_element(ENABLE_NODE, &config_terminal_cmd);
4003 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4004 }
4005 install_element (ENABLE_NODE, &show_startup_config_cmd);
4006 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01004007 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004008
4009 if (terminal) {
4010 install_element(ENABLE_NODE, &config_terminal_length_cmd);
4011 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4012 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004013 }
4014
4015 install_element(CONFIG_NODE, &hostname_cmd);
4016 install_element(CONFIG_NODE, &no_hostname_cmd);
4017
4018 if (terminal) {
4019 install_element(CONFIG_NODE, &password_cmd);
4020 install_element(CONFIG_NODE, &password_text_cmd);
4021 install_element(CONFIG_NODE, &enable_password_cmd);
4022 install_element(CONFIG_NODE, &enable_password_text_cmd);
4023 install_element(CONFIG_NODE, &no_enable_password_cmd);
4024
4025#ifdef VTY_CRYPT_PW
4026 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
4027 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
4028#endif
4029 install_element(CONFIG_NODE, &banner_motd_default_cmd);
4030 install_element(CONFIG_NODE, &banner_motd_file_cmd);
4031 install_element(CONFIG_NODE, &no_banner_motd_cmd);
4032 install_element(CONFIG_NODE, &service_terminal_length_cmd);
4033 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
4034
4035 }
4036 srand(time(NULL));
4037}
Harald Welte7acb30c2011-08-17 17:13:48 +02004038
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004039/*! @} */