blob: 5001d06eec0bd5bd5155f64ece3c1e12ce51e27e [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>
Oliver Smithd243c2a2021-07-09 17:19:32 +020049#include <osmocom/core/timer.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010050#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020051
Ruben Undheim766f77c2018-11-18 13:02:47 +010052#ifndef MAXPATHLEN
53 #define MAXPATHLEN 4096
54#endif
55
56
Harald Weltee881b1b2011-08-17 18:52:30 +020057/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020058 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020059 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020060 *
61 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020062
Harald Welte3fb0b6f2010-05-19 19:02:52 +020063#define CONFIGFILE_MASK 022
64
65void *tall_vty_cmd_ctx;
66
Oliver Smithd243c2a2021-07-09 17:19:32 +020067/* Set by on_dso_load_starttime() for "show uptime". */
68static struct timespec starttime;
69
Harald Welte3fb0b6f2010-05-19 19:02:52 +020070/* Command vector which includes some level of command lists. Normally
71 each daemon maintains each own cmdvec. */
72vector cmdvec;
73
74/* Host information structure. */
75struct host host;
76
77/* Standard command node structures. */
78struct cmd_node auth_node = {
79 AUTH_NODE,
80 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010081 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020082};
83
84struct cmd_node view_node = {
85 VIEW_NODE,
86 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010087 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020088};
89
90struct cmd_node auth_enable_node = {
91 AUTH_ENABLE_NODE,
92 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010093 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020094};
95
96struct cmd_node enable_node = {
97 ENABLE_NODE,
98 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010099 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200100};
101
102struct cmd_node config_node = {
103 CONFIG_NODE,
104 "%s(config)# ",
105 1
106};
107
108/* Default motd string. */
109const char *default_motd = "";
110
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200111/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200112 *
113 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200114void print_version(int print_copyright)
115{
Harald Welte237f6242010-05-25 23:00:45 +0200116 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200117 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200118 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200119}
120
121/* Utility function to concatenate argv argument into a single string
122 with inserting ' ' character between each argument. */
123char *argv_concat(const char **argv, int argc, int shift)
124{
125 int i;
126 size_t len;
127 char *str;
128 char *p;
129
130 len = 0;
131 for (i = shift; i < argc; i++)
132 len += strlen(argv[i]) + 1;
133 if (!len)
134 return NULL;
135 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
136 for (i = shift; i < argc; i++) {
137 size_t arglen;
138 memcpy(p, argv[i], (arglen = strlen(argv[i])));
139 p += arglen;
140 *p++ = ' ';
141 }
142 *(p - 1) = '\0';
143 return str;
144}
145
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200146/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
147 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
148 * in turn, this name us used for XML IDs in 'show online-help'. */
149static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
150{
151 const char *pos;
152 int dest = 0;
153
154 if (!prompt || !*prompt)
155 return "";
156
157 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
158 if (pos[0] == '%' && pos[1]) {
159 /* skip "%s"; loop pos++ does the second one. */
160 pos++;
161 continue;
162 }
163 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
164 continue;
165 name_buf[dest] = pos[0];
166 dest++;
167 }
168 name_buf[dest] = '\0';
169 return name_buf;
170}
171
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200172static void install_basic_node_commands(int node);
173
174/*! Install top node of command vector, without adding basic node commands. */
175static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200176{
177 vector_set_index(cmdvec, node->node, node);
178 node->func = func;
179 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200180 if (!*node->name)
181 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200182}
183
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200184/*! Install top node of command vector. */
185void install_node(struct cmd_node *node, int (*func) (struct vty *))
186{
187 install_node_bare(node, func);
188 install_basic_node_commands(node->node);
189}
190
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200191/* Compare two command's string. Used in sort_node (). */
192static int cmp_node(const void *p, const void *q)
193{
194 struct cmd_element *a = *(struct cmd_element **)p;
195 struct cmd_element *b = *(struct cmd_element **)q;
196
197 return strcmp(a->string, b->string);
198}
199
200static int cmp_desc(const void *p, const void *q)
201{
202 struct desc *a = *(struct desc **)p;
203 struct desc *b = *(struct desc **)q;
204
205 return strcmp(a->cmd, b->cmd);
206}
207
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200208/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200209void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200210{
211 unsigned int i, j;
212 struct cmd_node *cnode;
213 vector descvec;
214 struct cmd_element *cmd_element;
215
216 for (i = 0; i < vector_active(cmdvec); i++)
217 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
218 vector cmd_vector = cnode->cmd_vector;
219 qsort(cmd_vector->index, vector_active(cmd_vector),
220 sizeof(void *), cmp_node);
221
222 for (j = 0; j < vector_active(cmd_vector); j++)
223 if ((cmd_element =
224 vector_slot(cmd_vector, j)) != NULL
225 && vector_active(cmd_element->strvec)) {
226 descvec =
227 vector_slot(cmd_element->strvec,
228 vector_active
229 (cmd_element->strvec) -
230 1);
231 qsort(descvec->index,
232 vector_active(descvec),
233 sizeof(void *), cmp_desc);
234 }
235 }
236}
237
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200238/*! Break up string in command tokens. Return leading indents.
239 * \param[in] string String to split.
240 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
241 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
242 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
243 *
244 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
245 * so that \a indent can simply return the count of leading spaces.
246 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
247 */
248int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200249{
250 const char *cp, *start;
251 char *token;
252 int strlen;
253 vector strvec;
254
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200255 *strvec_p = NULL;
256 if (indent)
257 *indent = 0;
258
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200259 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200260 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261
262 cp = string;
263
264 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200265 while (isspace((int)*cp) && *cp != '\0') {
266 /* if we're counting indents, we need to be strict about them */
267 if (indent && (*cp != ' ') && (*cp != '\t')) {
268 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
269 if (*cp == '\n' || *cp == '\r') {
270 cp++;
271 string = cp;
272 continue;
273 }
274 return CMD_ERR_INVALID_INDENT;
275 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200276 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200277 }
278
279 if (indent)
280 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 /* Return if there is only white spaces */
283 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200284 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200285
286 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200287 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200288
289 /* Prepare return vector. */
290 strvec = vector_init(VECTOR_MIN_SIZE);
291
292 /* Copy each command piece and set into vector. */
293 while (1) {
294 start = cp;
295 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
296 *cp != '\0')
297 cp++;
298 strlen = cp - start;
299 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
300 memcpy(token, start, strlen);
301 *(token + strlen) = '\0';
302 vector_set(strvec, token);
303
304 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
305 *cp != '\0')
306 cp++;
307
308 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200309 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200310 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200311
312 *strvec_p = strvec;
313 return CMD_SUCCESS;
314}
315
316/*! Breaking up string into each command piece. I assume given
317 character is separated by a space character. Return value is a
318 vector which includes char ** data element. */
319vector cmd_make_strvec(const char *string)
320{
321 vector strvec;
322 cmd_make_strvec2(string, NULL, &strvec);
323 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200324}
325
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200326/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200327void cmd_free_strvec(vector v)
328{
329 unsigned int i;
330 char *cp;
331
332 if (!v)
333 return;
334
335 for (i = 0; i < vector_active(v); i++)
336 if ((cp = vector_slot(v, i)) != NULL)
337 talloc_free(cp);
338
339 vector_free(v);
340}
341
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200342/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200343static char *cmd_desc_str(const char **string)
344{
345 const char *cp, *start;
346 char *token;
347 int strlen;
348
349 cp = *string;
350
351 if (cp == NULL)
352 return NULL;
353
354 /* Skip white spaces. */
355 while (isspace((int)*cp) && *cp != '\0')
356 cp++;
357
358 /* Return if there is only white spaces */
359 if (*cp == '\0')
360 return NULL;
361
362 start = cp;
363
364 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
365 cp++;
366
367 strlen = cp - start;
368 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
369 memcpy(token, start, strlen);
370 *(token + strlen) = '\0';
371
372 *string = cp;
373
374 return token;
375}
376
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200377/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378static vector cmd_make_descvec(const char *string, const char *descstr)
379{
380 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100381 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200382 const char *sp;
383 char *token;
384 int len;
385 const char *cp;
386 const char *dp;
387 vector allvec;
388 vector strvec = NULL;
389 struct desc *desc;
390
391 cp = string;
392 dp = descstr;
393
394 if (cp == NULL)
395 return NULL;
396
397 allvec = vector_init(VECTOR_MIN_SIZE);
398
399 while (1) {
400 while (isspace((int)*cp) && *cp != '\0')
401 cp++;
402
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100403 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
404 if (cp[0] == '[' && cp[1] == '(') {
405 optional_brace = 1;
406 cp++;
407 }
408
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200409 if (*cp == '(') {
410 multiple = 1;
411 cp++;
412 }
413 if (*cp == ')') {
414 multiple = 0;
415 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100416 if (*cp == ']')
417 cp++;
418 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200419 }
420 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100421 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200422 cp++;
423 }
424
425 while (isspace((int)*cp) && *cp != '\0')
426 cp++;
427
428 if (*cp == '(') {
429 multiple = 1;
430 cp++;
431 }
432
433 if (*cp == '\0')
434 return allvec;
435
436 sp = cp;
437
438 while (!
439 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
440 || *cp == ')' || *cp == '|') && *cp != '\0')
441 cp++;
442
443 len = cp - sp;
444
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100445 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
446 if (optional_brace) {
447 /* Place each individual multi-choice token in its own square braces */
448 token[0] = '[';
449 memcpy(token + 1, sp, len);
450 token[1 + len] = ']';
451 token[2 + len] = '\0';
452 } else {
453 memcpy(token, sp, len);
454 *(token + len) = '\0';
455 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200456
457 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
458 desc->cmd = token;
459 desc->str = cmd_desc_str(&dp);
460
461 if (multiple) {
462 if (multiple == 1) {
463 strvec = vector_init(VECTOR_MIN_SIZE);
464 vector_set(allvec, strvec);
465 }
466 multiple++;
467 } else {
468 strvec = vector_init(VECTOR_MIN_SIZE);
469 vector_set(allvec, strvec);
470 }
471 vector_set(strvec, desc);
472 }
473}
474
475/* Count mandantory string vector size. This is to determine inputed
476 command has enough command length. */
477static int cmd_cmdsize(vector strvec)
478{
479 unsigned int i;
480 int size = 0;
481 vector descvec;
482 struct desc *desc;
483
484 for (i = 0; i < vector_active(strvec); i++)
485 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100486 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200487 && (desc = vector_slot(descvec, 0)) != NULL) {
488 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
489 return size;
490 else
491 size++;
492 } else
493 size++;
494 }
495 return size;
496}
497
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200498/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200499const char *cmd_prompt(enum node_type node)
500{
501 struct cmd_node *cnode;
502
503 cnode = vector_slot(cmdvec, node);
504 return cnode->prompt;
505}
506
Alexander Couzensad580ba2016-05-16 16:01:45 +0200507/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200508 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200509 * \param unsafe string
510 * \return a new talloc char *
511 */
512char *osmo_asciidoc_escape(const char *inp)
513{
514 int _strlen;
515 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200516 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200517
518 if (!inp)
519 return NULL;
520 _strlen = strlen(inp);
521
522 for (i = 0; i < _strlen; ++i) {
523 switch (inp[i]) {
524 case '|':
525 len += 2;
526 break;
527 default:
528 len += 1;
529 break;
530 }
531 }
532
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200533 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200534 if (!out)
535 return NULL;
536
537 out_ptr = out;
538
Alexander Couzensad580ba2016-05-16 16:01:45 +0200539 for (i = 0; i < _strlen; ++i) {
540 switch (inp[i]) {
541 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200542 /* Prepend escape character "\": */
543 *(out_ptr++) = '\\';
544 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200545 default:
546 *(out_ptr++) = inp[i];
547 break;
548 }
549 }
550
Alexander Couzensad580ba2016-05-16 16:01:45 +0200551 out_ptr[0] = '\0';
552 return out;
553}
554
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100555static char *xml_escape(const char *inp)
556{
557 int _strlen;
558 char *out, *out_ptr;
559 int len = 0, i, j;
560
561 if (!inp)
562 return NULL;
563 _strlen = strlen(inp);
564
565 for (i = 0; i < _strlen; ++i) {
566 switch (inp[i]) {
567 case '"':
568 len += 6;
569 break;
570 case '\'':
571 len += 6;
572 break;
573 case '<':
574 len += 4;
575 break;
576 case '>':
577 len += 4;
578 break;
579 case '&':
580 len += 5;
581 break;
582 default:
583 len += 1;
584 break;
585 }
586 }
587
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200588 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100589 if (!out)
590 return NULL;
591
592 out_ptr = out;
593
594#define ADD(out, str) \
595 for (j = 0; j < strlen(str); ++j) \
596 *(out++) = str[j];
597
598 for (i = 0; i < _strlen; ++i) {
599 switch (inp[i]) {
600 case '"':
601 ADD(out_ptr, "&quot;");
602 break;
603 case '\'':
604 ADD(out_ptr, "&apos;");
605 break;
606 case '<':
607 ADD(out_ptr, "&lt;");
608 break;
609 case '>':
610 ADD(out_ptr, "&gt;");
611 break;
612 case '&':
613 ADD(out_ptr, "&amp;");
614 break;
615 default:
616 *(out_ptr++) = inp[i];
617 break;
618 }
619 }
620
621#undef ADD
622
623 out_ptr[0] = '\0';
624 return out;
625}
626
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200627typedef int (*print_func_t)(void *data, const char *fmt, ...);
628
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700629static const struct value_string cmd_attr_desc[] = {
630 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700631 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700632 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700633 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700634 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700635 { 0, NULL }
636};
637
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700638/* Public attributes (to be printed in the VTY / XML reference) */
639#define CMD_ATTR_PUBLIC_MASK \
Vadim Yanitskiy67608452020-10-23 20:37:32 +0700640 (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700641
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700642/* Get a flag character for a global VTY command attribute */
643static char cmd_attr_get_flag(unsigned int attr)
644{
645 switch (attr) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700646 case CMD_ATTR_HIDDEN:
647 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700648 case CMD_ATTR_IMMEDIATE:
649 return '!';
650 case CMD_ATTR_NODE_EXIT:
651 return '@';
652 default:
653 return '.';
654 }
655}
656
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700657/* Description of attributes shared between the lib commands */
658static const char * const cmd_lib_attr_desc[32] = {
659 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
660 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200661 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
662 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200663 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
664 "This command applies on IPA link establishment",
665 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
666 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700667};
668
669/* Flag letters of attributes shared between the lib commands.
670 * NOTE: uppercase letters only, the rest is reserved for applications. */
671static const char cmd_lib_attr_letters[32] = {
672 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200673 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200674 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
675 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700676};
677
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100678/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200679 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100680 */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700681static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
682 void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100683{
684 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700685 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100686
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200687 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700688
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700689 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700690 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700691 print_func(data, " <attributes scope='global'>%s", newline);
692
693 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
694 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700695 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700696
697 if (~cmd->attr & cmd_attr_desc[i].value)
698 continue;
699
700 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700701 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700702 xml_att_desc, newline);
703 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700704
705 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
706 if (flag != '.')
707 print_func(data, " flag='%c'", flag);
708 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700709 }
710
711 print_func(data, " </attributes>%s", newline);
712 }
713
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700714 /* Print application specific attributes and their description */
715 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700716 const char * const *desc;
717 const char *letters;
718
719 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
720 print_func(data, " <attributes scope='library'>%s", newline);
721 letters = &cmd_lib_attr_letters[0];
722 desc = &cmd_lib_attr_desc[0];
723 } else {
724 print_func(data, " <attributes scope='application'>%s", newline);
725 letters = &host.app_info->usr_attr_letters[0];
726 desc = &host.app_info->usr_attr_desc[0];
727 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700728
729 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
730 char *xml_att_desc;
731 char flag;
732
733 /* Skip attribute if *not* set */
Harald Weltec296e292020-12-21 15:44:52 +0100734 if (~cmd->usrattr & ((unsigned)1 << i))
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700735 continue;
736
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700737 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700738 print_func(data, " <attribute doc='%s'", xml_att_desc);
739 talloc_free(xml_att_desc);
740
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700741 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700742 print_func(data, " flag='%c'", flag);
743 print_func(data, " />%s", newline);
744 }
745
746 print_func(data, " </attributes>%s", newline);
747 }
748
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200749 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100750
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700751 for (i = 0; i < vector_count(cmd->strvec); ++i) {
752 vector descvec = vector_slot(cmd->strvec, i);
753 int j;
754 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100755 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700756 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100757 if (desc == NULL)
758 continue;
759
760 xml_param = xml_escape(desc->cmd);
761 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200762 print_func(data, " <param name='%s' doc='%s' />%s",
763 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100764 talloc_free(xml_param);
765 talloc_free(xml_doc);
766 }
767 }
768
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200769 print_func(data, " </params>%s", newline);
770 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100771
772 talloc_free(xml_string);
773 return 0;
774}
775
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700776static bool vty_command_is_common(const struct cmd_element *cmd);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200777
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100778/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200779 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700780 *
781 * (gflag_mask, match = false) - print only those commands with non-matching flags.
782 * (gflag_mask, match = true) - print only those commands with matching flags.
783 *
784 * Some examples:
785 *
786 * Print all commands except deprecated and hidden (default mode):
787 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
788 * Print only deprecated and hidden commands:
789 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
790 * Print all commands except deprecated (expert mode):
791 * (CMD_ATTR_DEPRECATED, false)
792 * Print only hidden commands:
793 * (CMD_ATTR_HIDDEN, true)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100794 */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700795static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
796 unsigned char gflag_mask, bool match)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100797{
798 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200799 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100800
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200801 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100802
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200803 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200804 print_func(data, " <node id='_common_cmds_'>%s", newline);
805 print_func(data, " <name>Common Commands</name>%s", newline);
806 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
807 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200808 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700809 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200810 if (!cnode)
811 continue;
812 if (cnode->node != CONFIG_NODE)
813 continue;
814
815 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700816 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200817 if (!vty_command_is_common(elem))
818 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700819 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700820 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700821 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700822 continue;
823 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200824 }
825 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200826 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200827
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100828 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700829 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100830 if (!cnode)
831 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200832 if (vector_active(cnode->cmd_vector) < 1)
833 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100834
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200835 /* De-dup node IDs: how many times has this same name been used before? Count the first
836 * occurence as _1 and omit that first suffix, so that the first occurence is called
837 * 'name', the second becomes 'name_2', then 'name_3', ... */
838 same_name_count = 1;
839 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700840 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200841 if (!cnode2)
842 continue;
843 if (strcmp(cnode->name, cnode2->name) == 0)
844 same_name_count ++;
845 }
846
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200847 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200848 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200849 print_func(data, "_%d", same_name_count);
850 print_func(data, "'>%s", newline);
851 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100852
853 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700854 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200855 if (vty_command_is_common(elem))
856 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700857 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700858 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700859 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700860 continue;
861 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100862 }
863
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200864 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100865 }
866
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200867 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100868
869 return 0;
870}
871
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200872static int print_func_vty(void *data, const char *format, ...)
873{
874 struct vty *vty = data;
875 va_list args;
876 int rc;
877 va_start(args, format);
878 rc = vty_out_va(vty, format, args);
879 va_end(args);
880 return rc;
881}
882
883static int vty_dump_xml_ref_to_vty(struct vty *vty)
884{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700885 unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
886 if (!vty->expert_mode)
887 gflag_mask |= CMD_ATTR_HIDDEN;
888 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200889}
890
891static int print_func_stream(void *data, const char *format, ...)
892{
893 va_list args;
894 int rc;
895 va_start(args, format);
896 rc = vfprintf((FILE*)data, format, args);
897 va_end(args);
898 return rc;
899}
900
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700901const struct value_string vty_ref_gen_mode_names[] = {
902 { VTY_REF_GEN_MODE_DEFAULT, "default" },
903 { VTY_REF_GEN_MODE_EXPERT, "expert" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700904 { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700905 { 0, NULL }
906};
907
908const struct value_string vty_ref_gen_mode_desc[] = {
909 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
910 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700911 { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700912 { 0, NULL }
913};
914
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200915/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700916 * \param[out] stream Output stream to print the XML reference to.
917 * \param[in] mode The XML reference generation mode.
918 * \returns always 0 for now, no errors possible.
919 */
920int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
921{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700922 unsigned char gflag_mask;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700923 bool match = false;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700924
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700925 switch (mode) {
926 case VTY_REF_GEN_MODE_EXPERT:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700927 /* All commands except deprecated */
928 gflag_mask = CMD_ATTR_DEPRECATED;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700929 break;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700930 case VTY_REF_GEN_MODE_HIDDEN:
931 /* Only hidden commands */
932 gflag_mask = CMD_ATTR_HIDDEN;
933 match = true;
934 break;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700935 case VTY_REF_GEN_MODE_DEFAULT:
936 default:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700937 /* All commands except deprecated and hidden */
938 gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700939 break;
940 }
941
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700942 return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700943}
944
945/*! Print the XML reference of all VTY nodes to the given stream.
946 * \param[out] stream Output stream to print the XML reference to.
947 * \returns always 0 for now, no errors possible.
948 *
949 * NOTE: this function is deprecated because it does not allow to
950 * specify the XML reference generation mode (default mode
951 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200952 */
953int vty_dump_xml_ref(FILE *stream)
954{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700955 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200956}
957
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200958/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100959static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
960{
961 int i;
962
963 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
964 struct cmd_element *elem;
965 elem = vector_slot(cnode->cmd_vector, i);
966 if (!elem->string)
967 continue;
968 if (!strcmp(elem->string, cmdstring))
969 return 1;
970 }
971 return 0;
972}
973
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200974/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200975 * \param[in] ntype Node Type
976 * \param[cmd] element to be installed
977 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000978void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200979{
980 struct cmd_node *cnode;
981
982 cnode = vector_slot(cmdvec, ntype);
983
Harald Weltea99d45a2015-11-12 13:48:23 +0100984 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100985 /* ensure no _identical_ command has been registered at this
986 * node so far */
987 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200988
989 vector_set(cnode->cmd_vector, cmd);
990
991 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
992 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
993}
994
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700995/*! Install a library command into a node
996 * \param[in] ntype Node Type
997 * \param[in] cmd element to be installed
998 */
999void install_lib_element(int ntype, struct cmd_element *cmd)
1000{
1001 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1002 install_element(ntype, cmd);
1003}
1004
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001005/* Install a command into VIEW and ENABLE node */
1006void install_element_ve(struct cmd_element *cmd)
1007{
1008 install_element(VIEW_NODE, cmd);
1009 install_element(ENABLE_NODE, cmd);
1010}
1011
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001012/* Install a library command into VIEW and ENABLE node */
1013void install_lib_element_ve(struct cmd_element *cmd)
1014{
1015 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1016 install_element_ve(cmd);
1017}
1018
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001019#ifdef VTY_CRYPT_PW
1020static unsigned char itoa64[] =
1021 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1022
1023static void to64(char *s, long v, int n)
1024{
1025 while (--n >= 0) {
1026 *s++ = itoa64[v & 0x3f];
1027 v >>= 6;
1028 }
1029}
1030
1031static char *zencrypt(const char *passwd)
1032{
1033 char salt[6];
1034 struct timeval tv;
1035 char *crypt(const char *, const char *);
1036
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001037 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001038
1039 to64(&salt[0], random(), 3);
1040 to64(&salt[3], tv.tv_usec, 3);
1041 salt[5] = '\0';
1042
1043 return crypt(passwd, salt);
1044}
1045#endif
1046
1047/* This function write configuration of this host. */
1048static int config_write_host(struct vty *vty)
1049{
1050 if (host.name)
1051 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1052
1053 if (host.encrypt) {
1054 if (host.password_encrypt)
1055 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1056 VTY_NEWLINE);
1057 if (host.enable_encrypt)
1058 vty_out(vty, "enable password 8 %s%s",
1059 host.enable_encrypt, VTY_NEWLINE);
1060 } else {
1061 if (host.password)
1062 vty_out(vty, "password %s%s", host.password,
1063 VTY_NEWLINE);
1064 if (host.enable)
1065 vty_out(vty, "enable password %s%s", host.enable,
1066 VTY_NEWLINE);
1067 }
1068
1069 if (host.advanced)
1070 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1071
1072 if (host.encrypt)
1073 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1074
1075 if (host.lines >= 0)
1076 vty_out(vty, "service terminal-length %d%s", host.lines,
1077 VTY_NEWLINE);
1078
1079 if (host.motdfile)
1080 vty_out(vty, "banner motd file %s%s", host.motdfile,
1081 VTY_NEWLINE);
1082 else if (!host.motd)
1083 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1084
1085 return 1;
1086}
1087
1088/* Utility function for getting command vector. */
1089static vector cmd_node_vector(vector v, enum node_type ntype)
1090{
1091 struct cmd_node *cnode = vector_slot(v, ntype);
1092 return cnode->cmd_vector;
1093}
1094
1095/* Completion match types. */
1096enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001097 NO_MATCH = 0,
1098 ANY_MATCH,
1099 EXTEND_MATCH,
1100 IPV4_PREFIX_MATCH,
1101 IPV4_MATCH,
1102 IPV6_PREFIX_MATCH,
1103 IPV6_MATCH,
1104 RANGE_MATCH,
1105 VARARG_MATCH,
1106 PARTLY_MATCH,
1107 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001108};
1109
1110static enum match_type cmd_ipv4_match(const char *str)
1111{
1112 const char *sp;
1113 int dots = 0, nums = 0;
1114 char buf[4];
1115
1116 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001117 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001118
1119 for (;;) {
1120 memset(buf, 0, sizeof(buf));
1121 sp = str;
1122 while (*str != '\0') {
1123 if (*str == '.') {
1124 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126
1127 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001128 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001129
1130 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001131 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001132
1133 dots++;
1134 break;
1135 }
1136 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001137 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001138
1139 str++;
1140 }
1141
1142 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001143 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001144
1145 strncpy(buf, sp, str - sp);
1146 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 nums++;
1150
1151 if (*str == '\0')
1152 break;
1153
1154 str++;
1155 }
1156
1157 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001158 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001159
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001160 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001161}
1162
1163static enum match_type cmd_ipv4_prefix_match(const char *str)
1164{
1165 const char *sp;
1166 int dots = 0;
1167 char buf[4];
1168
1169 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001170 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001171
1172 for (;;) {
1173 memset(buf, 0, sizeof(buf));
1174 sp = str;
1175 while (*str != '\0' && *str != '/') {
1176 if (*str == '.') {
1177 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001178 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001179
1180 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001181 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001182
1183 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001184 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001185
1186 dots++;
1187 break;
1188 }
1189
1190 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001191 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001192
1193 str++;
1194 }
1195
1196 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001197 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001198
1199 strncpy(buf, sp, str - sp);
1200 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001201 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001202
1203 if (dots == 3) {
1204 if (*str == '/') {
1205 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001206 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207
1208 str++;
1209 break;
1210 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001211 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001212 }
1213
1214 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001215 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001216
1217 str++;
1218 }
1219
1220 sp = str;
1221 while (*str != '\0') {
1222 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001223 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001224
1225 str++;
1226 }
1227
1228 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001229 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001230
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001231 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001232}
1233
1234#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1235#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1236#define STATE_START 1
1237#define STATE_COLON 2
1238#define STATE_DOUBLE 3
1239#define STATE_ADDR 4
1240#define STATE_DOT 5
1241#define STATE_SLASH 6
1242#define STATE_MASK 7
1243
1244#ifdef HAVE_IPV6
1245
1246static enum match_type cmd_ipv6_match(const char *str)
1247{
1248 int state = STATE_START;
1249 int colons = 0, nums = 0, double_colon = 0;
1250 const char *sp = NULL;
1251 struct sockaddr_in6 sin6_dummy;
1252 int ret;
1253
1254 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001255 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001256
1257 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001258 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259
1260 /* use inet_pton that has a better support,
1261 * for example inet_pton can support the automatic addresses:
1262 * ::1.2.3.4
1263 */
1264 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1265
1266 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001267 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001268
1269 while (*str != '\0') {
1270 switch (state) {
1271 case STATE_START:
1272 if (*str == ':') {
1273 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001274 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001275 colons--;
1276 state = STATE_COLON;
1277 } else {
1278 sp = str;
1279 state = STATE_ADDR;
1280 }
1281
1282 continue;
1283 case STATE_COLON:
1284 colons++;
1285 if (*(str + 1) == ':')
1286 state = STATE_DOUBLE;
1287 else {
1288 sp = str + 1;
1289 state = STATE_ADDR;
1290 }
1291 break;
1292 case STATE_DOUBLE:
1293 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001294 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001295
1296 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001297 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001298 else {
1299 if (*(str + 1) != '\0')
1300 colons++;
1301 sp = str + 1;
1302 state = STATE_ADDR;
1303 }
1304
1305 double_colon++;
1306 nums++;
1307 break;
1308 case STATE_ADDR:
1309 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1310 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001311 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001312
1313 nums++;
1314 state = STATE_COLON;
1315 }
1316 if (*(str + 1) == '.')
1317 state = STATE_DOT;
1318 break;
1319 case STATE_DOT:
1320 state = STATE_ADDR;
1321 break;
1322 default:
1323 break;
1324 }
1325
1326 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001327 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001328
1329 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001330 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331
1332 str++;
1333 }
1334
1335#if 0
1336 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001337 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001338#endif /* 0 */
1339
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001341}
1342
1343static enum match_type cmd_ipv6_prefix_match(const char *str)
1344{
1345 int state = STATE_START;
1346 int colons = 0, nums = 0, double_colon = 0;
1347 int mask;
1348 const char *sp = NULL;
1349 char *endptr = NULL;
1350
1351 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001352 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001353
1354 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001355 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001356
1357 while (*str != '\0' && state != STATE_MASK) {
1358 switch (state) {
1359 case STATE_START:
1360 if (*str == ':') {
1361 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001362 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001363 colons--;
1364 state = STATE_COLON;
1365 } else {
1366 sp = str;
1367 state = STATE_ADDR;
1368 }
1369
1370 continue;
1371 case STATE_COLON:
1372 colons++;
1373 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001374 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001375 else if (*(str + 1) == ':')
1376 state = STATE_DOUBLE;
1377 else {
1378 sp = str + 1;
1379 state = STATE_ADDR;
1380 }
1381 break;
1382 case STATE_DOUBLE:
1383 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001384 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001385
1386 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001387 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001388 else {
1389 if (*(str + 1) != '\0' && *(str + 1) != '/')
1390 colons++;
1391 sp = str + 1;
1392
1393 if (*(str + 1) == '/')
1394 state = STATE_SLASH;
1395 else
1396 state = STATE_ADDR;
1397 }
1398
1399 double_colon++;
1400 nums += 1;
1401 break;
1402 case STATE_ADDR:
1403 if (*(str + 1) == ':' || *(str + 1) == '.'
1404 || *(str + 1) == '\0' || *(str + 1) == '/') {
1405 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001406 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001407
1408 for (; sp <= str; sp++)
1409 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001410 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001411
1412 nums++;
1413
1414 if (*(str + 1) == ':')
1415 state = STATE_COLON;
1416 else if (*(str + 1) == '.')
1417 state = STATE_DOT;
1418 else if (*(str + 1) == '/')
1419 state = STATE_SLASH;
1420 }
1421 break;
1422 case STATE_DOT:
1423 state = STATE_ADDR;
1424 break;
1425 case STATE_SLASH:
1426 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001427 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428
1429 state = STATE_MASK;
1430 break;
1431 default:
1432 break;
1433 }
1434
1435 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001436 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001437
1438 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001439 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001440
1441 str++;
1442 }
1443
1444 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001445 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001446
1447 mask = strtol(str, &endptr, 10);
1448 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001449 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001450
1451 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001452 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001453
1454/* I don't know why mask < 13 makes command match partly.
1455 Forgive me to make this comments. I Want to set static default route
1456 because of lack of function to originate default in ospf6d; sorry
1457 yasu
1458 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001459 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001460*/
1461
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001462 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001463}
1464
1465#endif /* HAVE_IPV6 */
1466
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001467
1468#if ULONG_MAX == 18446744073709551615UL
1469#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1470#elif ULONG_MAX == 4294967295UL
1471#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1472#else
1473#error "ULONG_MAX not defined!"
1474#endif
1475
1476#if LONG_MAX == 9223372036854775807L
1477#define DECIMAL_STRLEN_MAX_SIGNED 19
1478#elif LONG_MAX == 2147483647L
1479#define DECIMAL_STRLEN_MAX_SIGNED 10
1480#else
1481#error "LONG_MAX not defined!"
1482#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001483
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001484int vty_cmd_range_match(const char *range, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001485{
1486 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001487 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001488 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001489
1490 if (str == NULL)
1491 return 1;
1492
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001493 if (range[1] == '-') {
1494 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001495
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001496 val = strtol(str, &endptr, 10);
1497 if (*endptr != '\0')
1498 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001499
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001500 range += 2;
1501 p = strchr(range, '-');
1502 if (p == NULL)
1503 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001504 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001505 return 0;
1506 strncpy(buf, range, p - range);
1507 buf[p - range] = '\0';
1508 min = -strtol(buf, &endptr, 10);
1509 if (*endptr != '\0')
1510 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001511
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001512 range = p + 1;
1513 p = strchr(range, '>');
1514 if (p == NULL)
1515 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001516 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001517 return 0;
1518 strncpy(buf, range, p - range);
1519 buf[p - range] = '\0';
1520 max = strtol(buf, &endptr, 10);
1521 if (*endptr != '\0')
1522 return 0;
1523
1524 if (val < min || val > max)
1525 return 0;
1526 } else {
1527 unsigned long min, max, val;
1528
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001529 if (str[0] == '-')
1530 return 0;
1531
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001532 val = strtoul(str, &endptr, 10);
1533 if (*endptr != '\0')
1534 return 0;
1535
1536 range++;
1537 p = strchr(range, '-');
1538 if (p == NULL)
1539 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001540 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001541 return 0;
1542 strncpy(buf, range, p - range);
1543 buf[p - range] = '\0';
1544 min = strtoul(buf, &endptr, 10);
1545 if (*endptr != '\0')
1546 return 0;
1547
1548 range = p + 1;
1549 p = strchr(range, '>');
1550 if (p == NULL)
1551 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001552 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001553 return 0;
1554 strncpy(buf, range, p - range);
1555 buf[p - range] = '\0';
1556 max = strtoul(buf, &endptr, 10);
1557 if (*endptr != '\0')
1558 return 0;
1559
1560 if (val < min || val > max)
1561 return 0;
1562 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001563
1564 return 1;
1565}
1566
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001567/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001568static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001569{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001570 /* we've got "[blah]". We want to strip off the []s and redo the
1571 * match check for "blah"
1572 */
1573 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001574
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001575 if (len < 3)
1576 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001578 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001579}
1580
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001581static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001582cmd_match(const char *str, const char *command,
1583 enum match_type min, bool recur)
1584{
1585
1586 if (recur && CMD_OPTION(str))
1587 {
1588 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001589 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001590
1591 /* this would be a bug in a command, however handle it gracefully
1592 * as it we only discover it if a user tries to run it
1593 */
1594 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001595 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001596
1597 ret = cmd_match(tmp, command, min, false);
1598
1599 talloc_free(tmp);
1600
1601 return ret;
1602 }
1603 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001604 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001605 else if (CMD_RANGE(str))
1606 {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001607 if (vty_cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001608 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001609 }
1610#ifdef HAVE_IPV6
1611 else if (CMD_IPV6(str))
1612 {
1613 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001614 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001615 }
1616 else if (CMD_IPV6_PREFIX(str))
1617 {
1618 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001619 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001620 }
1621#endif /* HAVE_IPV6 */
1622 else if (CMD_IPV4(str))
1623 {
1624 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001625 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001626 }
1627 else if (CMD_IPV4_PREFIX(str))
1628 {
1629 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001630 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001631 }
1632 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001633 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001634 else if (strncmp(command, str, strlen(command)) == 0)
1635 {
1636 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001637 return EXACT_MATCH;
1638 else if (PARTLY_MATCH >= min)
1639 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001640 }
1641
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001642 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001643}
1644
1645/* Filter vector at the specified index and by the given command string, to
1646 * the desired matching level (thus allowing part matches), and return match
1647 * type flag.
1648 */
1649static enum match_type
1650cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001651{
1652 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001653 struct cmd_element *cmd_element;
1654 enum match_type match_type;
1655 vector descvec;
1656 struct desc *desc;
1657
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001658 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001659
1660 /* If command and cmd_element string does not match set NULL to vector */
1661 for (i = 0; i < vector_active(v); i++)
1662 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001663 if (index >= vector_active(cmd_element->strvec))
1664 vector_slot(v, i) = NULL;
1665 else {
1666 unsigned int j;
1667 int matched = 0;
1668
1669 descvec =
1670 vector_slot(cmd_element->strvec, index);
1671
1672 for (j = 0; j < vector_active(descvec); j++)
1673 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001674 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001675
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001676 ret = cmd_match (desc->cmd, command, level, true);
1677
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001678 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001679 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001680
1681 if (match_type < ret)
1682 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001683 }
1684 if (!matched)
1685 vector_slot(v, i) = NULL;
1686 }
1687 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001688
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001689 if (match_type == NO_MATCH)
1690 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001691
1692 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1693 * go again and filter out commands whose argument (at this index) is
1694 * 'weaker'. E.g., if we have 2 commands:
1695 *
1696 * foo bar <1-255>
1697 * foo bar BLAH
1698 *
1699 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001700 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001701 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1702 *
1703 * If we don't do a 2nd pass and filter it out, the higher-layers will
1704 * consider this to be ambiguous.
1705 */
1706 for (i = 0; i < vector_active(v); i++)
1707 if ((cmd_element = vector_slot(v, i)) != NULL) {
1708 if (index >= vector_active(cmd_element->strvec))
1709 vector_slot(v, i) = NULL;
1710 else {
1711 unsigned int j;
1712 int matched = 0;
1713
1714 descvec =
1715 vector_slot(cmd_element->strvec, index);
1716
1717 for (j = 0; j < vector_active(descvec); j++)
1718 if ((desc = vector_slot(descvec, j))) {
1719 enum match_type ret;
1720
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001721 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001722
1723 if (ret >= match_type)
1724 matched++;
1725 }
1726 if (!matched)
1727 vector_slot(v, i) = NULL;
1728 }
1729 }
1730
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001731 return match_type;
1732}
1733
1734/* Check ambiguous match */
1735static int
1736is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1737{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001738 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001739 unsigned int i;
1740 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001741 struct cmd_element *cmd_element;
1742 const char *matched = NULL;
1743 vector descvec;
1744 struct desc *desc;
1745
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001746 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1747 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1748 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1749 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1750 * that case, the string must remain allocated until this function exits or another match comes
1751 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1752 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1753 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1754 void *cmd_deopt_ctx = NULL;
1755
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001756 for (i = 0; i < vector_active(v); i++) {
1757 cmd_element = vector_slot(v, i);
1758 if (!cmd_element)
1759 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001760
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001761 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001762
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001763 descvec = vector_slot(cmd_element->strvec, index);
1764
1765 for (j = 0; j < vector_active(descvec); j++) {
1766 desc = vector_slot(descvec, j);
1767 if (!desc)
1768 continue;
1769
1770 enum match_type mtype;
1771 const char *str = desc->cmd;
1772
1773 if (CMD_OPTION(str)) {
1774 if (!cmd_deopt_ctx)
1775 cmd_deopt_ctx =
1776 talloc_named_const(tall_vty_cmd_ctx, 0,
1777 __func__);
1778 str = cmd_deopt(cmd_deopt_ctx, str);
1779 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001780 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001781 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001782
1783 switch (type) {
1784 case EXACT_MATCH:
1785 if (!(CMD_VARIABLE (str))
1786 && strcmp(command, str) == 0)
1787 match++;
1788 break;
1789 case PARTLY_MATCH:
1790 if (!(CMD_VARIABLE (str))
1791 && strncmp(command, str, strlen (command)) == 0)
1792 {
1793 if (matched
1794 && strcmp(matched,
1795 str) != 0) {
1796 ret = 1; /* There is ambiguous match. */
1797 goto free_and_return;
1798 } else
1799 matched = str;
1800 match++;
1801 }
1802 break;
1803 case RANGE_MATCH:
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001804 if (vty_cmd_range_match
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001805 (str, command)) {
1806 if (matched
1807 && strcmp(matched,
1808 str) != 0) {
1809 ret = 1;
1810 goto free_and_return;
1811 } else
1812 matched = str;
1813 match++;
1814 }
1815 break;
1816#ifdef HAVE_IPV6
1817 case IPV6_MATCH:
1818 if (CMD_IPV6(str))
1819 match++;
1820 break;
1821 case IPV6_PREFIX_MATCH:
1822 if ((mtype =
1823 cmd_ipv6_prefix_match
1824 (command)) != NO_MATCH) {
1825 if (mtype == PARTLY_MATCH) {
1826 ret = 2; /* There is incomplete match. */
1827 goto free_and_return;
1828 }
1829
1830 match++;
1831 }
1832 break;
1833#endif /* HAVE_IPV6 */
1834 case IPV4_MATCH:
1835 if (CMD_IPV4(str))
1836 match++;
1837 break;
1838 case IPV4_PREFIX_MATCH:
1839 if ((mtype =
1840 cmd_ipv4_prefix_match
1841 (command)) != NO_MATCH) {
1842 if (mtype == PARTLY_MATCH) {
1843 ret = 2; /* There is incomplete match. */
1844 goto free_and_return;
1845 }
1846
1847 match++;
1848 }
1849 break;
1850 case EXTEND_MATCH:
1851 if (CMD_VARIABLE (str))
1852 match++;
1853 break;
1854 case NO_MATCH:
1855 default:
1856 break;
1857 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001858 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001859 if (!match)
1860 vector_slot(v, i) = NULL;
1861 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001862
1863free_and_return:
1864 if (cmd_deopt_ctx)
1865 talloc_free(cmd_deopt_ctx);
1866 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001867}
1868
1869/* If src matches dst return dst string, otherwise return NULL */
1870static const char *cmd_entry_function(const char *src, const char *dst)
1871{
1872 /* Skip variable arguments. */
1873 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1874 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1875 return NULL;
1876
1877 /* In case of 'command \t', given src is NULL string. */
1878 if (src == NULL)
1879 return dst;
1880
1881 /* Matched with input string. */
1882 if (strncmp(src, dst, strlen(src)) == 0)
1883 return dst;
1884
1885 return NULL;
1886}
1887
1888/* If src matches dst return dst string, otherwise return NULL */
1889/* This version will return the dst string always if it is
1890 CMD_VARIABLE for '?' key processing */
1891static const char *cmd_entry_function_desc(const char *src, const char *dst)
1892{
1893 if (CMD_VARARG(dst))
1894 return dst;
1895
1896 if (CMD_RANGE(dst)) {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001897 if (vty_cmd_range_match(dst, src))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001898 return dst;
1899 else
1900 return NULL;
1901 }
1902#ifdef HAVE_IPV6
1903 if (CMD_IPV6(dst)) {
1904 if (cmd_ipv6_match(src))
1905 return dst;
1906 else
1907 return NULL;
1908 }
1909
1910 if (CMD_IPV6_PREFIX(dst)) {
1911 if (cmd_ipv6_prefix_match(src))
1912 return dst;
1913 else
1914 return NULL;
1915 }
1916#endif /* HAVE_IPV6 */
1917
1918 if (CMD_IPV4(dst)) {
1919 if (cmd_ipv4_match(src))
1920 return dst;
1921 else
1922 return NULL;
1923 }
1924
1925 if (CMD_IPV4_PREFIX(dst)) {
1926 if (cmd_ipv4_prefix_match(src))
1927 return dst;
1928 else
1929 return NULL;
1930 }
1931
1932 /* Optional or variable commands always match on '?' */
1933 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1934 return dst;
1935
1936 /* In case of 'command \t', given src is NULL string. */
1937 if (src == NULL)
1938 return dst;
1939
1940 if (strncmp(src, dst, strlen(src)) == 0)
1941 return dst;
1942 else
1943 return NULL;
1944}
1945
1946/* Check same string element existence. If it isn't there return
1947 1. */
1948static int cmd_unique_string(vector v, const char *str)
1949{
1950 unsigned int i;
1951 char *match;
1952
1953 for (i = 0; i < vector_active(v); i++)
1954 if ((match = vector_slot(v, i)) != NULL)
1955 if (strcmp(match, str) == 0)
1956 return 0;
1957 return 1;
1958}
1959
1960/* Compare string to description vector. If there is same string
1961 return 1 else return 0. */
1962static int desc_unique_string(vector v, const char *str)
1963{
1964 unsigned int i;
1965 struct desc *desc;
1966
1967 for (i = 0; i < vector_active(v); i++)
1968 if ((desc = vector_slot(v, i)) != NULL)
1969 if (strcmp(desc->cmd, str) == 0)
1970 return 1;
1971 return 0;
1972}
1973
1974static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1975{
1976 if (first_word != NULL &&
1977 node != AUTH_NODE &&
1978 node != VIEW_NODE &&
1979 node != AUTH_ENABLE_NODE &&
1980 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1981 return 1;
1982 return 0;
1983}
1984
1985/* '?' describe command support. */
1986static vector
1987cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1988{
1989 unsigned int i;
1990 vector cmd_vector;
1991#define INIT_MATCHVEC_SIZE 10
1992 vector matchvec;
1993 struct cmd_element *cmd_element;
1994 unsigned int index;
1995 int ret;
1996 enum match_type match;
1997 char *command;
1998 static struct desc desc_cr = { "<cr>", "" };
1999
2000 /* Set index. */
2001 if (vector_active(vline) == 0) {
2002 *status = CMD_ERR_NO_MATCH;
2003 return NULL;
2004 } else
2005 index = vector_active(vline) - 1;
2006
2007 /* Make copy vector of current node's command vector. */
2008 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2009
2010 /* Prepare match vector */
2011 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2012
2013 /* Filter commands. */
2014 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002015 for (i = 0; i < index; i++) {
2016 command = vector_slot(vline, i);
2017 if (!command)
2018 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002019
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002020 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002021
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002022 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002023 struct cmd_element *cmd_element;
2024 vector descvec;
2025 unsigned int j, k;
2026
2027 for (j = 0; j < vector_active(cmd_vector); j++)
2028 if ((cmd_element =
2029 vector_slot(cmd_vector, j)) != NULL
2030 &&
2031 (vector_active(cmd_element->strvec))) {
2032 descvec =
2033 vector_slot(cmd_element->
2034 strvec,
2035 vector_active
2036 (cmd_element->
2037 strvec) - 1);
2038 for (k = 0;
2039 k < vector_active(descvec);
2040 k++) {
2041 struct desc *desc =
2042 vector_slot(descvec,
2043 k);
2044 vector_set(matchvec,
2045 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002046 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002047 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002048
Harald Welte80d30fe2013-02-12 11:08:57 +01002049 vector_set(matchvec, &desc_cr);
2050 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002051
Harald Welte80d30fe2013-02-12 11:08:57 +01002052 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002053 }
2054
Harald Welte80d30fe2013-02-12 11:08:57 +01002055 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2056 match)) == 1) {
2057 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002058 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002059 *status = CMD_ERR_AMBIGUOUS;
2060 return NULL;
2061 } else if (ret == 2) {
2062 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002063 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002064 *status = CMD_ERR_NO_MATCH;
2065 return NULL;
2066 }
2067 }
2068
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002069 /* Prepare match vector */
2070 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2071
2072 /* Make sure that cmd_vector is filtered based on current word */
2073 command = vector_slot(vline, index);
2074 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002075 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002076
2077 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002078 for (i = 0; i < vector_active(cmd_vector); i++) {
2079 const char *string = NULL;
2080 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002081
Harald Welte80d30fe2013-02-12 11:08:57 +01002082 cmd_element = vector_slot(cmd_vector, i);
2083 if (!cmd_element)
2084 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002085
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002086 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2087 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002088 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002089 continue;
2090
Harald Welte80d30fe2013-02-12 11:08:57 +01002091 strvec = cmd_element->strvec;
2092
2093 /* if command is NULL, index may be equal to vector_active */
2094 if (command && index >= vector_active(strvec))
2095 vector_slot(cmd_vector, i) = NULL;
2096 else {
2097 /* Check if command is completed. */
2098 if (command == NULL
2099 && index == vector_active(strvec)) {
2100 string = "<cr>";
2101 if (!desc_unique_string(matchvec, string))
2102 vector_set(matchvec, &desc_cr);
2103 } else {
2104 unsigned int j;
2105 vector descvec = vector_slot(strvec, index);
2106 struct desc *desc;
2107
2108 for (j = 0; j < vector_active(descvec); j++) {
2109 desc = vector_slot(descvec, j);
2110 if (!desc)
2111 continue;
2112 string = cmd_entry_function_desc
2113 (command, desc->cmd);
2114 if (!string)
2115 continue;
2116 /* Uniqueness check */
2117 if (!desc_unique_string(matchvec, string))
2118 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002119 }
2120 }
2121 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002122 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002123 vector_free(cmd_vector);
2124
2125 if (vector_slot(matchvec, 0) == NULL) {
2126 vector_free(matchvec);
2127 *status = CMD_ERR_NO_MATCH;
2128 } else
2129 *status = CMD_SUCCESS;
2130
2131 return matchvec;
2132}
2133
2134vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2135{
2136 vector ret;
2137
2138 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2139 enum node_type onode;
2140 vector shifted_vline;
2141 unsigned int index;
2142
2143 onode = vty->node;
2144 vty->node = ENABLE_NODE;
2145 /* We can try it on enable node, cos' the vty is authenticated */
2146
2147 shifted_vline = vector_init(vector_count(vline));
2148 /* use memcpy? */
2149 for (index = 1; index < vector_active(vline); index++) {
2150 vector_set_index(shifted_vline, index - 1,
2151 vector_lookup(vline, index));
2152 }
2153
2154 ret = cmd_describe_command_real(shifted_vline, vty, status);
2155
2156 vector_free(shifted_vline);
2157 vty->node = onode;
2158 return ret;
2159 }
2160
2161 return cmd_describe_command_real(vline, vty, status);
2162}
2163
2164/* Check LCD of matched command. */
2165static int cmd_lcd(char **matched)
2166{
2167 int i;
2168 int j;
2169 int lcd = -1;
2170 char *s1, *s2;
2171 char c1, c2;
2172
2173 if (matched[0] == NULL || matched[1] == NULL)
2174 return 0;
2175
2176 for (i = 1; matched[i] != NULL; i++) {
2177 s1 = matched[i - 1];
2178 s2 = matched[i];
2179
2180 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2181 if (c1 != c2)
2182 break;
2183
2184 if (lcd < 0)
2185 lcd = j;
2186 else {
2187 if (lcd > j)
2188 lcd = j;
2189 }
2190 }
2191 return lcd;
2192}
2193
2194/* Command line completion support. */
2195static char **cmd_complete_command_real(vector vline, struct vty *vty,
2196 int *status)
2197{
2198 unsigned int i;
2199 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2200#define INIT_MATCHVEC_SIZE 10
2201 vector matchvec;
2202 struct cmd_element *cmd_element;
2203 unsigned int index;
2204 char **match_str;
2205 struct desc *desc;
2206 vector descvec;
2207 char *command;
2208 int lcd;
2209
2210 if (vector_active(vline) == 0) {
2211 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002212 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002213 return NULL;
2214 } else
2215 index = vector_active(vline) - 1;
2216
2217 /* First, filter by preceeding command string */
2218 for (i = 0; i < index; i++)
2219 if ((command = vector_slot(vline, i))) {
2220 enum match_type match;
2221 int ret;
2222
2223 /* First try completion match, if there is exactly match return 1 */
2224 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002225 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002226
2227 /* If there is exact match then filter ambiguous match else check
2228 ambiguousness. */
2229 if ((ret =
2230 is_cmd_ambiguous(command, cmd_vector, i,
2231 match)) == 1) {
2232 vector_free(cmd_vector);
2233 *status = CMD_ERR_AMBIGUOUS;
2234 return NULL;
2235 }
2236 /*
2237 else if (ret == 2)
2238 {
2239 vector_free (cmd_vector);
2240 *status = CMD_ERR_NO_MATCH;
2241 return NULL;
2242 }
2243 */
2244 }
2245
2246 /* Prepare match vector. */
2247 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2248
2249 /* Now we got into completion */
2250 for (i = 0; i < vector_active(cmd_vector); i++)
2251 if ((cmd_element = vector_slot(cmd_vector, i))) {
2252 const char *string;
2253 vector strvec = cmd_element->strvec;
2254
2255 /* Check field length */
2256 if (index >= vector_active(strvec))
2257 vector_slot(cmd_vector, i) = NULL;
2258 else {
2259 unsigned int j;
2260
2261 descvec = vector_slot(strvec, index);
2262 for (j = 0; j < vector_active(descvec); j++)
2263 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002264 const char *cmd = desc->cmd;
2265 char *tmp = NULL;
2266
2267 if (CMD_OPTION(desc->cmd)) {
2268 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2269 cmd = tmp;
2270 }
2271 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002272 if (cmd_unique_string (matchvec, string))
2273 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002274 if (tmp)
2275 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002276 }
2277 }
2278 }
2279
2280 /* We don't need cmd_vector any more. */
2281 vector_free(cmd_vector);
2282
2283 /* No matched command */
2284 if (vector_slot(matchvec, 0) == NULL) {
2285 vector_free(matchvec);
2286
2287 /* In case of 'command \t' pattern. Do you need '?' command at
2288 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002289 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002290 *status = CMD_ERR_NOTHING_TODO;
2291 else
2292 *status = CMD_ERR_NO_MATCH;
2293 return NULL;
2294 }
2295
2296 /* Only one matched */
2297 if (vector_slot(matchvec, 1) == NULL) {
2298 match_str = (char **)matchvec->index;
2299 vector_only_wrapper_free(matchvec);
2300 *status = CMD_COMPLETE_FULL_MATCH;
2301 return match_str;
2302 }
2303 /* Make it sure last element is NULL. */
2304 vector_set(matchvec, NULL);
2305
2306 /* Check LCD of matched strings. */
2307 if (vector_slot(vline, index) != NULL) {
2308 lcd = cmd_lcd((char **)matchvec->index);
2309
2310 if (lcd) {
2311 int len = strlen(vector_slot(vline, index));
2312
2313 if (len < lcd) {
2314 char *lcdstr;
2315
2316 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2317 "complete-lcdstr");
2318 memcpy(lcdstr, matchvec->index[0], lcd);
2319 lcdstr[lcd] = '\0';
2320
2321 /* match_str = (char **) &lcdstr; */
2322
2323 /* Free matchvec. */
2324 for (i = 0; i < vector_active(matchvec); i++) {
2325 if (vector_slot(matchvec, i))
2326 talloc_free(vector_slot(matchvec, i));
2327 }
2328 vector_free(matchvec);
2329
2330 /* Make new matchvec. */
2331 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2332 vector_set(matchvec, lcdstr);
2333 match_str = (char **)matchvec->index;
2334 vector_only_wrapper_free(matchvec);
2335
2336 *status = CMD_COMPLETE_MATCH;
2337 return match_str;
2338 }
2339 }
2340 }
2341
2342 match_str = (char **)matchvec->index;
2343 vector_only_wrapper_free(matchvec);
2344 *status = CMD_COMPLETE_LIST_MATCH;
2345 return match_str;
2346}
2347
2348char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2349{
2350 char **ret;
2351
2352 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2353 enum node_type onode;
2354 vector shifted_vline;
2355 unsigned int index;
2356
2357 onode = vty->node;
2358 vty->node = ENABLE_NODE;
2359 /* We can try it on enable node, cos' the vty is authenticated */
2360
2361 shifted_vline = vector_init(vector_count(vline));
2362 /* use memcpy? */
2363 for (index = 1; index < vector_active(vline); index++) {
2364 vector_set_index(shifted_vline, index - 1,
2365 vector_lookup(vline, index));
2366 }
2367
2368 ret = cmd_complete_command_real(shifted_vline, vty, status);
2369
2370 vector_free(shifted_vline);
2371 vty->node = onode;
2372 return ret;
2373 }
2374
2375 return cmd_complete_command_real(vline, vty, status);
2376}
2377
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002378static struct vty_parent_node *vty_parent(struct vty *vty)
2379{
2380 return llist_first_entry_or_null(&vty->parent_nodes,
2381 struct vty_parent_node,
2382 entry);
2383}
2384
2385static bool vty_pop_parent(struct vty *vty)
2386{
2387 struct vty_parent_node *parent = vty_parent(vty);
2388 if (!parent)
2389 return false;
2390 llist_del(&parent->entry);
2391 vty->node = parent->node;
2392 vty->priv = parent->priv;
2393 if (vty->indent)
2394 talloc_free(vty->indent);
2395 vty->indent = parent->indent;
2396 talloc_free(parent);
2397 return true;
2398}
2399
2400static void vty_clear_parents(struct vty *vty)
2401{
2402 while (vty_pop_parent(vty));
2403}
2404
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002405/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002406/*
2407 * This function MUST eventually converge on a node when called repeatedly,
2408 * there must not be any cycles.
2409 * All 'config' nodes shall converge on CONFIG_NODE.
2410 * All other 'enable' nodes shall converge on ENABLE_NODE.
2411 * All 'view' only nodes shall converge on VIEW_NODE.
2412 * All other nodes shall converge on themselves or it must be ensured,
2413 * that the user's rights are not extended anyhow by calling this function.
2414 *
2415 * Note that these requirements also apply to all functions that are used
2416 * as go_parent_cb.
2417 * Note also that this function relies on the is_config_child callback to
2418 * recognize non-config nodes if go_parent_cb is not set.
2419 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002420int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002421{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002422 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002423 case AUTH_NODE:
2424 case VIEW_NODE:
2425 case ENABLE_NODE:
2426 case CONFIG_NODE:
2427 vty_clear_parents(vty);
2428 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002429
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002430 case AUTH_ENABLE_NODE:
2431 vty->node = VIEW_NODE;
2432 vty_clear_parents(vty);
2433 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002434
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002435 default:
2436 if (host.app_info->go_parent_cb)
2437 host.app_info->go_parent_cb(vty);
2438 vty_pop_parent(vty);
2439 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002440 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002441
2442 return vty->node;
2443}
2444
2445/* Execute command by argument vline vector. */
2446static int
2447cmd_execute_command_real(vector vline, struct vty *vty,
2448 struct cmd_element **cmd)
2449{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002450 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002451 unsigned int index;
2452 vector cmd_vector;
2453 struct cmd_element *cmd_element;
2454 struct cmd_element *matched_element;
2455 unsigned int matched_count, incomplete_count;
2456 int argc;
2457 const char *argv[CMD_ARGC_MAX];
2458 enum match_type match = 0;
2459 int varflag;
2460 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002461 int rc;
2462 /* Used for temporary storage of cmd_deopt() allocated arguments during
2463 argv[] generation */
2464 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002465
2466 /* Make copy of command elements. */
2467 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2468
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002469 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002470 if ((command = vector_slot(vline, index))) {
2471 int ret;
2472
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002473 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002474 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002475
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002476 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002477 break;
2478
2479 ret =
2480 is_cmd_ambiguous(command, cmd_vector, index, match);
2481
2482 if (ret == 1) {
2483 vector_free(cmd_vector);
2484 return CMD_ERR_AMBIGUOUS;
2485 } else if (ret == 2) {
2486 vector_free(cmd_vector);
2487 return CMD_ERR_NO_MATCH;
2488 }
2489 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002490 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002491
2492 /* Check matched count. */
2493 matched_element = NULL;
2494 matched_count = 0;
2495 incomplete_count = 0;
2496
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002497 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002498 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002499 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002500 || index >= cmd_element->cmdsize) {
2501 matched_element = cmd_element;
2502#if 0
2503 printf("DEBUG: %s\n", cmd_element->string);
2504#endif
2505 matched_count++;
2506 } else {
2507 incomplete_count++;
2508 }
2509 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002510 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002511
2512 /* Finish of using cmd_vector. */
2513 vector_free(cmd_vector);
2514
2515 /* To execute command, matched_count must be 1. */
2516 if (matched_count == 0) {
2517 if (incomplete_count)
2518 return CMD_ERR_INCOMPLETE;
2519 else
2520 return CMD_ERR_NO_MATCH;
2521 }
2522
2523 if (matched_count > 1)
2524 return CMD_ERR_AMBIGUOUS;
2525
2526 /* Argument treatment */
2527 varflag = 0;
2528 argc = 0;
2529
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002530 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2531
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002532 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002533 if (argc == CMD_ARGC_MAX) {
2534 rc = CMD_ERR_EXEED_ARGC_MAX;
2535 goto rc_free_deopt_ctx;
2536 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002537 if (varflag) {
2538 argv[argc++] = vector_slot(vline, i);
2539 continue;
2540 }
2541
2542 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002543 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002544
2545 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002546 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002547 struct desc *desc = vector_slot(descvec, 0);
2548
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002549 if (CMD_OPTION(desc->cmd)) {
2550 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2551 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2552 } else {
2553 tmp_cmd = desc->cmd;
2554 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002555
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002556 if (CMD_VARARG(tmp_cmd))
2557 varflag = 1;
2558 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002559 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002560 else if (CMD_OPTION(desc->cmd))
2561 argv[argc++] = tmp_cmd;
2562 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002563 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002564 /* multi choice argument. look up which choice
2565 the user meant (can only be one after
2566 filtering and checking for ambigous). For instance,
2567 if user typed "th" for "(two|three)" arg, we
2568 want to pass "three" in argv[]. */
2569 for (j = 0; j < vector_active(descvec); j++) {
2570 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002571 if (!desc)
2572 continue;
2573 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2574 continue;
2575 if (CMD_OPTION(desc->cmd)) {
2576 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2577 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2578 } else {
2579 tmp_cmd = desc->cmd;
2580 }
2581
2582 if(CMD_VARIABLE(tmp_cmd)) {
2583 argv[argc++] = vector_slot(vline, i);
2584 } else {
2585 argv[argc++] = tmp_cmd;
2586 }
2587 break;
2588 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002589 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002590 }
2591
2592 /* For vtysh execution. */
2593 if (cmd)
2594 *cmd = matched_element;
2595
2596 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002597 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002598 else {
2599 /* Execute matched command. */
2600 struct vty_parent_node this_node = {
2601 .node = vty->node,
2602 .priv = vty->priv,
2603 .indent = vty->indent,
2604 };
2605 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002606 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002607
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002608 /* If we have stepped down into a child node, push a parent frame.
2609 * The causality is such: we don't expect every single node entry implementation to push
2610 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2611 * a parent node. Hence if the node changed without the parent node changing, we must
2612 * have stepped into a child node. */
2613 if (vty->node != this_node.node && parent == vty_parent(vty)
2614 && vty->node > CONFIG_NODE) {
2615 /* Push the parent node. */
2616 parent = talloc_zero(vty, struct vty_parent_node);
2617 *parent = this_node;
2618 llist_add(&parent->entry, &vty->parent_nodes);
2619 }
2620 }
2621
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002622rc_free_deopt_ctx:
2623 /* Now after we called the command func, we can free temporary strings */
2624 talloc_free(cmd_deopt_ctx);
2625 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002626}
2627
2628int
2629cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2630 int vtysh)
2631{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002632 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002633 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002634
2635 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002636
2637 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2638 vector shifted_vline;
2639 unsigned int index;
2640
2641 vty->node = ENABLE_NODE;
2642 /* We can try it on enable node, cos' the vty is authenticated */
2643
2644 shifted_vline = vector_init(vector_count(vline));
2645 /* use memcpy? */
2646 for (index = 1; index < vector_active(vline); index++) {
2647 vector_set_index(shifted_vline, index - 1,
2648 vector_lookup(vline, index));
2649 }
2650
2651 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2652
2653 vector_free(shifted_vline);
2654 vty->node = onode;
2655 return ret;
2656 }
2657
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002658 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002659}
2660
2661/* Execute command by argument readline. */
2662int
2663cmd_execute_command_strict(vector vline, struct vty *vty,
2664 struct cmd_element **cmd)
2665{
2666 unsigned int i;
2667 unsigned int index;
2668 vector cmd_vector;
2669 struct cmd_element *cmd_element;
2670 struct cmd_element *matched_element;
2671 unsigned int matched_count, incomplete_count;
2672 int argc;
2673 const char *argv[CMD_ARGC_MAX];
2674 int varflag;
2675 enum match_type match = 0;
2676 char *command;
2677
2678 /* Make copy of command element */
2679 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2680
2681 for (index = 0; index < vector_active(vline); index++)
2682 if ((command = vector_slot(vline, index))) {
2683 int ret;
2684
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002685 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002686 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002687
2688 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002689 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002690 break;
2691
2692 ret =
2693 is_cmd_ambiguous(command, cmd_vector, index, match);
2694 if (ret == 1) {
2695 vector_free(cmd_vector);
2696 return CMD_ERR_AMBIGUOUS;
2697 }
2698 if (ret == 2) {
2699 vector_free(cmd_vector);
2700 return CMD_ERR_NO_MATCH;
2701 }
2702 }
2703
2704 /* Check matched count. */
2705 matched_element = NULL;
2706 matched_count = 0;
2707 incomplete_count = 0;
2708 for (i = 0; i < vector_active(cmd_vector); i++)
2709 if (vector_slot(cmd_vector, i) != NULL) {
2710 cmd_element = vector_slot(cmd_vector, i);
2711
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002712 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002713 || index >= cmd_element->cmdsize) {
2714 matched_element = cmd_element;
2715 matched_count++;
2716 } else
2717 incomplete_count++;
2718 }
2719
2720 /* Finish of using cmd_vector. */
2721 vector_free(cmd_vector);
2722
2723 /* To execute command, matched_count must be 1. */
2724 if (matched_count == 0) {
2725 if (incomplete_count)
2726 return CMD_ERR_INCOMPLETE;
2727 else
2728 return CMD_ERR_NO_MATCH;
2729 }
2730
2731 if (matched_count > 1)
2732 return CMD_ERR_AMBIGUOUS;
2733
2734 /* Argument treatment */
2735 varflag = 0;
2736 argc = 0;
2737
2738 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002739 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002740 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002741 if (varflag) {
2742 argv[argc++] = vector_slot(vline, i);
2743 continue;
2744 }
2745
2746 vector descvec = vector_slot(matched_element->strvec, i);
2747
2748 if (vector_active(descvec) == 1) {
2749 struct desc *desc = vector_slot(descvec, 0);
2750
2751 if (CMD_VARARG(desc->cmd))
2752 varflag = 1;
2753
2754 if (varflag || CMD_VARIABLE(desc->cmd)
2755 || CMD_OPTION(desc->cmd))
2756 argv[argc++] = vector_slot(vline, i);
2757 } else {
2758 argv[argc++] = vector_slot(vline, i);
2759 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002760 }
2761
2762 /* For vtysh execution. */
2763 if (cmd)
2764 *cmd = matched_element;
2765
2766 if (matched_element->daemon)
2767 return CMD_SUCCESS_DAEMON;
2768
2769 /* Now execute matched command */
2770 return (*matched_element->func) (matched_element, vty, argc, argv);
2771}
2772
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002773static inline size_t len(const char *str)
2774{
2775 return str? strlen(str) : 0;
2776}
2777
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002778/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2779 * is longer than b, a must start with exactly b, and vice versa.
2780 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2781 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002782static int indent_cmp(const char *a, const char *b)
2783{
2784 size_t al, bl;
2785 al = len(a);
2786 bl = len(b);
2787 if (al > bl) {
2788 if (bl && strncmp(a, b, bl) != 0)
2789 return EINVAL;
2790 return 1;
2791 }
2792 /* al <= bl */
2793 if (al && strncmp(a, b, al) != 0)
2794 return EINVAL;
2795 return (al < bl)? -1 : 0;
2796}
2797
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002798/* Configration make from file. */
2799int config_from_file(struct vty *vty, FILE * fp)
2800{
2801 int ret;
2802 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002803 char *indent;
2804 int cmp;
2805 struct vty_parent_node this_node;
2806 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002807
2808 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002809 indent = NULL;
2810 vline = NULL;
2811 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002812
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002813 if (ret != CMD_SUCCESS)
2814 goto return_invalid_indent;
2815
2816 /* In case of comment or empty line */
2817 if (vline == NULL) {
2818 if (indent) {
2819 talloc_free(indent);
2820 indent = NULL;
2821 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002822 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002823 }
2824
Neels Hofmeyr43063632017-09-19 23:54:01 +02002825 /* We have a nonempty line. */
2826 if (!vty->indent) {
2827 /* We have just entered a node and expecting the first child to come up; but we
2828 * may also skip right back to a parent or ancestor level. */
2829 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002830
Neels Hofmeyr43063632017-09-19 23:54:01 +02002831 /* If there is no parent, record any indentation we encounter. */
2832 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2833
2834 if (cmp == EINVAL)
2835 goto return_invalid_indent;
2836
2837 if (cmp <= 0) {
2838 /* We have gone right back to the parent level or higher, we are skipping
2839 * this child node level entirely. Pop the parent to go back to a node
2840 * that was actually there (to reinstate vty->indent) and re-use below
2841 * go-parent while-loop to find an accurate match of indent in the node
2842 * ancestry. */
2843 vty_go_parent(vty);
2844 } else {
2845 /* The indent is deeper than the just entered parent, record the new
2846 * indentation characters. */
2847 vty->indent = talloc_strdup(vty, indent);
2848 /* This *is* the new indentation. */
2849 cmp = 0;
2850 }
2851 } else {
2852 /* There is a known indentation for this node level, validate and detect node
2853 * exits. */
2854 cmp = indent_cmp(indent, vty->indent);
2855 if (cmp == EINVAL)
2856 goto return_invalid_indent;
2857 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002858
2859 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2860 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2861 while (cmp < 0) {
2862 vty_go_parent(vty);
2863 cmp = indent_cmp(indent, vty->indent);
2864 if (cmp == EINVAL)
2865 goto return_invalid_indent;
2866 }
2867
2868 /* More indent without having entered a child node level? Either the parent node's indent
2869 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2870 * or the indentation increased even though the vty command didn't enter a child. */
2871 if (cmp > 0)
2872 goto return_invalid_indent;
2873
2874 /* Remember the current node before the command possibly changes it. */
2875 this_node = (struct vty_parent_node){
2876 .node = vty->node,
2877 .priv = vty->priv,
2878 .indent = vty->indent,
2879 };
2880
2881 parent = vty_parent(vty);
2882 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002883 cmd_free_strvec(vline);
2884
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002885 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002886 if (indent) {
2887 talloc_free(indent);
2888 indent = NULL;
2889 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002890 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002891 }
2892
2893 /* If we have stepped down into a child node, push a parent frame.
2894 * The causality is such: we don't expect every single node entry implementation to push
2895 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2896 * a parent node. Hence if the node changed without the parent node changing, we must
2897 * have stepped into a child node (and now expect a deeper indent). */
2898 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2899 /* Push the parent node. */
2900 parent = talloc_zero(vty, struct vty_parent_node);
2901 *parent = this_node;
2902 llist_add(&parent->entry, &vty->parent_nodes);
2903
2904 /* The current talloc'ed vty->indent string will now be owned by this parent
2905 * struct. Indicate that we don't know what deeper indent characters the user
2906 * will choose. */
2907 vty->indent = NULL;
2908 }
2909
2910 if (indent) {
2911 talloc_free(indent);
2912 indent = NULL;
2913 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002914 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002915 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2916 while (vty_parent(vty))
2917 vty_go_parent(vty);
2918
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002919 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002920
2921return_invalid_indent:
2922 if (vline)
2923 cmd_free_strvec(vline);
2924 if (indent) {
2925 talloc_free(indent);
2926 indent = NULL;
2927 }
2928 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002929}
2930
2931/* Configration from terminal */
2932DEFUN(config_terminal,
2933 config_terminal_cmd,
2934 "configure terminal",
2935 "Configuration from vty interface\n" "Configuration terminal\n")
2936{
2937 if (vty_config_lock(vty))
2938 vty->node = CONFIG_NODE;
2939 else {
2940 vty_out(vty, "VTY configuration is locked by other VTY%s",
2941 VTY_NEWLINE);
2942 return CMD_WARNING;
2943 }
2944 return CMD_SUCCESS;
2945}
2946
2947/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002948DEFUN(enable, config_enable_cmd,
2949 "enable [expert-mode]",
2950 "Turn on privileged mode command\n"
2951 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002952{
2953 /* If enable password is NULL, change to ENABLE_NODE */
2954 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2955 vty->type == VTY_SHELL_SERV)
2956 vty->node = ENABLE_NODE;
2957 else
2958 vty->node = AUTH_ENABLE_NODE;
2959
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002960 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002961
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002962 return CMD_SUCCESS;
2963}
2964
2965/* Disable command */
2966DEFUN(disable,
2967 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2968{
2969 if (vty->node == ENABLE_NODE)
2970 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002971
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002972 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002973
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002974 return CMD_SUCCESS;
2975}
2976
2977/* Down vty node level. */
2978gDEFUN(config_exit,
2979 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2980{
2981 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002982 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002983 case VIEW_NODE:
2984 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002985 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002986 break;
2987 case CONFIG_NODE:
2988 vty->node = ENABLE_NODE;
2989 vty_config_unlock(vty);
2990 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002991 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002992 if (vty->node > CONFIG_NODE)
2993 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002994 break;
2995 }
2996 return CMD_SUCCESS;
2997}
2998
2999/* End of configuration. */
3000 gDEFUN(config_end,
3001 config_end_cmd, "end", "End current mode and change to enable mode.")
3002{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003003 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02003004 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003005
3006 /* Repeatedly call go_parent until a top node is reached. */
3007 while (vty->node > CONFIG_NODE) {
3008 if (vty->node == last_node) {
3009 /* Ensure termination, this shouldn't happen. */
3010 break;
3011 }
3012 last_node = vty->node;
3013 vty_go_parent(vty);
3014 }
3015
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003016 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003017 if (vty->node > ENABLE_NODE)
3018 vty->node = ENABLE_NODE;
3019 vty->index = NULL;
3020 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003021 }
3022 return CMD_SUCCESS;
3023}
3024
3025/* Show version. */
3026DEFUN(show_version,
3027 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3028{
Harald Welte237f6242010-05-25 23:00:45 +02003029 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3030 host.app_info->version,
3031 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3032 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003033
3034 return CMD_SUCCESS;
3035}
3036
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003037DEFUN(show_online_help,
3038 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3039{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003040 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003041 return CMD_SUCCESS;
3042}
3043
Oliver Smith0c78bc62021-07-12 17:28:36 +02003044DEFUN(show_pid,
3045 show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
3046{
3047 vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
3048 return CMD_SUCCESS;
3049}
3050
Oliver Smithd243c2a2021-07-09 17:19:32 +02003051DEFUN(show_uptime,
3052 show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
3053{
3054 struct timespec now;
3055 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
3056
3057 time_t uptime = now.tv_sec - starttime.tv_sec;
3058 int d = uptime / (3600 * 24);
3059 int h = uptime / 3600 % 24;
3060 int m = uptime / 60 % 60;
3061 int s = uptime % 60;
3062
3063 vty_out(vty, "%s has been running for %dd %dh %dm %ds%s", host.app_info->name, d, h, m, s, VTY_NEWLINE);
3064
3065 return CMD_SUCCESS;
3066}
3067
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003068/* Help display function for all node. */
3069gDEFUN(config_help,
3070 config_help_cmd, "help", "Description of the interactive help system\n")
3071{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003072 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3073 "anytime at the command line please press '?'.%s%s"
3074 "If nothing matches, the help list will be empty and you must backup%s"
3075 " until entering a '?' shows the available options.%s"
3076 "Two styles of help are provided:%s"
3077 "1. Full help is available when you are ready to enter a%s"
3078 "command argument (e.g. 'show ?') and describes each possible%s"
3079 "argument.%s"
3080 "2. Partial help is provided when an abbreviated argument is entered%s"
3081 " and you want to know what arguments match the input%s"
3082 " (e.g. 'show me?'.)%s%s",
3083 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3084 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3085 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3086 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003087 return CMD_SUCCESS;
3088}
3089
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003090enum {
3091 ATTR_TYPE_GLOBAL = (1 << 0),
3092 ATTR_TYPE_LIB = (1 << 1),
3093 ATTR_TYPE_APP = (1 << 2),
3094};
3095
3096static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3097{
3098 const char *desc;
3099 unsigned int i;
3100 bool found;
3101 char flag;
3102
3103 if (attr_mask & ATTR_TYPE_GLOBAL) {
3104 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3105
3106 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003107 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003108 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003109
3110 /* Skip attributes without flags */
3111 if (flag != '.')
3112 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003113 }
3114 }
3115
3116 if (attr_mask & ATTR_TYPE_LIB) {
3117 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3118
3119 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3120 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3121 continue;
3122 found = true;
3123
3124 flag = cmd_lib_attr_letters[i];
3125 if (flag == '\0')
3126 flag = '.';
3127
3128 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3129 }
3130
3131 if (!found)
3132 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3133 }
3134
3135 if (attr_mask & ATTR_TYPE_APP) {
3136 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3137
3138 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3139 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3140 continue;
3141 found = true;
3142
3143 flag = host.app_info->usr_attr_letters[i];
3144 if (flag == '\0')
3145 flag = '.';
3146
3147 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3148 }
3149
3150 if (!found)
3151 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3152 }
3153}
3154
3155gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3156 "show vty-attributes",
3157 SHOW_STR "List of VTY attributes\n")
3158{
3159 print_attr_list(vty, 0xff);
3160 return CMD_SUCCESS;
3161}
3162
3163gDEFUN(show_vty_attr, show_vty_attr_cmd,
3164 "show vty-attributes (application|library|global)",
3165 SHOW_STR "List of VTY attributes\n"
3166 "Application specific attributes only\n"
3167 "Library specific attributes only\n"
3168 "Global attributes only\n")
3169{
3170 unsigned int attr_mask = 0;
3171
3172 if (argv[0][0] == 'g') /* global */
3173 attr_mask |= ATTR_TYPE_GLOBAL;
3174 else if (argv[0][0] == 'l') /* library */
3175 attr_mask |= ATTR_TYPE_LIB;
3176 else if (argv[0][0] == 'a') /* application */
3177 attr_mask |= ATTR_TYPE_APP;
3178
3179 print_attr_list(vty, attr_mask);
3180 return CMD_SUCCESS;
3181}
3182
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003183/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003184static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003185{
3186 unsigned int flag_mask = 0x00;
3187 unsigned int f, i;
3188
3189 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3190 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3191 const struct cmd_element *cmd;
3192 char flag_letter;
3193
3194 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3195 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003196 if (cmd->attr & CMD_ATTR_DEPRECATED)
3197 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003198 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003199 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003200 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003201 continue;
3202
3203 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3204 flag_letter = cmd_lib_attr_letters[f];
3205 else
3206 flag_letter = host.app_info->usr_attr_letters[f];
3207
3208 if (flag_letter == '\0')
3209 continue;
3210
3211 flag_mask |= (1 << f);
3212 break;
3213 }
3214 }
3215
3216 return flag_mask;
3217}
3218
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003219/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3220static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3221{
3222 static char char_mask[8 + 1];
3223 char *ptr = &char_mask[0];
3224
3225 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003226 if (cmd->attr & CMD_ATTR_HIDDEN)
3227 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3228 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003229 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3230 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3231 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3232 else
3233 *(ptr++) = '.';
3234
3235 *ptr = '\0';
3236
3237 return char_mask;
3238}
3239
3240/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003241static const char *cmd_flag_mask(const struct cmd_element *cmd,
3242 unsigned int flag_mask)
3243{
3244 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3245 char *ptr = &char_mask[0];
3246 char flag_letter;
3247 unsigned int f;
3248
3249 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003250 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003251 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003252 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003253 *(ptr++) = '.';
3254 continue;
3255 }
3256
3257 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3258 flag_letter = cmd_lib_attr_letters[f];
3259 else
3260 flag_letter = host.app_info->usr_attr_letters[f];
3261
3262 *(ptr++) = flag_letter ? flag_letter : '.';
3263 }
3264
3265 *ptr = '\0';
3266
3267 return char_mask;
3268}
3269
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003270/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003271gDEFUN(config_list, config_list_cmd,
3272 "list [with-flags]",
3273 "Print command list\n"
3274 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003275{
3276 unsigned int i;
3277 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003278 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003279 struct cmd_element *cmd;
3280
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003281 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003282 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003283
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003284 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3285 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3286 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003287 if (cmd->attr & CMD_ATTR_DEPRECATED)
3288 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003289 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003290 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003291 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003292 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3293 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003294 vty_out(vty, " %s %s %s%s",
3295 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003296 cmd_flag_mask(cmd, flag_mask),
3297 cmd->string, VTY_NEWLINE);
3298 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003299 }
3300
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003301 return CMD_SUCCESS;
3302}
3303
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003304static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003305{
3306 unsigned int i;
3307 int fd;
3308 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003309 char *config_file_tmp = NULL;
3310 char *config_file_sav = NULL;
3311 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003312 struct stat st;
3313
3314 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003315
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003316 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3317 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3318 * manually instead. */
3319
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003320 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003321 config_file_sav =
3322 _talloc_zero(tall_vty_cmd_ctx,
3323 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3324 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003325 if (!config_file_sav)
3326 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003327 strcpy(config_file_sav, config_file);
3328 strcat(config_file_sav, CONF_BACKUP_EXT);
3329
3330 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003331 "config_file_tmp");
3332 if (!config_file_tmp) {
3333 talloc_free(config_file_sav);
3334 return -1;
3335 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003336 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3337
3338 /* Open file to configuration write. */
3339 fd = mkstemp(config_file_tmp);
3340 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003341 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003342 talloc_free(config_file_tmp);
3343 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003344 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003345 }
3346
3347 /* Make vty for configuration file. */
3348 file_vty = vty_new();
3349 file_vty->fd = fd;
3350 file_vty->type = VTY_FILE;
3351
3352 /* Config file header print. */
3353 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003354 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003355 //vty_time_print (file_vty, 1);
3356 vty_out(file_vty, "!\n");
3357
3358 for (i = 0; i < vector_active(cmdvec); i++)
3359 if ((node = vector_slot(cmdvec, i)) && node->func) {
3360 if ((*node->func) (file_vty))
3361 vty_out(file_vty, "!\n");
3362 }
3363 vty_close(file_vty);
3364
3365 if (unlink(config_file_sav) != 0)
3366 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003367 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003368 talloc_free(config_file_sav);
3369 talloc_free(config_file_tmp);
3370 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003371 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003372 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003373
3374 /* Only link the .sav file if the original file exists */
3375 if (stat(config_file, &st) == 0) {
3376 if (link(config_file, config_file_sav) != 0) {
3377 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3378 talloc_free(config_file_sav);
3379 talloc_free(config_file_tmp);
3380 unlink(config_file_tmp);
3381 return -3;
3382 }
3383 sync();
3384 if (unlink(config_file) != 0) {
3385 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3386 talloc_free(config_file_sav);
3387 talloc_free(config_file_tmp);
3388 unlink(config_file_tmp);
3389 return -4;
3390 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003391 }
3392 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003393 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003394 talloc_free(config_file_sav);
3395 talloc_free(config_file_tmp);
3396 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003397 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003398 }
3399 unlink(config_file_tmp);
3400 sync();
3401
3402 talloc_free(config_file_sav);
3403 talloc_free(config_file_tmp);
3404
3405 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003406 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3407 return -6;
3408 }
3409
3410 return 0;
3411}
3412
3413
3414/* Write current configuration into file. */
3415DEFUN(config_write_file,
3416 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003417 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003418 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003419 "Write to configuration file\n"
3420 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003421{
3422 char *failed_file;
3423 int rc;
3424
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003425 if (host.app_info->config_is_consistent) {
3426 rc = host.app_info->config_is_consistent(vty);
3427 if (!rc) {
3428 vty_out(vty, "Configuration is not consistent%s",
3429 VTY_NEWLINE);
3430 return CMD_WARNING;
3431 }
3432 }
3433
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003434 if (argc == 1)
3435 host_config_set(argv[0]);
3436
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003437 if (host.config == NULL) {
3438 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3439 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003440 return CMD_WARNING;
3441 }
3442
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003443 rc = write_config_file(host.config, &failed_file);
3444 switch (rc) {
3445 case -1:
3446 vty_out(vty, "Can't open configuration file %s.%s",
3447 failed_file, VTY_NEWLINE);
3448 rc = CMD_WARNING;
3449 break;
3450 case -2:
3451 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3452 failed_file, VTY_NEWLINE);
3453 rc = CMD_WARNING;
3454 break;
3455 case -3:
3456 vty_out(vty, "Can't backup old configuration file %s.%s",
3457 failed_file, VTY_NEWLINE);
3458 rc = CMD_WARNING;
3459 break;
3460 case -4:
3461 vty_out(vty, "Can't unlink configuration file %s.%s",
3462 failed_file, VTY_NEWLINE);
3463 rc = CMD_WARNING;
3464 break;
3465 case -5:
3466 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3467 VTY_NEWLINE);
3468 rc = CMD_WARNING;
3469 break;
3470 case -6:
3471 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3472 failed_file, strerror(errno), errno, VTY_NEWLINE);
3473 rc = CMD_WARNING;
3474 break;
3475 default:
3476 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3477 rc = CMD_SUCCESS;
3478 break;
3479 }
3480
3481 talloc_free(failed_file);
3482 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003483}
3484
3485ALIAS(config_write_file,
3486 config_write_cmd,
3487 "write", "Write running configuration to memory, network, or terminal\n")
3488
3489 ALIAS(config_write_file,
3490 config_write_memory_cmd,
3491 "write memory",
3492 "Write running configuration to memory, network, or terminal\n"
3493 "Write configuration to the file (same as write file)\n")
3494
3495 ALIAS(config_write_file,
3496 copy_runningconfig_startupconfig_cmd,
3497 "copy running-config startup-config",
3498 "Copy configuration\n"
3499 "Copy running config to... \n"
3500 "Copy running config to startup config (same as write file)\n")
3501
3502/* Write current configuration into the terminal. */
3503 DEFUN(config_write_terminal,
3504 config_write_terminal_cmd,
3505 "write terminal",
3506 "Write running configuration to memory, network, or terminal\n"
3507 "Write to terminal\n")
3508{
3509 unsigned int i;
3510 struct cmd_node *node;
3511
3512 if (vty->type == VTY_SHELL_SERV) {
3513 for (i = 0; i < vector_active(cmdvec); i++)
3514 if ((node = vector_slot(cmdvec, i)) && node->func
3515 && node->vtysh) {
3516 if ((*node->func) (vty))
3517 vty_out(vty, "!%s", VTY_NEWLINE);
3518 }
3519 } else {
3520 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3521 VTY_NEWLINE);
3522 vty_out(vty, "!%s", VTY_NEWLINE);
3523
3524 for (i = 0; i < vector_active(cmdvec); i++)
3525 if ((node = vector_slot(cmdvec, i)) && node->func) {
3526 if ((*node->func) (vty))
3527 vty_out(vty, "!%s", VTY_NEWLINE);
3528 }
3529 vty_out(vty, "end%s", VTY_NEWLINE);
3530 }
3531 return CMD_SUCCESS;
3532}
3533
3534/* Write current configuration into the terminal. */
3535ALIAS(config_write_terminal,
3536 show_running_config_cmd,
3537 "show running-config", SHOW_STR "running configuration\n")
3538
3539/* Write startup configuration into the terminal. */
3540 DEFUN(show_startup_config,
3541 show_startup_config_cmd,
3542 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3543{
3544 char buf[BUFSIZ];
3545 FILE *confp;
3546
3547 confp = fopen(host.config, "r");
3548 if (confp == NULL) {
3549 vty_out(vty, "Can't open configuration file [%s]%s",
3550 host.config, VTY_NEWLINE);
3551 return CMD_WARNING;
3552 }
3553
3554 while (fgets(buf, BUFSIZ, confp)) {
3555 char *cp = buf;
3556
3557 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3558 cp++;
3559 *cp = '\0';
3560
3561 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3562 }
3563
3564 fclose(confp);
3565
3566 return CMD_SUCCESS;
3567}
3568
3569/* Hostname configuration */
3570DEFUN(config_hostname,
3571 hostname_cmd,
3572 "hostname WORD",
3573 "Set system's network name\n" "This system's network name\n")
3574{
3575 if (!isalpha((int)*argv[0])) {
3576 vty_out(vty, "Please specify string starting with alphabet%s",
3577 VTY_NEWLINE);
3578 return CMD_WARNING;
3579 }
3580
3581 if (host.name)
3582 talloc_free(host.name);
3583
3584 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3585 return CMD_SUCCESS;
3586}
3587
3588DEFUN(config_no_hostname,
3589 no_hostname_cmd,
3590 "no hostname [HOSTNAME]",
3591 NO_STR "Reset system's network name\n" "Host name of this router\n")
3592{
3593 if (host.name)
3594 talloc_free(host.name);
3595 host.name = NULL;
3596 return CMD_SUCCESS;
3597}
3598
3599/* VTY interface password set. */
3600DEFUN(config_password, password_cmd,
3601 "password (8|) WORD",
3602 "Assign the terminal connection password\n"
3603 "Specifies a HIDDEN password will follow\n"
3604 "dummy string \n" "The HIDDEN line password string\n")
3605{
3606 /* Argument check. */
3607 if (argc == 0) {
3608 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3609 return CMD_WARNING;
3610 }
3611
3612 if (argc == 2) {
3613 if (*argv[0] == '8') {
3614 if (host.password)
3615 talloc_free(host.password);
3616 host.password = NULL;
3617 if (host.password_encrypt)
3618 talloc_free(host.password_encrypt);
3619 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3620 return CMD_SUCCESS;
3621 } else {
3622 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3623 return CMD_WARNING;
3624 }
3625 }
3626
3627 if (!isalnum((int)*argv[0])) {
3628 vty_out(vty,
3629 "Please specify string starting with alphanumeric%s",
3630 VTY_NEWLINE);
3631 return CMD_WARNING;
3632 }
3633
3634 if (host.password)
3635 talloc_free(host.password);
3636 host.password = NULL;
3637
3638#ifdef VTY_CRYPT_PW
3639 if (host.encrypt) {
3640 if (host.password_encrypt)
3641 talloc_free(host.password_encrypt);
3642 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3643 } else
3644#endif
3645 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3646
3647 return CMD_SUCCESS;
3648}
3649
3650ALIAS(config_password, password_text_cmd,
3651 "password LINE",
3652 "Assign the terminal connection password\n"
3653 "The UNENCRYPTED (cleartext) line password\n")
3654
3655/* VTY enable password set. */
3656 DEFUN(config_enable_password, enable_password_cmd,
3657 "enable password (8|) WORD",
3658 "Modify enable password parameters\n"
3659 "Assign the privileged level password\n"
3660 "Specifies a HIDDEN password will follow\n"
3661 "dummy string \n" "The HIDDEN 'enable' password string\n")
3662{
3663 /* Argument check. */
3664 if (argc == 0) {
3665 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3666 return CMD_WARNING;
3667 }
3668
3669 /* Crypt type is specified. */
3670 if (argc == 2) {
3671 if (*argv[0] == '8') {
3672 if (host.enable)
3673 talloc_free(host.enable);
3674 host.enable = NULL;
3675
3676 if (host.enable_encrypt)
3677 talloc_free(host.enable_encrypt);
3678 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3679
3680 return CMD_SUCCESS;
3681 } else {
3682 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3683 return CMD_WARNING;
3684 }
3685 }
3686
3687 if (!isalnum((int)*argv[0])) {
3688 vty_out(vty,
3689 "Please specify string starting with alphanumeric%s",
3690 VTY_NEWLINE);
3691 return CMD_WARNING;
3692 }
3693
3694 if (host.enable)
3695 talloc_free(host.enable);
3696 host.enable = NULL;
3697
3698 /* Plain password input. */
3699#ifdef VTY_CRYPT_PW
3700 if (host.encrypt) {
3701 if (host.enable_encrypt)
3702 talloc_free(host.enable_encrypt);
3703 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3704 } else
3705#endif
3706 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3707
3708 return CMD_SUCCESS;
3709}
3710
3711ALIAS(config_enable_password,
3712 enable_password_text_cmd,
3713 "enable password LINE",
3714 "Modify enable password parameters\n"
3715 "Assign the privileged level password\n"
3716 "The UNENCRYPTED (cleartext) 'enable' password\n")
3717
3718/* VTY enable password delete. */
3719 DEFUN(no_config_enable_password, no_enable_password_cmd,
3720 "no enable password",
3721 NO_STR
3722 "Modify enable password parameters\n"
3723 "Assign the privileged level password\n")
3724{
3725 if (host.enable)
3726 talloc_free(host.enable);
3727 host.enable = NULL;
3728
3729 if (host.enable_encrypt)
3730 talloc_free(host.enable_encrypt);
3731 host.enable_encrypt = NULL;
3732
3733 return CMD_SUCCESS;
3734}
3735
3736#ifdef VTY_CRYPT_PW
3737DEFUN(service_password_encrypt,
3738 service_password_encrypt_cmd,
3739 "service password-encryption",
3740 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3741{
3742 if (host.encrypt)
3743 return CMD_SUCCESS;
3744
3745 host.encrypt = 1;
3746
3747 if (host.password) {
3748 if (host.password_encrypt)
3749 talloc_free(host.password_encrypt);
3750 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3751 }
3752 if (host.enable) {
3753 if (host.enable_encrypt)
3754 talloc_free(host.enable_encrypt);
3755 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3756 }
3757
3758 return CMD_SUCCESS;
3759}
3760
3761DEFUN(no_service_password_encrypt,
3762 no_service_password_encrypt_cmd,
3763 "no service password-encryption",
3764 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3765{
3766 if (!host.encrypt)
3767 return CMD_SUCCESS;
3768
3769 host.encrypt = 0;
3770
3771 if (host.password_encrypt)
3772 talloc_free(host.password_encrypt);
3773 host.password_encrypt = NULL;
3774
3775 if (host.enable_encrypt)
3776 talloc_free(host.enable_encrypt);
3777 host.enable_encrypt = NULL;
3778
3779 return CMD_SUCCESS;
3780}
3781#endif
3782
3783DEFUN(config_terminal_length, config_terminal_length_cmd,
3784 "terminal length <0-512>",
3785 "Set terminal line parameters\n"
3786 "Set number of lines on a screen\n"
3787 "Number of lines on screen (0 for no pausing)\n")
3788{
3789 int lines;
3790 char *endptr = NULL;
3791
3792 lines = strtol(argv[0], &endptr, 10);
3793 if (lines < 0 || lines > 512 || *endptr != '\0') {
3794 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3795 return CMD_WARNING;
3796 }
3797 vty->lines = lines;
3798
3799 return CMD_SUCCESS;
3800}
3801
3802DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3803 "terminal no length",
3804 "Set terminal line parameters\n"
3805 NO_STR "Set number of lines on a screen\n")
3806{
3807 vty->lines = -1;
3808 return CMD_SUCCESS;
3809}
3810
3811DEFUN(service_terminal_length, service_terminal_length_cmd,
3812 "service terminal-length <0-512>",
3813 "Set up miscellaneous service\n"
3814 "System wide terminal length configuration\n"
3815 "Number of lines of VTY (0 means no line control)\n")
3816{
3817 int lines;
3818 char *endptr = NULL;
3819
3820 lines = strtol(argv[0], &endptr, 10);
3821 if (lines < 0 || lines > 512 || *endptr != '\0') {
3822 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3823 return CMD_WARNING;
3824 }
3825 host.lines = lines;
3826
3827 return CMD_SUCCESS;
3828}
3829
3830DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3831 "no service terminal-length [<0-512>]",
3832 NO_STR
3833 "Set up miscellaneous service\n"
3834 "System wide terminal length configuration\n"
3835 "Number of lines of VTY (0 means no line control)\n")
3836{
3837 host.lines = -1;
3838 return CMD_SUCCESS;
3839}
3840
3841DEFUN_HIDDEN(do_echo,
3842 echo_cmd,
3843 "echo .MESSAGE",
3844 "Echo a message back to the vty\n" "The message to echo\n")
3845{
3846 char *message;
3847
3848 vty_out(vty, "%s%s",
3849 ((message =
3850 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3851 if (message)
3852 talloc_free(message);
3853 return CMD_SUCCESS;
3854}
3855
3856#if 0
3857DEFUN(config_logmsg,
3858 config_logmsg_cmd,
3859 "logmsg " LOG_LEVELS " .MESSAGE",
3860 "Send a message to enabled logging destinations\n"
3861 LOG_LEVEL_DESC "The message to send\n")
3862{
3863 int level;
3864 char *message;
3865
3866 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3867 return CMD_ERR_NO_MATCH;
3868
3869 zlog(NULL, level,
3870 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3871 if (message)
3872 talloc_free(message);
3873 return CMD_SUCCESS;
3874}
3875
3876DEFUN(show_logging,
3877 show_logging_cmd,
3878 "show logging", SHOW_STR "Show current logging configuration\n")
3879{
3880 struct zlog *zl = zlog_default;
3881
3882 vty_out(vty, "Syslog logging: ");
3883 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3884 vty_out(vty, "disabled");
3885 else
3886 vty_out(vty, "level %s, facility %s, ident %s",
3887 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3888 facility_name(zl->facility), zl->ident);
3889 vty_out(vty, "%s", VTY_NEWLINE);
3890
3891 vty_out(vty, "Stdout logging: ");
3892 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3893 vty_out(vty, "disabled");
3894 else
3895 vty_out(vty, "level %s",
3896 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3897 vty_out(vty, "%s", VTY_NEWLINE);
3898
3899 vty_out(vty, "Monitor logging: ");
3900 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3901 vty_out(vty, "disabled");
3902 else
3903 vty_out(vty, "level %s",
3904 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3905 vty_out(vty, "%s", VTY_NEWLINE);
3906
3907 vty_out(vty, "File logging: ");
3908 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3909 vty_out(vty, "disabled");
3910 else
3911 vty_out(vty, "level %s, filename %s",
3912 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3913 zl->filename);
3914 vty_out(vty, "%s", VTY_NEWLINE);
3915
3916 vty_out(vty, "Protocol name: %s%s",
3917 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3918 vty_out(vty, "Record priority: %s%s",
3919 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3920
3921 return CMD_SUCCESS;
3922}
3923
3924DEFUN(config_log_stdout,
3925 config_log_stdout_cmd,
3926 "log stdout", "Logging control\n" "Set stdout logging level\n")
3927{
3928 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3929 return CMD_SUCCESS;
3930}
3931
3932DEFUN(config_log_stdout_level,
3933 config_log_stdout_level_cmd,
3934 "log stdout " LOG_LEVELS,
3935 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3936{
3937 int level;
3938
3939 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3940 return CMD_ERR_NO_MATCH;
3941 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3942 return CMD_SUCCESS;
3943}
3944
3945DEFUN(no_config_log_stdout,
3946 no_config_log_stdout_cmd,
3947 "no log stdout [LEVEL]",
3948 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3949{
3950 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3951 return CMD_SUCCESS;
3952}
3953
3954DEFUN(config_log_monitor,
3955 config_log_monitor_cmd,
3956 "log monitor",
3957 "Logging control\n" "Set terminal line (monitor) logging level\n")
3958{
3959 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3960 return CMD_SUCCESS;
3961}
3962
3963DEFUN(config_log_monitor_level,
3964 config_log_monitor_level_cmd,
3965 "log monitor " LOG_LEVELS,
3966 "Logging control\n"
3967 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3968{
3969 int level;
3970
3971 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3972 return CMD_ERR_NO_MATCH;
3973 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3974 return CMD_SUCCESS;
3975}
3976
3977DEFUN(no_config_log_monitor,
3978 no_config_log_monitor_cmd,
3979 "no log monitor [LEVEL]",
3980 NO_STR
3981 "Logging control\n"
3982 "Disable terminal line (monitor) logging\n" "Logging level\n")
3983{
3984 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3985 return CMD_SUCCESS;
3986}
3987
3988static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3989{
3990 int ret;
3991 char *p = NULL;
3992 const char *fullpath;
3993
3994 /* Path detection. */
3995 if (!IS_DIRECTORY_SEP(*fname)) {
3996 char cwd[MAXPATHLEN + 1];
3997 cwd[MAXPATHLEN] = '\0';
3998
3999 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4000 zlog_err("config_log_file: Unable to alloc mem!");
4001 return CMD_WARNING;
4002 }
4003
4004 if ((p = _talloc_zero(tall_vcmd_ctx,
4005 strlen(cwd) + strlen(fname) + 2),
4006 "set_log_file")
4007 == NULL) {
4008 zlog_err("config_log_file: Unable to alloc mem!");
4009 return CMD_WARNING;
4010 }
4011 sprintf(p, "%s/%s", cwd, fname);
4012 fullpath = p;
4013 } else
4014 fullpath = fname;
4015
4016 ret = zlog_set_file(NULL, fullpath, loglevel);
4017
4018 if (p)
4019 talloc_free(p);
4020
4021 if (!ret) {
4022 vty_out(vty, "can't open logfile %s\n", fname);
4023 return CMD_WARNING;
4024 }
4025
4026 if (host.logfile)
4027 talloc_free(host.logfile);
4028
4029 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
4030
4031 return CMD_SUCCESS;
4032}
4033
4034DEFUN(config_log_file,
4035 config_log_file_cmd,
4036 "log file FILENAME",
4037 "Logging control\n" "Logging to file\n" "Logging filename\n")
4038{
4039 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4040}
4041
4042DEFUN(config_log_file_level,
4043 config_log_file_level_cmd,
4044 "log file FILENAME " LOG_LEVELS,
4045 "Logging control\n"
4046 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4047{
4048 int level;
4049
4050 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4051 return CMD_ERR_NO_MATCH;
4052 return set_log_file(vty, argv[0], level);
4053}
4054
4055DEFUN(no_config_log_file,
4056 no_config_log_file_cmd,
4057 "no log file [FILENAME]",
4058 NO_STR
4059 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4060{
4061 zlog_reset_file(NULL);
4062
4063 if (host.logfile)
4064 talloc_free(host.logfile);
4065
4066 host.logfile = NULL;
4067
4068 return CMD_SUCCESS;
4069}
4070
4071ALIAS(no_config_log_file,
4072 no_config_log_file_level_cmd,
4073 "no log file FILENAME LEVEL",
4074 NO_STR
4075 "Logging control\n"
4076 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4077
4078 DEFUN(config_log_syslog,
4079 config_log_syslog_cmd,
4080 "log syslog", "Logging control\n" "Set syslog logging level\n")
4081{
4082 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4083 return CMD_SUCCESS;
4084}
4085
4086DEFUN(config_log_syslog_level,
4087 config_log_syslog_level_cmd,
4088 "log syslog " LOG_LEVELS,
4089 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4090{
4091 int level;
4092
4093 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4094 return CMD_ERR_NO_MATCH;
4095 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4096 return CMD_SUCCESS;
4097}
4098
4099DEFUN_DEPRECATED(config_log_syslog_facility,
4100 config_log_syslog_facility_cmd,
4101 "log syslog facility " LOG_FACILITIES,
4102 "Logging control\n"
4103 "Logging goes to syslog\n"
4104 "(Deprecated) Facility parameter for syslog messages\n"
4105 LOG_FACILITY_DESC)
4106{
4107 int facility;
4108
4109 if ((facility = facility_match(argv[0])) < 0)
4110 return CMD_ERR_NO_MATCH;
4111
4112 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4113 zlog_default->facility = facility;
4114 return CMD_SUCCESS;
4115}
4116
4117DEFUN(no_config_log_syslog,
4118 no_config_log_syslog_cmd,
4119 "no log syslog [LEVEL]",
4120 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4121{
4122 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4123 return CMD_SUCCESS;
4124}
4125
4126ALIAS(no_config_log_syslog,
4127 no_config_log_syslog_facility_cmd,
4128 "no log syslog facility " LOG_FACILITIES,
4129 NO_STR
4130 "Logging control\n"
4131 "Logging goes to syslog\n"
4132 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4133
4134 DEFUN(config_log_facility,
4135 config_log_facility_cmd,
4136 "log facility " LOG_FACILITIES,
4137 "Logging control\n"
4138 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4139{
4140 int facility;
4141
4142 if ((facility = facility_match(argv[0])) < 0)
4143 return CMD_ERR_NO_MATCH;
4144 zlog_default->facility = facility;
4145 return CMD_SUCCESS;
4146}
4147
4148DEFUN(no_config_log_facility,
4149 no_config_log_facility_cmd,
4150 "no log facility [FACILITY]",
4151 NO_STR
4152 "Logging control\n"
4153 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4154{
4155 zlog_default->facility = LOG_DAEMON;
4156 return CMD_SUCCESS;
4157}
4158
4159DEFUN_DEPRECATED(config_log_trap,
4160 config_log_trap_cmd,
4161 "log trap " LOG_LEVELS,
4162 "Logging control\n"
4163 "(Deprecated) Set logging level and default for all destinations\n"
4164 LOG_LEVEL_DESC)
4165{
4166 int new_level;
4167 int i;
4168
4169 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4170 return CMD_ERR_NO_MATCH;
4171
4172 zlog_default->default_lvl = new_level;
4173 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4174 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4175 zlog_default->maxlvl[i] = new_level;
4176 return CMD_SUCCESS;
4177}
4178
4179DEFUN_DEPRECATED(no_config_log_trap,
4180 no_config_log_trap_cmd,
4181 "no log trap [LEVEL]",
4182 NO_STR
4183 "Logging control\n"
4184 "Permit all logging information\n" "Logging level\n")
4185{
4186 zlog_default->default_lvl = LOG_DEBUG;
4187 return CMD_SUCCESS;
4188}
4189
4190DEFUN(config_log_record_priority,
4191 config_log_record_priority_cmd,
4192 "log record-priority",
4193 "Logging control\n"
4194 "Log the priority of the message within the message\n")
4195{
4196 zlog_default->record_priority = 1;
4197 return CMD_SUCCESS;
4198}
4199
4200DEFUN(no_config_log_record_priority,
4201 no_config_log_record_priority_cmd,
4202 "no log record-priority",
4203 NO_STR
4204 "Logging control\n"
4205 "Do not log the priority of the message within the message\n")
4206{
4207 zlog_default->record_priority = 0;
4208 return CMD_SUCCESS;
4209}
4210#endif
4211
4212DEFUN(banner_motd_file,
4213 banner_motd_file_cmd,
4214 "banner motd file [FILE]",
4215 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4216{
4217 if (host.motdfile)
4218 talloc_free(host.motdfile);
4219 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4220
4221 return CMD_SUCCESS;
4222}
4223
4224DEFUN(banner_motd_default,
4225 banner_motd_default_cmd,
4226 "banner motd default",
4227 "Set banner string\n" "Strings for motd\n" "Default string\n")
4228{
4229 host.motd = default_motd;
4230 return CMD_SUCCESS;
4231}
4232
4233DEFUN(no_banner_motd,
4234 no_banner_motd_cmd,
4235 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4236{
4237 host.motd = NULL;
4238 if (host.motdfile)
4239 talloc_free(host.motdfile);
4240 host.motdfile = NULL;
4241 return CMD_SUCCESS;
4242}
4243
4244/* Set config filename. Called from vty.c */
4245void host_config_set(const char *filename)
4246{
4247 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4248}
4249
Pau Espin Pedrolebb6c1f2021-05-17 18:54:21 +02004250const char *host_config_file(void)
4251{
4252 return host.config;
4253}
4254
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004255/*! Deprecated, now happens implicitly when calling install_node().
4256 * Users of the API may still attempt to call this function, hence
4257 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004258void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004259{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004260}
4261
4262/*! Deprecated, now happens implicitly when calling install_node().
4263 * Users of the API may still attempt to call this function, hence
4264 * leave it here as a no-op. */
4265void vty_install_default(int node)
4266{
4267}
4268
4269/*! Install common commands like 'exit' and 'list'. */
4270static void install_basic_node_commands(int node)
4271{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004272 install_lib_element(node, &config_help_cmd);
4273 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004274
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004275 install_lib_element(node, &show_vty_attr_all_cmd);
4276 install_lib_element(node, &show_vty_attr_cmd);
4277
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004278 install_lib_element(node, &config_write_terminal_cmd);
4279 install_lib_element(node, &config_write_file_cmd);
4280 install_lib_element(node, &config_write_memory_cmd);
4281 install_lib_element(node, &config_write_cmd);
4282 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004283
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004284 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004285
4286 if (node >= CONFIG_NODE) {
4287 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004288 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004289 }
4290}
4291
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004292/*! Return true if a node is installed by install_basic_node_commands(), so
4293 * that we can avoid repeating them for each and every node during 'show
4294 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004295static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004296{
4297 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004298 || cmd == &show_vty_attr_all_cmd
4299 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004300 || cmd == &config_list_cmd
4301 || cmd == &config_write_terminal_cmd
4302 || cmd == &config_write_file_cmd
4303 || cmd == &config_write_memory_cmd
4304 || cmd == &config_write_cmd
4305 || cmd == &show_running_config_cmd
4306 || cmd == &config_exit_cmd
4307 || cmd == &config_end_cmd)
4308 return true;
4309 return false;
4310}
4311
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004312/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004313 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004314 * \param[in] vty the vty of the code
4315 * \param[in] filename where to store the file
4316 * \return 0 in case of success.
4317 *
4318 * If the filename already exists create a filename.sav
4319 * version with the current code.
4320 *
4321 */
4322int osmo_vty_write_config_file(const char *filename)
4323{
4324 char *failed_file;
4325 int rc;
4326
4327 rc = write_config_file(filename, &failed_file);
4328 talloc_free(failed_file);
4329 return rc;
4330}
4331
4332/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004333 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004334 * \return 0 in case of success.
4335 *
4336 * If the filename already exists create a filename.sav
4337 * version with the current code.
4338 *
4339 */
4340int osmo_vty_save_config_file(void)
4341{
4342 char *failed_file;
4343 int rc;
4344
4345 if (host.config == NULL)
4346 return -7;
4347
4348 rc = write_config_file(host.config, &failed_file);
4349 talloc_free(failed_file);
4350 return rc;
4351}
4352
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004353/* Initialize command interface. Install basic nodes and commands. */
4354void cmd_init(int terminal)
4355{
4356 /* Allocate initial top vector of commands. */
4357 cmdvec = vector_init(VECTOR_MIN_SIZE);
4358
4359 /* Default host value settings. */
4360 host.name = NULL;
4361 host.password = NULL;
4362 host.enable = NULL;
4363 host.logfile = NULL;
4364 host.config = NULL;
4365 host.lines = -1;
4366 host.motd = default_motd;
4367 host.motdfile = NULL;
4368
4369 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004370 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004371 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004372 install_node_bare(&auth_node, NULL);
4373 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004374 install_node(&config_node, config_write_host);
4375
4376 /* Each node's basic commands. */
Oliver Smith0c78bc62021-07-12 17:28:36 +02004377 install_lib_element(VIEW_NODE, &show_pid_cmd);
Oliver Smithd243c2a2021-07-09 17:19:32 +02004378 install_lib_element(VIEW_NODE, &show_uptime_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004379 install_lib_element(VIEW_NODE, &show_version_cmd);
4380 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004381 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004382 install_lib_element(VIEW_NODE, &config_list_cmd);
4383 install_lib_element(VIEW_NODE, &config_exit_cmd);
4384 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004385 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4386 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004387 install_lib_element(VIEW_NODE, &config_enable_cmd);
4388 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4389 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4390 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004391 }
4392
4393 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004394 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4395 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4396 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004397 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004398 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4399 install_lib_element(ENABLE_NODE, &show_version_cmd);
4400 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004401
4402 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004403 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4404 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4405 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004406 }
4407
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004408 install_lib_element(CONFIG_NODE, &hostname_cmd);
4409 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004410
4411 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004412 install_lib_element(CONFIG_NODE, &password_cmd);
4413 install_lib_element(CONFIG_NODE, &password_text_cmd);
4414 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4415 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4416 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004417
4418#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004419 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4420 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004421#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004422 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4423 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4424 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4425 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4426 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004427
4428 }
4429 srand(time(NULL));
4430}
Harald Welte7acb30c2011-08-17 17:13:48 +02004431
Oliver Smithd243c2a2021-07-09 17:19:32 +02004432static __attribute__((constructor)) void on_dso_load_starttime(void)
4433{
4434 osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
4435}
4436
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004437/* FIXME: execute this section in the unit test instead */
4438static __attribute__((constructor)) void on_dso_load(void)
4439{
4440 unsigned int i, j;
4441
4442 /* Check total number of the library specific attributes */
4443 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4444
4445 /* Check for duplicates in the list of library specific flags */
4446 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4447 if (cmd_lib_attr_letters[i] == '\0')
4448 continue;
4449
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004450 /* Some flag characters are reserved for global attributes */
4451 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4452 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4453 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4454
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004455 /* Only upper case flag letters are allowed for libraries */
4456 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4457 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4458
4459 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4460 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4461 }
4462}
4463
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004464/*! @} */