blob: 30c39a721ee795d498effcc996a72d8317dee61e [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" },
629 { 0, NULL }
630};
631
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100632/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200633 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100634 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200635static 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 +0100636{
637 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700638 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100639
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200640 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700641
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700642 /* Print global attributes and their description */
643 if (cmd->attr != 0x00) { /* ... if at least one flag is set */
644 print_func(data, " <attributes scope='global'>%s", newline);
645
646 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
647 char *xml_att_desc;
648
649 if (~cmd->attr & cmd_attr_desc[i].value)
650 continue;
651
652 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
653 print_func(data, " <attribute doc='%s' />%s",
654 xml_att_desc, newline);
655 talloc_free(xml_att_desc);
656 }
657
658 print_func(data, " </attributes>%s", newline);
659 }
660
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700661 /* Print application specific attributes and their description */
662 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
663 print_func(data, " <attributes scope='application'>%s", newline);
664
665 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
666 char *xml_att_desc;
667 char flag;
668
669 /* Skip attribute if *not* set */
670 if (~cmd->usrattr & (1 << i))
671 continue;
672
673 xml_att_desc = xml_escape(host.app_info->usr_attr_desc[i]);
674 print_func(data, " <attribute doc='%s'", xml_att_desc);
675 talloc_free(xml_att_desc);
676
677 if ((flag = host.app_info->usr_attr_letters[i]) != '\0')
678 print_func(data, " flag='%c'", flag);
679 print_func(data, " />%s", newline);
680 }
681
682 print_func(data, " </attributes>%s", newline);
683 }
684
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200685 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100686
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700687 for (i = 0; i < vector_count(cmd->strvec); ++i) {
688 vector descvec = vector_slot(cmd->strvec, i);
689 int j;
690 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100691 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700692 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100693 if (desc == NULL)
694 continue;
695
696 xml_param = xml_escape(desc->cmd);
697 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200698 print_func(data, " <param name='%s' doc='%s' />%s",
699 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100700 talloc_free(xml_param);
701 talloc_free(xml_doc);
702 }
703 }
704
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200705 print_func(data, " </params>%s", newline);
706 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100707
708 talloc_free(xml_string);
709 return 0;
710}
711
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200712static bool vty_command_is_common(struct cmd_element *cmd);
713
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100714/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200715 * 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 +0100716 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200717static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100718{
719 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200720 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100721
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200722 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100723
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200724 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200725 print_func(data, " <node id='_common_cmds_'>%s", newline);
726 print_func(data, " <name>Common Commands</name>%s", newline);
727 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
728 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200729 for (i = 0; i < vector_active(cmdvec); ++i) {
730 struct cmd_node *cnode;
731 cnode = vector_slot(cmdvec, i);
732 if (!cnode)
733 continue;
734 if (cnode->node != CONFIG_NODE)
735 continue;
736
737 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
738 struct cmd_element *elem;
739 elem = vector_slot(cnode->cmd_vector, j);
740 if (!vty_command_is_common(elem))
741 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200742 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200743 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200744 }
745 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200746 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200747
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100748 for (i = 0; i < vector_active(cmdvec); ++i) {
749 struct cmd_node *cnode;
750 cnode = vector_slot(cmdvec, i);
751 if (!cnode)
752 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200753 if (vector_active(cnode->cmd_vector) < 1)
754 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100755
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200756 /* De-dup node IDs: how many times has this same name been used before? Count the first
757 * occurence as _1 and omit that first suffix, so that the first occurence is called
758 * 'name', the second becomes 'name_2', then 'name_3', ... */
759 same_name_count = 1;
760 for (j = 0; j < i; ++j) {
761 struct cmd_node *cnode2;
762 cnode2 = vector_slot(cmdvec, j);
763 if (!cnode2)
764 continue;
765 if (strcmp(cnode->name, cnode2->name) == 0)
766 same_name_count ++;
767 }
768
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200769 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200770 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200771 print_func(data, "_%d", same_name_count);
772 print_func(data, "'>%s", newline);
773 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100774
775 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
776 struct cmd_element *elem;
777 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200778 if (vty_command_is_common(elem))
779 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200780 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200781 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100782 }
783
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200784 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100785 }
786
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200787 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100788
789 return 0;
790}
791
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200792static int print_func_vty(void *data, const char *format, ...)
793{
794 struct vty *vty = data;
795 va_list args;
796 int rc;
797 va_start(args, format);
798 rc = vty_out_va(vty, format, args);
799 va_end(args);
800 return rc;
801}
802
803static int vty_dump_xml_ref_to_vty(struct vty *vty)
804{
805 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
806}
807
808static int print_func_stream(void *data, const char *format, ...)
809{
810 va_list args;
811 int rc;
812 va_start(args, format);
813 rc = vfprintf((FILE*)data, format, args);
814 va_end(args);
815 return rc;
816}
817
818/*! Print the XML reference of all VTY nodes to the given stream.
819 */
820int vty_dump_xml_ref(FILE *stream)
821{
822 return vty_dump_nodes(print_func_stream, stream, "\n");
823}
824
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200825/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100826static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
827{
828 int i;
829
830 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
831 struct cmd_element *elem;
832 elem = vector_slot(cnode->cmd_vector, i);
833 if (!elem->string)
834 continue;
835 if (!strcmp(elem->string, cmdstring))
836 return 1;
837 }
838 return 0;
839}
840
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200841/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200842 * \param[in] ntype Node Type
843 * \param[cmd] element to be installed
844 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000845void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200846{
847 struct cmd_node *cnode;
848
849 cnode = vector_slot(cmdvec, ntype);
850
Harald Weltea99d45a2015-11-12 13:48:23 +0100851 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100852 /* ensure no _identical_ command has been registered at this
853 * node so far */
854 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200855
856 vector_set(cnode->cmd_vector, cmd);
857
858 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
859 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
860}
861
862/* Install a command into VIEW and ENABLE node */
863void install_element_ve(struct cmd_element *cmd)
864{
865 install_element(VIEW_NODE, cmd);
866 install_element(ENABLE_NODE, cmd);
867}
868
869#ifdef VTY_CRYPT_PW
870static unsigned char itoa64[] =
871 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
872
873static void to64(char *s, long v, int n)
874{
875 while (--n >= 0) {
876 *s++ = itoa64[v & 0x3f];
877 v >>= 6;
878 }
879}
880
881static char *zencrypt(const char *passwd)
882{
883 char salt[6];
884 struct timeval tv;
885 char *crypt(const char *, const char *);
886
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200887 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200888
889 to64(&salt[0], random(), 3);
890 to64(&salt[3], tv.tv_usec, 3);
891 salt[5] = '\0';
892
893 return crypt(passwd, salt);
894}
895#endif
896
897/* This function write configuration of this host. */
898static int config_write_host(struct vty *vty)
899{
900 if (host.name)
901 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
902
903 if (host.encrypt) {
904 if (host.password_encrypt)
905 vty_out(vty, "password 8 %s%s", host.password_encrypt,
906 VTY_NEWLINE);
907 if (host.enable_encrypt)
908 vty_out(vty, "enable password 8 %s%s",
909 host.enable_encrypt, VTY_NEWLINE);
910 } else {
911 if (host.password)
912 vty_out(vty, "password %s%s", host.password,
913 VTY_NEWLINE);
914 if (host.enable)
915 vty_out(vty, "enable password %s%s", host.enable,
916 VTY_NEWLINE);
917 }
918
919 if (host.advanced)
920 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
921
922 if (host.encrypt)
923 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
924
925 if (host.lines >= 0)
926 vty_out(vty, "service terminal-length %d%s", host.lines,
927 VTY_NEWLINE);
928
929 if (host.motdfile)
930 vty_out(vty, "banner motd file %s%s", host.motdfile,
931 VTY_NEWLINE);
932 else if (!host.motd)
933 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
934
935 return 1;
936}
937
938/* Utility function for getting command vector. */
939static vector cmd_node_vector(vector v, enum node_type ntype)
940{
941 struct cmd_node *cnode = vector_slot(v, ntype);
942 return cnode->cmd_vector;
943}
944
945/* Completion match types. */
946enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200947 NO_MATCH = 0,
948 ANY_MATCH,
949 EXTEND_MATCH,
950 IPV4_PREFIX_MATCH,
951 IPV4_MATCH,
952 IPV6_PREFIX_MATCH,
953 IPV6_MATCH,
954 RANGE_MATCH,
955 VARARG_MATCH,
956 PARTLY_MATCH,
957 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200958};
959
960static enum match_type cmd_ipv4_match(const char *str)
961{
962 const char *sp;
963 int dots = 0, nums = 0;
964 char buf[4];
965
966 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200967 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200968
969 for (;;) {
970 memset(buf, 0, sizeof(buf));
971 sp = str;
972 while (*str != '\0') {
973 if (*str == '.') {
974 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200975 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200976
977 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200978 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200979
980 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200981 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200982
983 dots++;
984 break;
985 }
986 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200987 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200988
989 str++;
990 }
991
992 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200993 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200994
995 strncpy(buf, sp, str - sp);
996 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200997 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200998
999 nums++;
1000
1001 if (*str == '\0')
1002 break;
1003
1004 str++;
1005 }
1006
1007 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001008 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001009
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001010 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001011}
1012
1013static enum match_type cmd_ipv4_prefix_match(const char *str)
1014{
1015 const char *sp;
1016 int dots = 0;
1017 char buf[4];
1018
1019 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001020 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001021
1022 for (;;) {
1023 memset(buf, 0, sizeof(buf));
1024 sp = str;
1025 while (*str != '\0' && *str != '/') {
1026 if (*str == '.') {
1027 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001028 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001029
1030 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001031 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001032
1033 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001034 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001035
1036 dots++;
1037 break;
1038 }
1039
1040 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001041 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001042
1043 str++;
1044 }
1045
1046 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001047 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001048
1049 strncpy(buf, sp, str - sp);
1050 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001051 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001052
1053 if (dots == 3) {
1054 if (*str == '/') {
1055 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001056 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001057
1058 str++;
1059 break;
1060 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001061 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001062 }
1063
1064 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001065 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001066
1067 str++;
1068 }
1069
1070 sp = str;
1071 while (*str != '\0') {
1072 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001073 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001074
1075 str++;
1076 }
1077
1078 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001079 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001080
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001081 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001082}
1083
1084#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1085#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1086#define STATE_START 1
1087#define STATE_COLON 2
1088#define STATE_DOUBLE 3
1089#define STATE_ADDR 4
1090#define STATE_DOT 5
1091#define STATE_SLASH 6
1092#define STATE_MASK 7
1093
1094#ifdef HAVE_IPV6
1095
1096static enum match_type cmd_ipv6_match(const char *str)
1097{
1098 int state = STATE_START;
1099 int colons = 0, nums = 0, double_colon = 0;
1100 const char *sp = NULL;
1101 struct sockaddr_in6 sin6_dummy;
1102 int ret;
1103
1104 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001105 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001106
1107 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001108 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001109
1110 /* use inet_pton that has a better support,
1111 * for example inet_pton can support the automatic addresses:
1112 * ::1.2.3.4
1113 */
1114 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1115
1116 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001117 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001118
1119 while (*str != '\0') {
1120 switch (state) {
1121 case STATE_START:
1122 if (*str == ':') {
1123 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001124 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125 colons--;
1126 state = STATE_COLON;
1127 } else {
1128 sp = str;
1129 state = STATE_ADDR;
1130 }
1131
1132 continue;
1133 case STATE_COLON:
1134 colons++;
1135 if (*(str + 1) == ':')
1136 state = STATE_DOUBLE;
1137 else {
1138 sp = str + 1;
1139 state = STATE_ADDR;
1140 }
1141 break;
1142 case STATE_DOUBLE:
1143 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001144 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001145
1146 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148 else {
1149 if (*(str + 1) != '\0')
1150 colons++;
1151 sp = str + 1;
1152 state = STATE_ADDR;
1153 }
1154
1155 double_colon++;
1156 nums++;
1157 break;
1158 case STATE_ADDR:
1159 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1160 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001161 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001162
1163 nums++;
1164 state = STATE_COLON;
1165 }
1166 if (*(str + 1) == '.')
1167 state = STATE_DOT;
1168 break;
1169 case STATE_DOT:
1170 state = STATE_ADDR;
1171 break;
1172 default:
1173 break;
1174 }
1175
1176 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001177 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001178
1179 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001180 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001181
1182 str++;
1183 }
1184
1185#if 0
1186 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001187 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001188#endif /* 0 */
1189
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001190 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001191}
1192
1193static enum match_type cmd_ipv6_prefix_match(const char *str)
1194{
1195 int state = STATE_START;
1196 int colons = 0, nums = 0, double_colon = 0;
1197 int mask;
1198 const char *sp = NULL;
1199 char *endptr = NULL;
1200
1201 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001202 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001203
1204 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001205 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001206
1207 while (*str != '\0' && state != STATE_MASK) {
1208 switch (state) {
1209 case STATE_START:
1210 if (*str == ':') {
1211 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001212 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001213 colons--;
1214 state = STATE_COLON;
1215 } else {
1216 sp = str;
1217 state = STATE_ADDR;
1218 }
1219
1220 continue;
1221 case STATE_COLON:
1222 colons++;
1223 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001224 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001225 else if (*(str + 1) == ':')
1226 state = STATE_DOUBLE;
1227 else {
1228 sp = str + 1;
1229 state = STATE_ADDR;
1230 }
1231 break;
1232 case STATE_DOUBLE:
1233 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001234 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235
1236 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001237 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001238 else {
1239 if (*(str + 1) != '\0' && *(str + 1) != '/')
1240 colons++;
1241 sp = str + 1;
1242
1243 if (*(str + 1) == '/')
1244 state = STATE_SLASH;
1245 else
1246 state = STATE_ADDR;
1247 }
1248
1249 double_colon++;
1250 nums += 1;
1251 break;
1252 case STATE_ADDR:
1253 if (*(str + 1) == ':' || *(str + 1) == '.'
1254 || *(str + 1) == '\0' || *(str + 1) == '/') {
1255 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001256 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001257
1258 for (; sp <= str; sp++)
1259 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001260 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001261
1262 nums++;
1263
1264 if (*(str + 1) == ':')
1265 state = STATE_COLON;
1266 else if (*(str + 1) == '.')
1267 state = STATE_DOT;
1268 else if (*(str + 1) == '/')
1269 state = STATE_SLASH;
1270 }
1271 break;
1272 case STATE_DOT:
1273 state = STATE_ADDR;
1274 break;
1275 case STATE_SLASH:
1276 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001277 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278
1279 state = STATE_MASK;
1280 break;
1281 default:
1282 break;
1283 }
1284
1285 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001286 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001287
1288 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001289 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001290
1291 str++;
1292 }
1293
1294 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001295 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001296
1297 mask = strtol(str, &endptr, 10);
1298 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001299 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001300
1301 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001302 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001303
1304/* I don't know why mask < 13 makes command match partly.
1305 Forgive me to make this comments. I Want to set static default route
1306 because of lack of function to originate default in ospf6d; sorry
1307 yasu
1308 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001309 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001310*/
1311
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001312 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001313}
1314
1315#endif /* HAVE_IPV6 */
1316
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001317
1318#if ULONG_MAX == 18446744073709551615UL
1319#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1320#elif ULONG_MAX == 4294967295UL
1321#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1322#else
1323#error "ULONG_MAX not defined!"
1324#endif
1325
1326#if LONG_MAX == 9223372036854775807L
1327#define DECIMAL_STRLEN_MAX_SIGNED 19
1328#elif LONG_MAX == 2147483647L
1329#define DECIMAL_STRLEN_MAX_SIGNED 10
1330#else
1331#error "LONG_MAX not defined!"
1332#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001333
1334static int cmd_range_match(const char *range, const char *str)
1335{
1336 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001337 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001338 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001339
1340 if (str == NULL)
1341 return 1;
1342
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001343 if (range[1] == '-') {
1344 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001345
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001346 val = strtol(str, &endptr, 10);
1347 if (*endptr != '\0')
1348 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001349
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001350 range += 2;
1351 p = strchr(range, '-');
1352 if (p == NULL)
1353 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001354 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001355 return 0;
1356 strncpy(buf, range, p - range);
1357 buf[p - range] = '\0';
1358 min = -strtol(buf, &endptr, 10);
1359 if (*endptr != '\0')
1360 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001361
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001362 range = p + 1;
1363 p = strchr(range, '>');
1364 if (p == NULL)
1365 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001366 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001367 return 0;
1368 strncpy(buf, range, p - range);
1369 buf[p - range] = '\0';
1370 max = strtol(buf, &endptr, 10);
1371 if (*endptr != '\0')
1372 return 0;
1373
1374 if (val < min || val > max)
1375 return 0;
1376 } else {
1377 unsigned long min, max, val;
1378
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001379 if (str[0] == '-')
1380 return 0;
1381
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001382 val = strtoul(str, &endptr, 10);
1383 if (*endptr != '\0')
1384 return 0;
1385
1386 range++;
1387 p = strchr(range, '-');
1388 if (p == NULL)
1389 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001390 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001391 return 0;
1392 strncpy(buf, range, p - range);
1393 buf[p - range] = '\0';
1394 min = strtoul(buf, &endptr, 10);
1395 if (*endptr != '\0')
1396 return 0;
1397
1398 range = p + 1;
1399 p = strchr(range, '>');
1400 if (p == NULL)
1401 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001402 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001403 return 0;
1404 strncpy(buf, range, p - range);
1405 buf[p - range] = '\0';
1406 max = strtoul(buf, &endptr, 10);
1407 if (*endptr != '\0')
1408 return 0;
1409
1410 if (val < min || val > max)
1411 return 0;
1412 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001413
1414 return 1;
1415}
1416
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001417/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001418static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001419{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001420 /* we've got "[blah]". We want to strip off the []s and redo the
1421 * match check for "blah"
1422 */
1423 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001424
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001425 if (len < 3)
1426 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001427
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001428 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001429}
1430
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001431static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001432cmd_match(const char *str, const char *command,
1433 enum match_type min, bool recur)
1434{
1435
1436 if (recur && CMD_OPTION(str))
1437 {
1438 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001439 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001440
1441 /* this would be a bug in a command, however handle it gracefully
1442 * as it we only discover it if a user tries to run it
1443 */
1444 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001445 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001446
1447 ret = cmd_match(tmp, command, min, false);
1448
1449 talloc_free(tmp);
1450
1451 return ret;
1452 }
1453 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001454 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001455 else if (CMD_RANGE(str))
1456 {
1457 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001458 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001459 }
1460#ifdef HAVE_IPV6
1461 else if (CMD_IPV6(str))
1462 {
1463 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001464 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001465 }
1466 else if (CMD_IPV6_PREFIX(str))
1467 {
1468 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001469 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001470 }
1471#endif /* HAVE_IPV6 */
1472 else if (CMD_IPV4(str))
1473 {
1474 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001475 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001476 }
1477 else if (CMD_IPV4_PREFIX(str))
1478 {
1479 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001480 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001481 }
1482 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001483 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001484 else if (strncmp(command, str, strlen(command)) == 0)
1485 {
1486 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001487 return EXACT_MATCH;
1488 else if (PARTLY_MATCH >= min)
1489 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001490 }
1491
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001492 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001493}
1494
1495/* Filter vector at the specified index and by the given command string, to
1496 * the desired matching level (thus allowing part matches), and return match
1497 * type flag.
1498 */
1499static enum match_type
1500cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001501{
1502 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001503 struct cmd_element *cmd_element;
1504 enum match_type match_type;
1505 vector descvec;
1506 struct desc *desc;
1507
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001508 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001509
1510 /* If command and cmd_element string does not match set NULL to vector */
1511 for (i = 0; i < vector_active(v); i++)
1512 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001513 if (index >= vector_active(cmd_element->strvec))
1514 vector_slot(v, i) = NULL;
1515 else {
1516 unsigned int j;
1517 int matched = 0;
1518
1519 descvec =
1520 vector_slot(cmd_element->strvec, index);
1521
1522 for (j = 0; j < vector_active(descvec); j++)
1523 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001524 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001525
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001526 ret = cmd_match (desc->cmd, command, level, true);
1527
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001528 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001529 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001530
1531 if (match_type < ret)
1532 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001533 }
1534 if (!matched)
1535 vector_slot(v, i) = NULL;
1536 }
1537 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001538
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001539 if (match_type == NO_MATCH)
1540 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001541
1542 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1543 * go again and filter out commands whose argument (at this index) is
1544 * 'weaker'. E.g., if we have 2 commands:
1545 *
1546 * foo bar <1-255>
1547 * foo bar BLAH
1548 *
1549 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001550 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001551 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1552 *
1553 * If we don't do a 2nd pass and filter it out, the higher-layers will
1554 * consider this to be ambiguous.
1555 */
1556 for (i = 0; i < vector_active(v); i++)
1557 if ((cmd_element = vector_slot(v, i)) != NULL) {
1558 if (index >= vector_active(cmd_element->strvec))
1559 vector_slot(v, i) = NULL;
1560 else {
1561 unsigned int j;
1562 int matched = 0;
1563
1564 descvec =
1565 vector_slot(cmd_element->strvec, index);
1566
1567 for (j = 0; j < vector_active(descvec); j++)
1568 if ((desc = vector_slot(descvec, j))) {
1569 enum match_type ret;
1570
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001571 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001572
1573 if (ret >= match_type)
1574 matched++;
1575 }
1576 if (!matched)
1577 vector_slot(v, i) = NULL;
1578 }
1579 }
1580
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001581 return match_type;
1582}
1583
1584/* Check ambiguous match */
1585static int
1586is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1587{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001588 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001589 unsigned int i;
1590 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001591 struct cmd_element *cmd_element;
1592 const char *matched = NULL;
1593 vector descvec;
1594 struct desc *desc;
1595
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001596 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1597 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1598 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1599 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1600 * that case, the string must remain allocated until this function exits or another match comes
1601 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1602 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1603 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1604 void *cmd_deopt_ctx = NULL;
1605
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001606 for (i = 0; i < vector_active(v); i++) {
1607 cmd_element = vector_slot(v, i);
1608 if (!cmd_element)
1609 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001610
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001611 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001612
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001613 descvec = vector_slot(cmd_element->strvec, index);
1614
1615 for (j = 0; j < vector_active(descvec); j++) {
1616 desc = vector_slot(descvec, j);
1617 if (!desc)
1618 continue;
1619
1620 enum match_type mtype;
1621 const char *str = desc->cmd;
1622
1623 if (CMD_OPTION(str)) {
1624 if (!cmd_deopt_ctx)
1625 cmd_deopt_ctx =
1626 talloc_named_const(tall_vty_cmd_ctx, 0,
1627 __func__);
1628 str = cmd_deopt(cmd_deopt_ctx, str);
1629 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001630 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001631 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001632
1633 switch (type) {
1634 case EXACT_MATCH:
1635 if (!(CMD_VARIABLE (str))
1636 && strcmp(command, str) == 0)
1637 match++;
1638 break;
1639 case PARTLY_MATCH:
1640 if (!(CMD_VARIABLE (str))
1641 && strncmp(command, str, strlen (command)) == 0)
1642 {
1643 if (matched
1644 && strcmp(matched,
1645 str) != 0) {
1646 ret = 1; /* There is ambiguous match. */
1647 goto free_and_return;
1648 } else
1649 matched = str;
1650 match++;
1651 }
1652 break;
1653 case RANGE_MATCH:
1654 if (cmd_range_match
1655 (str, command)) {
1656 if (matched
1657 && strcmp(matched,
1658 str) != 0) {
1659 ret = 1;
1660 goto free_and_return;
1661 } else
1662 matched = str;
1663 match++;
1664 }
1665 break;
1666#ifdef HAVE_IPV6
1667 case IPV6_MATCH:
1668 if (CMD_IPV6(str))
1669 match++;
1670 break;
1671 case IPV6_PREFIX_MATCH:
1672 if ((mtype =
1673 cmd_ipv6_prefix_match
1674 (command)) != NO_MATCH) {
1675 if (mtype == PARTLY_MATCH) {
1676 ret = 2; /* There is incomplete match. */
1677 goto free_and_return;
1678 }
1679
1680 match++;
1681 }
1682 break;
1683#endif /* HAVE_IPV6 */
1684 case IPV4_MATCH:
1685 if (CMD_IPV4(str))
1686 match++;
1687 break;
1688 case IPV4_PREFIX_MATCH:
1689 if ((mtype =
1690 cmd_ipv4_prefix_match
1691 (command)) != NO_MATCH) {
1692 if (mtype == PARTLY_MATCH) {
1693 ret = 2; /* There is incomplete match. */
1694 goto free_and_return;
1695 }
1696
1697 match++;
1698 }
1699 break;
1700 case EXTEND_MATCH:
1701 if (CMD_VARIABLE (str))
1702 match++;
1703 break;
1704 case NO_MATCH:
1705 default:
1706 break;
1707 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001708 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001709 if (!match)
1710 vector_slot(v, i) = NULL;
1711 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001712
1713free_and_return:
1714 if (cmd_deopt_ctx)
1715 talloc_free(cmd_deopt_ctx);
1716 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001717}
1718
1719/* If src matches dst return dst string, otherwise return NULL */
1720static const char *cmd_entry_function(const char *src, const char *dst)
1721{
1722 /* Skip variable arguments. */
1723 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1724 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1725 return NULL;
1726
1727 /* In case of 'command \t', given src is NULL string. */
1728 if (src == NULL)
1729 return dst;
1730
1731 /* Matched with input string. */
1732 if (strncmp(src, dst, strlen(src)) == 0)
1733 return dst;
1734
1735 return NULL;
1736}
1737
1738/* If src matches dst return dst string, otherwise return NULL */
1739/* This version will return the dst string always if it is
1740 CMD_VARIABLE for '?' key processing */
1741static const char *cmd_entry_function_desc(const char *src, const char *dst)
1742{
1743 if (CMD_VARARG(dst))
1744 return dst;
1745
1746 if (CMD_RANGE(dst)) {
1747 if (cmd_range_match(dst, src))
1748 return dst;
1749 else
1750 return NULL;
1751 }
1752#ifdef HAVE_IPV6
1753 if (CMD_IPV6(dst)) {
1754 if (cmd_ipv6_match(src))
1755 return dst;
1756 else
1757 return NULL;
1758 }
1759
1760 if (CMD_IPV6_PREFIX(dst)) {
1761 if (cmd_ipv6_prefix_match(src))
1762 return dst;
1763 else
1764 return NULL;
1765 }
1766#endif /* HAVE_IPV6 */
1767
1768 if (CMD_IPV4(dst)) {
1769 if (cmd_ipv4_match(src))
1770 return dst;
1771 else
1772 return NULL;
1773 }
1774
1775 if (CMD_IPV4_PREFIX(dst)) {
1776 if (cmd_ipv4_prefix_match(src))
1777 return dst;
1778 else
1779 return NULL;
1780 }
1781
1782 /* Optional or variable commands always match on '?' */
1783 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1784 return dst;
1785
1786 /* In case of 'command \t', given src is NULL string. */
1787 if (src == NULL)
1788 return dst;
1789
1790 if (strncmp(src, dst, strlen(src)) == 0)
1791 return dst;
1792 else
1793 return NULL;
1794}
1795
1796/* Check same string element existence. If it isn't there return
1797 1. */
1798static int cmd_unique_string(vector v, const char *str)
1799{
1800 unsigned int i;
1801 char *match;
1802
1803 for (i = 0; i < vector_active(v); i++)
1804 if ((match = vector_slot(v, i)) != NULL)
1805 if (strcmp(match, str) == 0)
1806 return 0;
1807 return 1;
1808}
1809
1810/* Compare string to description vector. If there is same string
1811 return 1 else return 0. */
1812static int desc_unique_string(vector v, const char *str)
1813{
1814 unsigned int i;
1815 struct desc *desc;
1816
1817 for (i = 0; i < vector_active(v); i++)
1818 if ((desc = vector_slot(v, i)) != NULL)
1819 if (strcmp(desc->cmd, str) == 0)
1820 return 1;
1821 return 0;
1822}
1823
1824static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1825{
1826 if (first_word != NULL &&
1827 node != AUTH_NODE &&
1828 node != VIEW_NODE &&
1829 node != AUTH_ENABLE_NODE &&
1830 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1831 return 1;
1832 return 0;
1833}
1834
1835/* '?' describe command support. */
1836static vector
1837cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1838{
1839 unsigned int i;
1840 vector cmd_vector;
1841#define INIT_MATCHVEC_SIZE 10
1842 vector matchvec;
1843 struct cmd_element *cmd_element;
1844 unsigned int index;
1845 int ret;
1846 enum match_type match;
1847 char *command;
1848 static struct desc desc_cr = { "<cr>", "" };
1849
1850 /* Set index. */
1851 if (vector_active(vline) == 0) {
1852 *status = CMD_ERR_NO_MATCH;
1853 return NULL;
1854 } else
1855 index = vector_active(vline) - 1;
1856
1857 /* Make copy vector of current node's command vector. */
1858 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1859
1860 /* Prepare match vector */
1861 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1862
1863 /* Filter commands. */
1864 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001865 for (i = 0; i < index; i++) {
1866 command = vector_slot(vline, i);
1867 if (!command)
1868 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001869
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001870 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001871
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001872 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001873 struct cmd_element *cmd_element;
1874 vector descvec;
1875 unsigned int j, k;
1876
1877 for (j = 0; j < vector_active(cmd_vector); j++)
1878 if ((cmd_element =
1879 vector_slot(cmd_vector, j)) != NULL
1880 &&
1881 (vector_active(cmd_element->strvec))) {
1882 descvec =
1883 vector_slot(cmd_element->
1884 strvec,
1885 vector_active
1886 (cmd_element->
1887 strvec) - 1);
1888 for (k = 0;
1889 k < vector_active(descvec);
1890 k++) {
1891 struct desc *desc =
1892 vector_slot(descvec,
1893 k);
1894 vector_set(matchvec,
1895 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001896 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001897 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001898
Harald Welte80d30fe2013-02-12 11:08:57 +01001899 vector_set(matchvec, &desc_cr);
1900 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001901
Harald Welte80d30fe2013-02-12 11:08:57 +01001902 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001903 }
1904
Harald Welte80d30fe2013-02-12 11:08:57 +01001905 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1906 match)) == 1) {
1907 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001908 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001909 *status = CMD_ERR_AMBIGUOUS;
1910 return NULL;
1911 } else if (ret == 2) {
1912 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001913 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001914 *status = CMD_ERR_NO_MATCH;
1915 return NULL;
1916 }
1917 }
1918
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001919 /* Prepare match vector */
1920 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1921
1922 /* Make sure that cmd_vector is filtered based on current word */
1923 command = vector_slot(vline, index);
1924 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001925 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001926
1927 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001928 for (i = 0; i < vector_active(cmd_vector); i++) {
1929 const char *string = NULL;
1930 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001931
Harald Welte80d30fe2013-02-12 11:08:57 +01001932 cmd_element = vector_slot(cmd_vector, i);
1933 if (!cmd_element)
1934 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001935
Harald Welted17aa592013-02-12 11:11:34 +01001936 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1937 continue;
1938
Harald Welte80d30fe2013-02-12 11:08:57 +01001939 strvec = cmd_element->strvec;
1940
1941 /* if command is NULL, index may be equal to vector_active */
1942 if (command && index >= vector_active(strvec))
1943 vector_slot(cmd_vector, i) = NULL;
1944 else {
1945 /* Check if command is completed. */
1946 if (command == NULL
1947 && index == vector_active(strvec)) {
1948 string = "<cr>";
1949 if (!desc_unique_string(matchvec, string))
1950 vector_set(matchvec, &desc_cr);
1951 } else {
1952 unsigned int j;
1953 vector descvec = vector_slot(strvec, index);
1954 struct desc *desc;
1955
1956 for (j = 0; j < vector_active(descvec); j++) {
1957 desc = vector_slot(descvec, j);
1958 if (!desc)
1959 continue;
1960 string = cmd_entry_function_desc
1961 (command, desc->cmd);
1962 if (!string)
1963 continue;
1964 /* Uniqueness check */
1965 if (!desc_unique_string(matchvec, string))
1966 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001967 }
1968 }
1969 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001970 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001971 vector_free(cmd_vector);
1972
1973 if (vector_slot(matchvec, 0) == NULL) {
1974 vector_free(matchvec);
1975 *status = CMD_ERR_NO_MATCH;
1976 } else
1977 *status = CMD_SUCCESS;
1978
1979 return matchvec;
1980}
1981
1982vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1983{
1984 vector ret;
1985
1986 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1987 enum node_type onode;
1988 vector shifted_vline;
1989 unsigned int index;
1990
1991 onode = vty->node;
1992 vty->node = ENABLE_NODE;
1993 /* We can try it on enable node, cos' the vty is authenticated */
1994
1995 shifted_vline = vector_init(vector_count(vline));
1996 /* use memcpy? */
1997 for (index = 1; index < vector_active(vline); index++) {
1998 vector_set_index(shifted_vline, index - 1,
1999 vector_lookup(vline, index));
2000 }
2001
2002 ret = cmd_describe_command_real(shifted_vline, vty, status);
2003
2004 vector_free(shifted_vline);
2005 vty->node = onode;
2006 return ret;
2007 }
2008
2009 return cmd_describe_command_real(vline, vty, status);
2010}
2011
2012/* Check LCD of matched command. */
2013static int cmd_lcd(char **matched)
2014{
2015 int i;
2016 int j;
2017 int lcd = -1;
2018 char *s1, *s2;
2019 char c1, c2;
2020
2021 if (matched[0] == NULL || matched[1] == NULL)
2022 return 0;
2023
2024 for (i = 1; matched[i] != NULL; i++) {
2025 s1 = matched[i - 1];
2026 s2 = matched[i];
2027
2028 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2029 if (c1 != c2)
2030 break;
2031
2032 if (lcd < 0)
2033 lcd = j;
2034 else {
2035 if (lcd > j)
2036 lcd = j;
2037 }
2038 }
2039 return lcd;
2040}
2041
2042/* Command line completion support. */
2043static char **cmd_complete_command_real(vector vline, struct vty *vty,
2044 int *status)
2045{
2046 unsigned int i;
2047 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2048#define INIT_MATCHVEC_SIZE 10
2049 vector matchvec;
2050 struct cmd_element *cmd_element;
2051 unsigned int index;
2052 char **match_str;
2053 struct desc *desc;
2054 vector descvec;
2055 char *command;
2056 int lcd;
2057
2058 if (vector_active(vline) == 0) {
2059 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002060 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002061 return NULL;
2062 } else
2063 index = vector_active(vline) - 1;
2064
2065 /* First, filter by preceeding command string */
2066 for (i = 0; i < index; i++)
2067 if ((command = vector_slot(vline, i))) {
2068 enum match_type match;
2069 int ret;
2070
2071 /* First try completion match, if there is exactly match return 1 */
2072 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002073 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002074
2075 /* If there is exact match then filter ambiguous match else check
2076 ambiguousness. */
2077 if ((ret =
2078 is_cmd_ambiguous(command, cmd_vector, i,
2079 match)) == 1) {
2080 vector_free(cmd_vector);
2081 *status = CMD_ERR_AMBIGUOUS;
2082 return NULL;
2083 }
2084 /*
2085 else if (ret == 2)
2086 {
2087 vector_free (cmd_vector);
2088 *status = CMD_ERR_NO_MATCH;
2089 return NULL;
2090 }
2091 */
2092 }
2093
2094 /* Prepare match vector. */
2095 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2096
2097 /* Now we got into completion */
2098 for (i = 0; i < vector_active(cmd_vector); i++)
2099 if ((cmd_element = vector_slot(cmd_vector, i))) {
2100 const char *string;
2101 vector strvec = cmd_element->strvec;
2102
2103 /* Check field length */
2104 if (index >= vector_active(strvec))
2105 vector_slot(cmd_vector, i) = NULL;
2106 else {
2107 unsigned int j;
2108
2109 descvec = vector_slot(strvec, index);
2110 for (j = 0; j < vector_active(descvec); j++)
2111 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002112 const char *cmd = desc->cmd;
2113 char *tmp = NULL;
2114
2115 if (CMD_OPTION(desc->cmd)) {
2116 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2117 cmd = tmp;
2118 }
2119 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002120 if (cmd_unique_string (matchvec, string))
2121 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002122 if (tmp)
2123 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002124 }
2125 }
2126 }
2127
2128 /* We don't need cmd_vector any more. */
2129 vector_free(cmd_vector);
2130
2131 /* No matched command */
2132 if (vector_slot(matchvec, 0) == NULL) {
2133 vector_free(matchvec);
2134
2135 /* In case of 'command \t' pattern. Do you need '?' command at
2136 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002137 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002138 *status = CMD_ERR_NOTHING_TODO;
2139 else
2140 *status = CMD_ERR_NO_MATCH;
2141 return NULL;
2142 }
2143
2144 /* Only one matched */
2145 if (vector_slot(matchvec, 1) == NULL) {
2146 match_str = (char **)matchvec->index;
2147 vector_only_wrapper_free(matchvec);
2148 *status = CMD_COMPLETE_FULL_MATCH;
2149 return match_str;
2150 }
2151 /* Make it sure last element is NULL. */
2152 vector_set(matchvec, NULL);
2153
2154 /* Check LCD of matched strings. */
2155 if (vector_slot(vline, index) != NULL) {
2156 lcd = cmd_lcd((char **)matchvec->index);
2157
2158 if (lcd) {
2159 int len = strlen(vector_slot(vline, index));
2160
2161 if (len < lcd) {
2162 char *lcdstr;
2163
2164 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2165 "complete-lcdstr");
2166 memcpy(lcdstr, matchvec->index[0], lcd);
2167 lcdstr[lcd] = '\0';
2168
2169 /* match_str = (char **) &lcdstr; */
2170
2171 /* Free matchvec. */
2172 for (i = 0; i < vector_active(matchvec); i++) {
2173 if (vector_slot(matchvec, i))
2174 talloc_free(vector_slot(matchvec, i));
2175 }
2176 vector_free(matchvec);
2177
2178 /* Make new matchvec. */
2179 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2180 vector_set(matchvec, lcdstr);
2181 match_str = (char **)matchvec->index;
2182 vector_only_wrapper_free(matchvec);
2183
2184 *status = CMD_COMPLETE_MATCH;
2185 return match_str;
2186 }
2187 }
2188 }
2189
2190 match_str = (char **)matchvec->index;
2191 vector_only_wrapper_free(matchvec);
2192 *status = CMD_COMPLETE_LIST_MATCH;
2193 return match_str;
2194}
2195
2196char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2197{
2198 char **ret;
2199
2200 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2201 enum node_type onode;
2202 vector shifted_vline;
2203 unsigned int index;
2204
2205 onode = vty->node;
2206 vty->node = ENABLE_NODE;
2207 /* We can try it on enable node, cos' the vty is authenticated */
2208
2209 shifted_vline = vector_init(vector_count(vline));
2210 /* use memcpy? */
2211 for (index = 1; index < vector_active(vline); index++) {
2212 vector_set_index(shifted_vline, index - 1,
2213 vector_lookup(vline, index));
2214 }
2215
2216 ret = cmd_complete_command_real(shifted_vline, vty, status);
2217
2218 vector_free(shifted_vline);
2219 vty->node = onode;
2220 return ret;
2221 }
2222
2223 return cmd_complete_command_real(vline, vty, status);
2224}
2225
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002226static struct vty_parent_node *vty_parent(struct vty *vty)
2227{
2228 return llist_first_entry_or_null(&vty->parent_nodes,
2229 struct vty_parent_node,
2230 entry);
2231}
2232
2233static bool vty_pop_parent(struct vty *vty)
2234{
2235 struct vty_parent_node *parent = vty_parent(vty);
2236 if (!parent)
2237 return false;
2238 llist_del(&parent->entry);
2239 vty->node = parent->node;
2240 vty->priv = parent->priv;
2241 if (vty->indent)
2242 talloc_free(vty->indent);
2243 vty->indent = parent->indent;
2244 talloc_free(parent);
2245 return true;
2246}
2247
2248static void vty_clear_parents(struct vty *vty)
2249{
2250 while (vty_pop_parent(vty));
2251}
2252
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002253/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002254/*
2255 * This function MUST eventually converge on a node when called repeatedly,
2256 * there must not be any cycles.
2257 * All 'config' nodes shall converge on CONFIG_NODE.
2258 * All other 'enable' nodes shall converge on ENABLE_NODE.
2259 * All 'view' only nodes shall converge on VIEW_NODE.
2260 * All other nodes shall converge on themselves or it must be ensured,
2261 * that the user's rights are not extended anyhow by calling this function.
2262 *
2263 * Note that these requirements also apply to all functions that are used
2264 * as go_parent_cb.
2265 * Note also that this function relies on the is_config_child callback to
2266 * recognize non-config nodes if go_parent_cb is not set.
2267 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002268int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002269{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002270 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002271 case AUTH_NODE:
2272 case VIEW_NODE:
2273 case ENABLE_NODE:
2274 case CONFIG_NODE:
2275 vty_clear_parents(vty);
2276 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002277
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002278 case AUTH_ENABLE_NODE:
2279 vty->node = VIEW_NODE;
2280 vty_clear_parents(vty);
2281 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002282
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002283 default:
2284 if (host.app_info->go_parent_cb)
2285 host.app_info->go_parent_cb(vty);
2286 vty_pop_parent(vty);
2287 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002288 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002289
2290 return vty->node;
2291}
2292
2293/* Execute command by argument vline vector. */
2294static int
2295cmd_execute_command_real(vector vline, struct vty *vty,
2296 struct cmd_element **cmd)
2297{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002298 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002299 unsigned int index;
2300 vector cmd_vector;
2301 struct cmd_element *cmd_element;
2302 struct cmd_element *matched_element;
2303 unsigned int matched_count, incomplete_count;
2304 int argc;
2305 const char *argv[CMD_ARGC_MAX];
2306 enum match_type match = 0;
2307 int varflag;
2308 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002309 int rc;
2310 /* Used for temporary storage of cmd_deopt() allocated arguments during
2311 argv[] generation */
2312 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002313
2314 /* Make copy of command elements. */
2315 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2316
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002317 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002318 if ((command = vector_slot(vline, index))) {
2319 int ret;
2320
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002321 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002322 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002323
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002324 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002325 break;
2326
2327 ret =
2328 is_cmd_ambiguous(command, cmd_vector, index, match);
2329
2330 if (ret == 1) {
2331 vector_free(cmd_vector);
2332 return CMD_ERR_AMBIGUOUS;
2333 } else if (ret == 2) {
2334 vector_free(cmd_vector);
2335 return CMD_ERR_NO_MATCH;
2336 }
2337 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002338 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002339
2340 /* Check matched count. */
2341 matched_element = NULL;
2342 matched_count = 0;
2343 incomplete_count = 0;
2344
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002345 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002346 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002347 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002348 || index >= cmd_element->cmdsize) {
2349 matched_element = cmd_element;
2350#if 0
2351 printf("DEBUG: %s\n", cmd_element->string);
2352#endif
2353 matched_count++;
2354 } else {
2355 incomplete_count++;
2356 }
2357 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002358 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002359
2360 /* Finish of using cmd_vector. */
2361 vector_free(cmd_vector);
2362
2363 /* To execute command, matched_count must be 1. */
2364 if (matched_count == 0) {
2365 if (incomplete_count)
2366 return CMD_ERR_INCOMPLETE;
2367 else
2368 return CMD_ERR_NO_MATCH;
2369 }
2370
2371 if (matched_count > 1)
2372 return CMD_ERR_AMBIGUOUS;
2373
2374 /* Argument treatment */
2375 varflag = 0;
2376 argc = 0;
2377
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002378 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2379
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002380 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002381 if (argc == CMD_ARGC_MAX) {
2382 rc = CMD_ERR_EXEED_ARGC_MAX;
2383 goto rc_free_deopt_ctx;
2384 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002385 if (varflag) {
2386 argv[argc++] = vector_slot(vline, i);
2387 continue;
2388 }
2389
2390 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002391 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002392
2393 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002394 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002395 struct desc *desc = vector_slot(descvec, 0);
2396
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002397 if (CMD_OPTION(desc->cmd)) {
2398 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2399 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2400 } else {
2401 tmp_cmd = desc->cmd;
2402 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002403
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002404 if (CMD_VARARG(tmp_cmd))
2405 varflag = 1;
2406 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002407 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002408 else if (CMD_OPTION(desc->cmd))
2409 argv[argc++] = tmp_cmd;
2410 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002411 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002412 /* multi choice argument. look up which choice
2413 the user meant (can only be one after
2414 filtering and checking for ambigous). For instance,
2415 if user typed "th" for "(two|three)" arg, we
2416 want to pass "three" in argv[]. */
2417 for (j = 0; j < vector_active(descvec); j++) {
2418 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002419 if (!desc)
2420 continue;
2421 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2422 continue;
2423 if (CMD_OPTION(desc->cmd)) {
2424 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2425 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2426 } else {
2427 tmp_cmd = desc->cmd;
2428 }
2429
2430 if(CMD_VARIABLE(tmp_cmd)) {
2431 argv[argc++] = vector_slot(vline, i);
2432 } else {
2433 argv[argc++] = tmp_cmd;
2434 }
2435 break;
2436 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002437 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002438 }
2439
2440 /* For vtysh execution. */
2441 if (cmd)
2442 *cmd = matched_element;
2443
2444 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002445 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002446 else {
2447 /* Execute matched command. */
2448 struct vty_parent_node this_node = {
2449 .node = vty->node,
2450 .priv = vty->priv,
2451 .indent = vty->indent,
2452 };
2453 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002454 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002455
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002456 /* If we have stepped down into a child node, push a parent frame.
2457 * The causality is such: we don't expect every single node entry implementation to push
2458 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2459 * a parent node. Hence if the node changed without the parent node changing, we must
2460 * have stepped into a child node. */
2461 if (vty->node != this_node.node && parent == vty_parent(vty)
2462 && vty->node > CONFIG_NODE) {
2463 /* Push the parent node. */
2464 parent = talloc_zero(vty, struct vty_parent_node);
2465 *parent = this_node;
2466 llist_add(&parent->entry, &vty->parent_nodes);
2467 }
2468 }
2469
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002470rc_free_deopt_ctx:
2471 /* Now after we called the command func, we can free temporary strings */
2472 talloc_free(cmd_deopt_ctx);
2473 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002474}
2475
2476int
2477cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2478 int vtysh)
2479{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002480 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002481 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002482
2483 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002484
2485 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2486 vector shifted_vline;
2487 unsigned int index;
2488
2489 vty->node = ENABLE_NODE;
2490 /* We can try it on enable node, cos' the vty is authenticated */
2491
2492 shifted_vline = vector_init(vector_count(vline));
2493 /* use memcpy? */
2494 for (index = 1; index < vector_active(vline); index++) {
2495 vector_set_index(shifted_vline, index - 1,
2496 vector_lookup(vline, index));
2497 }
2498
2499 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2500
2501 vector_free(shifted_vline);
2502 vty->node = onode;
2503 return ret;
2504 }
2505
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002506 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002507}
2508
2509/* Execute command by argument readline. */
2510int
2511cmd_execute_command_strict(vector vline, struct vty *vty,
2512 struct cmd_element **cmd)
2513{
2514 unsigned int i;
2515 unsigned int index;
2516 vector cmd_vector;
2517 struct cmd_element *cmd_element;
2518 struct cmd_element *matched_element;
2519 unsigned int matched_count, incomplete_count;
2520 int argc;
2521 const char *argv[CMD_ARGC_MAX];
2522 int varflag;
2523 enum match_type match = 0;
2524 char *command;
2525
2526 /* Make copy of command element */
2527 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2528
2529 for (index = 0; index < vector_active(vline); index++)
2530 if ((command = vector_slot(vline, index))) {
2531 int ret;
2532
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002533 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002534 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002535
2536 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002537 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002538 break;
2539
2540 ret =
2541 is_cmd_ambiguous(command, cmd_vector, index, match);
2542 if (ret == 1) {
2543 vector_free(cmd_vector);
2544 return CMD_ERR_AMBIGUOUS;
2545 }
2546 if (ret == 2) {
2547 vector_free(cmd_vector);
2548 return CMD_ERR_NO_MATCH;
2549 }
2550 }
2551
2552 /* Check matched count. */
2553 matched_element = NULL;
2554 matched_count = 0;
2555 incomplete_count = 0;
2556 for (i = 0; i < vector_active(cmd_vector); i++)
2557 if (vector_slot(cmd_vector, i) != NULL) {
2558 cmd_element = vector_slot(cmd_vector, i);
2559
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002560 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002561 || index >= cmd_element->cmdsize) {
2562 matched_element = cmd_element;
2563 matched_count++;
2564 } else
2565 incomplete_count++;
2566 }
2567
2568 /* Finish of using cmd_vector. */
2569 vector_free(cmd_vector);
2570
2571 /* To execute command, matched_count must be 1. */
2572 if (matched_count == 0) {
2573 if (incomplete_count)
2574 return CMD_ERR_INCOMPLETE;
2575 else
2576 return CMD_ERR_NO_MATCH;
2577 }
2578
2579 if (matched_count > 1)
2580 return CMD_ERR_AMBIGUOUS;
2581
2582 /* Argument treatment */
2583 varflag = 0;
2584 argc = 0;
2585
2586 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002587 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002588 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002589 if (varflag) {
2590 argv[argc++] = vector_slot(vline, i);
2591 continue;
2592 }
2593
2594 vector descvec = vector_slot(matched_element->strvec, i);
2595
2596 if (vector_active(descvec) == 1) {
2597 struct desc *desc = vector_slot(descvec, 0);
2598
2599 if (CMD_VARARG(desc->cmd))
2600 varflag = 1;
2601
2602 if (varflag || CMD_VARIABLE(desc->cmd)
2603 || CMD_OPTION(desc->cmd))
2604 argv[argc++] = vector_slot(vline, i);
2605 } else {
2606 argv[argc++] = vector_slot(vline, i);
2607 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002608 }
2609
2610 /* For vtysh execution. */
2611 if (cmd)
2612 *cmd = matched_element;
2613
2614 if (matched_element->daemon)
2615 return CMD_SUCCESS_DAEMON;
2616
2617 /* Now execute matched command */
2618 return (*matched_element->func) (matched_element, vty, argc, argv);
2619}
2620
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002621static inline size_t len(const char *str)
2622{
2623 return str? strlen(str) : 0;
2624}
2625
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002626/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2627 * is longer than b, a must start with exactly b, and vice versa.
2628 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2629 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002630static int indent_cmp(const char *a, const char *b)
2631{
2632 size_t al, bl;
2633 al = len(a);
2634 bl = len(b);
2635 if (al > bl) {
2636 if (bl && strncmp(a, b, bl) != 0)
2637 return EINVAL;
2638 return 1;
2639 }
2640 /* al <= bl */
2641 if (al && strncmp(a, b, al) != 0)
2642 return EINVAL;
2643 return (al < bl)? -1 : 0;
2644}
2645
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002646/* Configration make from file. */
2647int config_from_file(struct vty *vty, FILE * fp)
2648{
2649 int ret;
2650 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002651 char *indent;
2652 int cmp;
2653 struct vty_parent_node this_node;
2654 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002655
2656 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002657 indent = NULL;
2658 vline = NULL;
2659 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002660
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002661 if (ret != CMD_SUCCESS)
2662 goto return_invalid_indent;
2663
2664 /* In case of comment or empty line */
2665 if (vline == NULL) {
2666 if (indent) {
2667 talloc_free(indent);
2668 indent = NULL;
2669 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002670 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002671 }
2672
Neels Hofmeyr43063632017-09-19 23:54:01 +02002673 /* We have a nonempty line. */
2674 if (!vty->indent) {
2675 /* We have just entered a node and expecting the first child to come up; but we
2676 * may also skip right back to a parent or ancestor level. */
2677 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002678
Neels Hofmeyr43063632017-09-19 23:54:01 +02002679 /* If there is no parent, record any indentation we encounter. */
2680 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2681
2682 if (cmp == EINVAL)
2683 goto return_invalid_indent;
2684
2685 if (cmp <= 0) {
2686 /* We have gone right back to the parent level or higher, we are skipping
2687 * this child node level entirely. Pop the parent to go back to a node
2688 * that was actually there (to reinstate vty->indent) and re-use below
2689 * go-parent while-loop to find an accurate match of indent in the node
2690 * ancestry. */
2691 vty_go_parent(vty);
2692 } else {
2693 /* The indent is deeper than the just entered parent, record the new
2694 * indentation characters. */
2695 vty->indent = talloc_strdup(vty, indent);
2696 /* This *is* the new indentation. */
2697 cmp = 0;
2698 }
2699 } else {
2700 /* There is a known indentation for this node level, validate and detect node
2701 * exits. */
2702 cmp = indent_cmp(indent, vty->indent);
2703 if (cmp == EINVAL)
2704 goto return_invalid_indent;
2705 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002706
2707 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2708 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2709 while (cmp < 0) {
2710 vty_go_parent(vty);
2711 cmp = indent_cmp(indent, vty->indent);
2712 if (cmp == EINVAL)
2713 goto return_invalid_indent;
2714 }
2715
2716 /* More indent without having entered a child node level? Either the parent node's indent
2717 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2718 * or the indentation increased even though the vty command didn't enter a child. */
2719 if (cmp > 0)
2720 goto return_invalid_indent;
2721
2722 /* Remember the current node before the command possibly changes it. */
2723 this_node = (struct vty_parent_node){
2724 .node = vty->node,
2725 .priv = vty->priv,
2726 .indent = vty->indent,
2727 };
2728
2729 parent = vty_parent(vty);
2730 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002731 cmd_free_strvec(vline);
2732
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002733 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002734 if (indent) {
2735 talloc_free(indent);
2736 indent = NULL;
2737 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002738 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002739 }
2740
2741 /* If we have stepped down into a child node, push a parent frame.
2742 * The causality is such: we don't expect every single node entry implementation to push
2743 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2744 * a parent node. Hence if the node changed without the parent node changing, we must
2745 * have stepped into a child node (and now expect a deeper indent). */
2746 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2747 /* Push the parent node. */
2748 parent = talloc_zero(vty, struct vty_parent_node);
2749 *parent = this_node;
2750 llist_add(&parent->entry, &vty->parent_nodes);
2751
2752 /* The current talloc'ed vty->indent string will now be owned by this parent
2753 * struct. Indicate that we don't know what deeper indent characters the user
2754 * will choose. */
2755 vty->indent = NULL;
2756 }
2757
2758 if (indent) {
2759 talloc_free(indent);
2760 indent = NULL;
2761 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002762 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002763 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2764 while (vty_parent(vty))
2765 vty_go_parent(vty);
2766
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002767 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002768
2769return_invalid_indent:
2770 if (vline)
2771 cmd_free_strvec(vline);
2772 if (indent) {
2773 talloc_free(indent);
2774 indent = NULL;
2775 }
2776 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002777}
2778
2779/* Configration from terminal */
2780DEFUN(config_terminal,
2781 config_terminal_cmd,
2782 "configure terminal",
2783 "Configuration from vty interface\n" "Configuration terminal\n")
2784{
2785 if (vty_config_lock(vty))
2786 vty->node = CONFIG_NODE;
2787 else {
2788 vty_out(vty, "VTY configuration is locked by other VTY%s",
2789 VTY_NEWLINE);
2790 return CMD_WARNING;
2791 }
2792 return CMD_SUCCESS;
2793}
2794
2795/* Enable command */
2796DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2797{
2798 /* If enable password is NULL, change to ENABLE_NODE */
2799 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2800 vty->type == VTY_SHELL_SERV)
2801 vty->node = ENABLE_NODE;
2802 else
2803 vty->node = AUTH_ENABLE_NODE;
2804
2805 return CMD_SUCCESS;
2806}
2807
2808/* Disable command */
2809DEFUN(disable,
2810 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2811{
2812 if (vty->node == ENABLE_NODE)
2813 vty->node = VIEW_NODE;
2814 return CMD_SUCCESS;
2815}
2816
2817/* Down vty node level. */
2818gDEFUN(config_exit,
2819 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2820{
2821 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002822 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002823 case VIEW_NODE:
2824 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002825 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002826 break;
2827 case CONFIG_NODE:
2828 vty->node = ENABLE_NODE;
2829 vty_config_unlock(vty);
2830 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002831 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002832 if (vty->node > CONFIG_NODE)
2833 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002834 break;
2835 }
2836 return CMD_SUCCESS;
2837}
2838
2839/* End of configuration. */
2840 gDEFUN(config_end,
2841 config_end_cmd, "end", "End current mode and change to enable mode.")
2842{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002843 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002844 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002845
2846 /* Repeatedly call go_parent until a top node is reached. */
2847 while (vty->node > CONFIG_NODE) {
2848 if (vty->node == last_node) {
2849 /* Ensure termination, this shouldn't happen. */
2850 break;
2851 }
2852 last_node = vty->node;
2853 vty_go_parent(vty);
2854 }
2855
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002856 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002857 if (vty->node > ENABLE_NODE)
2858 vty->node = ENABLE_NODE;
2859 vty->index = NULL;
2860 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002861 }
2862 return CMD_SUCCESS;
2863}
2864
2865/* Show version. */
2866DEFUN(show_version,
2867 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2868{
Harald Welte237f6242010-05-25 23:00:45 +02002869 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2870 host.app_info->version,
2871 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2872 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002873
2874 return CMD_SUCCESS;
2875}
2876
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002877DEFUN(show_online_help,
2878 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2879{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002880 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002881 return CMD_SUCCESS;
2882}
2883
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002884/* Help display function for all node. */
2885gDEFUN(config_help,
2886 config_help_cmd, "help", "Description of the interactive help system\n")
2887{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002888 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2889 "anytime at the command line please press '?'.%s%s"
2890 "If nothing matches, the help list will be empty and you must backup%s"
2891 " until entering a '?' shows the available options.%s"
2892 "Two styles of help are provided:%s"
2893 "1. Full help is available when you are ready to enter a%s"
2894 "command argument (e.g. 'show ?') and describes each possible%s"
2895 "argument.%s"
2896 "2. Partial help is provided when an abbreviated argument is entered%s"
2897 " and you want to know what arguments match the input%s"
2898 " (e.g. 'show me?'.)%s%s",
2899 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2900 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2901 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2902 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002903 return CMD_SUCCESS;
2904}
2905
2906/* Help display function for all node. */
2907gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2908{
2909 unsigned int i;
2910 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2911 struct cmd_element *cmd;
2912
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002913 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2914 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2915 continue;
2916 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2917 continue;
2918 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2919 }
2920
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002921 return CMD_SUCCESS;
2922}
2923
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002924static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002925{
2926 unsigned int i;
2927 int fd;
2928 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002929 char *config_file_tmp = NULL;
2930 char *config_file_sav = NULL;
2931 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002932 struct stat st;
2933
2934 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002935
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002936 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2937 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2938 * manually instead. */
2939
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002940 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002941 config_file_sav =
2942 _talloc_zero(tall_vty_cmd_ctx,
2943 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2944 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002945 if (!config_file_sav)
2946 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002947 strcpy(config_file_sav, config_file);
2948 strcat(config_file_sav, CONF_BACKUP_EXT);
2949
2950 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002951 "config_file_tmp");
2952 if (!config_file_tmp) {
2953 talloc_free(config_file_sav);
2954 return -1;
2955 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002956 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2957
2958 /* Open file to configuration write. */
2959 fd = mkstemp(config_file_tmp);
2960 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002961 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002962 talloc_free(config_file_tmp);
2963 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002964 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002965 }
2966
2967 /* Make vty for configuration file. */
2968 file_vty = vty_new();
2969 file_vty->fd = fd;
2970 file_vty->type = VTY_FILE;
2971
2972 /* Config file header print. */
2973 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002974 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002975 //vty_time_print (file_vty, 1);
2976 vty_out(file_vty, "!\n");
2977
2978 for (i = 0; i < vector_active(cmdvec); i++)
2979 if ((node = vector_slot(cmdvec, i)) && node->func) {
2980 if ((*node->func) (file_vty))
2981 vty_out(file_vty, "!\n");
2982 }
2983 vty_close(file_vty);
2984
2985 if (unlink(config_file_sav) != 0)
2986 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002987 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002988 talloc_free(config_file_sav);
2989 talloc_free(config_file_tmp);
2990 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002991 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002992 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002993
2994 /* Only link the .sav file if the original file exists */
2995 if (stat(config_file, &st) == 0) {
2996 if (link(config_file, config_file_sav) != 0) {
2997 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2998 talloc_free(config_file_sav);
2999 talloc_free(config_file_tmp);
3000 unlink(config_file_tmp);
3001 return -3;
3002 }
3003 sync();
3004 if (unlink(config_file) != 0) {
3005 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3006 talloc_free(config_file_sav);
3007 talloc_free(config_file_tmp);
3008 unlink(config_file_tmp);
3009 return -4;
3010 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003011 }
3012 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003013 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003014 talloc_free(config_file_sav);
3015 talloc_free(config_file_tmp);
3016 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003017 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003018 }
3019 unlink(config_file_tmp);
3020 sync();
3021
3022 talloc_free(config_file_sav);
3023 talloc_free(config_file_tmp);
3024
3025 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003026 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3027 return -6;
3028 }
3029
3030 return 0;
3031}
3032
3033
3034/* Write current configuration into file. */
3035DEFUN(config_write_file,
3036 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003037 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003038 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003039 "Write to configuration file\n"
3040 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003041{
3042 char *failed_file;
3043 int rc;
3044
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003045 if (host.app_info->config_is_consistent) {
3046 rc = host.app_info->config_is_consistent(vty);
3047 if (!rc) {
3048 vty_out(vty, "Configuration is not consistent%s",
3049 VTY_NEWLINE);
3050 return CMD_WARNING;
3051 }
3052 }
3053
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003054 if (argc == 1)
3055 host_config_set(argv[0]);
3056
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003057 if (host.config == NULL) {
3058 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3059 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003060 return CMD_WARNING;
3061 }
3062
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003063 rc = write_config_file(host.config, &failed_file);
3064 switch (rc) {
3065 case -1:
3066 vty_out(vty, "Can't open configuration file %s.%s",
3067 failed_file, VTY_NEWLINE);
3068 rc = CMD_WARNING;
3069 break;
3070 case -2:
3071 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3072 failed_file, VTY_NEWLINE);
3073 rc = CMD_WARNING;
3074 break;
3075 case -3:
3076 vty_out(vty, "Can't backup old configuration file %s.%s",
3077 failed_file, VTY_NEWLINE);
3078 rc = CMD_WARNING;
3079 break;
3080 case -4:
3081 vty_out(vty, "Can't unlink configuration file %s.%s",
3082 failed_file, VTY_NEWLINE);
3083 rc = CMD_WARNING;
3084 break;
3085 case -5:
3086 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3087 VTY_NEWLINE);
3088 rc = CMD_WARNING;
3089 break;
3090 case -6:
3091 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3092 failed_file, strerror(errno), errno, VTY_NEWLINE);
3093 rc = CMD_WARNING;
3094 break;
3095 default:
3096 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3097 rc = CMD_SUCCESS;
3098 break;
3099 }
3100
3101 talloc_free(failed_file);
3102 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003103}
3104
3105ALIAS(config_write_file,
3106 config_write_cmd,
3107 "write", "Write running configuration to memory, network, or terminal\n")
3108
3109 ALIAS(config_write_file,
3110 config_write_memory_cmd,
3111 "write memory",
3112 "Write running configuration to memory, network, or terminal\n"
3113 "Write configuration to the file (same as write file)\n")
3114
3115 ALIAS(config_write_file,
3116 copy_runningconfig_startupconfig_cmd,
3117 "copy running-config startup-config",
3118 "Copy configuration\n"
3119 "Copy running config to... \n"
3120 "Copy running config to startup config (same as write file)\n")
3121
3122/* Write current configuration into the terminal. */
3123 DEFUN(config_write_terminal,
3124 config_write_terminal_cmd,
3125 "write terminal",
3126 "Write running configuration to memory, network, or terminal\n"
3127 "Write to terminal\n")
3128{
3129 unsigned int i;
3130 struct cmd_node *node;
3131
3132 if (vty->type == VTY_SHELL_SERV) {
3133 for (i = 0; i < vector_active(cmdvec); i++)
3134 if ((node = vector_slot(cmdvec, i)) && node->func
3135 && node->vtysh) {
3136 if ((*node->func) (vty))
3137 vty_out(vty, "!%s", VTY_NEWLINE);
3138 }
3139 } else {
3140 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3141 VTY_NEWLINE);
3142 vty_out(vty, "!%s", VTY_NEWLINE);
3143
3144 for (i = 0; i < vector_active(cmdvec); i++)
3145 if ((node = vector_slot(cmdvec, i)) && node->func) {
3146 if ((*node->func) (vty))
3147 vty_out(vty, "!%s", VTY_NEWLINE);
3148 }
3149 vty_out(vty, "end%s", VTY_NEWLINE);
3150 }
3151 return CMD_SUCCESS;
3152}
3153
3154/* Write current configuration into the terminal. */
3155ALIAS(config_write_terminal,
3156 show_running_config_cmd,
3157 "show running-config", SHOW_STR "running configuration\n")
3158
3159/* Write startup configuration into the terminal. */
3160 DEFUN(show_startup_config,
3161 show_startup_config_cmd,
3162 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3163{
3164 char buf[BUFSIZ];
3165 FILE *confp;
3166
3167 confp = fopen(host.config, "r");
3168 if (confp == NULL) {
3169 vty_out(vty, "Can't open configuration file [%s]%s",
3170 host.config, VTY_NEWLINE);
3171 return CMD_WARNING;
3172 }
3173
3174 while (fgets(buf, BUFSIZ, confp)) {
3175 char *cp = buf;
3176
3177 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3178 cp++;
3179 *cp = '\0';
3180
3181 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3182 }
3183
3184 fclose(confp);
3185
3186 return CMD_SUCCESS;
3187}
3188
3189/* Hostname configuration */
3190DEFUN(config_hostname,
3191 hostname_cmd,
3192 "hostname WORD",
3193 "Set system's network name\n" "This system's network name\n")
3194{
3195 if (!isalpha((int)*argv[0])) {
3196 vty_out(vty, "Please specify string starting with alphabet%s",
3197 VTY_NEWLINE);
3198 return CMD_WARNING;
3199 }
3200
3201 if (host.name)
3202 talloc_free(host.name);
3203
3204 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3205 return CMD_SUCCESS;
3206}
3207
3208DEFUN(config_no_hostname,
3209 no_hostname_cmd,
3210 "no hostname [HOSTNAME]",
3211 NO_STR "Reset system's network name\n" "Host name of this router\n")
3212{
3213 if (host.name)
3214 talloc_free(host.name);
3215 host.name = NULL;
3216 return CMD_SUCCESS;
3217}
3218
3219/* VTY interface password set. */
3220DEFUN(config_password, password_cmd,
3221 "password (8|) WORD",
3222 "Assign the terminal connection password\n"
3223 "Specifies a HIDDEN password will follow\n"
3224 "dummy string \n" "The HIDDEN line password string\n")
3225{
3226 /* Argument check. */
3227 if (argc == 0) {
3228 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3229 return CMD_WARNING;
3230 }
3231
3232 if (argc == 2) {
3233 if (*argv[0] == '8') {
3234 if (host.password)
3235 talloc_free(host.password);
3236 host.password = NULL;
3237 if (host.password_encrypt)
3238 talloc_free(host.password_encrypt);
3239 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3240 return CMD_SUCCESS;
3241 } else {
3242 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3243 return CMD_WARNING;
3244 }
3245 }
3246
3247 if (!isalnum((int)*argv[0])) {
3248 vty_out(vty,
3249 "Please specify string starting with alphanumeric%s",
3250 VTY_NEWLINE);
3251 return CMD_WARNING;
3252 }
3253
3254 if (host.password)
3255 talloc_free(host.password);
3256 host.password = NULL;
3257
3258#ifdef VTY_CRYPT_PW
3259 if (host.encrypt) {
3260 if (host.password_encrypt)
3261 talloc_free(host.password_encrypt);
3262 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3263 } else
3264#endif
3265 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3266
3267 return CMD_SUCCESS;
3268}
3269
3270ALIAS(config_password, password_text_cmd,
3271 "password LINE",
3272 "Assign the terminal connection password\n"
3273 "The UNENCRYPTED (cleartext) line password\n")
3274
3275/* VTY enable password set. */
3276 DEFUN(config_enable_password, enable_password_cmd,
3277 "enable password (8|) WORD",
3278 "Modify enable password parameters\n"
3279 "Assign the privileged level password\n"
3280 "Specifies a HIDDEN password will follow\n"
3281 "dummy string \n" "The HIDDEN 'enable' password string\n")
3282{
3283 /* Argument check. */
3284 if (argc == 0) {
3285 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3286 return CMD_WARNING;
3287 }
3288
3289 /* Crypt type is specified. */
3290 if (argc == 2) {
3291 if (*argv[0] == '8') {
3292 if (host.enable)
3293 talloc_free(host.enable);
3294 host.enable = NULL;
3295
3296 if (host.enable_encrypt)
3297 talloc_free(host.enable_encrypt);
3298 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3299
3300 return CMD_SUCCESS;
3301 } else {
3302 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3303 return CMD_WARNING;
3304 }
3305 }
3306
3307 if (!isalnum((int)*argv[0])) {
3308 vty_out(vty,
3309 "Please specify string starting with alphanumeric%s",
3310 VTY_NEWLINE);
3311 return CMD_WARNING;
3312 }
3313
3314 if (host.enable)
3315 talloc_free(host.enable);
3316 host.enable = NULL;
3317
3318 /* Plain password input. */
3319#ifdef VTY_CRYPT_PW
3320 if (host.encrypt) {
3321 if (host.enable_encrypt)
3322 talloc_free(host.enable_encrypt);
3323 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3324 } else
3325#endif
3326 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3327
3328 return CMD_SUCCESS;
3329}
3330
3331ALIAS(config_enable_password,
3332 enable_password_text_cmd,
3333 "enable password LINE",
3334 "Modify enable password parameters\n"
3335 "Assign the privileged level password\n"
3336 "The UNENCRYPTED (cleartext) 'enable' password\n")
3337
3338/* VTY enable password delete. */
3339 DEFUN(no_config_enable_password, no_enable_password_cmd,
3340 "no enable password",
3341 NO_STR
3342 "Modify enable password parameters\n"
3343 "Assign the privileged level password\n")
3344{
3345 if (host.enable)
3346 talloc_free(host.enable);
3347 host.enable = NULL;
3348
3349 if (host.enable_encrypt)
3350 talloc_free(host.enable_encrypt);
3351 host.enable_encrypt = NULL;
3352
3353 return CMD_SUCCESS;
3354}
3355
3356#ifdef VTY_CRYPT_PW
3357DEFUN(service_password_encrypt,
3358 service_password_encrypt_cmd,
3359 "service password-encryption",
3360 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3361{
3362 if (host.encrypt)
3363 return CMD_SUCCESS;
3364
3365 host.encrypt = 1;
3366
3367 if (host.password) {
3368 if (host.password_encrypt)
3369 talloc_free(host.password_encrypt);
3370 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3371 }
3372 if (host.enable) {
3373 if (host.enable_encrypt)
3374 talloc_free(host.enable_encrypt);
3375 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3376 }
3377
3378 return CMD_SUCCESS;
3379}
3380
3381DEFUN(no_service_password_encrypt,
3382 no_service_password_encrypt_cmd,
3383 "no service password-encryption",
3384 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3385{
3386 if (!host.encrypt)
3387 return CMD_SUCCESS;
3388
3389 host.encrypt = 0;
3390
3391 if (host.password_encrypt)
3392 talloc_free(host.password_encrypt);
3393 host.password_encrypt = NULL;
3394
3395 if (host.enable_encrypt)
3396 talloc_free(host.enable_encrypt);
3397 host.enable_encrypt = NULL;
3398
3399 return CMD_SUCCESS;
3400}
3401#endif
3402
3403DEFUN(config_terminal_length, config_terminal_length_cmd,
3404 "terminal length <0-512>",
3405 "Set terminal line parameters\n"
3406 "Set number of lines on a screen\n"
3407 "Number of lines on screen (0 for no pausing)\n")
3408{
3409 int lines;
3410 char *endptr = NULL;
3411
3412 lines = strtol(argv[0], &endptr, 10);
3413 if (lines < 0 || lines > 512 || *endptr != '\0') {
3414 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3415 return CMD_WARNING;
3416 }
3417 vty->lines = lines;
3418
3419 return CMD_SUCCESS;
3420}
3421
3422DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3423 "terminal no length",
3424 "Set terminal line parameters\n"
3425 NO_STR "Set number of lines on a screen\n")
3426{
3427 vty->lines = -1;
3428 return CMD_SUCCESS;
3429}
3430
3431DEFUN(service_terminal_length, service_terminal_length_cmd,
3432 "service terminal-length <0-512>",
3433 "Set up miscellaneous service\n"
3434 "System wide terminal length configuration\n"
3435 "Number of lines of VTY (0 means no line control)\n")
3436{
3437 int lines;
3438 char *endptr = NULL;
3439
3440 lines = strtol(argv[0], &endptr, 10);
3441 if (lines < 0 || lines > 512 || *endptr != '\0') {
3442 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3443 return CMD_WARNING;
3444 }
3445 host.lines = lines;
3446
3447 return CMD_SUCCESS;
3448}
3449
3450DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3451 "no service terminal-length [<0-512>]",
3452 NO_STR
3453 "Set up miscellaneous service\n"
3454 "System wide terminal length configuration\n"
3455 "Number of lines of VTY (0 means no line control)\n")
3456{
3457 host.lines = -1;
3458 return CMD_SUCCESS;
3459}
3460
3461DEFUN_HIDDEN(do_echo,
3462 echo_cmd,
3463 "echo .MESSAGE",
3464 "Echo a message back to the vty\n" "The message to echo\n")
3465{
3466 char *message;
3467
3468 vty_out(vty, "%s%s",
3469 ((message =
3470 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3471 if (message)
3472 talloc_free(message);
3473 return CMD_SUCCESS;
3474}
3475
3476#if 0
3477DEFUN(config_logmsg,
3478 config_logmsg_cmd,
3479 "logmsg " LOG_LEVELS " .MESSAGE",
3480 "Send a message to enabled logging destinations\n"
3481 LOG_LEVEL_DESC "The message to send\n")
3482{
3483 int level;
3484 char *message;
3485
3486 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3487 return CMD_ERR_NO_MATCH;
3488
3489 zlog(NULL, level,
3490 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3491 if (message)
3492 talloc_free(message);
3493 return CMD_SUCCESS;
3494}
3495
3496DEFUN(show_logging,
3497 show_logging_cmd,
3498 "show logging", SHOW_STR "Show current logging configuration\n")
3499{
3500 struct zlog *zl = zlog_default;
3501
3502 vty_out(vty, "Syslog logging: ");
3503 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3504 vty_out(vty, "disabled");
3505 else
3506 vty_out(vty, "level %s, facility %s, ident %s",
3507 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3508 facility_name(zl->facility), zl->ident);
3509 vty_out(vty, "%s", VTY_NEWLINE);
3510
3511 vty_out(vty, "Stdout logging: ");
3512 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3513 vty_out(vty, "disabled");
3514 else
3515 vty_out(vty, "level %s",
3516 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3517 vty_out(vty, "%s", VTY_NEWLINE);
3518
3519 vty_out(vty, "Monitor logging: ");
3520 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3521 vty_out(vty, "disabled");
3522 else
3523 vty_out(vty, "level %s",
3524 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3525 vty_out(vty, "%s", VTY_NEWLINE);
3526
3527 vty_out(vty, "File logging: ");
3528 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3529 vty_out(vty, "disabled");
3530 else
3531 vty_out(vty, "level %s, filename %s",
3532 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3533 zl->filename);
3534 vty_out(vty, "%s", VTY_NEWLINE);
3535
3536 vty_out(vty, "Protocol name: %s%s",
3537 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3538 vty_out(vty, "Record priority: %s%s",
3539 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3540
3541 return CMD_SUCCESS;
3542}
3543
3544DEFUN(config_log_stdout,
3545 config_log_stdout_cmd,
3546 "log stdout", "Logging control\n" "Set stdout logging level\n")
3547{
3548 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3549 return CMD_SUCCESS;
3550}
3551
3552DEFUN(config_log_stdout_level,
3553 config_log_stdout_level_cmd,
3554 "log stdout " LOG_LEVELS,
3555 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3556{
3557 int level;
3558
3559 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3560 return CMD_ERR_NO_MATCH;
3561 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3562 return CMD_SUCCESS;
3563}
3564
3565DEFUN(no_config_log_stdout,
3566 no_config_log_stdout_cmd,
3567 "no log stdout [LEVEL]",
3568 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3569{
3570 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3571 return CMD_SUCCESS;
3572}
3573
3574DEFUN(config_log_monitor,
3575 config_log_monitor_cmd,
3576 "log monitor",
3577 "Logging control\n" "Set terminal line (monitor) logging level\n")
3578{
3579 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3580 return CMD_SUCCESS;
3581}
3582
3583DEFUN(config_log_monitor_level,
3584 config_log_monitor_level_cmd,
3585 "log monitor " LOG_LEVELS,
3586 "Logging control\n"
3587 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3588{
3589 int level;
3590
3591 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3592 return CMD_ERR_NO_MATCH;
3593 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3594 return CMD_SUCCESS;
3595}
3596
3597DEFUN(no_config_log_monitor,
3598 no_config_log_monitor_cmd,
3599 "no log monitor [LEVEL]",
3600 NO_STR
3601 "Logging control\n"
3602 "Disable terminal line (monitor) logging\n" "Logging level\n")
3603{
3604 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3605 return CMD_SUCCESS;
3606}
3607
3608static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3609{
3610 int ret;
3611 char *p = NULL;
3612 const char *fullpath;
3613
3614 /* Path detection. */
3615 if (!IS_DIRECTORY_SEP(*fname)) {
3616 char cwd[MAXPATHLEN + 1];
3617 cwd[MAXPATHLEN] = '\0';
3618
3619 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3620 zlog_err("config_log_file: Unable to alloc mem!");
3621 return CMD_WARNING;
3622 }
3623
3624 if ((p = _talloc_zero(tall_vcmd_ctx,
3625 strlen(cwd) + strlen(fname) + 2),
3626 "set_log_file")
3627 == NULL) {
3628 zlog_err("config_log_file: Unable to alloc mem!");
3629 return CMD_WARNING;
3630 }
3631 sprintf(p, "%s/%s", cwd, fname);
3632 fullpath = p;
3633 } else
3634 fullpath = fname;
3635
3636 ret = zlog_set_file(NULL, fullpath, loglevel);
3637
3638 if (p)
3639 talloc_free(p);
3640
3641 if (!ret) {
3642 vty_out(vty, "can't open logfile %s\n", fname);
3643 return CMD_WARNING;
3644 }
3645
3646 if (host.logfile)
3647 talloc_free(host.logfile);
3648
3649 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3650
3651 return CMD_SUCCESS;
3652}
3653
3654DEFUN(config_log_file,
3655 config_log_file_cmd,
3656 "log file FILENAME",
3657 "Logging control\n" "Logging to file\n" "Logging filename\n")
3658{
3659 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3660}
3661
3662DEFUN(config_log_file_level,
3663 config_log_file_level_cmd,
3664 "log file FILENAME " LOG_LEVELS,
3665 "Logging control\n"
3666 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3667{
3668 int level;
3669
3670 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3671 return CMD_ERR_NO_MATCH;
3672 return set_log_file(vty, argv[0], level);
3673}
3674
3675DEFUN(no_config_log_file,
3676 no_config_log_file_cmd,
3677 "no log file [FILENAME]",
3678 NO_STR
3679 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3680{
3681 zlog_reset_file(NULL);
3682
3683 if (host.logfile)
3684 talloc_free(host.logfile);
3685
3686 host.logfile = NULL;
3687
3688 return CMD_SUCCESS;
3689}
3690
3691ALIAS(no_config_log_file,
3692 no_config_log_file_level_cmd,
3693 "no log file FILENAME LEVEL",
3694 NO_STR
3695 "Logging control\n"
3696 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3697
3698 DEFUN(config_log_syslog,
3699 config_log_syslog_cmd,
3700 "log syslog", "Logging control\n" "Set syslog logging level\n")
3701{
3702 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3703 return CMD_SUCCESS;
3704}
3705
3706DEFUN(config_log_syslog_level,
3707 config_log_syslog_level_cmd,
3708 "log syslog " LOG_LEVELS,
3709 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3710{
3711 int level;
3712
3713 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3714 return CMD_ERR_NO_MATCH;
3715 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3716 return CMD_SUCCESS;
3717}
3718
3719DEFUN_DEPRECATED(config_log_syslog_facility,
3720 config_log_syslog_facility_cmd,
3721 "log syslog facility " LOG_FACILITIES,
3722 "Logging control\n"
3723 "Logging goes to syslog\n"
3724 "(Deprecated) Facility parameter for syslog messages\n"
3725 LOG_FACILITY_DESC)
3726{
3727 int facility;
3728
3729 if ((facility = facility_match(argv[0])) < 0)
3730 return CMD_ERR_NO_MATCH;
3731
3732 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3733 zlog_default->facility = facility;
3734 return CMD_SUCCESS;
3735}
3736
3737DEFUN(no_config_log_syslog,
3738 no_config_log_syslog_cmd,
3739 "no log syslog [LEVEL]",
3740 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3741{
3742 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3743 return CMD_SUCCESS;
3744}
3745
3746ALIAS(no_config_log_syslog,
3747 no_config_log_syslog_facility_cmd,
3748 "no log syslog facility " LOG_FACILITIES,
3749 NO_STR
3750 "Logging control\n"
3751 "Logging goes to syslog\n"
3752 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3753
3754 DEFUN(config_log_facility,
3755 config_log_facility_cmd,
3756 "log facility " LOG_FACILITIES,
3757 "Logging control\n"
3758 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3759{
3760 int facility;
3761
3762 if ((facility = facility_match(argv[0])) < 0)
3763 return CMD_ERR_NO_MATCH;
3764 zlog_default->facility = facility;
3765 return CMD_SUCCESS;
3766}
3767
3768DEFUN(no_config_log_facility,
3769 no_config_log_facility_cmd,
3770 "no log facility [FACILITY]",
3771 NO_STR
3772 "Logging control\n"
3773 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3774{
3775 zlog_default->facility = LOG_DAEMON;
3776 return CMD_SUCCESS;
3777}
3778
3779DEFUN_DEPRECATED(config_log_trap,
3780 config_log_trap_cmd,
3781 "log trap " LOG_LEVELS,
3782 "Logging control\n"
3783 "(Deprecated) Set logging level and default for all destinations\n"
3784 LOG_LEVEL_DESC)
3785{
3786 int new_level;
3787 int i;
3788
3789 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3790 return CMD_ERR_NO_MATCH;
3791
3792 zlog_default->default_lvl = new_level;
3793 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3794 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3795 zlog_default->maxlvl[i] = new_level;
3796 return CMD_SUCCESS;
3797}
3798
3799DEFUN_DEPRECATED(no_config_log_trap,
3800 no_config_log_trap_cmd,
3801 "no log trap [LEVEL]",
3802 NO_STR
3803 "Logging control\n"
3804 "Permit all logging information\n" "Logging level\n")
3805{
3806 zlog_default->default_lvl = LOG_DEBUG;
3807 return CMD_SUCCESS;
3808}
3809
3810DEFUN(config_log_record_priority,
3811 config_log_record_priority_cmd,
3812 "log record-priority",
3813 "Logging control\n"
3814 "Log the priority of the message within the message\n")
3815{
3816 zlog_default->record_priority = 1;
3817 return CMD_SUCCESS;
3818}
3819
3820DEFUN(no_config_log_record_priority,
3821 no_config_log_record_priority_cmd,
3822 "no log record-priority",
3823 NO_STR
3824 "Logging control\n"
3825 "Do not log the priority of the message within the message\n")
3826{
3827 zlog_default->record_priority = 0;
3828 return CMD_SUCCESS;
3829}
3830#endif
3831
3832DEFUN(banner_motd_file,
3833 banner_motd_file_cmd,
3834 "banner motd file [FILE]",
3835 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3836{
3837 if (host.motdfile)
3838 talloc_free(host.motdfile);
3839 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3840
3841 return CMD_SUCCESS;
3842}
3843
3844DEFUN(banner_motd_default,
3845 banner_motd_default_cmd,
3846 "banner motd default",
3847 "Set banner string\n" "Strings for motd\n" "Default string\n")
3848{
3849 host.motd = default_motd;
3850 return CMD_SUCCESS;
3851}
3852
3853DEFUN(no_banner_motd,
3854 no_banner_motd_cmd,
3855 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3856{
3857 host.motd = NULL;
3858 if (host.motdfile)
3859 talloc_free(host.motdfile);
3860 host.motdfile = NULL;
3861 return CMD_SUCCESS;
3862}
3863
3864/* Set config filename. Called from vty.c */
3865void host_config_set(const char *filename)
3866{
3867 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3868}
3869
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003870/*! Deprecated, now happens implicitly when calling install_node().
3871 * Users of the API may still attempt to call this function, hence
3872 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003873void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003874{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003875}
3876
3877/*! Deprecated, now happens implicitly when calling install_node().
3878 * Users of the API may still attempt to call this function, hence
3879 * leave it here as a no-op. */
3880void vty_install_default(int node)
3881{
3882}
3883
3884/*! Install common commands like 'exit' and 'list'. */
3885static void install_basic_node_commands(int node)
3886{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003887 install_element(node, &config_help_cmd);
3888 install_element(node, &config_list_cmd);
3889
3890 install_element(node, &config_write_terminal_cmd);
3891 install_element(node, &config_write_file_cmd);
3892 install_element(node, &config_write_memory_cmd);
3893 install_element(node, &config_write_cmd);
3894 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003895
3896 install_element(node, &config_exit_cmd);
3897
3898 if (node >= CONFIG_NODE) {
3899 /* It's not a top node. */
3900 install_element(node, &config_end_cmd);
3901 }
3902}
3903
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003904/*! Return true if a node is installed by install_basic_node_commands(), so
3905 * that we can avoid repeating them for each and every node during 'show
3906 * running-config' */
3907static bool vty_command_is_common(struct cmd_element *cmd)
3908{
3909 if (cmd == &config_help_cmd
3910 || cmd == &config_list_cmd
3911 || cmd == &config_write_terminal_cmd
3912 || cmd == &config_write_file_cmd
3913 || cmd == &config_write_memory_cmd
3914 || cmd == &config_write_cmd
3915 || cmd == &show_running_config_cmd
3916 || cmd == &config_exit_cmd
3917 || cmd == &config_end_cmd)
3918 return true;
3919 return false;
3920}
3921
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003922/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003923 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003924 * \param[in] vty the vty of the code
3925 * \param[in] filename where to store the file
3926 * \return 0 in case of success.
3927 *
3928 * If the filename already exists create a filename.sav
3929 * version with the current code.
3930 *
3931 */
3932int osmo_vty_write_config_file(const char *filename)
3933{
3934 char *failed_file;
3935 int rc;
3936
3937 rc = write_config_file(filename, &failed_file);
3938 talloc_free(failed_file);
3939 return rc;
3940}
3941
3942/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003943 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003944 * \return 0 in case of success.
3945 *
3946 * If the filename already exists create a filename.sav
3947 * version with the current code.
3948 *
3949 */
3950int osmo_vty_save_config_file(void)
3951{
3952 char *failed_file;
3953 int rc;
3954
3955 if (host.config == NULL)
3956 return -7;
3957
3958 rc = write_config_file(host.config, &failed_file);
3959 talloc_free(failed_file);
3960 return rc;
3961}
3962
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003963/* Initialize command interface. Install basic nodes and commands. */
3964void cmd_init(int terminal)
3965{
3966 /* Allocate initial top vector of commands. */
3967 cmdvec = vector_init(VECTOR_MIN_SIZE);
3968
3969 /* Default host value settings. */
3970 host.name = NULL;
3971 host.password = NULL;
3972 host.enable = NULL;
3973 host.logfile = NULL;
3974 host.config = NULL;
3975 host.lines = -1;
3976 host.motd = default_motd;
3977 host.motdfile = NULL;
3978
3979 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003980 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003981 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003982 install_node_bare(&auth_node, NULL);
3983 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003984 install_node(&config_node, config_write_host);
3985
3986 /* Each node's basic commands. */
3987 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003988 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003989 if (terminal) {
3990 install_element(VIEW_NODE, &config_list_cmd);
3991 install_element(VIEW_NODE, &config_exit_cmd);
3992 install_element(VIEW_NODE, &config_help_cmd);
3993 install_element(VIEW_NODE, &config_enable_cmd);
3994 install_element(VIEW_NODE, &config_terminal_length_cmd);
3995 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3996 install_element(VIEW_NODE, &echo_cmd);
3997 }
3998
3999 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004000 install_element(ENABLE_NODE, &config_disable_cmd);
4001 install_element(ENABLE_NODE, &config_terminal_cmd);
4002 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4003 }
4004 install_element (ENABLE_NODE, &show_startup_config_cmd);
4005 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01004006 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004007
4008 if (terminal) {
4009 install_element(ENABLE_NODE, &config_terminal_length_cmd);
4010 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4011 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004012 }
4013
4014 install_element(CONFIG_NODE, &hostname_cmd);
4015 install_element(CONFIG_NODE, &no_hostname_cmd);
4016
4017 if (terminal) {
4018 install_element(CONFIG_NODE, &password_cmd);
4019 install_element(CONFIG_NODE, &password_text_cmd);
4020 install_element(CONFIG_NODE, &enable_password_cmd);
4021 install_element(CONFIG_NODE, &enable_password_text_cmd);
4022 install_element(CONFIG_NODE, &no_enable_password_cmd);
4023
4024#ifdef VTY_CRYPT_PW
4025 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
4026 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
4027#endif
4028 install_element(CONFIG_NODE, &banner_motd_default_cmd);
4029 install_element(CONFIG_NODE, &banner_motd_file_cmd);
4030 install_element(CONFIG_NODE, &no_banner_motd_cmd);
4031 install_element(CONFIG_NODE, &service_terminal_length_cmd);
4032 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
4033
4034 }
4035 srand(time(NULL));
4036}
Harald Welte7acb30c2011-08-17 17:13:48 +02004037
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004038/*! @} */