blob: 3112fee4930616894ad0fd83dc715e9251f8bb28 [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>
Oliver Smith8a482fd2021-07-12 18:18:51 +020041#include <signal.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020042#include <sys/time.h>
43#include <sys/stat.h>
Oliver Smith8a482fd2021-07-12 18:18:51 +020044#include <sys/types.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020045
46#include <osmocom/vty/vector.h>
47#include <osmocom/vty/vty.h>
48#include <osmocom/vty/command.h>
49
Oliver Smith8a482fd2021-07-12 18:18:51 +020050#include <osmocom/core/logging.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010051#include <osmocom/core/talloc.h>
Oliver Smithd243c2a2021-07-09 17:19:32 +020052#include <osmocom/core/timer.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010053#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020054
Ruben Undheim766f77c2018-11-18 13:02:47 +010055#ifndef MAXPATHLEN
56 #define MAXPATHLEN 4096
57#endif
58
59
Harald Weltee881b1b2011-08-17 18:52:30 +020060/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020061 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020062 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020063 *
64 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020065
Harald Welte3fb0b6f2010-05-19 19:02:52 +020066#define CONFIGFILE_MASK 022
67
68void *tall_vty_cmd_ctx;
69
Oliver Smithd243c2a2021-07-09 17:19:32 +020070/* Set by on_dso_load_starttime() for "show uptime". */
71static struct timespec starttime;
72
Harald Welte3fb0b6f2010-05-19 19:02:52 +020073/* Command vector which includes some level of command lists. Normally
74 each daemon maintains each own cmdvec. */
75vector cmdvec;
76
77/* Host information structure. */
78struct host host;
79
80/* Standard command node structures. */
81struct cmd_node auth_node = {
82 AUTH_NODE,
83 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010084 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020085};
86
87struct cmd_node view_node = {
88 VIEW_NODE,
89 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010090 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020091};
92
93struct cmd_node auth_enable_node = {
94 AUTH_ENABLE_NODE,
95 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010096 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020097};
98
99struct cmd_node enable_node = {
100 ENABLE_NODE,
101 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +0100102 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200103};
104
105struct cmd_node config_node = {
106 CONFIG_NODE,
107 "%s(config)# ",
108 1
109};
110
111/* Default motd string. */
112const char *default_motd = "";
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200115 *
116 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200117void print_version(int print_copyright)
118{
Harald Welte237f6242010-05-25 23:00:45 +0200119 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200120 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200121 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200122}
123
124/* Utility function to concatenate argv argument into a single string
125 with inserting ' ' character between each argument. */
126char *argv_concat(const char **argv, int argc, int shift)
127{
128 int i;
129 size_t len;
130 char *str;
131 char *p;
132
133 len = 0;
134 for (i = shift; i < argc; i++)
135 len += strlen(argv[i]) + 1;
136 if (!len)
137 return NULL;
138 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
139 for (i = shift; i < argc; i++) {
140 size_t arglen;
141 memcpy(p, argv[i], (arglen = strlen(argv[i])));
142 p += arglen;
143 *p++ = ' ';
144 }
145 *(p - 1) = '\0';
146 return str;
147}
148
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200149/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
150 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
151 * in turn, this name us used for XML IDs in 'show online-help'. */
152static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
153{
154 const char *pos;
155 int dest = 0;
156
157 if (!prompt || !*prompt)
158 return "";
159
160 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
161 if (pos[0] == '%' && pos[1]) {
162 /* skip "%s"; loop pos++ does the second one. */
163 pos++;
164 continue;
165 }
166 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
167 continue;
168 name_buf[dest] = pos[0];
169 dest++;
170 }
171 name_buf[dest] = '\0';
172 return name_buf;
173}
174
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200175static void install_basic_node_commands(int node);
176
177/*! Install top node of command vector, without adding basic node commands. */
178static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200179{
180 vector_set_index(cmdvec, node->node, node);
181 node->func = func;
182 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200183 if (!*node->name)
184 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200185}
186
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200187/*! Install top node of command vector. */
188void install_node(struct cmd_node *node, int (*func) (struct vty *))
189{
190 install_node_bare(node, func);
191 install_basic_node_commands(node->node);
192}
193
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200194/* Compare two command's string. Used in sort_node (). */
195static int cmp_node(const void *p, const void *q)
196{
197 struct cmd_element *a = *(struct cmd_element **)p;
198 struct cmd_element *b = *(struct cmd_element **)q;
199
200 return strcmp(a->string, b->string);
201}
202
203static int cmp_desc(const void *p, const void *q)
204{
205 struct desc *a = *(struct desc **)p;
206 struct desc *b = *(struct desc **)q;
207
208 return strcmp(a->cmd, b->cmd);
209}
210
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200211/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200212void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200213{
214 unsigned int i, j;
215 struct cmd_node *cnode;
216 vector descvec;
217 struct cmd_element *cmd_element;
218
219 for (i = 0; i < vector_active(cmdvec); i++)
220 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
221 vector cmd_vector = cnode->cmd_vector;
222 qsort(cmd_vector->index, vector_active(cmd_vector),
223 sizeof(void *), cmp_node);
224
225 for (j = 0; j < vector_active(cmd_vector); j++)
226 if ((cmd_element =
227 vector_slot(cmd_vector, j)) != NULL
228 && vector_active(cmd_element->strvec)) {
229 descvec =
230 vector_slot(cmd_element->strvec,
231 vector_active
232 (cmd_element->strvec) -
233 1);
234 qsort(descvec->index,
235 vector_active(descvec),
236 sizeof(void *), cmp_desc);
237 }
238 }
239}
240
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200241/*! Break up string in command tokens. Return leading indents.
242 * \param[in] string String to split.
243 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
244 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
245 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
246 *
247 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
248 * so that \a indent can simply return the count of leading spaces.
249 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
250 */
251int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200252{
253 const char *cp, *start;
254 char *token;
255 int strlen;
256 vector strvec;
257
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200258 *strvec_p = NULL;
259 if (indent)
260 *indent = 0;
261
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200262 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200263 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200264
265 cp = string;
266
267 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200268 while (isspace((int)*cp) && *cp != '\0') {
269 /* if we're counting indents, we need to be strict about them */
270 if (indent && (*cp != ' ') && (*cp != '\t')) {
271 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
272 if (*cp == '\n' || *cp == '\r') {
273 cp++;
274 string = cp;
275 continue;
276 }
277 return CMD_ERR_INVALID_INDENT;
278 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200279 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 }
281
282 if (indent)
283 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Return if there is only white spaces */
286 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200287 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200288
289 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200290 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200291
292 /* Prepare return vector. */
293 strvec = vector_init(VECTOR_MIN_SIZE);
294
295 /* Copy each command piece and set into vector. */
296 while (1) {
297 start = cp;
298 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
299 *cp != '\0')
300 cp++;
301 strlen = cp - start;
302 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
303 memcpy(token, start, strlen);
304 *(token + strlen) = '\0';
305 vector_set(strvec, token);
306
307 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
308 *cp != '\0')
309 cp++;
310
311 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200312 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200313 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200314
315 *strvec_p = strvec;
316 return CMD_SUCCESS;
317}
318
319/*! Breaking up string into each command piece. I assume given
320 character is separated by a space character. Return value is a
321 vector which includes char ** data element. */
322vector cmd_make_strvec(const char *string)
323{
324 vector strvec;
325 cmd_make_strvec2(string, NULL, &strvec);
326 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200327}
328
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200329/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200330void cmd_free_strvec(vector v)
331{
332 unsigned int i;
333 char *cp;
334
335 if (!v)
336 return;
337
338 for (i = 0; i < vector_active(v); i++)
339 if ((cp = vector_slot(v, i)) != NULL)
340 talloc_free(cp);
341
342 vector_free(v);
343}
344
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200345/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200346static char *cmd_desc_str(const char **string)
347{
348 const char *cp, *start;
349 char *token;
350 int strlen;
351
352 cp = *string;
353
354 if (cp == NULL)
355 return NULL;
356
357 /* Skip white spaces. */
358 while (isspace((int)*cp) && *cp != '\0')
359 cp++;
360
361 /* Return if there is only white spaces */
362 if (*cp == '\0')
363 return NULL;
364
365 start = cp;
366
367 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
368 cp++;
369
370 strlen = cp - start;
371 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
372 memcpy(token, start, strlen);
373 *(token + strlen) = '\0';
374
375 *string = cp;
376
377 return token;
378}
379
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200380/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200381static vector cmd_make_descvec(const char *string, const char *descstr)
382{
383 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100384 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200385 const char *sp;
386 char *token;
387 int len;
388 const char *cp;
389 const char *dp;
390 vector allvec;
391 vector strvec = NULL;
392 struct desc *desc;
393
394 cp = string;
395 dp = descstr;
396
397 if (cp == NULL)
398 return NULL;
399
400 allvec = vector_init(VECTOR_MIN_SIZE);
401
402 while (1) {
403 while (isspace((int)*cp) && *cp != '\0')
404 cp++;
405
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100406 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
407 if (cp[0] == '[' && cp[1] == '(') {
408 optional_brace = 1;
409 cp++;
410 }
411
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200412 if (*cp == '(') {
413 multiple = 1;
414 cp++;
415 }
416 if (*cp == ')') {
417 multiple = 0;
418 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100419 if (*cp == ']')
420 cp++;
421 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200422 }
423 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100424 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200425 cp++;
426 }
427
428 while (isspace((int)*cp) && *cp != '\0')
429 cp++;
430
431 if (*cp == '(') {
432 multiple = 1;
433 cp++;
434 }
435
436 if (*cp == '\0')
437 return allvec;
438
439 sp = cp;
440
441 while (!
442 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
443 || *cp == ')' || *cp == '|') && *cp != '\0')
444 cp++;
445
446 len = cp - sp;
447
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100448 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
449 if (optional_brace) {
450 /* Place each individual multi-choice token in its own square braces */
451 token[0] = '[';
452 memcpy(token + 1, sp, len);
453 token[1 + len] = ']';
454 token[2 + len] = '\0';
455 } else {
456 memcpy(token, sp, len);
457 *(token + len) = '\0';
458 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200459
460 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
461 desc->cmd = token;
462 desc->str = cmd_desc_str(&dp);
463
464 if (multiple) {
465 if (multiple == 1) {
466 strvec = vector_init(VECTOR_MIN_SIZE);
467 vector_set(allvec, strvec);
468 }
469 multiple++;
470 } else {
471 strvec = vector_init(VECTOR_MIN_SIZE);
472 vector_set(allvec, strvec);
473 }
474 vector_set(strvec, desc);
475 }
476}
477
478/* Count mandantory string vector size. This is to determine inputed
479 command has enough command length. */
480static int cmd_cmdsize(vector strvec)
481{
482 unsigned int i;
483 int size = 0;
484 vector descvec;
485 struct desc *desc;
486
487 for (i = 0; i < vector_active(strvec); i++)
488 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100489 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200490 && (desc = vector_slot(descvec, 0)) != NULL) {
491 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
492 return size;
493 else
494 size++;
495 } else
496 size++;
497 }
498 return size;
499}
500
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200501/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200502const char *cmd_prompt(enum node_type node)
503{
504 struct cmd_node *cnode;
505
506 cnode = vector_slot(cmdvec, node);
507 return cnode->prompt;
508}
509
Alexander Couzensad580ba2016-05-16 16:01:45 +0200510/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200511 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200512 * \param unsafe string
513 * \return a new talloc char *
514 */
515char *osmo_asciidoc_escape(const char *inp)
516{
517 int _strlen;
518 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200519 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200520
521 if (!inp)
522 return NULL;
523 _strlen = strlen(inp);
524
525 for (i = 0; i < _strlen; ++i) {
526 switch (inp[i]) {
527 case '|':
528 len += 2;
529 break;
530 default:
531 len += 1;
532 break;
533 }
534 }
535
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200536 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200537 if (!out)
538 return NULL;
539
540 out_ptr = out;
541
Alexander Couzensad580ba2016-05-16 16:01:45 +0200542 for (i = 0; i < _strlen; ++i) {
543 switch (inp[i]) {
544 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200545 /* Prepend escape character "\": */
546 *(out_ptr++) = '\\';
547 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200548 default:
549 *(out_ptr++) = inp[i];
550 break;
551 }
552 }
553
Alexander Couzensad580ba2016-05-16 16:01:45 +0200554 out_ptr[0] = '\0';
555 return out;
556}
557
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100558static char *xml_escape(const char *inp)
559{
560 int _strlen;
561 char *out, *out_ptr;
562 int len = 0, i, j;
563
564 if (!inp)
565 return NULL;
566 _strlen = strlen(inp);
567
568 for (i = 0; i < _strlen; ++i) {
569 switch (inp[i]) {
570 case '"':
571 len += 6;
572 break;
573 case '\'':
574 len += 6;
575 break;
576 case '<':
577 len += 4;
578 break;
579 case '>':
580 len += 4;
581 break;
582 case '&':
583 len += 5;
584 break;
585 default:
586 len += 1;
587 break;
588 }
589 }
590
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200591 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100592 if (!out)
593 return NULL;
594
595 out_ptr = out;
596
597#define ADD(out, str) \
598 for (j = 0; j < strlen(str); ++j) \
599 *(out++) = str[j];
600
601 for (i = 0; i < _strlen; ++i) {
602 switch (inp[i]) {
603 case '"':
604 ADD(out_ptr, "&quot;");
605 break;
606 case '\'':
607 ADD(out_ptr, "&apos;");
608 break;
609 case '<':
610 ADD(out_ptr, "&lt;");
611 break;
612 case '>':
613 ADD(out_ptr, "&gt;");
614 break;
615 case '&':
616 ADD(out_ptr, "&amp;");
617 break;
618 default:
619 *(out_ptr++) = inp[i];
620 break;
621 }
622 }
623
624#undef ADD
625
626 out_ptr[0] = '\0';
627 return out;
628}
629
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200630typedef int (*print_func_t)(void *data, const char *fmt, ...);
631
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700632static const struct value_string cmd_attr_desc[] = {
633 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700634 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700635 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700636 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700637 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700638 { 0, NULL }
639};
640
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700641/* Public attributes (to be printed in the VTY / XML reference) */
642#define CMD_ATTR_PUBLIC_MASK \
Vadim Yanitskiy67608452020-10-23 20:37:32 +0700643 (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700644
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700645/* Get a flag character for a global VTY command attribute */
646static char cmd_attr_get_flag(unsigned int attr)
647{
648 switch (attr) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700649 case CMD_ATTR_HIDDEN:
650 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700651 case CMD_ATTR_IMMEDIATE:
652 return '!';
653 case CMD_ATTR_NODE_EXIT:
654 return '@';
655 default:
656 return '.';
657 }
658}
659
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700660/* Description of attributes shared between the lib commands */
661static const char * const cmd_lib_attr_desc[32] = {
662 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
663 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200664 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
665 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200666 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
667 "This command applies on IPA link establishment",
668 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
669 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700670};
671
672/* Flag letters of attributes shared between the lib commands.
673 * NOTE: uppercase letters only, the rest is reserved for applications. */
674static const char cmd_lib_attr_letters[32] = {
675 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200676 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200677 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
678 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700679};
680
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100681/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200682 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100683 */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700684static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
685 void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100686{
687 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700688 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100689
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200690 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700691
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700692 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700693 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700694 print_func(data, " <attributes scope='global'>%s", newline);
695
696 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
697 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700698 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700699
700 if (~cmd->attr & cmd_attr_desc[i].value)
701 continue;
702
703 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700704 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700705 xml_att_desc, newline);
706 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700707
708 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
709 if (flag != '.')
710 print_func(data, " flag='%c'", flag);
711 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700712 }
713
714 print_func(data, " </attributes>%s", newline);
715 }
716
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700717 /* Print application specific attributes and their description */
718 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700719 const char * const *desc;
720 const char *letters;
721
722 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
723 print_func(data, " <attributes scope='library'>%s", newline);
724 letters = &cmd_lib_attr_letters[0];
725 desc = &cmd_lib_attr_desc[0];
726 } else {
727 print_func(data, " <attributes scope='application'>%s", newline);
728 letters = &host.app_info->usr_attr_letters[0];
729 desc = &host.app_info->usr_attr_desc[0];
730 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700731
732 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
733 char *xml_att_desc;
734 char flag;
735
736 /* Skip attribute if *not* set */
Harald Weltec296e292020-12-21 15:44:52 +0100737 if (~cmd->usrattr & ((unsigned)1 << i))
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700738 continue;
739
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700740 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700741 print_func(data, " <attribute doc='%s'", xml_att_desc);
742 talloc_free(xml_att_desc);
743
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700744 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700745 print_func(data, " flag='%c'", flag);
746 print_func(data, " />%s", newline);
747 }
748
749 print_func(data, " </attributes>%s", newline);
750 }
751
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200752 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100753
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700754 for (i = 0; i < vector_count(cmd->strvec); ++i) {
755 vector descvec = vector_slot(cmd->strvec, i);
756 int j;
757 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100758 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700759 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100760 if (desc == NULL)
761 continue;
762
763 xml_param = xml_escape(desc->cmd);
764 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200765 print_func(data, " <param name='%s' doc='%s' />%s",
766 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100767 talloc_free(xml_param);
768 talloc_free(xml_doc);
769 }
770 }
771
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200772 print_func(data, " </params>%s", newline);
773 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100774
775 talloc_free(xml_string);
776 return 0;
777}
778
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700779static bool vty_command_is_common(const struct cmd_element *cmd);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200780
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100781/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200782 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700783 *
784 * (gflag_mask, match = false) - print only those commands with non-matching flags.
785 * (gflag_mask, match = true) - print only those commands with matching flags.
786 *
787 * Some examples:
788 *
789 * Print all commands except deprecated and hidden (default mode):
790 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
791 * Print only deprecated and hidden commands:
792 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
793 * Print all commands except deprecated (expert mode):
794 * (CMD_ATTR_DEPRECATED, false)
795 * Print only hidden commands:
796 * (CMD_ATTR_HIDDEN, true)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100797 */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700798static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
799 unsigned char gflag_mask, bool match)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100800{
801 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200802 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100803
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200804 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100805
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200806 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200807 print_func(data, " <node id='_common_cmds_'>%s", newline);
808 print_func(data, " <name>Common Commands</name>%s", newline);
809 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
810 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200811 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700812 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200813 if (!cnode)
814 continue;
815 if (cnode->node != CONFIG_NODE)
816 continue;
817
818 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700819 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200820 if (!vty_command_is_common(elem))
821 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700822 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700823 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700824 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700825 continue;
826 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200827 }
828 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200829 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200830
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100831 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700832 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100833 if (!cnode)
834 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200835 if (vector_active(cnode->cmd_vector) < 1)
836 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100837
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200838 /* De-dup node IDs: how many times has this same name been used before? Count the first
839 * occurence as _1 and omit that first suffix, so that the first occurence is called
840 * 'name', the second becomes 'name_2', then 'name_3', ... */
841 same_name_count = 1;
842 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700843 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200844 if (!cnode2)
845 continue;
846 if (strcmp(cnode->name, cnode2->name) == 0)
847 same_name_count ++;
848 }
849
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200850 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200851 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200852 print_func(data, "_%d", same_name_count);
853 print_func(data, "'>%s", newline);
854 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100855
856 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700857 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200858 if (vty_command_is_common(elem))
859 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700860 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700861 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700862 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700863 continue;
864 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100865 }
866
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200867 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100868 }
869
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200870 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100871
872 return 0;
873}
874
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200875static int print_func_vty(void *data, const char *format, ...)
876{
877 struct vty *vty = data;
878 va_list args;
879 int rc;
880 va_start(args, format);
881 rc = vty_out_va(vty, format, args);
882 va_end(args);
883 return rc;
884}
885
886static int vty_dump_xml_ref_to_vty(struct vty *vty)
887{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700888 unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
889 if (!vty->expert_mode)
890 gflag_mask |= CMD_ATTR_HIDDEN;
891 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200892}
893
894static int print_func_stream(void *data, const char *format, ...)
895{
896 va_list args;
897 int rc;
898 va_start(args, format);
899 rc = vfprintf((FILE*)data, format, args);
900 va_end(args);
901 return rc;
902}
903
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700904const struct value_string vty_ref_gen_mode_names[] = {
905 { VTY_REF_GEN_MODE_DEFAULT, "default" },
906 { VTY_REF_GEN_MODE_EXPERT, "expert" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700907 { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700908 { 0, NULL }
909};
910
911const struct value_string vty_ref_gen_mode_desc[] = {
912 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
913 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700914 { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700915 { 0, NULL }
916};
917
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200918/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700919 * \param[out] stream Output stream to print the XML reference to.
920 * \param[in] mode The XML reference generation mode.
921 * \returns always 0 for now, no errors possible.
922 */
923int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
924{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700925 unsigned char gflag_mask;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700926 bool match = false;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700927
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700928 switch (mode) {
929 case VTY_REF_GEN_MODE_EXPERT:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700930 /* All commands except deprecated */
931 gflag_mask = CMD_ATTR_DEPRECATED;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700932 break;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700933 case VTY_REF_GEN_MODE_HIDDEN:
934 /* Only hidden commands */
935 gflag_mask = CMD_ATTR_HIDDEN;
936 match = true;
937 break;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700938 case VTY_REF_GEN_MODE_DEFAULT:
939 default:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700940 /* All commands except deprecated and hidden */
941 gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700942 break;
943 }
944
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700945 return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700946}
947
948/*! Print the XML reference of all VTY nodes to the given stream.
949 * \param[out] stream Output stream to print the XML reference to.
950 * \returns always 0 for now, no errors possible.
951 *
952 * NOTE: this function is deprecated because it does not allow to
953 * specify the XML reference generation mode (default mode
954 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200955 */
956int vty_dump_xml_ref(FILE *stream)
957{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700958 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200959}
960
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200961/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100962static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
963{
964 int i;
965
966 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
967 struct cmd_element *elem;
968 elem = vector_slot(cnode->cmd_vector, i);
969 if (!elem->string)
970 continue;
971 if (!strcmp(elem->string, cmdstring))
972 return 1;
973 }
974 return 0;
975}
976
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200977/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200978 * \param[in] ntype Node Type
979 * \param[cmd] element to be installed
980 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000981void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200982{
983 struct cmd_node *cnode;
984
985 cnode = vector_slot(cmdvec, ntype);
986
Harald Weltea99d45a2015-11-12 13:48:23 +0100987 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100988 /* ensure no _identical_ command has been registered at this
989 * node so far */
990 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200991
992 vector_set(cnode->cmd_vector, cmd);
993
994 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
995 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
996}
997
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700998/*! Install a library command into a node
999 * \param[in] ntype Node Type
1000 * \param[in] cmd element to be installed
1001 */
1002void install_lib_element(int ntype, struct cmd_element *cmd)
1003{
1004 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1005 install_element(ntype, cmd);
1006}
1007
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001008/* Install a command into VIEW and ENABLE node */
1009void install_element_ve(struct cmd_element *cmd)
1010{
1011 install_element(VIEW_NODE, cmd);
1012 install_element(ENABLE_NODE, cmd);
1013}
1014
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001015/* Install a library command into VIEW and ENABLE node */
1016void install_lib_element_ve(struct cmd_element *cmd)
1017{
1018 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1019 install_element_ve(cmd);
1020}
1021
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022#ifdef VTY_CRYPT_PW
1023static unsigned char itoa64[] =
1024 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1025
1026static void to64(char *s, long v, int n)
1027{
1028 while (--n >= 0) {
1029 *s++ = itoa64[v & 0x3f];
1030 v >>= 6;
1031 }
1032}
1033
1034static char *zencrypt(const char *passwd)
1035{
1036 char salt[6];
1037 struct timeval tv;
1038 char *crypt(const char *, const char *);
1039
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001040 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001041
1042 to64(&salt[0], random(), 3);
1043 to64(&salt[3], tv.tv_usec, 3);
1044 salt[5] = '\0';
1045
1046 return crypt(passwd, salt);
1047}
1048#endif
1049
1050/* This function write configuration of this host. */
1051static int config_write_host(struct vty *vty)
1052{
1053 if (host.name)
1054 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1055
1056 if (host.encrypt) {
1057 if (host.password_encrypt)
1058 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1059 VTY_NEWLINE);
1060 if (host.enable_encrypt)
1061 vty_out(vty, "enable password 8 %s%s",
1062 host.enable_encrypt, VTY_NEWLINE);
1063 } else {
1064 if (host.password)
1065 vty_out(vty, "password %s%s", host.password,
1066 VTY_NEWLINE);
1067 if (host.enable)
1068 vty_out(vty, "enable password %s%s", host.enable,
1069 VTY_NEWLINE);
1070 }
1071
1072 if (host.advanced)
1073 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1074
1075 if (host.encrypt)
1076 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1077
1078 if (host.lines >= 0)
1079 vty_out(vty, "service terminal-length %d%s", host.lines,
1080 VTY_NEWLINE);
1081
1082 if (host.motdfile)
1083 vty_out(vty, "banner motd file %s%s", host.motdfile,
1084 VTY_NEWLINE);
1085 else if (!host.motd)
1086 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1087
1088 return 1;
1089}
1090
1091/* Utility function for getting command vector. */
1092static vector cmd_node_vector(vector v, enum node_type ntype)
1093{
1094 struct cmd_node *cnode = vector_slot(v, ntype);
1095 return cnode->cmd_vector;
1096}
1097
1098/* Completion match types. */
1099enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001100 NO_MATCH = 0,
1101 ANY_MATCH,
1102 EXTEND_MATCH,
1103 IPV4_PREFIX_MATCH,
1104 IPV4_MATCH,
1105 IPV6_PREFIX_MATCH,
1106 IPV6_MATCH,
1107 RANGE_MATCH,
1108 VARARG_MATCH,
1109 PARTLY_MATCH,
1110 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111};
1112
1113static enum match_type cmd_ipv4_match(const char *str)
1114{
1115 const char *sp;
1116 int dots = 0, nums = 0;
1117 char buf[4];
1118
1119 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001120 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001121
1122 for (;;) {
1123 memset(buf, 0, sizeof(buf));
1124 sp = str;
1125 while (*str != '\0') {
1126 if (*str == '.') {
1127 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001128 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001129
1130 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001131 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001132
1133 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001134 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001135
1136 dots++;
1137 break;
1138 }
1139 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001140 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001141
1142 str++;
1143 }
1144
1145 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001146 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001147
1148 strncpy(buf, sp, str - sp);
1149 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 nums++;
1153
1154 if (*str == '\0')
1155 break;
1156
1157 str++;
1158 }
1159
1160 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001161 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001162
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001163 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001164}
1165
1166static enum match_type cmd_ipv4_prefix_match(const char *str)
1167{
1168 const char *sp;
1169 int dots = 0;
1170 char buf[4];
1171
1172 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001173 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001174
1175 for (;;) {
1176 memset(buf, 0, sizeof(buf));
1177 sp = str;
1178 while (*str != '\0' && *str != '/') {
1179 if (*str == '.') {
1180 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001181 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001182
1183 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001184 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001185
1186 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001187 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001188
1189 dots++;
1190 break;
1191 }
1192
1193 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001194 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001195
1196 str++;
1197 }
1198
1199 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001200 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001201
1202 strncpy(buf, sp, str - sp);
1203 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001204 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001205
1206 if (dots == 3) {
1207 if (*str == '/') {
1208 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001209 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001210
1211 str++;
1212 break;
1213 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001214 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001215 }
1216
1217 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001218 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001219
1220 str++;
1221 }
1222
1223 sp = str;
1224 while (*str != '\0') {
1225 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001226 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001227
1228 str++;
1229 }
1230
1231 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001232 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001234 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235}
1236
1237#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1238#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1239#define STATE_START 1
1240#define STATE_COLON 2
1241#define STATE_DOUBLE 3
1242#define STATE_ADDR 4
1243#define STATE_DOT 5
1244#define STATE_SLASH 6
1245#define STATE_MASK 7
1246
1247#ifdef HAVE_IPV6
1248
1249static enum match_type cmd_ipv6_match(const char *str)
1250{
1251 int state = STATE_START;
1252 int colons = 0, nums = 0, double_colon = 0;
1253 const char *sp = NULL;
1254 struct sockaddr_in6 sin6_dummy;
1255 int ret;
1256
1257 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001258 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259
1260 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001261 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001262
1263 /* use inet_pton that has a better support,
1264 * for example inet_pton can support the automatic addresses:
1265 * ::1.2.3.4
1266 */
1267 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1268
1269 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001270 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001271
1272 while (*str != '\0') {
1273 switch (state) {
1274 case STATE_START:
1275 if (*str == ':') {
1276 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001277 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278 colons--;
1279 state = STATE_COLON;
1280 } else {
1281 sp = str;
1282 state = STATE_ADDR;
1283 }
1284
1285 continue;
1286 case STATE_COLON:
1287 colons++;
1288 if (*(str + 1) == ':')
1289 state = STATE_DOUBLE;
1290 else {
1291 sp = str + 1;
1292 state = STATE_ADDR;
1293 }
1294 break;
1295 case STATE_DOUBLE:
1296 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001297 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001298
1299 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001300 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001301 else {
1302 if (*(str + 1) != '\0')
1303 colons++;
1304 sp = str + 1;
1305 state = STATE_ADDR;
1306 }
1307
1308 double_colon++;
1309 nums++;
1310 break;
1311 case STATE_ADDR:
1312 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1313 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001314 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001315
1316 nums++;
1317 state = STATE_COLON;
1318 }
1319 if (*(str + 1) == '.')
1320 state = STATE_DOT;
1321 break;
1322 case STATE_DOT:
1323 state = STATE_ADDR;
1324 break;
1325 default:
1326 break;
1327 }
1328
1329 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001330 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331
1332 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001333 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001334
1335 str++;
1336 }
1337
1338#if 0
1339 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001341#endif /* 0 */
1342
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001343 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001344}
1345
1346static enum match_type cmd_ipv6_prefix_match(const char *str)
1347{
1348 int state = STATE_START;
1349 int colons = 0, nums = 0, double_colon = 0;
1350 int mask;
1351 const char *sp = NULL;
1352 char *endptr = NULL;
1353
1354 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001355 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001356
1357 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001358 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001359
1360 while (*str != '\0' && state != STATE_MASK) {
1361 switch (state) {
1362 case STATE_START:
1363 if (*str == ':') {
1364 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001365 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001366 colons--;
1367 state = STATE_COLON;
1368 } else {
1369 sp = str;
1370 state = STATE_ADDR;
1371 }
1372
1373 continue;
1374 case STATE_COLON:
1375 colons++;
1376 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001377 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378 else if (*(str + 1) == ':')
1379 state = STATE_DOUBLE;
1380 else {
1381 sp = str + 1;
1382 state = STATE_ADDR;
1383 }
1384 break;
1385 case STATE_DOUBLE:
1386 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001387 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001388
1389 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001390 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001391 else {
1392 if (*(str + 1) != '\0' && *(str + 1) != '/')
1393 colons++;
1394 sp = str + 1;
1395
1396 if (*(str + 1) == '/')
1397 state = STATE_SLASH;
1398 else
1399 state = STATE_ADDR;
1400 }
1401
1402 double_colon++;
1403 nums += 1;
1404 break;
1405 case STATE_ADDR:
1406 if (*(str + 1) == ':' || *(str + 1) == '.'
1407 || *(str + 1) == '\0' || *(str + 1) == '/') {
1408 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001409 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001410
1411 for (; sp <= str; sp++)
1412 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001413 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001414
1415 nums++;
1416
1417 if (*(str + 1) == ':')
1418 state = STATE_COLON;
1419 else if (*(str + 1) == '.')
1420 state = STATE_DOT;
1421 else if (*(str + 1) == '/')
1422 state = STATE_SLASH;
1423 }
1424 break;
1425 case STATE_DOT:
1426 state = STATE_ADDR;
1427 break;
1428 case STATE_SLASH:
1429 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001430 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001431
1432 state = STATE_MASK;
1433 break;
1434 default:
1435 break;
1436 }
1437
1438 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001439 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001440
1441 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001442 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001443
1444 str++;
1445 }
1446
1447 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001448 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001449
1450 mask = strtol(str, &endptr, 10);
1451 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001452 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001453
1454 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001455 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001456
1457/* I don't know why mask < 13 makes command match partly.
1458 Forgive me to make this comments. I Want to set static default route
1459 because of lack of function to originate default in ospf6d; sorry
1460 yasu
1461 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001462 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001463*/
1464
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001465 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001466}
1467
1468#endif /* HAVE_IPV6 */
1469
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001470
1471#if ULONG_MAX == 18446744073709551615UL
1472#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1473#elif ULONG_MAX == 4294967295UL
1474#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1475#else
1476#error "ULONG_MAX not defined!"
1477#endif
1478
1479#if LONG_MAX == 9223372036854775807L
1480#define DECIMAL_STRLEN_MAX_SIGNED 19
1481#elif LONG_MAX == 2147483647L
1482#define DECIMAL_STRLEN_MAX_SIGNED 10
1483#else
1484#error "LONG_MAX not defined!"
1485#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001486
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001487int vty_cmd_range_match(const char *range, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001488{
1489 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001490 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001491 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001492
1493 if (str == NULL)
1494 return 1;
1495
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001496 if (range[1] == '-') {
1497 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001498
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001499 val = strtol(str, &endptr, 10);
1500 if (*endptr != '\0')
1501 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001502
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001503 range += 2;
1504 p = strchr(range, '-');
1505 if (p == NULL)
1506 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001507 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001508 return 0;
1509 strncpy(buf, range, p - range);
1510 buf[p - range] = '\0';
1511 min = -strtol(buf, &endptr, 10);
1512 if (*endptr != '\0')
1513 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001514
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001515 range = p + 1;
1516 p = strchr(range, '>');
1517 if (p == NULL)
1518 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001519 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001520 return 0;
1521 strncpy(buf, range, p - range);
1522 buf[p - range] = '\0';
1523 max = strtol(buf, &endptr, 10);
1524 if (*endptr != '\0')
1525 return 0;
1526
1527 if (val < min || val > max)
1528 return 0;
1529 } else {
1530 unsigned long min, max, val;
1531
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001532 if (str[0] == '-')
1533 return 0;
1534
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001535 val = strtoul(str, &endptr, 10);
1536 if (*endptr != '\0')
1537 return 0;
1538
1539 range++;
1540 p = strchr(range, '-');
1541 if (p == NULL)
1542 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001543 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001544 return 0;
1545 strncpy(buf, range, p - range);
1546 buf[p - range] = '\0';
1547 min = strtoul(buf, &endptr, 10);
1548 if (*endptr != '\0')
1549 return 0;
1550
1551 range = p + 1;
1552 p = strchr(range, '>');
1553 if (p == NULL)
1554 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001555 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001556 return 0;
1557 strncpy(buf, range, p - range);
1558 buf[p - range] = '\0';
1559 max = strtoul(buf, &endptr, 10);
1560 if (*endptr != '\0')
1561 return 0;
1562
1563 if (val < min || val > max)
1564 return 0;
1565 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001566
1567 return 1;
1568}
1569
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001570/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001571static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001572{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001573 /* we've got "[blah]". We want to strip off the []s and redo the
1574 * match check for "blah"
1575 */
1576 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001578 if (len < 3)
1579 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001580
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001581 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001582}
1583
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001584static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001585cmd_match(const char *str, const char *command,
1586 enum match_type min, bool recur)
1587{
1588
1589 if (recur && CMD_OPTION(str))
1590 {
1591 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001592 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001593
1594 /* this would be a bug in a command, however handle it gracefully
1595 * as it we only discover it if a user tries to run it
1596 */
1597 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001598 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001599
1600 ret = cmd_match(tmp, command, min, false);
1601
1602 talloc_free(tmp);
1603
1604 return ret;
1605 }
1606 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001607 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001608 else if (CMD_RANGE(str))
1609 {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001610 if (vty_cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001611 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001612 }
1613#ifdef HAVE_IPV6
1614 else if (CMD_IPV6(str))
1615 {
1616 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001617 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001618 }
1619 else if (CMD_IPV6_PREFIX(str))
1620 {
1621 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001622 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001623 }
1624#endif /* HAVE_IPV6 */
1625 else if (CMD_IPV4(str))
1626 {
1627 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001628 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001629 }
1630 else if (CMD_IPV4_PREFIX(str))
1631 {
1632 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001633 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001634 }
1635 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001636 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001637 else if (strncmp(command, str, strlen(command)) == 0)
1638 {
1639 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001640 return EXACT_MATCH;
1641 else if (PARTLY_MATCH >= min)
1642 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001643 }
1644
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001645 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001646}
1647
1648/* Filter vector at the specified index and by the given command string, to
1649 * the desired matching level (thus allowing part matches), and return match
1650 * type flag.
1651 */
1652static enum match_type
1653cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001654{
1655 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001656 struct cmd_element *cmd_element;
1657 enum match_type match_type;
1658 vector descvec;
1659 struct desc *desc;
1660
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001661 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001662
1663 /* If command and cmd_element string does not match set NULL to vector */
1664 for (i = 0; i < vector_active(v); i++)
1665 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001666 if (index >= vector_active(cmd_element->strvec))
1667 vector_slot(v, i) = NULL;
1668 else {
1669 unsigned int j;
1670 int matched = 0;
1671
1672 descvec =
1673 vector_slot(cmd_element->strvec, index);
1674
1675 for (j = 0; j < vector_active(descvec); j++)
1676 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001677 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001678
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001679 ret = cmd_match (desc->cmd, command, level, true);
1680
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001681 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001682 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001683
1684 if (match_type < ret)
1685 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001686 }
1687 if (!matched)
1688 vector_slot(v, i) = NULL;
1689 }
1690 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001691
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001692 if (match_type == NO_MATCH)
1693 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001694
1695 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1696 * go again and filter out commands whose argument (at this index) is
1697 * 'weaker'. E.g., if we have 2 commands:
1698 *
1699 * foo bar <1-255>
1700 * foo bar BLAH
1701 *
1702 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001703 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001704 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1705 *
1706 * If we don't do a 2nd pass and filter it out, the higher-layers will
1707 * consider this to be ambiguous.
1708 */
1709 for (i = 0; i < vector_active(v); i++)
1710 if ((cmd_element = vector_slot(v, i)) != NULL) {
1711 if (index >= vector_active(cmd_element->strvec))
1712 vector_slot(v, i) = NULL;
1713 else {
1714 unsigned int j;
1715 int matched = 0;
1716
1717 descvec =
1718 vector_slot(cmd_element->strvec, index);
1719
1720 for (j = 0; j < vector_active(descvec); j++)
1721 if ((desc = vector_slot(descvec, j))) {
1722 enum match_type ret;
1723
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001724 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001725
1726 if (ret >= match_type)
1727 matched++;
1728 }
1729 if (!matched)
1730 vector_slot(v, i) = NULL;
1731 }
1732 }
1733
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001734 return match_type;
1735}
1736
1737/* Check ambiguous match */
1738static int
1739is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1740{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001741 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001742 unsigned int i;
1743 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001744 struct cmd_element *cmd_element;
1745 const char *matched = NULL;
1746 vector descvec;
1747 struct desc *desc;
1748
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001749 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1750 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1751 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1752 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1753 * that case, the string must remain allocated until this function exits or another match comes
1754 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1755 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1756 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1757 void *cmd_deopt_ctx = NULL;
1758
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001759 for (i = 0; i < vector_active(v); i++) {
1760 cmd_element = vector_slot(v, i);
1761 if (!cmd_element)
1762 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001763
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001764 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001765
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001766 descvec = vector_slot(cmd_element->strvec, index);
1767
1768 for (j = 0; j < vector_active(descvec); j++) {
1769 desc = vector_slot(descvec, j);
1770 if (!desc)
1771 continue;
1772
1773 enum match_type mtype;
1774 const char *str = desc->cmd;
1775
1776 if (CMD_OPTION(str)) {
1777 if (!cmd_deopt_ctx)
1778 cmd_deopt_ctx =
1779 talloc_named_const(tall_vty_cmd_ctx, 0,
1780 __func__);
1781 str = cmd_deopt(cmd_deopt_ctx, str);
1782 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001783 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001784 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001785
1786 switch (type) {
1787 case EXACT_MATCH:
1788 if (!(CMD_VARIABLE (str))
1789 && strcmp(command, str) == 0)
1790 match++;
1791 break;
1792 case PARTLY_MATCH:
1793 if (!(CMD_VARIABLE (str))
1794 && strncmp(command, str, strlen (command)) == 0)
1795 {
1796 if (matched
1797 && strcmp(matched,
1798 str) != 0) {
1799 ret = 1; /* There is ambiguous match. */
1800 goto free_and_return;
1801 } else
1802 matched = str;
1803 match++;
1804 }
1805 break;
1806 case RANGE_MATCH:
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001807 if (vty_cmd_range_match
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001808 (str, command)) {
1809 if (matched
1810 && strcmp(matched,
1811 str) != 0) {
1812 ret = 1;
1813 goto free_and_return;
1814 } else
1815 matched = str;
1816 match++;
1817 }
1818 break;
1819#ifdef HAVE_IPV6
1820 case IPV6_MATCH:
1821 if (CMD_IPV6(str))
1822 match++;
1823 break;
1824 case IPV6_PREFIX_MATCH:
1825 if ((mtype =
1826 cmd_ipv6_prefix_match
1827 (command)) != NO_MATCH) {
1828 if (mtype == PARTLY_MATCH) {
1829 ret = 2; /* There is incomplete match. */
1830 goto free_and_return;
1831 }
1832
1833 match++;
1834 }
1835 break;
1836#endif /* HAVE_IPV6 */
1837 case IPV4_MATCH:
1838 if (CMD_IPV4(str))
1839 match++;
1840 break;
1841 case IPV4_PREFIX_MATCH:
1842 if ((mtype =
1843 cmd_ipv4_prefix_match
1844 (command)) != NO_MATCH) {
1845 if (mtype == PARTLY_MATCH) {
1846 ret = 2; /* There is incomplete match. */
1847 goto free_and_return;
1848 }
1849
1850 match++;
1851 }
1852 break;
1853 case EXTEND_MATCH:
1854 if (CMD_VARIABLE (str))
1855 match++;
1856 break;
1857 case NO_MATCH:
1858 default:
1859 break;
1860 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001861 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001862 if (!match)
1863 vector_slot(v, i) = NULL;
1864 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001865
1866free_and_return:
1867 if (cmd_deopt_ctx)
1868 talloc_free(cmd_deopt_ctx);
1869 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001870}
1871
1872/* If src matches dst return dst string, otherwise return NULL */
1873static const char *cmd_entry_function(const char *src, const char *dst)
1874{
1875 /* Skip variable arguments. */
1876 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1877 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1878 return NULL;
1879
1880 /* In case of 'command \t', given src is NULL string. */
1881 if (src == NULL)
1882 return dst;
1883
1884 /* Matched with input string. */
1885 if (strncmp(src, dst, strlen(src)) == 0)
1886 return dst;
1887
1888 return NULL;
1889}
1890
1891/* If src matches dst return dst string, otherwise return NULL */
1892/* This version will return the dst string always if it is
1893 CMD_VARIABLE for '?' key processing */
1894static const char *cmd_entry_function_desc(const char *src, const char *dst)
1895{
1896 if (CMD_VARARG(dst))
1897 return dst;
1898
1899 if (CMD_RANGE(dst)) {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001900 if (vty_cmd_range_match(dst, src))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001901 return dst;
1902 else
1903 return NULL;
1904 }
1905#ifdef HAVE_IPV6
1906 if (CMD_IPV6(dst)) {
1907 if (cmd_ipv6_match(src))
1908 return dst;
1909 else
1910 return NULL;
1911 }
1912
1913 if (CMD_IPV6_PREFIX(dst)) {
1914 if (cmd_ipv6_prefix_match(src))
1915 return dst;
1916 else
1917 return NULL;
1918 }
1919#endif /* HAVE_IPV6 */
1920
1921 if (CMD_IPV4(dst)) {
1922 if (cmd_ipv4_match(src))
1923 return dst;
1924 else
1925 return NULL;
1926 }
1927
1928 if (CMD_IPV4_PREFIX(dst)) {
1929 if (cmd_ipv4_prefix_match(src))
1930 return dst;
1931 else
1932 return NULL;
1933 }
1934
1935 /* Optional or variable commands always match on '?' */
1936 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1937 return dst;
1938
1939 /* In case of 'command \t', given src is NULL string. */
1940 if (src == NULL)
1941 return dst;
1942
1943 if (strncmp(src, dst, strlen(src)) == 0)
1944 return dst;
1945 else
1946 return NULL;
1947}
1948
1949/* Check same string element existence. If it isn't there return
1950 1. */
1951static int cmd_unique_string(vector v, const char *str)
1952{
1953 unsigned int i;
1954 char *match;
1955
1956 for (i = 0; i < vector_active(v); i++)
1957 if ((match = vector_slot(v, i)) != NULL)
1958 if (strcmp(match, str) == 0)
1959 return 0;
1960 return 1;
1961}
1962
1963/* Compare string to description vector. If there is same string
1964 return 1 else return 0. */
1965static int desc_unique_string(vector v, const char *str)
1966{
1967 unsigned int i;
1968 struct desc *desc;
1969
1970 for (i = 0; i < vector_active(v); i++)
1971 if ((desc = vector_slot(v, i)) != NULL)
1972 if (strcmp(desc->cmd, str) == 0)
1973 return 1;
1974 return 0;
1975}
1976
1977static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1978{
1979 if (first_word != NULL &&
1980 node != AUTH_NODE &&
1981 node != VIEW_NODE &&
1982 node != AUTH_ENABLE_NODE &&
1983 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1984 return 1;
1985 return 0;
1986}
1987
1988/* '?' describe command support. */
1989static vector
1990cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1991{
1992 unsigned int i;
1993 vector cmd_vector;
1994#define INIT_MATCHVEC_SIZE 10
1995 vector matchvec;
1996 struct cmd_element *cmd_element;
1997 unsigned int index;
1998 int ret;
1999 enum match_type match;
2000 char *command;
2001 static struct desc desc_cr = { "<cr>", "" };
2002
2003 /* Set index. */
2004 if (vector_active(vline) == 0) {
2005 *status = CMD_ERR_NO_MATCH;
2006 return NULL;
2007 } else
2008 index = vector_active(vline) - 1;
2009
2010 /* Make copy vector of current node's command vector. */
2011 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2012
2013 /* Prepare match vector */
2014 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2015
2016 /* Filter commands. */
2017 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002018 for (i = 0; i < index; i++) {
2019 command = vector_slot(vline, i);
2020 if (!command)
2021 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002022
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002023 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002024
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002025 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002026 struct cmd_element *cmd_element;
2027 vector descvec;
2028 unsigned int j, k;
2029
2030 for (j = 0; j < vector_active(cmd_vector); j++)
2031 if ((cmd_element =
2032 vector_slot(cmd_vector, j)) != NULL
2033 &&
2034 (vector_active(cmd_element->strvec))) {
2035 descvec =
2036 vector_slot(cmd_element->
2037 strvec,
2038 vector_active
2039 (cmd_element->
2040 strvec) - 1);
2041 for (k = 0;
2042 k < vector_active(descvec);
2043 k++) {
2044 struct desc *desc =
2045 vector_slot(descvec,
2046 k);
2047 vector_set(matchvec,
2048 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002049 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002050 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002051
Harald Welte80d30fe2013-02-12 11:08:57 +01002052 vector_set(matchvec, &desc_cr);
2053 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002054
Harald Welte80d30fe2013-02-12 11:08:57 +01002055 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002056 }
2057
Harald Welte80d30fe2013-02-12 11:08:57 +01002058 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2059 match)) == 1) {
2060 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002061 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002062 *status = CMD_ERR_AMBIGUOUS;
2063 return NULL;
2064 } else if (ret == 2) {
2065 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002066 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002067 *status = CMD_ERR_NO_MATCH;
2068 return NULL;
2069 }
2070 }
2071
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002072 /* Prepare match vector */
2073 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2074
2075 /* Make sure that cmd_vector is filtered based on current word */
2076 command = vector_slot(vline, index);
2077 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002078 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002079
2080 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002081 for (i = 0; i < vector_active(cmd_vector); i++) {
2082 const char *string = NULL;
2083 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002084
Harald Welte80d30fe2013-02-12 11:08:57 +01002085 cmd_element = vector_slot(cmd_vector, i);
2086 if (!cmd_element)
2087 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002088
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002089 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2090 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002091 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002092 continue;
2093
Harald Welte80d30fe2013-02-12 11:08:57 +01002094 strvec = cmd_element->strvec;
2095
2096 /* if command is NULL, index may be equal to vector_active */
2097 if (command && index >= vector_active(strvec))
2098 vector_slot(cmd_vector, i) = NULL;
2099 else {
2100 /* Check if command is completed. */
2101 if (command == NULL
2102 && index == vector_active(strvec)) {
2103 string = "<cr>";
2104 if (!desc_unique_string(matchvec, string))
2105 vector_set(matchvec, &desc_cr);
2106 } else {
2107 unsigned int j;
2108 vector descvec = vector_slot(strvec, index);
2109 struct desc *desc;
2110
2111 for (j = 0; j < vector_active(descvec); j++) {
2112 desc = vector_slot(descvec, j);
2113 if (!desc)
2114 continue;
2115 string = cmd_entry_function_desc
2116 (command, desc->cmd);
2117 if (!string)
2118 continue;
2119 /* Uniqueness check */
2120 if (!desc_unique_string(matchvec, string))
2121 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002122 }
2123 }
2124 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002125 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002126 vector_free(cmd_vector);
2127
2128 if (vector_slot(matchvec, 0) == NULL) {
2129 vector_free(matchvec);
2130 *status = CMD_ERR_NO_MATCH;
2131 } else
2132 *status = CMD_SUCCESS;
2133
2134 return matchvec;
2135}
2136
2137vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2138{
2139 vector ret;
2140
2141 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2142 enum node_type onode;
2143 vector shifted_vline;
2144 unsigned int index;
2145
2146 onode = vty->node;
2147 vty->node = ENABLE_NODE;
2148 /* We can try it on enable node, cos' the vty is authenticated */
2149
2150 shifted_vline = vector_init(vector_count(vline));
2151 /* use memcpy? */
2152 for (index = 1; index < vector_active(vline); index++) {
2153 vector_set_index(shifted_vline, index - 1,
2154 vector_lookup(vline, index));
2155 }
2156
2157 ret = cmd_describe_command_real(shifted_vline, vty, status);
2158
2159 vector_free(shifted_vline);
2160 vty->node = onode;
2161 return ret;
2162 }
2163
2164 return cmd_describe_command_real(vline, vty, status);
2165}
2166
2167/* Check LCD of matched command. */
2168static int cmd_lcd(char **matched)
2169{
2170 int i;
2171 int j;
2172 int lcd = -1;
2173 char *s1, *s2;
2174 char c1, c2;
2175
2176 if (matched[0] == NULL || matched[1] == NULL)
2177 return 0;
2178
2179 for (i = 1; matched[i] != NULL; i++) {
2180 s1 = matched[i - 1];
2181 s2 = matched[i];
2182
2183 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2184 if (c1 != c2)
2185 break;
2186
2187 if (lcd < 0)
2188 lcd = j;
2189 else {
2190 if (lcd > j)
2191 lcd = j;
2192 }
2193 }
2194 return lcd;
2195}
2196
2197/* Command line completion support. */
2198static char **cmd_complete_command_real(vector vline, struct vty *vty,
2199 int *status)
2200{
2201 unsigned int i;
2202 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2203#define INIT_MATCHVEC_SIZE 10
2204 vector matchvec;
2205 struct cmd_element *cmd_element;
2206 unsigned int index;
2207 char **match_str;
2208 struct desc *desc;
2209 vector descvec;
2210 char *command;
2211 int lcd;
2212
2213 if (vector_active(vline) == 0) {
2214 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002215 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002216 return NULL;
2217 } else
2218 index = vector_active(vline) - 1;
2219
2220 /* First, filter by preceeding command string */
2221 for (i = 0; i < index; i++)
2222 if ((command = vector_slot(vline, i))) {
2223 enum match_type match;
2224 int ret;
2225
2226 /* First try completion match, if there is exactly match return 1 */
2227 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002228 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002229
2230 /* If there is exact match then filter ambiguous match else check
2231 ambiguousness. */
2232 if ((ret =
2233 is_cmd_ambiguous(command, cmd_vector, i,
2234 match)) == 1) {
2235 vector_free(cmd_vector);
2236 *status = CMD_ERR_AMBIGUOUS;
2237 return NULL;
2238 }
2239 /*
2240 else if (ret == 2)
2241 {
2242 vector_free (cmd_vector);
2243 *status = CMD_ERR_NO_MATCH;
2244 return NULL;
2245 }
2246 */
2247 }
2248
2249 /* Prepare match vector. */
2250 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2251
2252 /* Now we got into completion */
2253 for (i = 0; i < vector_active(cmd_vector); i++)
2254 if ((cmd_element = vector_slot(cmd_vector, i))) {
2255 const char *string;
2256 vector strvec = cmd_element->strvec;
2257
2258 /* Check field length */
2259 if (index >= vector_active(strvec))
2260 vector_slot(cmd_vector, i) = NULL;
2261 else {
2262 unsigned int j;
2263
2264 descvec = vector_slot(strvec, index);
2265 for (j = 0; j < vector_active(descvec); j++)
2266 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002267 const char *cmd = desc->cmd;
2268 char *tmp = NULL;
2269
2270 if (CMD_OPTION(desc->cmd)) {
2271 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2272 cmd = tmp;
2273 }
2274 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002275 if (cmd_unique_string (matchvec, string))
2276 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002277 if (tmp)
2278 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002279 }
2280 }
2281 }
2282
2283 /* We don't need cmd_vector any more. */
2284 vector_free(cmd_vector);
2285
2286 /* No matched command */
2287 if (vector_slot(matchvec, 0) == NULL) {
2288 vector_free(matchvec);
2289
2290 /* In case of 'command \t' pattern. Do you need '?' command at
2291 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002292 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002293 *status = CMD_ERR_NOTHING_TODO;
2294 else
2295 *status = CMD_ERR_NO_MATCH;
2296 return NULL;
2297 }
2298
2299 /* Only one matched */
2300 if (vector_slot(matchvec, 1) == NULL) {
2301 match_str = (char **)matchvec->index;
2302 vector_only_wrapper_free(matchvec);
2303 *status = CMD_COMPLETE_FULL_MATCH;
2304 return match_str;
2305 }
2306 /* Make it sure last element is NULL. */
2307 vector_set(matchvec, NULL);
2308
2309 /* Check LCD of matched strings. */
2310 if (vector_slot(vline, index) != NULL) {
2311 lcd = cmd_lcd((char **)matchvec->index);
2312
2313 if (lcd) {
2314 int len = strlen(vector_slot(vline, index));
2315
2316 if (len < lcd) {
2317 char *lcdstr;
2318
2319 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2320 "complete-lcdstr");
2321 memcpy(lcdstr, matchvec->index[0], lcd);
2322 lcdstr[lcd] = '\0';
2323
2324 /* match_str = (char **) &lcdstr; */
2325
2326 /* Free matchvec. */
2327 for (i = 0; i < vector_active(matchvec); i++) {
2328 if (vector_slot(matchvec, i))
2329 talloc_free(vector_slot(matchvec, i));
2330 }
2331 vector_free(matchvec);
2332
2333 /* Make new matchvec. */
2334 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2335 vector_set(matchvec, lcdstr);
2336 match_str = (char **)matchvec->index;
2337 vector_only_wrapper_free(matchvec);
2338
2339 *status = CMD_COMPLETE_MATCH;
2340 return match_str;
2341 }
2342 }
2343 }
2344
2345 match_str = (char **)matchvec->index;
2346 vector_only_wrapper_free(matchvec);
2347 *status = CMD_COMPLETE_LIST_MATCH;
2348 return match_str;
2349}
2350
2351char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2352{
2353 char **ret;
2354
2355 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2356 enum node_type onode;
2357 vector shifted_vline;
2358 unsigned int index;
2359
2360 onode = vty->node;
2361 vty->node = ENABLE_NODE;
2362 /* We can try it on enable node, cos' the vty is authenticated */
2363
2364 shifted_vline = vector_init(vector_count(vline));
2365 /* use memcpy? */
2366 for (index = 1; index < vector_active(vline); index++) {
2367 vector_set_index(shifted_vline, index - 1,
2368 vector_lookup(vline, index));
2369 }
2370
2371 ret = cmd_complete_command_real(shifted_vline, vty, status);
2372
2373 vector_free(shifted_vline);
2374 vty->node = onode;
2375 return ret;
2376 }
2377
2378 return cmd_complete_command_real(vline, vty, status);
2379}
2380
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002381static struct vty_parent_node *vty_parent(struct vty *vty)
2382{
2383 return llist_first_entry_or_null(&vty->parent_nodes,
2384 struct vty_parent_node,
2385 entry);
2386}
2387
2388static bool vty_pop_parent(struct vty *vty)
2389{
2390 struct vty_parent_node *parent = vty_parent(vty);
2391 if (!parent)
2392 return false;
2393 llist_del(&parent->entry);
2394 vty->node = parent->node;
2395 vty->priv = parent->priv;
2396 if (vty->indent)
2397 talloc_free(vty->indent);
2398 vty->indent = parent->indent;
2399 talloc_free(parent);
2400 return true;
2401}
2402
2403static void vty_clear_parents(struct vty *vty)
2404{
2405 while (vty_pop_parent(vty));
2406}
2407
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002408/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002409/*
2410 * This function MUST eventually converge on a node when called repeatedly,
2411 * there must not be any cycles.
2412 * All 'config' nodes shall converge on CONFIG_NODE.
2413 * All other 'enable' nodes shall converge on ENABLE_NODE.
2414 * All 'view' only nodes shall converge on VIEW_NODE.
2415 * All other nodes shall converge on themselves or it must be ensured,
2416 * that the user's rights are not extended anyhow by calling this function.
2417 *
2418 * Note that these requirements also apply to all functions that are used
2419 * as go_parent_cb.
2420 * Note also that this function relies on the is_config_child callback to
2421 * recognize non-config nodes if go_parent_cb is not set.
2422 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002423int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002424{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002425 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002426 case AUTH_NODE:
2427 case VIEW_NODE:
2428 case ENABLE_NODE:
2429 case CONFIG_NODE:
2430 vty_clear_parents(vty);
2431 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002432
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002433 case AUTH_ENABLE_NODE:
2434 vty->node = VIEW_NODE;
2435 vty_clear_parents(vty);
2436 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002437
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002438 default:
2439 if (host.app_info->go_parent_cb)
2440 host.app_info->go_parent_cb(vty);
2441 vty_pop_parent(vty);
2442 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002443 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002444
2445 return vty->node;
2446}
2447
2448/* Execute command by argument vline vector. */
2449static int
2450cmd_execute_command_real(vector vline, struct vty *vty,
2451 struct cmd_element **cmd)
2452{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002453 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002454 unsigned int index;
2455 vector cmd_vector;
2456 struct cmd_element *cmd_element;
2457 struct cmd_element *matched_element;
2458 unsigned int matched_count, incomplete_count;
2459 int argc;
2460 const char *argv[CMD_ARGC_MAX];
2461 enum match_type match = 0;
2462 int varflag;
2463 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002464 int rc;
2465 /* Used for temporary storage of cmd_deopt() allocated arguments during
2466 argv[] generation */
2467 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002468
2469 /* Make copy of command elements. */
2470 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2471
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002472 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002473 if ((command = vector_slot(vline, index))) {
2474 int ret;
2475
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002476 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002477 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002478
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002479 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002480 break;
2481
2482 ret =
2483 is_cmd_ambiguous(command, cmd_vector, index, match);
2484
2485 if (ret == 1) {
2486 vector_free(cmd_vector);
2487 return CMD_ERR_AMBIGUOUS;
2488 } else if (ret == 2) {
2489 vector_free(cmd_vector);
2490 return CMD_ERR_NO_MATCH;
2491 }
2492 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002493 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002494
2495 /* Check matched count. */
2496 matched_element = NULL;
2497 matched_count = 0;
2498 incomplete_count = 0;
2499
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002500 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002501 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002502 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002503 || index >= cmd_element->cmdsize) {
2504 matched_element = cmd_element;
2505#if 0
2506 printf("DEBUG: %s\n", cmd_element->string);
2507#endif
2508 matched_count++;
2509 } else {
2510 incomplete_count++;
2511 }
2512 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002513 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002514
2515 /* Finish of using cmd_vector. */
2516 vector_free(cmd_vector);
2517
2518 /* To execute command, matched_count must be 1. */
2519 if (matched_count == 0) {
2520 if (incomplete_count)
2521 return CMD_ERR_INCOMPLETE;
2522 else
2523 return CMD_ERR_NO_MATCH;
2524 }
2525
2526 if (matched_count > 1)
2527 return CMD_ERR_AMBIGUOUS;
2528
2529 /* Argument treatment */
2530 varflag = 0;
2531 argc = 0;
2532
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002533 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2534
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002535 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002536 if (argc == CMD_ARGC_MAX) {
2537 rc = CMD_ERR_EXEED_ARGC_MAX;
2538 goto rc_free_deopt_ctx;
2539 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002540 if (varflag) {
2541 argv[argc++] = vector_slot(vline, i);
2542 continue;
2543 }
2544
2545 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002546 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002547
2548 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002549 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002550 struct desc *desc = vector_slot(descvec, 0);
2551
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002552 if (CMD_OPTION(desc->cmd)) {
2553 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2554 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2555 } else {
2556 tmp_cmd = desc->cmd;
2557 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002558
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002559 if (CMD_VARARG(tmp_cmd))
2560 varflag = 1;
2561 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002562 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002563 else if (CMD_OPTION(desc->cmd))
2564 argv[argc++] = tmp_cmd;
2565 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002566 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002567 /* multi choice argument. look up which choice
2568 the user meant (can only be one after
2569 filtering and checking for ambigous). For instance,
2570 if user typed "th" for "(two|three)" arg, we
2571 want to pass "three" in argv[]. */
2572 for (j = 0; j < vector_active(descvec); j++) {
2573 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002574 if (!desc)
2575 continue;
2576 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2577 continue;
2578 if (CMD_OPTION(desc->cmd)) {
2579 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2580 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2581 } else {
2582 tmp_cmd = desc->cmd;
2583 }
2584
2585 if(CMD_VARIABLE(tmp_cmd)) {
2586 argv[argc++] = vector_slot(vline, i);
2587 } else {
2588 argv[argc++] = tmp_cmd;
2589 }
2590 break;
2591 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002592 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002593 }
2594
2595 /* For vtysh execution. */
2596 if (cmd)
2597 *cmd = matched_element;
2598
2599 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002600 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002601 else {
2602 /* Execute matched command. */
2603 struct vty_parent_node this_node = {
2604 .node = vty->node,
2605 .priv = vty->priv,
2606 .indent = vty->indent,
2607 };
2608 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002609 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002610
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002611 /* If we have stepped down into a child node, push a parent frame.
2612 * The causality is such: we don't expect every single node entry implementation to push
2613 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2614 * a parent node. Hence if the node changed without the parent node changing, we must
2615 * have stepped into a child node. */
2616 if (vty->node != this_node.node && parent == vty_parent(vty)
2617 && vty->node > CONFIG_NODE) {
2618 /* Push the parent node. */
2619 parent = talloc_zero(vty, struct vty_parent_node);
2620 *parent = this_node;
2621 llist_add(&parent->entry, &vty->parent_nodes);
2622 }
2623 }
2624
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002625rc_free_deopt_ctx:
2626 /* Now after we called the command func, we can free temporary strings */
2627 talloc_free(cmd_deopt_ctx);
2628 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002629}
2630
2631int
2632cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2633 int vtysh)
2634{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002635 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002636 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002637
2638 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002639
2640 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2641 vector shifted_vline;
2642 unsigned int index;
2643
2644 vty->node = ENABLE_NODE;
2645 /* We can try it on enable node, cos' the vty is authenticated */
2646
2647 shifted_vline = vector_init(vector_count(vline));
2648 /* use memcpy? */
2649 for (index = 1; index < vector_active(vline); index++) {
2650 vector_set_index(shifted_vline, index - 1,
2651 vector_lookup(vline, index));
2652 }
2653
2654 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2655
2656 vector_free(shifted_vline);
2657 vty->node = onode;
2658 return ret;
2659 }
2660
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002661 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002662}
2663
2664/* Execute command by argument readline. */
2665int
2666cmd_execute_command_strict(vector vline, struct vty *vty,
2667 struct cmd_element **cmd)
2668{
2669 unsigned int i;
2670 unsigned int index;
2671 vector cmd_vector;
2672 struct cmd_element *cmd_element;
2673 struct cmd_element *matched_element;
2674 unsigned int matched_count, incomplete_count;
2675 int argc;
2676 const char *argv[CMD_ARGC_MAX];
2677 int varflag;
2678 enum match_type match = 0;
2679 char *command;
2680
2681 /* Make copy of command element */
2682 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2683
2684 for (index = 0; index < vector_active(vline); index++)
2685 if ((command = vector_slot(vline, index))) {
2686 int ret;
2687
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002688 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002689 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002690
2691 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002692 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002693 break;
2694
2695 ret =
2696 is_cmd_ambiguous(command, cmd_vector, index, match);
2697 if (ret == 1) {
2698 vector_free(cmd_vector);
2699 return CMD_ERR_AMBIGUOUS;
2700 }
2701 if (ret == 2) {
2702 vector_free(cmd_vector);
2703 return CMD_ERR_NO_MATCH;
2704 }
2705 }
2706
2707 /* Check matched count. */
2708 matched_element = NULL;
2709 matched_count = 0;
2710 incomplete_count = 0;
2711 for (i = 0; i < vector_active(cmd_vector); i++)
2712 if (vector_slot(cmd_vector, i) != NULL) {
2713 cmd_element = vector_slot(cmd_vector, i);
2714
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002715 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002716 || index >= cmd_element->cmdsize) {
2717 matched_element = cmd_element;
2718 matched_count++;
2719 } else
2720 incomplete_count++;
2721 }
2722
2723 /* Finish of using cmd_vector. */
2724 vector_free(cmd_vector);
2725
2726 /* To execute command, matched_count must be 1. */
2727 if (matched_count == 0) {
2728 if (incomplete_count)
2729 return CMD_ERR_INCOMPLETE;
2730 else
2731 return CMD_ERR_NO_MATCH;
2732 }
2733
2734 if (matched_count > 1)
2735 return CMD_ERR_AMBIGUOUS;
2736
2737 /* Argument treatment */
2738 varflag = 0;
2739 argc = 0;
2740
2741 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002742 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002743 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002744 if (varflag) {
2745 argv[argc++] = vector_slot(vline, i);
2746 continue;
2747 }
2748
2749 vector descvec = vector_slot(matched_element->strvec, i);
2750
2751 if (vector_active(descvec) == 1) {
2752 struct desc *desc = vector_slot(descvec, 0);
2753
2754 if (CMD_VARARG(desc->cmd))
2755 varflag = 1;
2756
2757 if (varflag || CMD_VARIABLE(desc->cmd)
2758 || CMD_OPTION(desc->cmd))
2759 argv[argc++] = vector_slot(vline, i);
2760 } else {
2761 argv[argc++] = vector_slot(vline, i);
2762 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002763 }
2764
2765 /* For vtysh execution. */
2766 if (cmd)
2767 *cmd = matched_element;
2768
2769 if (matched_element->daemon)
2770 return CMD_SUCCESS_DAEMON;
2771
2772 /* Now execute matched command */
2773 return (*matched_element->func) (matched_element, vty, argc, argv);
2774}
2775
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002776static inline size_t len(const char *str)
2777{
2778 return str? strlen(str) : 0;
2779}
2780
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002781/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2782 * is longer than b, a must start with exactly b, and vice versa.
2783 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2784 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002785static int indent_cmp(const char *a, const char *b)
2786{
2787 size_t al, bl;
2788 al = len(a);
2789 bl = len(b);
2790 if (al > bl) {
2791 if (bl && strncmp(a, b, bl) != 0)
2792 return EINVAL;
2793 return 1;
2794 }
2795 /* al <= bl */
2796 if (al && strncmp(a, b, al) != 0)
2797 return EINVAL;
2798 return (al < bl)? -1 : 0;
2799}
2800
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002801/* Configration make from file. */
2802int config_from_file(struct vty *vty, FILE * fp)
2803{
2804 int ret;
2805 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002806 char *indent;
2807 int cmp;
2808 struct vty_parent_node this_node;
2809 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002810
2811 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002812 indent = NULL;
2813 vline = NULL;
2814 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002815
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002816 if (ret != CMD_SUCCESS)
2817 goto return_invalid_indent;
2818
2819 /* In case of comment or empty line */
2820 if (vline == NULL) {
2821 if (indent) {
2822 talloc_free(indent);
2823 indent = NULL;
2824 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002825 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002826 }
2827
Neels Hofmeyr43063632017-09-19 23:54:01 +02002828 /* We have a nonempty line. */
2829 if (!vty->indent) {
2830 /* We have just entered a node and expecting the first child to come up; but we
2831 * may also skip right back to a parent or ancestor level. */
2832 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002833
Neels Hofmeyr43063632017-09-19 23:54:01 +02002834 /* If there is no parent, record any indentation we encounter. */
2835 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2836
2837 if (cmp == EINVAL)
2838 goto return_invalid_indent;
2839
2840 if (cmp <= 0) {
2841 /* We have gone right back to the parent level or higher, we are skipping
2842 * this child node level entirely. Pop the parent to go back to a node
2843 * that was actually there (to reinstate vty->indent) and re-use below
2844 * go-parent while-loop to find an accurate match of indent in the node
2845 * ancestry. */
2846 vty_go_parent(vty);
2847 } else {
2848 /* The indent is deeper than the just entered parent, record the new
2849 * indentation characters. */
2850 vty->indent = talloc_strdup(vty, indent);
2851 /* This *is* the new indentation. */
2852 cmp = 0;
2853 }
2854 } else {
2855 /* There is a known indentation for this node level, validate and detect node
2856 * exits. */
2857 cmp = indent_cmp(indent, vty->indent);
2858 if (cmp == EINVAL)
2859 goto return_invalid_indent;
2860 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002861
2862 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2863 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2864 while (cmp < 0) {
2865 vty_go_parent(vty);
2866 cmp = indent_cmp(indent, vty->indent);
2867 if (cmp == EINVAL)
2868 goto return_invalid_indent;
2869 }
2870
2871 /* More indent without having entered a child node level? Either the parent node's indent
2872 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2873 * or the indentation increased even though the vty command didn't enter a child. */
2874 if (cmp > 0)
2875 goto return_invalid_indent;
2876
2877 /* Remember the current node before the command possibly changes it. */
2878 this_node = (struct vty_parent_node){
2879 .node = vty->node,
2880 .priv = vty->priv,
2881 .indent = vty->indent,
2882 };
2883
2884 parent = vty_parent(vty);
2885 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002886 cmd_free_strvec(vline);
2887
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002888 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002889 if (indent) {
2890 talloc_free(indent);
2891 indent = NULL;
2892 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002893 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002894 }
2895
2896 /* If we have stepped down into a child node, push a parent frame.
2897 * The causality is such: we don't expect every single node entry implementation to push
2898 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2899 * a parent node. Hence if the node changed without the parent node changing, we must
2900 * have stepped into a child node (and now expect a deeper indent). */
2901 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2902 /* Push the parent node. */
2903 parent = talloc_zero(vty, struct vty_parent_node);
2904 *parent = this_node;
2905 llist_add(&parent->entry, &vty->parent_nodes);
2906
2907 /* The current talloc'ed vty->indent string will now be owned by this parent
2908 * struct. Indicate that we don't know what deeper indent characters the user
2909 * will choose. */
2910 vty->indent = NULL;
2911 }
2912
2913 if (indent) {
2914 talloc_free(indent);
2915 indent = NULL;
2916 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002917 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002918 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2919 while (vty_parent(vty))
2920 vty_go_parent(vty);
2921
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002922 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002923
2924return_invalid_indent:
2925 if (vline)
2926 cmd_free_strvec(vline);
2927 if (indent) {
2928 talloc_free(indent);
2929 indent = NULL;
2930 }
2931 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002932}
2933
2934/* Configration from terminal */
2935DEFUN(config_terminal,
2936 config_terminal_cmd,
2937 "configure terminal",
2938 "Configuration from vty interface\n" "Configuration terminal\n")
2939{
2940 if (vty_config_lock(vty))
2941 vty->node = CONFIG_NODE;
2942 else {
2943 vty_out(vty, "VTY configuration is locked by other VTY%s",
2944 VTY_NEWLINE);
2945 return CMD_WARNING;
2946 }
2947 return CMD_SUCCESS;
2948}
2949
2950/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002951DEFUN(enable, config_enable_cmd,
2952 "enable [expert-mode]",
2953 "Turn on privileged mode command\n"
2954 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002955{
2956 /* If enable password is NULL, change to ENABLE_NODE */
2957 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2958 vty->type == VTY_SHELL_SERV)
2959 vty->node = ENABLE_NODE;
2960 else
2961 vty->node = AUTH_ENABLE_NODE;
2962
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002963 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002964
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002965 return CMD_SUCCESS;
2966}
2967
2968/* Disable command */
2969DEFUN(disable,
2970 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2971{
2972 if (vty->node == ENABLE_NODE)
2973 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002974
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002975 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002976
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002977 return CMD_SUCCESS;
2978}
2979
2980/* Down vty node level. */
2981gDEFUN(config_exit,
2982 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2983{
2984 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002985 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002986 case VIEW_NODE:
2987 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002988 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002989 break;
2990 case CONFIG_NODE:
2991 vty->node = ENABLE_NODE;
2992 vty_config_unlock(vty);
2993 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002994 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002995 if (vty->node > CONFIG_NODE)
2996 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002997 break;
2998 }
2999 return CMD_SUCCESS;
3000}
3001
3002/* End of configuration. */
3003 gDEFUN(config_end,
3004 config_end_cmd, "end", "End current mode and change to enable mode.")
3005{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003006 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02003007 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003008
3009 /* Repeatedly call go_parent until a top node is reached. */
3010 while (vty->node > CONFIG_NODE) {
3011 if (vty->node == last_node) {
3012 /* Ensure termination, this shouldn't happen. */
3013 break;
3014 }
3015 last_node = vty->node;
3016 vty_go_parent(vty);
3017 }
3018
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003019 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003020 if (vty->node > ENABLE_NODE)
3021 vty->node = ENABLE_NODE;
3022 vty->index = NULL;
3023 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003024 }
3025 return CMD_SUCCESS;
3026}
3027
Oliver Smith8a482fd2021-07-12 18:18:51 +02003028DEFUN(shutdown,
3029 shutdown_cmd, "shutdown", "Request a shutdown of the program\n")
3030{
3031 LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n");
3032 vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE);
3033
3034 /* Same exit path as if it was killed by the service manager */
3035 kill(getpid(), SIGTERM);
3036
3037 return CMD_SUCCESS;
3038}
3039
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003040/* Show version. */
3041DEFUN(show_version,
3042 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3043{
Harald Welte237f6242010-05-25 23:00:45 +02003044 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3045 host.app_info->version,
3046 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3047 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003048
3049 return CMD_SUCCESS;
3050}
3051
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003052DEFUN(show_online_help,
3053 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3054{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003055 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003056 return CMD_SUCCESS;
3057}
3058
Oliver Smith0c78bc62021-07-12 17:28:36 +02003059DEFUN(show_pid,
3060 show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
3061{
3062 vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
3063 return CMD_SUCCESS;
3064}
3065
Oliver Smithd243c2a2021-07-09 17:19:32 +02003066DEFUN(show_uptime,
3067 show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
3068{
3069 struct timespec now;
3070 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
3071
3072 time_t uptime = now.tv_sec - starttime.tv_sec;
3073 int d = uptime / (3600 * 24);
3074 int h = uptime / 3600 % 24;
3075 int m = uptime / 60 % 60;
3076 int s = uptime % 60;
3077
3078 vty_out(vty, "%s has been running for %dd %dh %dm %ds%s", host.app_info->name, d, h, m, s, VTY_NEWLINE);
3079
3080 return CMD_SUCCESS;
3081}
3082
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003083/* Help display function for all node. */
3084gDEFUN(config_help,
3085 config_help_cmd, "help", "Description of the interactive help system\n")
3086{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003087 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3088 "anytime at the command line please press '?'.%s%s"
3089 "If nothing matches, the help list will be empty and you must backup%s"
3090 " until entering a '?' shows the available options.%s"
3091 "Two styles of help are provided:%s"
3092 "1. Full help is available when you are ready to enter a%s"
3093 "command argument (e.g. 'show ?') and describes each possible%s"
3094 "argument.%s"
3095 "2. Partial help is provided when an abbreviated argument is entered%s"
3096 " and you want to know what arguments match the input%s"
3097 " (e.g. 'show me?'.)%s%s",
3098 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3099 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3100 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3101 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003102 return CMD_SUCCESS;
3103}
3104
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003105enum {
3106 ATTR_TYPE_GLOBAL = (1 << 0),
3107 ATTR_TYPE_LIB = (1 << 1),
3108 ATTR_TYPE_APP = (1 << 2),
3109};
3110
3111static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3112{
3113 const char *desc;
3114 unsigned int i;
3115 bool found;
3116 char flag;
3117
3118 if (attr_mask & ATTR_TYPE_GLOBAL) {
3119 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3120
3121 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003122 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003123 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003124
3125 /* Skip attributes without flags */
3126 if (flag != '.')
3127 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003128 }
3129 }
3130
3131 if (attr_mask & ATTR_TYPE_LIB) {
3132 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3133
3134 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3135 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3136 continue;
3137 found = true;
3138
3139 flag = cmd_lib_attr_letters[i];
3140 if (flag == '\0')
3141 flag = '.';
3142
3143 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3144 }
3145
3146 if (!found)
3147 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3148 }
3149
3150 if (attr_mask & ATTR_TYPE_APP) {
3151 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3152
3153 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3154 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3155 continue;
3156 found = true;
3157
3158 flag = host.app_info->usr_attr_letters[i];
3159 if (flag == '\0')
3160 flag = '.';
3161
3162 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3163 }
3164
3165 if (!found)
3166 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3167 }
3168}
3169
3170gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3171 "show vty-attributes",
3172 SHOW_STR "List of VTY attributes\n")
3173{
3174 print_attr_list(vty, 0xff);
3175 return CMD_SUCCESS;
3176}
3177
3178gDEFUN(show_vty_attr, show_vty_attr_cmd,
3179 "show vty-attributes (application|library|global)",
3180 SHOW_STR "List of VTY attributes\n"
3181 "Application specific attributes only\n"
3182 "Library specific attributes only\n"
3183 "Global attributes only\n")
3184{
3185 unsigned int attr_mask = 0;
3186
3187 if (argv[0][0] == 'g') /* global */
3188 attr_mask |= ATTR_TYPE_GLOBAL;
3189 else if (argv[0][0] == 'l') /* library */
3190 attr_mask |= ATTR_TYPE_LIB;
3191 else if (argv[0][0] == 'a') /* application */
3192 attr_mask |= ATTR_TYPE_APP;
3193
3194 print_attr_list(vty, attr_mask);
3195 return CMD_SUCCESS;
3196}
3197
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003198/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003199static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003200{
3201 unsigned int flag_mask = 0x00;
3202 unsigned int f, i;
3203
3204 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3205 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3206 const struct cmd_element *cmd;
3207 char flag_letter;
3208
3209 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3210 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003211 if (cmd->attr & CMD_ATTR_DEPRECATED)
3212 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003213 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003214 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003215 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003216 continue;
3217
3218 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3219 flag_letter = cmd_lib_attr_letters[f];
3220 else
3221 flag_letter = host.app_info->usr_attr_letters[f];
3222
3223 if (flag_letter == '\0')
3224 continue;
3225
3226 flag_mask |= (1 << f);
3227 break;
3228 }
3229 }
3230
3231 return flag_mask;
3232}
3233
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003234/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3235static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3236{
3237 static char char_mask[8 + 1];
3238 char *ptr = &char_mask[0];
3239
3240 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003241 if (cmd->attr & CMD_ATTR_HIDDEN)
3242 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3243 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003244 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3245 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3246 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3247 else
3248 *(ptr++) = '.';
3249
3250 *ptr = '\0';
3251
3252 return char_mask;
3253}
3254
3255/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003256static const char *cmd_flag_mask(const struct cmd_element *cmd,
3257 unsigned int flag_mask)
3258{
3259 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3260 char *ptr = &char_mask[0];
3261 char flag_letter;
3262 unsigned int f;
3263
3264 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003265 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003266 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003267 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003268 *(ptr++) = '.';
3269 continue;
3270 }
3271
3272 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3273 flag_letter = cmd_lib_attr_letters[f];
3274 else
3275 flag_letter = host.app_info->usr_attr_letters[f];
3276
3277 *(ptr++) = flag_letter ? flag_letter : '.';
3278 }
3279
3280 *ptr = '\0';
3281
3282 return char_mask;
3283}
3284
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003285/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003286gDEFUN(config_list, config_list_cmd,
3287 "list [with-flags]",
3288 "Print command list\n"
3289 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003290{
3291 unsigned int i;
3292 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003293 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003294 struct cmd_element *cmd;
3295
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003296 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003297 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003298
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003299 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3300 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3301 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003302 if (cmd->attr & CMD_ATTR_DEPRECATED)
3303 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003304 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003305 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003306 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003307 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3308 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003309 vty_out(vty, " %s %s %s%s",
3310 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003311 cmd_flag_mask(cmd, flag_mask),
3312 cmd->string, VTY_NEWLINE);
3313 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003314 }
3315
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003316 return CMD_SUCCESS;
3317}
3318
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003319static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003320{
3321 unsigned int i;
3322 int fd;
3323 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003324 char *config_file_tmp = NULL;
3325 char *config_file_sav = NULL;
3326 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003327 struct stat st;
3328
3329 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003330
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003331 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3332 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3333 * manually instead. */
3334
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003335 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003336 config_file_sav =
3337 _talloc_zero(tall_vty_cmd_ctx,
3338 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3339 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003340 if (!config_file_sav)
3341 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003342 strcpy(config_file_sav, config_file);
3343 strcat(config_file_sav, CONF_BACKUP_EXT);
3344
3345 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003346 "config_file_tmp");
3347 if (!config_file_tmp) {
3348 talloc_free(config_file_sav);
3349 return -1;
3350 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003351 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3352
3353 /* Open file to configuration write. */
3354 fd = mkstemp(config_file_tmp);
3355 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003356 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003357 talloc_free(config_file_tmp);
3358 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003359 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003360 }
3361
3362 /* Make vty for configuration file. */
3363 file_vty = vty_new();
3364 file_vty->fd = fd;
3365 file_vty->type = VTY_FILE;
3366
3367 /* Config file header print. */
3368 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003369 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003370 //vty_time_print (file_vty, 1);
3371 vty_out(file_vty, "!\n");
3372
3373 for (i = 0; i < vector_active(cmdvec); i++)
3374 if ((node = vector_slot(cmdvec, i)) && node->func) {
3375 if ((*node->func) (file_vty))
3376 vty_out(file_vty, "!\n");
3377 }
3378 vty_close(file_vty);
3379
3380 if (unlink(config_file_sav) != 0)
3381 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003382 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003383 talloc_free(config_file_sav);
3384 talloc_free(config_file_tmp);
3385 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003386 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003387 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003388
3389 /* Only link the .sav file if the original file exists */
3390 if (stat(config_file, &st) == 0) {
3391 if (link(config_file, config_file_sav) != 0) {
3392 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3393 talloc_free(config_file_sav);
3394 talloc_free(config_file_tmp);
3395 unlink(config_file_tmp);
3396 return -3;
3397 }
3398 sync();
3399 if (unlink(config_file) != 0) {
3400 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3401 talloc_free(config_file_sav);
3402 talloc_free(config_file_tmp);
3403 unlink(config_file_tmp);
3404 return -4;
3405 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003406 }
3407 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003408 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003409 talloc_free(config_file_sav);
3410 talloc_free(config_file_tmp);
3411 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003412 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003413 }
3414 unlink(config_file_tmp);
3415 sync();
3416
3417 talloc_free(config_file_sav);
3418 talloc_free(config_file_tmp);
3419
3420 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003421 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3422 return -6;
3423 }
3424
3425 return 0;
3426}
3427
3428
3429/* Write current configuration into file. */
3430DEFUN(config_write_file,
3431 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003432 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003433 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003434 "Write to configuration file\n"
3435 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003436{
3437 char *failed_file;
3438 int rc;
3439
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003440 if (host.app_info->config_is_consistent) {
3441 rc = host.app_info->config_is_consistent(vty);
3442 if (!rc) {
3443 vty_out(vty, "Configuration is not consistent%s",
3444 VTY_NEWLINE);
3445 return CMD_WARNING;
3446 }
3447 }
3448
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003449 if (argc == 1)
3450 host_config_set(argv[0]);
3451
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003452 if (host.config == NULL) {
3453 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3454 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003455 return CMD_WARNING;
3456 }
3457
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003458 rc = write_config_file(host.config, &failed_file);
3459 switch (rc) {
3460 case -1:
3461 vty_out(vty, "Can't open configuration file %s.%s",
3462 failed_file, VTY_NEWLINE);
3463 rc = CMD_WARNING;
3464 break;
3465 case -2:
3466 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3467 failed_file, VTY_NEWLINE);
3468 rc = CMD_WARNING;
3469 break;
3470 case -3:
3471 vty_out(vty, "Can't backup old configuration file %s.%s",
3472 failed_file, VTY_NEWLINE);
3473 rc = CMD_WARNING;
3474 break;
3475 case -4:
3476 vty_out(vty, "Can't unlink configuration file %s.%s",
3477 failed_file, VTY_NEWLINE);
3478 rc = CMD_WARNING;
3479 break;
3480 case -5:
3481 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3482 VTY_NEWLINE);
3483 rc = CMD_WARNING;
3484 break;
3485 case -6:
3486 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3487 failed_file, strerror(errno), errno, VTY_NEWLINE);
3488 rc = CMD_WARNING;
3489 break;
3490 default:
3491 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3492 rc = CMD_SUCCESS;
3493 break;
3494 }
3495
3496 talloc_free(failed_file);
3497 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003498}
3499
3500ALIAS(config_write_file,
3501 config_write_cmd,
3502 "write", "Write running configuration to memory, network, or terminal\n")
3503
3504 ALIAS(config_write_file,
3505 config_write_memory_cmd,
3506 "write memory",
3507 "Write running configuration to memory, network, or terminal\n"
3508 "Write configuration to the file (same as write file)\n")
3509
3510 ALIAS(config_write_file,
3511 copy_runningconfig_startupconfig_cmd,
3512 "copy running-config startup-config",
3513 "Copy configuration\n"
3514 "Copy running config to... \n"
3515 "Copy running config to startup config (same as write file)\n")
3516
3517/* Write current configuration into the terminal. */
3518 DEFUN(config_write_terminal,
3519 config_write_terminal_cmd,
3520 "write terminal",
3521 "Write running configuration to memory, network, or terminal\n"
3522 "Write to terminal\n")
3523{
3524 unsigned int i;
3525 struct cmd_node *node;
3526
3527 if (vty->type == VTY_SHELL_SERV) {
3528 for (i = 0; i < vector_active(cmdvec); i++)
3529 if ((node = vector_slot(cmdvec, i)) && node->func
3530 && node->vtysh) {
3531 if ((*node->func) (vty))
3532 vty_out(vty, "!%s", VTY_NEWLINE);
3533 }
3534 } else {
3535 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3536 VTY_NEWLINE);
3537 vty_out(vty, "!%s", VTY_NEWLINE);
3538
3539 for (i = 0; i < vector_active(cmdvec); i++)
3540 if ((node = vector_slot(cmdvec, i)) && node->func) {
3541 if ((*node->func) (vty))
3542 vty_out(vty, "!%s", VTY_NEWLINE);
3543 }
3544 vty_out(vty, "end%s", VTY_NEWLINE);
3545 }
3546 return CMD_SUCCESS;
3547}
3548
3549/* Write current configuration into the terminal. */
3550ALIAS(config_write_terminal,
3551 show_running_config_cmd,
3552 "show running-config", SHOW_STR "running configuration\n")
3553
3554/* Write startup configuration into the terminal. */
3555 DEFUN(show_startup_config,
3556 show_startup_config_cmd,
3557 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3558{
3559 char buf[BUFSIZ];
3560 FILE *confp;
3561
3562 confp = fopen(host.config, "r");
3563 if (confp == NULL) {
3564 vty_out(vty, "Can't open configuration file [%s]%s",
3565 host.config, VTY_NEWLINE);
3566 return CMD_WARNING;
3567 }
3568
3569 while (fgets(buf, BUFSIZ, confp)) {
3570 char *cp = buf;
3571
3572 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3573 cp++;
3574 *cp = '\0';
3575
3576 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3577 }
3578
3579 fclose(confp);
3580
3581 return CMD_SUCCESS;
3582}
3583
3584/* Hostname configuration */
3585DEFUN(config_hostname,
3586 hostname_cmd,
3587 "hostname WORD",
3588 "Set system's network name\n" "This system's network name\n")
3589{
3590 if (!isalpha((int)*argv[0])) {
3591 vty_out(vty, "Please specify string starting with alphabet%s",
3592 VTY_NEWLINE);
3593 return CMD_WARNING;
3594 }
3595
3596 if (host.name)
3597 talloc_free(host.name);
3598
3599 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3600 return CMD_SUCCESS;
3601}
3602
3603DEFUN(config_no_hostname,
3604 no_hostname_cmd,
3605 "no hostname [HOSTNAME]",
3606 NO_STR "Reset system's network name\n" "Host name of this router\n")
3607{
3608 if (host.name)
3609 talloc_free(host.name);
3610 host.name = NULL;
3611 return CMD_SUCCESS;
3612}
3613
3614/* VTY interface password set. */
3615DEFUN(config_password, password_cmd,
3616 "password (8|) WORD",
3617 "Assign the terminal connection password\n"
3618 "Specifies a HIDDEN password will follow\n"
3619 "dummy string \n" "The HIDDEN line password string\n")
3620{
3621 /* Argument check. */
3622 if (argc == 0) {
3623 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3624 return CMD_WARNING;
3625 }
3626
3627 if (argc == 2) {
3628 if (*argv[0] == '8') {
3629 if (host.password)
3630 talloc_free(host.password);
3631 host.password = NULL;
3632 if (host.password_encrypt)
3633 talloc_free(host.password_encrypt);
3634 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3635 return CMD_SUCCESS;
3636 } else {
3637 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3638 return CMD_WARNING;
3639 }
3640 }
3641
3642 if (!isalnum((int)*argv[0])) {
3643 vty_out(vty,
3644 "Please specify string starting with alphanumeric%s",
3645 VTY_NEWLINE);
3646 return CMD_WARNING;
3647 }
3648
3649 if (host.password)
3650 talloc_free(host.password);
3651 host.password = NULL;
3652
3653#ifdef VTY_CRYPT_PW
3654 if (host.encrypt) {
3655 if (host.password_encrypt)
3656 talloc_free(host.password_encrypt);
3657 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3658 } else
3659#endif
3660 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3661
3662 return CMD_SUCCESS;
3663}
3664
3665ALIAS(config_password, password_text_cmd,
3666 "password LINE",
3667 "Assign the terminal connection password\n"
3668 "The UNENCRYPTED (cleartext) line password\n")
3669
3670/* VTY enable password set. */
3671 DEFUN(config_enable_password, enable_password_cmd,
3672 "enable password (8|) WORD",
3673 "Modify enable password parameters\n"
3674 "Assign the privileged level password\n"
3675 "Specifies a HIDDEN password will follow\n"
3676 "dummy string \n" "The HIDDEN 'enable' password string\n")
3677{
3678 /* Argument check. */
3679 if (argc == 0) {
3680 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3681 return CMD_WARNING;
3682 }
3683
3684 /* Crypt type is specified. */
3685 if (argc == 2) {
3686 if (*argv[0] == '8') {
3687 if (host.enable)
3688 talloc_free(host.enable);
3689 host.enable = NULL;
3690
3691 if (host.enable_encrypt)
3692 talloc_free(host.enable_encrypt);
3693 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3694
3695 return CMD_SUCCESS;
3696 } else {
3697 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3698 return CMD_WARNING;
3699 }
3700 }
3701
3702 if (!isalnum((int)*argv[0])) {
3703 vty_out(vty,
3704 "Please specify string starting with alphanumeric%s",
3705 VTY_NEWLINE);
3706 return CMD_WARNING;
3707 }
3708
3709 if (host.enable)
3710 talloc_free(host.enable);
3711 host.enable = NULL;
3712
3713 /* Plain password input. */
3714#ifdef VTY_CRYPT_PW
3715 if (host.encrypt) {
3716 if (host.enable_encrypt)
3717 talloc_free(host.enable_encrypt);
3718 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3719 } else
3720#endif
3721 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3722
3723 return CMD_SUCCESS;
3724}
3725
3726ALIAS(config_enable_password,
3727 enable_password_text_cmd,
3728 "enable password LINE",
3729 "Modify enable password parameters\n"
3730 "Assign the privileged level password\n"
3731 "The UNENCRYPTED (cleartext) 'enable' password\n")
3732
3733/* VTY enable password delete. */
3734 DEFUN(no_config_enable_password, no_enable_password_cmd,
3735 "no enable password",
3736 NO_STR
3737 "Modify enable password parameters\n"
3738 "Assign the privileged level password\n")
3739{
3740 if (host.enable)
3741 talloc_free(host.enable);
3742 host.enable = NULL;
3743
3744 if (host.enable_encrypt)
3745 talloc_free(host.enable_encrypt);
3746 host.enable_encrypt = NULL;
3747
3748 return CMD_SUCCESS;
3749}
3750
3751#ifdef VTY_CRYPT_PW
3752DEFUN(service_password_encrypt,
3753 service_password_encrypt_cmd,
3754 "service password-encryption",
3755 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3756{
3757 if (host.encrypt)
3758 return CMD_SUCCESS;
3759
3760 host.encrypt = 1;
3761
3762 if (host.password) {
3763 if (host.password_encrypt)
3764 talloc_free(host.password_encrypt);
3765 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3766 }
3767 if (host.enable) {
3768 if (host.enable_encrypt)
3769 talloc_free(host.enable_encrypt);
3770 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3771 }
3772
3773 return CMD_SUCCESS;
3774}
3775
3776DEFUN(no_service_password_encrypt,
3777 no_service_password_encrypt_cmd,
3778 "no service password-encryption",
3779 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3780{
3781 if (!host.encrypt)
3782 return CMD_SUCCESS;
3783
3784 host.encrypt = 0;
3785
3786 if (host.password_encrypt)
3787 talloc_free(host.password_encrypt);
3788 host.password_encrypt = NULL;
3789
3790 if (host.enable_encrypt)
3791 talloc_free(host.enable_encrypt);
3792 host.enable_encrypt = NULL;
3793
3794 return CMD_SUCCESS;
3795}
3796#endif
3797
3798DEFUN(config_terminal_length, config_terminal_length_cmd,
3799 "terminal length <0-512>",
3800 "Set terminal line parameters\n"
3801 "Set number of lines on a screen\n"
3802 "Number of lines on screen (0 for no pausing)\n")
3803{
3804 int lines;
3805 char *endptr = NULL;
3806
3807 lines = strtol(argv[0], &endptr, 10);
3808 if (lines < 0 || lines > 512 || *endptr != '\0') {
3809 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3810 return CMD_WARNING;
3811 }
3812 vty->lines = lines;
3813
3814 return CMD_SUCCESS;
3815}
3816
3817DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3818 "terminal no length",
3819 "Set terminal line parameters\n"
3820 NO_STR "Set number of lines on a screen\n")
3821{
3822 vty->lines = -1;
3823 return CMD_SUCCESS;
3824}
3825
3826DEFUN(service_terminal_length, service_terminal_length_cmd,
3827 "service terminal-length <0-512>",
3828 "Set up miscellaneous service\n"
3829 "System wide terminal length configuration\n"
3830 "Number of lines of VTY (0 means no line control)\n")
3831{
3832 int lines;
3833 char *endptr = NULL;
3834
3835 lines = strtol(argv[0], &endptr, 10);
3836 if (lines < 0 || lines > 512 || *endptr != '\0') {
3837 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3838 return CMD_WARNING;
3839 }
3840 host.lines = lines;
3841
3842 return CMD_SUCCESS;
3843}
3844
3845DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3846 "no service terminal-length [<0-512>]",
3847 NO_STR
3848 "Set up miscellaneous service\n"
3849 "System wide terminal length configuration\n"
3850 "Number of lines of VTY (0 means no line control)\n")
3851{
3852 host.lines = -1;
3853 return CMD_SUCCESS;
3854}
3855
3856DEFUN_HIDDEN(do_echo,
3857 echo_cmd,
3858 "echo .MESSAGE",
3859 "Echo a message back to the vty\n" "The message to echo\n")
3860{
3861 char *message;
3862
3863 vty_out(vty, "%s%s",
3864 ((message =
3865 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3866 if (message)
3867 talloc_free(message);
3868 return CMD_SUCCESS;
3869}
3870
3871#if 0
3872DEFUN(config_logmsg,
3873 config_logmsg_cmd,
3874 "logmsg " LOG_LEVELS " .MESSAGE",
3875 "Send a message to enabled logging destinations\n"
3876 LOG_LEVEL_DESC "The message to send\n")
3877{
3878 int level;
3879 char *message;
3880
3881 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3882 return CMD_ERR_NO_MATCH;
3883
3884 zlog(NULL, level,
3885 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3886 if (message)
3887 talloc_free(message);
3888 return CMD_SUCCESS;
3889}
3890
3891DEFUN(show_logging,
3892 show_logging_cmd,
3893 "show logging", SHOW_STR "Show current logging configuration\n")
3894{
3895 struct zlog *zl = zlog_default;
3896
3897 vty_out(vty, "Syslog logging: ");
3898 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3899 vty_out(vty, "disabled");
3900 else
3901 vty_out(vty, "level %s, facility %s, ident %s",
3902 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3903 facility_name(zl->facility), zl->ident);
3904 vty_out(vty, "%s", VTY_NEWLINE);
3905
3906 vty_out(vty, "Stdout logging: ");
3907 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3908 vty_out(vty, "disabled");
3909 else
3910 vty_out(vty, "level %s",
3911 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3912 vty_out(vty, "%s", VTY_NEWLINE);
3913
3914 vty_out(vty, "Monitor logging: ");
3915 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3916 vty_out(vty, "disabled");
3917 else
3918 vty_out(vty, "level %s",
3919 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3920 vty_out(vty, "%s", VTY_NEWLINE);
3921
3922 vty_out(vty, "File logging: ");
3923 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3924 vty_out(vty, "disabled");
3925 else
3926 vty_out(vty, "level %s, filename %s",
3927 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3928 zl->filename);
3929 vty_out(vty, "%s", VTY_NEWLINE);
3930
3931 vty_out(vty, "Protocol name: %s%s",
3932 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3933 vty_out(vty, "Record priority: %s%s",
3934 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3935
3936 return CMD_SUCCESS;
3937}
3938
3939DEFUN(config_log_stdout,
3940 config_log_stdout_cmd,
3941 "log stdout", "Logging control\n" "Set stdout logging level\n")
3942{
3943 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3944 return CMD_SUCCESS;
3945}
3946
3947DEFUN(config_log_stdout_level,
3948 config_log_stdout_level_cmd,
3949 "log stdout " LOG_LEVELS,
3950 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3951{
3952 int level;
3953
3954 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3955 return CMD_ERR_NO_MATCH;
3956 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3957 return CMD_SUCCESS;
3958}
3959
3960DEFUN(no_config_log_stdout,
3961 no_config_log_stdout_cmd,
3962 "no log stdout [LEVEL]",
3963 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3964{
3965 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3966 return CMD_SUCCESS;
3967}
3968
3969DEFUN(config_log_monitor,
3970 config_log_monitor_cmd,
3971 "log monitor",
3972 "Logging control\n" "Set terminal line (monitor) logging level\n")
3973{
3974 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3975 return CMD_SUCCESS;
3976}
3977
3978DEFUN(config_log_monitor_level,
3979 config_log_monitor_level_cmd,
3980 "log monitor " LOG_LEVELS,
3981 "Logging control\n"
3982 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3983{
3984 int level;
3985
3986 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3987 return CMD_ERR_NO_MATCH;
3988 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3989 return CMD_SUCCESS;
3990}
3991
3992DEFUN(no_config_log_monitor,
3993 no_config_log_monitor_cmd,
3994 "no log monitor [LEVEL]",
3995 NO_STR
3996 "Logging control\n"
3997 "Disable terminal line (monitor) logging\n" "Logging level\n")
3998{
3999 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
4000 return CMD_SUCCESS;
4001}
4002
4003static int set_log_file(struct vty *vty, const char *fname, int loglevel)
4004{
4005 int ret;
4006 char *p = NULL;
4007 const char *fullpath;
4008
4009 /* Path detection. */
4010 if (!IS_DIRECTORY_SEP(*fname)) {
4011 char cwd[MAXPATHLEN + 1];
4012 cwd[MAXPATHLEN] = '\0';
4013
4014 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4015 zlog_err("config_log_file: Unable to alloc mem!");
4016 return CMD_WARNING;
4017 }
4018
4019 if ((p = _talloc_zero(tall_vcmd_ctx,
4020 strlen(cwd) + strlen(fname) + 2),
4021 "set_log_file")
4022 == NULL) {
4023 zlog_err("config_log_file: Unable to alloc mem!");
4024 return CMD_WARNING;
4025 }
4026 sprintf(p, "%s/%s", cwd, fname);
4027 fullpath = p;
4028 } else
4029 fullpath = fname;
4030
4031 ret = zlog_set_file(NULL, fullpath, loglevel);
4032
4033 if (p)
4034 talloc_free(p);
4035
4036 if (!ret) {
4037 vty_out(vty, "can't open logfile %s\n", fname);
4038 return CMD_WARNING;
4039 }
4040
4041 if (host.logfile)
4042 talloc_free(host.logfile);
4043
4044 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
4045
4046 return CMD_SUCCESS;
4047}
4048
4049DEFUN(config_log_file,
4050 config_log_file_cmd,
4051 "log file FILENAME",
4052 "Logging control\n" "Logging to file\n" "Logging filename\n")
4053{
4054 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4055}
4056
4057DEFUN(config_log_file_level,
4058 config_log_file_level_cmd,
4059 "log file FILENAME " LOG_LEVELS,
4060 "Logging control\n"
4061 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4062{
4063 int level;
4064
4065 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4066 return CMD_ERR_NO_MATCH;
4067 return set_log_file(vty, argv[0], level);
4068}
4069
4070DEFUN(no_config_log_file,
4071 no_config_log_file_cmd,
4072 "no log file [FILENAME]",
4073 NO_STR
4074 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4075{
4076 zlog_reset_file(NULL);
4077
4078 if (host.logfile)
4079 talloc_free(host.logfile);
4080
4081 host.logfile = NULL;
4082
4083 return CMD_SUCCESS;
4084}
4085
4086ALIAS(no_config_log_file,
4087 no_config_log_file_level_cmd,
4088 "no log file FILENAME LEVEL",
4089 NO_STR
4090 "Logging control\n"
4091 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4092
4093 DEFUN(config_log_syslog,
4094 config_log_syslog_cmd,
4095 "log syslog", "Logging control\n" "Set syslog logging level\n")
4096{
4097 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4098 return CMD_SUCCESS;
4099}
4100
4101DEFUN(config_log_syslog_level,
4102 config_log_syslog_level_cmd,
4103 "log syslog " LOG_LEVELS,
4104 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4105{
4106 int level;
4107
4108 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4109 return CMD_ERR_NO_MATCH;
4110 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4111 return CMD_SUCCESS;
4112}
4113
4114DEFUN_DEPRECATED(config_log_syslog_facility,
4115 config_log_syslog_facility_cmd,
4116 "log syslog facility " LOG_FACILITIES,
4117 "Logging control\n"
4118 "Logging goes to syslog\n"
4119 "(Deprecated) Facility parameter for syslog messages\n"
4120 LOG_FACILITY_DESC)
4121{
4122 int facility;
4123
4124 if ((facility = facility_match(argv[0])) < 0)
4125 return CMD_ERR_NO_MATCH;
4126
4127 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4128 zlog_default->facility = facility;
4129 return CMD_SUCCESS;
4130}
4131
4132DEFUN(no_config_log_syslog,
4133 no_config_log_syslog_cmd,
4134 "no log syslog [LEVEL]",
4135 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4136{
4137 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4138 return CMD_SUCCESS;
4139}
4140
4141ALIAS(no_config_log_syslog,
4142 no_config_log_syslog_facility_cmd,
4143 "no log syslog facility " LOG_FACILITIES,
4144 NO_STR
4145 "Logging control\n"
4146 "Logging goes to syslog\n"
4147 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4148
4149 DEFUN(config_log_facility,
4150 config_log_facility_cmd,
4151 "log facility " LOG_FACILITIES,
4152 "Logging control\n"
4153 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4154{
4155 int facility;
4156
4157 if ((facility = facility_match(argv[0])) < 0)
4158 return CMD_ERR_NO_MATCH;
4159 zlog_default->facility = facility;
4160 return CMD_SUCCESS;
4161}
4162
4163DEFUN(no_config_log_facility,
4164 no_config_log_facility_cmd,
4165 "no log facility [FACILITY]",
4166 NO_STR
4167 "Logging control\n"
4168 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4169{
4170 zlog_default->facility = LOG_DAEMON;
4171 return CMD_SUCCESS;
4172}
4173
4174DEFUN_DEPRECATED(config_log_trap,
4175 config_log_trap_cmd,
4176 "log trap " LOG_LEVELS,
4177 "Logging control\n"
4178 "(Deprecated) Set logging level and default for all destinations\n"
4179 LOG_LEVEL_DESC)
4180{
4181 int new_level;
4182 int i;
4183
4184 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4185 return CMD_ERR_NO_MATCH;
4186
4187 zlog_default->default_lvl = new_level;
4188 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4189 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4190 zlog_default->maxlvl[i] = new_level;
4191 return CMD_SUCCESS;
4192}
4193
4194DEFUN_DEPRECATED(no_config_log_trap,
4195 no_config_log_trap_cmd,
4196 "no log trap [LEVEL]",
4197 NO_STR
4198 "Logging control\n"
4199 "Permit all logging information\n" "Logging level\n")
4200{
4201 zlog_default->default_lvl = LOG_DEBUG;
4202 return CMD_SUCCESS;
4203}
4204
4205DEFUN(config_log_record_priority,
4206 config_log_record_priority_cmd,
4207 "log record-priority",
4208 "Logging control\n"
4209 "Log the priority of the message within the message\n")
4210{
4211 zlog_default->record_priority = 1;
4212 return CMD_SUCCESS;
4213}
4214
4215DEFUN(no_config_log_record_priority,
4216 no_config_log_record_priority_cmd,
4217 "no log record-priority",
4218 NO_STR
4219 "Logging control\n"
4220 "Do not log the priority of the message within the message\n")
4221{
4222 zlog_default->record_priority = 0;
4223 return CMD_SUCCESS;
4224}
4225#endif
4226
4227DEFUN(banner_motd_file,
4228 banner_motd_file_cmd,
4229 "banner motd file [FILE]",
4230 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4231{
4232 if (host.motdfile)
4233 talloc_free(host.motdfile);
4234 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4235
4236 return CMD_SUCCESS;
4237}
4238
4239DEFUN(banner_motd_default,
4240 banner_motd_default_cmd,
4241 "banner motd default",
4242 "Set banner string\n" "Strings for motd\n" "Default string\n")
4243{
4244 host.motd = default_motd;
4245 return CMD_SUCCESS;
4246}
4247
4248DEFUN(no_banner_motd,
4249 no_banner_motd_cmd,
4250 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4251{
4252 host.motd = NULL;
4253 if (host.motdfile)
4254 talloc_free(host.motdfile);
4255 host.motdfile = NULL;
4256 return CMD_SUCCESS;
4257}
4258
4259/* Set config filename. Called from vty.c */
4260void host_config_set(const char *filename)
4261{
4262 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4263}
4264
Pau Espin Pedrolebb6c1f2021-05-17 18:54:21 +02004265const char *host_config_file(void)
4266{
4267 return host.config;
4268}
4269
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004270/*! Deprecated, now happens implicitly when calling install_node().
4271 * Users of the API may still attempt to call this function, hence
4272 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004273void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004274{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004275}
4276
4277/*! Deprecated, now happens implicitly when calling install_node().
4278 * Users of the API may still attempt to call this function, hence
4279 * leave it here as a no-op. */
4280void vty_install_default(int node)
4281{
4282}
4283
4284/*! Install common commands like 'exit' and 'list'. */
4285static void install_basic_node_commands(int node)
4286{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004287 install_lib_element(node, &config_help_cmd);
4288 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004289
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004290 install_lib_element(node, &show_vty_attr_all_cmd);
4291 install_lib_element(node, &show_vty_attr_cmd);
4292
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004293 install_lib_element(node, &config_write_terminal_cmd);
4294 install_lib_element(node, &config_write_file_cmd);
4295 install_lib_element(node, &config_write_memory_cmd);
4296 install_lib_element(node, &config_write_cmd);
4297 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004298
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004299 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004300
4301 if (node >= CONFIG_NODE) {
4302 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004303 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004304 }
4305}
4306
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004307/*! Return true if a node is installed by install_basic_node_commands(), so
4308 * that we can avoid repeating them for each and every node during 'show
4309 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004310static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004311{
4312 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004313 || cmd == &show_vty_attr_all_cmd
4314 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004315 || cmd == &config_list_cmd
4316 || cmd == &config_write_terminal_cmd
4317 || cmd == &config_write_file_cmd
4318 || cmd == &config_write_memory_cmd
4319 || cmd == &config_write_cmd
4320 || cmd == &show_running_config_cmd
4321 || cmd == &config_exit_cmd
4322 || cmd == &config_end_cmd)
4323 return true;
4324 return false;
4325}
4326
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004327/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004328 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004329 * \param[in] vty the vty of the code
4330 * \param[in] filename where to store the file
4331 * \return 0 in case of success.
4332 *
4333 * If the filename already exists create a filename.sav
4334 * version with the current code.
4335 *
4336 */
4337int osmo_vty_write_config_file(const char *filename)
4338{
4339 char *failed_file;
4340 int rc;
4341
4342 rc = write_config_file(filename, &failed_file);
4343 talloc_free(failed_file);
4344 return rc;
4345}
4346
4347/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004348 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004349 * \return 0 in case of success.
4350 *
4351 * If the filename already exists create a filename.sav
4352 * version with the current code.
4353 *
4354 */
4355int osmo_vty_save_config_file(void)
4356{
4357 char *failed_file;
4358 int rc;
4359
4360 if (host.config == NULL)
4361 return -7;
4362
4363 rc = write_config_file(host.config, &failed_file);
4364 talloc_free(failed_file);
4365 return rc;
4366}
4367
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004368/* Initialize command interface. Install basic nodes and commands. */
4369void cmd_init(int terminal)
4370{
4371 /* Allocate initial top vector of commands. */
4372 cmdvec = vector_init(VECTOR_MIN_SIZE);
4373
4374 /* Default host value settings. */
4375 host.name = NULL;
4376 host.password = NULL;
4377 host.enable = NULL;
4378 host.logfile = NULL;
4379 host.config = NULL;
4380 host.lines = -1;
4381 host.motd = default_motd;
4382 host.motdfile = NULL;
4383
4384 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004385 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004386 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004387 install_node_bare(&auth_node, NULL);
4388 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004389 install_node(&config_node, config_write_host);
4390
4391 /* Each node's basic commands. */
Oliver Smith0c78bc62021-07-12 17:28:36 +02004392 install_lib_element(VIEW_NODE, &show_pid_cmd);
Oliver Smithd243c2a2021-07-09 17:19:32 +02004393 install_lib_element(VIEW_NODE, &show_uptime_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004394 install_lib_element(VIEW_NODE, &show_version_cmd);
4395 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004396 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004397 install_lib_element(VIEW_NODE, &config_list_cmd);
4398 install_lib_element(VIEW_NODE, &config_exit_cmd);
4399 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004400 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4401 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004402 install_lib_element(VIEW_NODE, &config_enable_cmd);
4403 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4404 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4405 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004406 }
4407
4408 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004409 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4410 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4411 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Oliver Smith8a482fd2021-07-12 18:18:51 +02004412 install_lib_element(ENABLE_NODE, &shutdown_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004413 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004414 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4415 install_lib_element(ENABLE_NODE, &show_version_cmd);
4416 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004417
4418 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004419 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4420 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4421 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004422 }
4423
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004424 install_lib_element(CONFIG_NODE, &hostname_cmd);
4425 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004426
4427 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004428 install_lib_element(CONFIG_NODE, &password_cmd);
4429 install_lib_element(CONFIG_NODE, &password_text_cmd);
4430 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4431 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4432 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004433
4434#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004435 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4436 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004437#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004438 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4439 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4440 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4441 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4442 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004443
4444 }
4445 srand(time(NULL));
4446}
Harald Welte7acb30c2011-08-17 17:13:48 +02004447
Oliver Smithd243c2a2021-07-09 17:19:32 +02004448static __attribute__((constructor)) void on_dso_load_starttime(void)
4449{
4450 osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
4451}
4452
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004453/* FIXME: execute this section in the unit test instead */
4454static __attribute__((constructor)) void on_dso_load(void)
4455{
4456 unsigned int i, j;
4457
4458 /* Check total number of the library specific attributes */
4459 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4460
4461 /* Check for duplicates in the list of library specific flags */
4462 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4463 if (cmd_lib_attr_letters[i] == '\0')
4464 continue;
4465
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004466 /* Some flag characters are reserved for global attributes */
4467 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4468 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4469 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4470
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004471 /* Only upper case flag letters are allowed for libraries */
4472 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4473 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4474
4475 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4476 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4477 }
4478}
4479
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004480/*! @} */