blob: d7b8026ff09e924b35344333e79964ecf5cddcae [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);
Pau Espin Pedrol079149e2022-06-16 17:08:34 +02001095 OSMO_ASSERT(cnode != NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001096 return cnode->cmd_vector;
1097}
1098
1099/* Completion match types. */
1100enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001101 NO_MATCH = 0,
1102 ANY_MATCH,
1103 EXTEND_MATCH,
1104 IPV4_PREFIX_MATCH,
1105 IPV4_MATCH,
1106 IPV6_PREFIX_MATCH,
1107 IPV6_MATCH,
1108 RANGE_MATCH,
1109 VARARG_MATCH,
1110 PARTLY_MATCH,
1111 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001112};
1113
1114static enum match_type cmd_ipv4_match(const char *str)
1115{
1116 const char *sp;
1117 int dots = 0, nums = 0;
1118 char buf[4];
1119
1120 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001121 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001122
1123 for (;;) {
1124 memset(buf, 0, sizeof(buf));
1125 sp = str;
1126 while (*str != '\0') {
1127 if (*str == '.') {
1128 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001129 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130
1131 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001132 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001133
1134 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001135 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001136
1137 dots++;
1138 break;
1139 }
1140 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001141 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001142
1143 str++;
1144 }
1145
1146 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 strncpy(buf, sp, str - sp);
1150 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001151 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001152
1153 nums++;
1154
1155 if (*str == '\0')
1156 break;
1157
1158 str++;
1159 }
1160
1161 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001162 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001163
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001164 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001165}
1166
1167static enum match_type cmd_ipv4_prefix_match(const char *str)
1168{
1169 const char *sp;
1170 int dots = 0;
1171 char buf[4];
1172
1173 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001174 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001175
1176 for (;;) {
1177 memset(buf, 0, sizeof(buf));
1178 sp = str;
1179 while (*str != '\0' && *str != '/') {
1180 if (*str == '.') {
1181 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001182 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001183
1184 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001185 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186
1187 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001188 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001189
1190 dots++;
1191 break;
1192 }
1193
1194 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001195 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001196
1197 str++;
1198 }
1199
1200 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001201 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001202
1203 strncpy(buf, sp, str - sp);
1204 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001205 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001206
1207 if (dots == 3) {
1208 if (*str == '/') {
1209 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001210 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001211
1212 str++;
1213 break;
1214 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001215 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001216 }
1217
1218 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001219 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001220
1221 str++;
1222 }
1223
1224 sp = str;
1225 while (*str != '\0') {
1226 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001227 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001228
1229 str++;
1230 }
1231
1232 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001233 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001234
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001235 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001236}
1237
1238#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1239#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1240#define STATE_START 1
1241#define STATE_COLON 2
1242#define STATE_DOUBLE 3
1243#define STATE_ADDR 4
1244#define STATE_DOT 5
1245#define STATE_SLASH 6
1246#define STATE_MASK 7
1247
1248#ifdef HAVE_IPV6
1249
1250static enum match_type cmd_ipv6_match(const char *str)
1251{
1252 int state = STATE_START;
1253 int colons = 0, nums = 0, double_colon = 0;
1254 const char *sp = NULL;
1255 struct sockaddr_in6 sin6_dummy;
1256 int ret;
1257
1258 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001259 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001260
1261 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001262 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001263
1264 /* use inet_pton that has a better support,
1265 * for example inet_pton can support the automatic addresses:
1266 * ::1.2.3.4
1267 */
1268 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1269
1270 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001271 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001272
1273 while (*str != '\0') {
1274 switch (state) {
1275 case STATE_START:
1276 if (*str == ':') {
1277 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001278 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001279 colons--;
1280 state = STATE_COLON;
1281 } else {
1282 sp = str;
1283 state = STATE_ADDR;
1284 }
1285
1286 continue;
1287 case STATE_COLON:
1288 colons++;
1289 if (*(str + 1) == ':')
1290 state = STATE_DOUBLE;
1291 else {
1292 sp = str + 1;
1293 state = STATE_ADDR;
1294 }
1295 break;
1296 case STATE_DOUBLE:
1297 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001298 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001299
1300 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001301 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302 else {
1303 if (*(str + 1) != '\0')
1304 colons++;
1305 sp = str + 1;
1306 state = STATE_ADDR;
1307 }
1308
1309 double_colon++;
1310 nums++;
1311 break;
1312 case STATE_ADDR:
1313 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1314 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001315 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001316
1317 nums++;
1318 state = STATE_COLON;
1319 }
1320 if (*(str + 1) == '.')
1321 state = STATE_DOT;
1322 break;
1323 case STATE_DOT:
1324 state = STATE_ADDR;
1325 break;
1326 default:
1327 break;
1328 }
1329
1330 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001331 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001332
1333 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001334 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001335
1336 str++;
1337 }
1338
1339#if 0
1340 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001341 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001342#endif /* 0 */
1343
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001344 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001345}
1346
1347static enum match_type cmd_ipv6_prefix_match(const char *str)
1348{
1349 int state = STATE_START;
1350 int colons = 0, nums = 0, double_colon = 0;
1351 int mask;
1352 const char *sp = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001353
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
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02001450 if (osmo_str_to_int(&mask, str, 10, 0, 128))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001451 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001452
1453/* I don't know why mask < 13 makes command match partly.
1454 Forgive me to make this comments. I Want to set static default route
1455 because of lack of function to originate default in ospf6d; sorry
1456 yasu
1457 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001458 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459*/
1460
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001461 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001462}
1463
1464#endif /* HAVE_IPV6 */
1465
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001466
1467#if ULONG_MAX == 18446744073709551615UL
1468#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1469#elif ULONG_MAX == 4294967295UL
1470#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1471#else
1472#error "ULONG_MAX not defined!"
1473#endif
1474
1475#if LONG_MAX == 9223372036854775807L
1476#define DECIMAL_STRLEN_MAX_SIGNED 19
1477#elif LONG_MAX == 2147483647L
1478#define DECIMAL_STRLEN_MAX_SIGNED 10
1479#else
1480#error "LONG_MAX not defined!"
1481#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001482
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001483int vty_cmd_range_match(const char *range, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001484{
1485 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001486 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001488
1489 if (str == NULL)
1490 return 1;
1491
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001492 if (range[1] == '-') {
1493 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001494
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001495 val = strtol(str, &endptr, 10);
1496 if (*endptr != '\0')
1497 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001498
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001499 range += 2;
1500 p = strchr(range, '-');
1501 if (p == NULL)
1502 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001503 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001504 return 0;
1505 strncpy(buf, range, p - range);
1506 buf[p - range] = '\0';
1507 min = -strtol(buf, &endptr, 10);
1508 if (*endptr != '\0')
1509 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001510
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001511 range = p + 1;
1512 p = strchr(range, '>');
1513 if (p == NULL)
1514 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001515 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001516 return 0;
1517 strncpy(buf, range, p - range);
1518 buf[p - range] = '\0';
1519 max = strtol(buf, &endptr, 10);
1520 if (*endptr != '\0')
1521 return 0;
1522
1523 if (val < min || val > max)
1524 return 0;
1525 } else {
1526 unsigned long min, max, val;
1527
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001528 if (str[0] == '-')
1529 return 0;
1530
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001531 val = strtoul(str, &endptr, 10);
1532 if (*endptr != '\0')
1533 return 0;
1534
1535 range++;
1536 p = strchr(range, '-');
1537 if (p == NULL)
1538 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001539 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001540 return 0;
1541 strncpy(buf, range, p - range);
1542 buf[p - range] = '\0';
1543 min = strtoul(buf, &endptr, 10);
1544 if (*endptr != '\0')
1545 return 0;
1546
1547 range = p + 1;
1548 p = strchr(range, '>');
1549 if (p == NULL)
1550 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001551 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001552 return 0;
1553 strncpy(buf, range, p - range);
1554 buf[p - range] = '\0';
1555 max = strtoul(buf, &endptr, 10);
1556 if (*endptr != '\0')
1557 return 0;
1558
1559 if (val < min || val > max)
1560 return 0;
1561 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001562
1563 return 1;
1564}
1565
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001566/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001567static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001568{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001569 /* we've got "[blah]". We want to strip off the []s and redo the
1570 * match check for "blah"
1571 */
1572 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001573
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001574 if (len < 3)
1575 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001576
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001577 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001578}
1579
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001580static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001581cmd_match(const char *str, const char *command,
1582 enum match_type min, bool recur)
1583{
1584
1585 if (recur && CMD_OPTION(str))
1586 {
1587 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001588 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001589
1590 /* this would be a bug in a command, however handle it gracefully
1591 * as it we only discover it if a user tries to run it
1592 */
1593 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001594 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001595
1596 ret = cmd_match(tmp, command, min, false);
1597
1598 talloc_free(tmp);
1599
1600 return ret;
1601 }
1602 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001603 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001604 else if (CMD_RANGE(str))
1605 {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001606 if (vty_cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001607 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001608 }
1609#ifdef HAVE_IPV6
1610 else if (CMD_IPV6(str))
1611 {
1612 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001613 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001614 }
1615 else if (CMD_IPV6_PREFIX(str))
1616 {
1617 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001618 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001619 }
1620#endif /* HAVE_IPV6 */
1621 else if (CMD_IPV4(str))
1622 {
1623 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001624 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001625 }
1626 else if (CMD_IPV4_PREFIX(str))
1627 {
1628 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001629 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001630 }
1631 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001632 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001633 else if (strncmp(command, str, strlen(command)) == 0)
1634 {
1635 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001636 return EXACT_MATCH;
1637 else if (PARTLY_MATCH >= min)
1638 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001639 }
1640
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001641 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001642}
1643
1644/* Filter vector at the specified index and by the given command string, to
1645 * the desired matching level (thus allowing part matches), and return match
1646 * type flag.
1647 */
1648static enum match_type
1649cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001650{
1651 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001652 struct cmd_element *cmd_element;
1653 enum match_type match_type;
1654 vector descvec;
1655 struct desc *desc;
1656
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001657 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001658
1659 /* If command and cmd_element string does not match set NULL to vector */
1660 for (i = 0; i < vector_active(v); i++)
1661 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001662 if (index >= vector_active(cmd_element->strvec))
1663 vector_slot(v, i) = NULL;
1664 else {
1665 unsigned int j;
1666 int matched = 0;
1667
1668 descvec =
1669 vector_slot(cmd_element->strvec, index);
1670
1671 for (j = 0; j < vector_active(descvec); j++)
1672 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001673 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001674
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001675 ret = cmd_match (desc->cmd, command, level, true);
1676
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001677 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001678 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001679
1680 if (match_type < ret)
1681 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001682 }
1683 if (!matched)
1684 vector_slot(v, i) = NULL;
1685 }
1686 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001687
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001688 if (match_type == NO_MATCH)
1689 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001690
1691 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1692 * go again and filter out commands whose argument (at this index) is
1693 * 'weaker'. E.g., if we have 2 commands:
1694 *
1695 * foo bar <1-255>
1696 * foo bar BLAH
1697 *
1698 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001699 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001700 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1701 *
1702 * If we don't do a 2nd pass and filter it out, the higher-layers will
1703 * consider this to be ambiguous.
1704 */
1705 for (i = 0; i < vector_active(v); i++)
1706 if ((cmd_element = vector_slot(v, i)) != NULL) {
1707 if (index >= vector_active(cmd_element->strvec))
1708 vector_slot(v, i) = NULL;
1709 else {
1710 unsigned int j;
1711 int matched = 0;
1712
1713 descvec =
1714 vector_slot(cmd_element->strvec, index);
1715
1716 for (j = 0; j < vector_active(descvec); j++)
1717 if ((desc = vector_slot(descvec, j))) {
1718 enum match_type ret;
1719
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001720 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001721
1722 if (ret >= match_type)
1723 matched++;
1724 }
1725 if (!matched)
1726 vector_slot(v, i) = NULL;
1727 }
1728 }
1729
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001730 return match_type;
1731}
1732
1733/* Check ambiguous match */
1734static int
1735is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1736{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001737 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001738 unsigned int i;
1739 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001740 struct cmd_element *cmd_element;
1741 const char *matched = NULL;
1742 vector descvec;
1743 struct desc *desc;
1744
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001745 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1746 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1747 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1748 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1749 * that case, the string must remain allocated until this function exits or another match comes
1750 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1751 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1752 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1753 void *cmd_deopt_ctx = NULL;
1754
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001755 for (i = 0; i < vector_active(v); i++) {
1756 cmd_element = vector_slot(v, i);
1757 if (!cmd_element)
1758 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001759
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001760 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001761
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001762 descvec = vector_slot(cmd_element->strvec, index);
1763
1764 for (j = 0; j < vector_active(descvec); j++) {
1765 desc = vector_slot(descvec, j);
1766 if (!desc)
1767 continue;
1768
1769 enum match_type mtype;
1770 const char *str = desc->cmd;
1771
1772 if (CMD_OPTION(str)) {
1773 if (!cmd_deopt_ctx)
1774 cmd_deopt_ctx =
1775 talloc_named_const(tall_vty_cmd_ctx, 0,
1776 __func__);
1777 str = cmd_deopt(cmd_deopt_ctx, str);
1778 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001779 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001780 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001781
1782 switch (type) {
1783 case EXACT_MATCH:
1784 if (!(CMD_VARIABLE (str))
1785 && strcmp(command, str) == 0)
1786 match++;
1787 break;
1788 case PARTLY_MATCH:
1789 if (!(CMD_VARIABLE (str))
1790 && strncmp(command, str, strlen (command)) == 0)
1791 {
1792 if (matched
1793 && strcmp(matched,
1794 str) != 0) {
1795 ret = 1; /* There is ambiguous match. */
1796 goto free_and_return;
1797 } else
1798 matched = str;
1799 match++;
1800 }
1801 break;
1802 case RANGE_MATCH:
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001803 if (vty_cmd_range_match
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001804 (str, command)) {
1805 if (matched
1806 && strcmp(matched,
1807 str) != 0) {
1808 ret = 1;
1809 goto free_and_return;
1810 } else
1811 matched = str;
1812 match++;
1813 }
1814 break;
1815#ifdef HAVE_IPV6
1816 case IPV6_MATCH:
1817 if (CMD_IPV6(str))
1818 match++;
1819 break;
1820 case IPV6_PREFIX_MATCH:
1821 if ((mtype =
1822 cmd_ipv6_prefix_match
1823 (command)) != NO_MATCH) {
1824 if (mtype == PARTLY_MATCH) {
1825 ret = 2; /* There is incomplete match. */
1826 goto free_and_return;
1827 }
1828
1829 match++;
1830 }
1831 break;
1832#endif /* HAVE_IPV6 */
1833 case IPV4_MATCH:
1834 if (CMD_IPV4(str))
1835 match++;
1836 break;
1837 case IPV4_PREFIX_MATCH:
1838 if ((mtype =
1839 cmd_ipv4_prefix_match
1840 (command)) != NO_MATCH) {
1841 if (mtype == PARTLY_MATCH) {
1842 ret = 2; /* There is incomplete match. */
1843 goto free_and_return;
1844 }
1845
1846 match++;
1847 }
1848 break;
1849 case EXTEND_MATCH:
1850 if (CMD_VARIABLE (str))
1851 match++;
1852 break;
1853 case NO_MATCH:
1854 default:
1855 break;
1856 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001857 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001858 if (!match)
1859 vector_slot(v, i) = NULL;
1860 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001861
1862free_and_return:
1863 if (cmd_deopt_ctx)
1864 talloc_free(cmd_deopt_ctx);
1865 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001866}
1867
1868/* If src matches dst return dst string, otherwise return NULL */
1869static const char *cmd_entry_function(const char *src, const char *dst)
1870{
1871 /* Skip variable arguments. */
1872 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1873 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1874 return NULL;
1875
1876 /* In case of 'command \t', given src is NULL string. */
1877 if (src == NULL)
1878 return dst;
1879
1880 /* Matched with input string. */
1881 if (strncmp(src, dst, strlen(src)) == 0)
1882 return dst;
1883
1884 return NULL;
1885}
1886
1887/* If src matches dst return dst string, otherwise return NULL */
1888/* This version will return the dst string always if it is
1889 CMD_VARIABLE for '?' key processing */
1890static const char *cmd_entry_function_desc(const char *src, const char *dst)
1891{
1892 if (CMD_VARARG(dst))
1893 return dst;
1894
1895 if (CMD_RANGE(dst)) {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001896 if (vty_cmd_range_match(dst, src))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001897 return dst;
1898 else
1899 return NULL;
1900 }
1901#ifdef HAVE_IPV6
1902 if (CMD_IPV6(dst)) {
1903 if (cmd_ipv6_match(src))
1904 return dst;
1905 else
1906 return NULL;
1907 }
1908
1909 if (CMD_IPV6_PREFIX(dst)) {
1910 if (cmd_ipv6_prefix_match(src))
1911 return dst;
1912 else
1913 return NULL;
1914 }
1915#endif /* HAVE_IPV6 */
1916
1917 if (CMD_IPV4(dst)) {
1918 if (cmd_ipv4_match(src))
1919 return dst;
1920 else
1921 return NULL;
1922 }
1923
1924 if (CMD_IPV4_PREFIX(dst)) {
1925 if (cmd_ipv4_prefix_match(src))
1926 return dst;
1927 else
1928 return NULL;
1929 }
1930
1931 /* Optional or variable commands always match on '?' */
1932 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1933 return dst;
1934
1935 /* In case of 'command \t', given src is NULL string. */
1936 if (src == NULL)
1937 return dst;
1938
1939 if (strncmp(src, dst, strlen(src)) == 0)
1940 return dst;
1941 else
1942 return NULL;
1943}
1944
1945/* Check same string element existence. If it isn't there return
1946 1. */
1947static int cmd_unique_string(vector v, const char *str)
1948{
1949 unsigned int i;
1950 char *match;
1951
1952 for (i = 0; i < vector_active(v); i++)
1953 if ((match = vector_slot(v, i)) != NULL)
1954 if (strcmp(match, str) == 0)
1955 return 0;
1956 return 1;
1957}
1958
1959/* Compare string to description vector. If there is same string
1960 return 1 else return 0. */
1961static int desc_unique_string(vector v, const char *str)
1962{
1963 unsigned int i;
1964 struct desc *desc;
1965
1966 for (i = 0; i < vector_active(v); i++)
1967 if ((desc = vector_slot(v, i)) != NULL)
1968 if (strcmp(desc->cmd, str) == 0)
1969 return 1;
1970 return 0;
1971}
1972
1973static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1974{
1975 if (first_word != NULL &&
1976 node != AUTH_NODE &&
1977 node != VIEW_NODE &&
1978 node != AUTH_ENABLE_NODE &&
1979 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1980 return 1;
1981 return 0;
1982}
1983
1984/* '?' describe command support. */
1985static vector
1986cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1987{
1988 unsigned int i;
1989 vector cmd_vector;
1990#define INIT_MATCHVEC_SIZE 10
1991 vector matchvec;
1992 struct cmd_element *cmd_element;
1993 unsigned int index;
1994 int ret;
1995 enum match_type match;
1996 char *command;
1997 static struct desc desc_cr = { "<cr>", "" };
1998
1999 /* Set index. */
2000 if (vector_active(vline) == 0) {
2001 *status = CMD_ERR_NO_MATCH;
2002 return NULL;
2003 } else
2004 index = vector_active(vline) - 1;
2005
2006 /* Make copy vector of current node's command vector. */
2007 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2008
2009 /* Prepare match vector */
2010 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2011
2012 /* Filter commands. */
2013 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002014 for (i = 0; i < index; i++) {
2015 command = vector_slot(vline, i);
2016 if (!command)
2017 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002018
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002019 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002020
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002021 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002022 struct cmd_element *cmd_element;
2023 vector descvec;
2024 unsigned int j, k;
2025
2026 for (j = 0; j < vector_active(cmd_vector); j++)
2027 if ((cmd_element =
2028 vector_slot(cmd_vector, j)) != NULL
2029 &&
2030 (vector_active(cmd_element->strvec))) {
2031 descvec =
2032 vector_slot(cmd_element->
2033 strvec,
2034 vector_active
2035 (cmd_element->
2036 strvec) - 1);
2037 for (k = 0;
2038 k < vector_active(descvec);
2039 k++) {
2040 struct desc *desc =
2041 vector_slot(descvec,
2042 k);
2043 vector_set(matchvec,
2044 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002045 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002046 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002047
Harald Welte80d30fe2013-02-12 11:08:57 +01002048 vector_set(matchvec, &desc_cr);
2049 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002050
Harald Welte80d30fe2013-02-12 11:08:57 +01002051 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002052 }
2053
Harald Welte80d30fe2013-02-12 11:08:57 +01002054 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2055 match)) == 1) {
2056 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002057 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002058 *status = CMD_ERR_AMBIGUOUS;
2059 return NULL;
2060 } else if (ret == 2) {
2061 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002062 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002063 *status = CMD_ERR_NO_MATCH;
2064 return NULL;
2065 }
2066 }
2067
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002068 /* Prepare match vector */
2069 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2070
2071 /* Make sure that cmd_vector is filtered based on current word */
2072 command = vector_slot(vline, index);
2073 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002074 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002075
2076 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002077 for (i = 0; i < vector_active(cmd_vector); i++) {
2078 const char *string = NULL;
2079 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002080
Harald Welte80d30fe2013-02-12 11:08:57 +01002081 cmd_element = vector_slot(cmd_vector, i);
2082 if (!cmd_element)
2083 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002084
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002085 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2086 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002087 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002088 continue;
2089
Harald Welte80d30fe2013-02-12 11:08:57 +01002090 strvec = cmd_element->strvec;
2091
2092 /* if command is NULL, index may be equal to vector_active */
2093 if (command && index >= vector_active(strvec))
2094 vector_slot(cmd_vector, i) = NULL;
2095 else {
2096 /* Check if command is completed. */
2097 if (command == NULL
2098 && index == vector_active(strvec)) {
2099 string = "<cr>";
2100 if (!desc_unique_string(matchvec, string))
2101 vector_set(matchvec, &desc_cr);
2102 } else {
2103 unsigned int j;
2104 vector descvec = vector_slot(strvec, index);
2105 struct desc *desc;
2106
2107 for (j = 0; j < vector_active(descvec); j++) {
2108 desc = vector_slot(descvec, j);
2109 if (!desc)
2110 continue;
2111 string = cmd_entry_function_desc
2112 (command, desc->cmd);
2113 if (!string)
2114 continue;
2115 /* Uniqueness check */
2116 if (!desc_unique_string(matchvec, string))
2117 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002118 }
2119 }
2120 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002121 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002122 vector_free(cmd_vector);
2123
2124 if (vector_slot(matchvec, 0) == NULL) {
2125 vector_free(matchvec);
2126 *status = CMD_ERR_NO_MATCH;
2127 } else
2128 *status = CMD_SUCCESS;
2129
2130 return matchvec;
2131}
2132
2133vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2134{
2135 vector ret;
2136
2137 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2138 enum node_type onode;
2139 vector shifted_vline;
2140 unsigned int index;
2141
2142 onode = vty->node;
2143 vty->node = ENABLE_NODE;
2144 /* We can try it on enable node, cos' the vty is authenticated */
2145
2146 shifted_vline = vector_init(vector_count(vline));
2147 /* use memcpy? */
2148 for (index = 1; index < vector_active(vline); index++) {
2149 vector_set_index(shifted_vline, index - 1,
2150 vector_lookup(vline, index));
2151 }
2152
2153 ret = cmd_describe_command_real(shifted_vline, vty, status);
2154
2155 vector_free(shifted_vline);
2156 vty->node = onode;
2157 return ret;
2158 }
2159
2160 return cmd_describe_command_real(vline, vty, status);
2161}
2162
2163/* Check LCD of matched command. */
2164static int cmd_lcd(char **matched)
2165{
2166 int i;
2167 int j;
2168 int lcd = -1;
2169 char *s1, *s2;
2170 char c1, c2;
2171
2172 if (matched[0] == NULL || matched[1] == NULL)
2173 return 0;
2174
2175 for (i = 1; matched[i] != NULL; i++) {
2176 s1 = matched[i - 1];
2177 s2 = matched[i];
2178
2179 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2180 if (c1 != c2)
2181 break;
2182
2183 if (lcd < 0)
2184 lcd = j;
2185 else {
2186 if (lcd > j)
2187 lcd = j;
2188 }
2189 }
2190 return lcd;
2191}
2192
2193/* Command line completion support. */
2194static char **cmd_complete_command_real(vector vline, struct vty *vty,
2195 int *status)
2196{
2197 unsigned int i;
2198 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2199#define INIT_MATCHVEC_SIZE 10
2200 vector matchvec;
2201 struct cmd_element *cmd_element;
2202 unsigned int index;
2203 char **match_str;
2204 struct desc *desc;
2205 vector descvec;
2206 char *command;
2207 int lcd;
2208
2209 if (vector_active(vline) == 0) {
2210 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002211 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002212 return NULL;
2213 } else
2214 index = vector_active(vline) - 1;
2215
2216 /* First, filter by preceeding command string */
2217 for (i = 0; i < index; i++)
2218 if ((command = vector_slot(vline, i))) {
2219 enum match_type match;
2220 int ret;
2221
2222 /* First try completion match, if there is exactly match return 1 */
2223 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002224 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002225
2226 /* If there is exact match then filter ambiguous match else check
2227 ambiguousness. */
2228 if ((ret =
2229 is_cmd_ambiguous(command, cmd_vector, i,
2230 match)) == 1) {
2231 vector_free(cmd_vector);
2232 *status = CMD_ERR_AMBIGUOUS;
2233 return NULL;
2234 }
2235 /*
2236 else if (ret == 2)
2237 {
2238 vector_free (cmd_vector);
2239 *status = CMD_ERR_NO_MATCH;
2240 return NULL;
2241 }
2242 */
2243 }
2244
2245 /* Prepare match vector. */
2246 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2247
2248 /* Now we got into completion */
2249 for (i = 0; i < vector_active(cmd_vector); i++)
2250 if ((cmd_element = vector_slot(cmd_vector, i))) {
2251 const char *string;
2252 vector strvec = cmd_element->strvec;
2253
2254 /* Check field length */
2255 if (index >= vector_active(strvec))
2256 vector_slot(cmd_vector, i) = NULL;
2257 else {
2258 unsigned int j;
2259
2260 descvec = vector_slot(strvec, index);
2261 for (j = 0; j < vector_active(descvec); j++)
2262 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002263 const char *cmd = desc->cmd;
2264 char *tmp = NULL;
2265
2266 if (CMD_OPTION(desc->cmd)) {
2267 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2268 cmd = tmp;
2269 }
2270 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002271 if (cmd_unique_string (matchvec, string))
2272 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002273 if (tmp)
2274 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002275 }
2276 }
2277 }
2278
2279 /* We don't need cmd_vector any more. */
2280 vector_free(cmd_vector);
2281
2282 /* No matched command */
2283 if (vector_slot(matchvec, 0) == NULL) {
2284 vector_free(matchvec);
2285
2286 /* In case of 'command \t' pattern. Do you need '?' command at
2287 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002288 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002289 *status = CMD_ERR_NOTHING_TODO;
2290 else
2291 *status = CMD_ERR_NO_MATCH;
2292 return NULL;
2293 }
2294
2295 /* Only one matched */
2296 if (vector_slot(matchvec, 1) == NULL) {
2297 match_str = (char **)matchvec->index;
2298 vector_only_wrapper_free(matchvec);
2299 *status = CMD_COMPLETE_FULL_MATCH;
2300 return match_str;
2301 }
2302 /* Make it sure last element is NULL. */
2303 vector_set(matchvec, NULL);
2304
2305 /* Check LCD of matched strings. */
2306 if (vector_slot(vline, index) != NULL) {
2307 lcd = cmd_lcd((char **)matchvec->index);
2308
2309 if (lcd) {
2310 int len = strlen(vector_slot(vline, index));
2311
2312 if (len < lcd) {
2313 char *lcdstr;
2314
2315 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2316 "complete-lcdstr");
2317 memcpy(lcdstr, matchvec->index[0], lcd);
2318 lcdstr[lcd] = '\0';
2319
2320 /* match_str = (char **) &lcdstr; */
2321
2322 /* Free matchvec. */
2323 for (i = 0; i < vector_active(matchvec); i++) {
2324 if (vector_slot(matchvec, i))
2325 talloc_free(vector_slot(matchvec, i));
2326 }
2327 vector_free(matchvec);
2328
2329 /* Make new matchvec. */
2330 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2331 vector_set(matchvec, lcdstr);
2332 match_str = (char **)matchvec->index;
2333 vector_only_wrapper_free(matchvec);
2334
2335 *status = CMD_COMPLETE_MATCH;
2336 return match_str;
2337 }
2338 }
2339 }
2340
2341 match_str = (char **)matchvec->index;
2342 vector_only_wrapper_free(matchvec);
2343 *status = CMD_COMPLETE_LIST_MATCH;
2344 return match_str;
2345}
2346
2347char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2348{
2349 char **ret;
2350
2351 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2352 enum node_type onode;
2353 vector shifted_vline;
2354 unsigned int index;
2355
2356 onode = vty->node;
2357 vty->node = ENABLE_NODE;
2358 /* We can try it on enable node, cos' the vty is authenticated */
2359
2360 shifted_vline = vector_init(vector_count(vline));
2361 /* use memcpy? */
2362 for (index = 1; index < vector_active(vline); index++) {
2363 vector_set_index(shifted_vline, index - 1,
2364 vector_lookup(vline, index));
2365 }
2366
2367 ret = cmd_complete_command_real(shifted_vline, vty, status);
2368
2369 vector_free(shifted_vline);
2370 vty->node = onode;
2371 return ret;
2372 }
2373
2374 return cmd_complete_command_real(vline, vty, status);
2375}
2376
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002377static struct vty_parent_node *vty_parent(struct vty *vty)
2378{
2379 return llist_first_entry_or_null(&vty->parent_nodes,
2380 struct vty_parent_node,
2381 entry);
2382}
2383
2384static bool vty_pop_parent(struct vty *vty)
2385{
2386 struct vty_parent_node *parent = vty_parent(vty);
2387 if (!parent)
2388 return false;
2389 llist_del(&parent->entry);
2390 vty->node = parent->node;
2391 vty->priv = parent->priv;
2392 if (vty->indent)
2393 talloc_free(vty->indent);
2394 vty->indent = parent->indent;
2395 talloc_free(parent);
2396 return true;
2397}
2398
2399static void vty_clear_parents(struct vty *vty)
2400{
2401 while (vty_pop_parent(vty));
2402}
2403
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002404/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002405/*
2406 * This function MUST eventually converge on a node when called repeatedly,
2407 * there must not be any cycles.
2408 * All 'config' nodes shall converge on CONFIG_NODE.
2409 * All other 'enable' nodes shall converge on ENABLE_NODE.
2410 * All 'view' only nodes shall converge on VIEW_NODE.
2411 * All other nodes shall converge on themselves or it must be ensured,
2412 * that the user's rights are not extended anyhow by calling this function.
2413 *
2414 * Note that these requirements also apply to all functions that are used
2415 * as go_parent_cb.
2416 * Note also that this function relies on the is_config_child callback to
2417 * recognize non-config nodes if go_parent_cb is not set.
2418 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002419int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002420{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002421 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002422 case AUTH_NODE:
2423 case VIEW_NODE:
2424 case ENABLE_NODE:
2425 case CONFIG_NODE:
2426 vty_clear_parents(vty);
2427 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002428
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002429 case AUTH_ENABLE_NODE:
2430 vty->node = VIEW_NODE;
2431 vty_clear_parents(vty);
2432 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002433
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002434 default:
2435 if (host.app_info->go_parent_cb)
2436 host.app_info->go_parent_cb(vty);
2437 vty_pop_parent(vty);
2438 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002439 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002440
2441 return vty->node;
2442}
2443
2444/* Execute command by argument vline vector. */
2445static int
2446cmd_execute_command_real(vector vline, struct vty *vty,
2447 struct cmd_element **cmd)
2448{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002449 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002450 unsigned int index;
2451 vector cmd_vector;
2452 struct cmd_element *cmd_element;
2453 struct cmd_element *matched_element;
2454 unsigned int matched_count, incomplete_count;
2455 int argc;
2456 const char *argv[CMD_ARGC_MAX];
2457 enum match_type match = 0;
2458 int varflag;
2459 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002460 int rc;
2461 /* Used for temporary storage of cmd_deopt() allocated arguments during
2462 argv[] generation */
2463 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002464
2465 /* Make copy of command elements. */
2466 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2467
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002468 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002469 if ((command = vector_slot(vline, index))) {
2470 int ret;
2471
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002472 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002473 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002474
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002475 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002476 break;
2477
2478 ret =
2479 is_cmd_ambiguous(command, cmd_vector, index, match);
2480
2481 if (ret == 1) {
2482 vector_free(cmd_vector);
2483 return CMD_ERR_AMBIGUOUS;
2484 } else if (ret == 2) {
2485 vector_free(cmd_vector);
2486 return CMD_ERR_NO_MATCH;
2487 }
2488 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002489 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002490
2491 /* Check matched count. */
2492 matched_element = NULL;
2493 matched_count = 0;
2494 incomplete_count = 0;
2495
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002496 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002497 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002498 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002499 || index >= cmd_element->cmdsize) {
2500 matched_element = cmd_element;
2501#if 0
2502 printf("DEBUG: %s\n", cmd_element->string);
2503#endif
2504 matched_count++;
2505 } else {
2506 incomplete_count++;
2507 }
2508 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002509 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002510
2511 /* Finish of using cmd_vector. */
2512 vector_free(cmd_vector);
2513
2514 /* To execute command, matched_count must be 1. */
2515 if (matched_count == 0) {
2516 if (incomplete_count)
2517 return CMD_ERR_INCOMPLETE;
2518 else
2519 return CMD_ERR_NO_MATCH;
2520 }
2521
2522 if (matched_count > 1)
2523 return CMD_ERR_AMBIGUOUS;
2524
2525 /* Argument treatment */
2526 varflag = 0;
2527 argc = 0;
2528
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002529 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2530
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002531 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002532 if (argc == CMD_ARGC_MAX) {
2533 rc = CMD_ERR_EXEED_ARGC_MAX;
2534 goto rc_free_deopt_ctx;
2535 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002536 if (varflag) {
2537 argv[argc++] = vector_slot(vline, i);
2538 continue;
2539 }
2540
2541 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002542 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002543
2544 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002545 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002546 struct desc *desc = vector_slot(descvec, 0);
2547
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002548 if (CMD_OPTION(desc->cmd)) {
2549 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2550 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2551 } else {
2552 tmp_cmd = desc->cmd;
2553 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002554
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002555 if (CMD_VARARG(tmp_cmd))
2556 varflag = 1;
2557 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002558 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002559 else if (CMD_OPTION(desc->cmd))
2560 argv[argc++] = tmp_cmd;
2561 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002562 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002563 /* multi choice argument. look up which choice
2564 the user meant (can only be one after
2565 filtering and checking for ambigous). For instance,
2566 if user typed "th" for "(two|three)" arg, we
2567 want to pass "three" in argv[]. */
2568 for (j = 0; j < vector_active(descvec); j++) {
2569 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002570 if (!desc)
2571 continue;
2572 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2573 continue;
2574 if (CMD_OPTION(desc->cmd)) {
2575 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2576 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2577 } else {
2578 tmp_cmd = desc->cmd;
2579 }
2580
2581 if(CMD_VARIABLE(tmp_cmd)) {
2582 argv[argc++] = vector_slot(vline, i);
2583 } else {
2584 argv[argc++] = tmp_cmd;
2585 }
2586 break;
2587 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002588 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002589 }
2590
2591 /* For vtysh execution. */
2592 if (cmd)
2593 *cmd = matched_element;
2594
2595 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002596 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002597 else {
2598 /* Execute matched command. */
2599 struct vty_parent_node this_node = {
2600 .node = vty->node,
2601 .priv = vty->priv,
2602 .indent = vty->indent,
2603 };
2604 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002605 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002606
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002607 /* If we have stepped down into a child node, push a parent frame.
2608 * The causality is such: we don't expect every single node entry implementation to push
2609 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2610 * a parent node. Hence if the node changed without the parent node changing, we must
2611 * have stepped into a child node. */
2612 if (vty->node != this_node.node && parent == vty_parent(vty)
2613 && vty->node > CONFIG_NODE) {
2614 /* Push the parent node. */
2615 parent = talloc_zero(vty, struct vty_parent_node);
2616 *parent = this_node;
2617 llist_add(&parent->entry, &vty->parent_nodes);
2618 }
2619 }
2620
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002621rc_free_deopt_ctx:
2622 /* Now after we called the command func, we can free temporary strings */
2623 talloc_free(cmd_deopt_ctx);
2624 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002625}
2626
2627int
2628cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2629 int vtysh)
2630{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002631 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002632 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002633
2634 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002635
2636 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2637 vector shifted_vline;
2638 unsigned int index;
2639
2640 vty->node = ENABLE_NODE;
2641 /* We can try it on enable node, cos' the vty is authenticated */
2642
2643 shifted_vline = vector_init(vector_count(vline));
2644 /* use memcpy? */
2645 for (index = 1; index < vector_active(vline); index++) {
2646 vector_set_index(shifted_vline, index - 1,
2647 vector_lookup(vline, index));
2648 }
2649
2650 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2651
2652 vector_free(shifted_vline);
2653 vty->node = onode;
2654 return ret;
2655 }
2656
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002657 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002658}
2659
2660/* Execute command by argument readline. */
2661int
2662cmd_execute_command_strict(vector vline, struct vty *vty,
2663 struct cmd_element **cmd)
2664{
2665 unsigned int i;
2666 unsigned int index;
2667 vector cmd_vector;
2668 struct cmd_element *cmd_element;
2669 struct cmd_element *matched_element;
2670 unsigned int matched_count, incomplete_count;
2671 int argc;
2672 const char *argv[CMD_ARGC_MAX];
2673 int varflag;
2674 enum match_type match = 0;
2675 char *command;
2676
2677 /* Make copy of command element */
2678 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2679
2680 for (index = 0; index < vector_active(vline); index++)
2681 if ((command = vector_slot(vline, index))) {
2682 int ret;
2683
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002684 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002685 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002686
2687 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002688 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002689 break;
2690
2691 ret =
2692 is_cmd_ambiguous(command, cmd_vector, index, match);
2693 if (ret == 1) {
2694 vector_free(cmd_vector);
2695 return CMD_ERR_AMBIGUOUS;
2696 }
2697 if (ret == 2) {
2698 vector_free(cmd_vector);
2699 return CMD_ERR_NO_MATCH;
2700 }
2701 }
2702
2703 /* Check matched count. */
2704 matched_element = NULL;
2705 matched_count = 0;
2706 incomplete_count = 0;
2707 for (i = 0; i < vector_active(cmd_vector); i++)
2708 if (vector_slot(cmd_vector, i) != NULL) {
2709 cmd_element = vector_slot(cmd_vector, i);
2710
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002711 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002712 || index >= cmd_element->cmdsize) {
2713 matched_element = cmd_element;
2714 matched_count++;
2715 } else
2716 incomplete_count++;
2717 }
2718
2719 /* Finish of using cmd_vector. */
2720 vector_free(cmd_vector);
2721
2722 /* To execute command, matched_count must be 1. */
2723 if (matched_count == 0) {
2724 if (incomplete_count)
2725 return CMD_ERR_INCOMPLETE;
2726 else
2727 return CMD_ERR_NO_MATCH;
2728 }
2729
2730 if (matched_count > 1)
2731 return CMD_ERR_AMBIGUOUS;
2732
2733 /* Argument treatment */
2734 varflag = 0;
2735 argc = 0;
2736
2737 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002738 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002739 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002740 if (varflag) {
2741 argv[argc++] = vector_slot(vline, i);
2742 continue;
2743 }
2744
2745 vector descvec = vector_slot(matched_element->strvec, i);
2746
2747 if (vector_active(descvec) == 1) {
2748 struct desc *desc = vector_slot(descvec, 0);
2749
2750 if (CMD_VARARG(desc->cmd))
2751 varflag = 1;
2752
2753 if (varflag || CMD_VARIABLE(desc->cmd)
2754 || CMD_OPTION(desc->cmd))
2755 argv[argc++] = vector_slot(vline, i);
2756 } else {
2757 argv[argc++] = vector_slot(vline, i);
2758 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002759 }
2760
2761 /* For vtysh execution. */
2762 if (cmd)
2763 *cmd = matched_element;
2764
2765 if (matched_element->daemon)
2766 return CMD_SUCCESS_DAEMON;
2767
2768 /* Now execute matched command */
2769 return (*matched_element->func) (matched_element, vty, argc, argv);
2770}
2771
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002772static inline size_t len(const char *str)
2773{
2774 return str? strlen(str) : 0;
2775}
2776
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002777/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2778 * is longer than b, a must start with exactly b, and vice versa.
2779 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2780 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002781static int indent_cmp(const char *a, const char *b)
2782{
2783 size_t al, bl;
2784 al = len(a);
2785 bl = len(b);
2786 if (al > bl) {
2787 if (bl && strncmp(a, b, bl) != 0)
2788 return EINVAL;
2789 return 1;
2790 }
2791 /* al <= bl */
2792 if (al && strncmp(a, b, al) != 0)
2793 return EINVAL;
2794 return (al < bl)? -1 : 0;
2795}
2796
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002797/* Configration make from file. */
2798int config_from_file(struct vty *vty, FILE * fp)
2799{
2800 int ret;
2801 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002802 char *indent;
2803 int cmp;
2804 struct vty_parent_node this_node;
2805 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002806
2807 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002808 indent = NULL;
2809 vline = NULL;
2810 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002811
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002812 if (ret != CMD_SUCCESS)
2813 goto return_invalid_indent;
2814
2815 /* In case of comment or empty line */
2816 if (vline == NULL) {
2817 if (indent) {
2818 talloc_free(indent);
2819 indent = NULL;
2820 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002821 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002822 }
2823
Neels Hofmeyr43063632017-09-19 23:54:01 +02002824 /* We have a nonempty line. */
2825 if (!vty->indent) {
2826 /* We have just entered a node and expecting the first child to come up; but we
2827 * may also skip right back to a parent or ancestor level. */
2828 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002829
Neels Hofmeyr43063632017-09-19 23:54:01 +02002830 /* If there is no parent, record any indentation we encounter. */
2831 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2832
2833 if (cmp == EINVAL)
2834 goto return_invalid_indent;
2835
2836 if (cmp <= 0) {
2837 /* We have gone right back to the parent level or higher, we are skipping
2838 * this child node level entirely. Pop the parent to go back to a node
2839 * that was actually there (to reinstate vty->indent) and re-use below
2840 * go-parent while-loop to find an accurate match of indent in the node
2841 * ancestry. */
2842 vty_go_parent(vty);
2843 } else {
2844 /* The indent is deeper than the just entered parent, record the new
2845 * indentation characters. */
2846 vty->indent = talloc_strdup(vty, indent);
2847 /* This *is* the new indentation. */
2848 cmp = 0;
2849 }
2850 } else {
2851 /* There is a known indentation for this node level, validate and detect node
2852 * exits. */
2853 cmp = indent_cmp(indent, vty->indent);
2854 if (cmp == EINVAL)
2855 goto return_invalid_indent;
2856 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002857
2858 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2859 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2860 while (cmp < 0) {
2861 vty_go_parent(vty);
2862 cmp = indent_cmp(indent, vty->indent);
2863 if (cmp == EINVAL)
2864 goto return_invalid_indent;
2865 }
2866
2867 /* More indent without having entered a child node level? Either the parent node's indent
2868 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2869 * or the indentation increased even though the vty command didn't enter a child. */
2870 if (cmp > 0)
2871 goto return_invalid_indent;
2872
2873 /* Remember the current node before the command possibly changes it. */
2874 this_node = (struct vty_parent_node){
2875 .node = vty->node,
2876 .priv = vty->priv,
2877 .indent = vty->indent,
2878 };
2879
2880 parent = vty_parent(vty);
2881 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002882 cmd_free_strvec(vline);
2883
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002884 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002885 if (indent) {
2886 talloc_free(indent);
2887 indent = NULL;
2888 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002889 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002890 }
2891
2892 /* If we have stepped down into a child node, push a parent frame.
2893 * The causality is such: we don't expect every single node entry implementation to push
2894 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2895 * a parent node. Hence if the node changed without the parent node changing, we must
2896 * have stepped into a child node (and now expect a deeper indent). */
2897 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2898 /* Push the parent node. */
2899 parent = talloc_zero(vty, struct vty_parent_node);
2900 *parent = this_node;
2901 llist_add(&parent->entry, &vty->parent_nodes);
2902
2903 /* The current talloc'ed vty->indent string will now be owned by this parent
2904 * struct. Indicate that we don't know what deeper indent characters the user
2905 * will choose. */
2906 vty->indent = NULL;
2907 }
2908
2909 if (indent) {
2910 talloc_free(indent);
2911 indent = NULL;
2912 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002913 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002914 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2915 while (vty_parent(vty))
2916 vty_go_parent(vty);
2917
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002918 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002919
2920return_invalid_indent:
2921 if (vline)
2922 cmd_free_strvec(vline);
2923 if (indent) {
2924 talloc_free(indent);
2925 indent = NULL;
2926 }
2927 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002928}
2929
2930/* Configration from terminal */
2931DEFUN(config_terminal,
2932 config_terminal_cmd,
2933 "configure terminal",
2934 "Configuration from vty interface\n" "Configuration terminal\n")
2935{
2936 if (vty_config_lock(vty))
2937 vty->node = CONFIG_NODE;
2938 else {
2939 vty_out(vty, "VTY configuration is locked by other VTY%s",
2940 VTY_NEWLINE);
2941 return CMD_WARNING;
2942 }
2943 return CMD_SUCCESS;
2944}
2945
2946/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002947DEFUN(enable, config_enable_cmd,
2948 "enable [expert-mode]",
2949 "Turn on privileged mode command\n"
2950 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002951{
2952 /* If enable password is NULL, change to ENABLE_NODE */
2953 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2954 vty->type == VTY_SHELL_SERV)
2955 vty->node = ENABLE_NODE;
2956 else
2957 vty->node = AUTH_ENABLE_NODE;
2958
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002959 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002960
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002961 return CMD_SUCCESS;
2962}
2963
2964/* Disable command */
2965DEFUN(disable,
2966 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2967{
2968 if (vty->node == ENABLE_NODE)
2969 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002970
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002971 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002972
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002973 return CMD_SUCCESS;
2974}
2975
2976/* Down vty node level. */
2977gDEFUN(config_exit,
2978 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2979{
2980 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002981 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002982 case VIEW_NODE:
2983 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002984 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002985 break;
2986 case CONFIG_NODE:
2987 vty->node = ENABLE_NODE;
2988 vty_config_unlock(vty);
2989 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002990 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002991 if (vty->node > CONFIG_NODE)
2992 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002993 break;
2994 }
2995 return CMD_SUCCESS;
2996}
2997
2998/* End of configuration. */
2999 gDEFUN(config_end,
3000 config_end_cmd, "end", "End current mode and change to enable mode.")
3001{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003002 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02003003 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003004
3005 /* Repeatedly call go_parent until a top node is reached. */
3006 while (vty->node > CONFIG_NODE) {
3007 if (vty->node == last_node) {
3008 /* Ensure termination, this shouldn't happen. */
3009 break;
3010 }
3011 last_node = vty->node;
3012 vty_go_parent(vty);
3013 }
3014
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003015 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003016 if (vty->node > ENABLE_NODE)
3017 vty->node = ENABLE_NODE;
3018 vty->index = NULL;
3019 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003020 }
3021 return CMD_SUCCESS;
3022}
3023
Oliver Smith8a482fd2021-07-12 18:18:51 +02003024DEFUN(shutdown,
3025 shutdown_cmd, "shutdown", "Request a shutdown of the program\n")
3026{
3027 LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n");
3028 vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE);
3029
3030 /* Same exit path as if it was killed by the service manager */
3031 kill(getpid(), SIGTERM);
3032
3033 return CMD_SUCCESS;
3034}
3035
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003036/* Show version. */
3037DEFUN(show_version,
3038 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3039{
Harald Welte237f6242010-05-25 23:00:45 +02003040 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3041 host.app_info->version,
3042 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3043 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003044
3045 return CMD_SUCCESS;
3046}
3047
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003048DEFUN(show_online_help,
3049 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3050{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003051 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003052 return CMD_SUCCESS;
3053}
3054
Oliver Smith0c78bc62021-07-12 17:28:36 +02003055DEFUN(show_pid,
3056 show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
3057{
3058 vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
3059 return CMD_SUCCESS;
3060}
3061
Oliver Smithd243c2a2021-07-09 17:19:32 +02003062DEFUN(show_uptime,
3063 show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
3064{
Alexander Couzens06929162021-09-05 23:08:59 +02003065 vty_out(vty, "%s has been running for ", host.app_info->name);
3066 vty_out_uptime(vty, &starttime);
3067 vty_out_newline(vty);
Oliver Smithd243c2a2021-07-09 17:19:32 +02003068
3069 return CMD_SUCCESS;
3070}
3071
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003072/* Help display function for all node. */
3073gDEFUN(config_help,
3074 config_help_cmd, "help", "Description of the interactive help system\n")
3075{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003076 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3077 "anytime at the command line please press '?'.%s%s"
3078 "If nothing matches, the help list will be empty and you must backup%s"
3079 " until entering a '?' shows the available options.%s"
3080 "Two styles of help are provided:%s"
3081 "1. Full help is available when you are ready to enter a%s"
3082 "command argument (e.g. 'show ?') and describes each possible%s"
3083 "argument.%s"
3084 "2. Partial help is provided when an abbreviated argument is entered%s"
3085 " and you want to know what arguments match the input%s"
3086 " (e.g. 'show me?'.)%s%s",
3087 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3088 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3089 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3090 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003091 return CMD_SUCCESS;
3092}
3093
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003094enum {
3095 ATTR_TYPE_GLOBAL = (1 << 0),
3096 ATTR_TYPE_LIB = (1 << 1),
3097 ATTR_TYPE_APP = (1 << 2),
3098};
3099
3100static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3101{
3102 const char *desc;
3103 unsigned int i;
3104 bool found;
3105 char flag;
3106
3107 if (attr_mask & ATTR_TYPE_GLOBAL) {
3108 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3109
3110 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003111 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003112 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003113
3114 /* Skip attributes without flags */
3115 if (flag != '.')
3116 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003117 }
3118 }
3119
3120 if (attr_mask & ATTR_TYPE_LIB) {
3121 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3122
3123 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3124 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3125 continue;
3126 found = true;
3127
3128 flag = cmd_lib_attr_letters[i];
3129 if (flag == '\0')
3130 flag = '.';
3131
3132 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3133 }
3134
3135 if (!found)
3136 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3137 }
3138
3139 if (attr_mask & ATTR_TYPE_APP) {
3140 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3141
3142 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3143 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3144 continue;
3145 found = true;
3146
3147 flag = host.app_info->usr_attr_letters[i];
3148 if (flag == '\0')
3149 flag = '.';
3150
3151 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3152 }
3153
3154 if (!found)
3155 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3156 }
3157}
3158
3159gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3160 "show vty-attributes",
3161 SHOW_STR "List of VTY attributes\n")
3162{
3163 print_attr_list(vty, 0xff);
3164 return CMD_SUCCESS;
3165}
3166
3167gDEFUN(show_vty_attr, show_vty_attr_cmd,
3168 "show vty-attributes (application|library|global)",
3169 SHOW_STR "List of VTY attributes\n"
3170 "Application specific attributes only\n"
3171 "Library specific attributes only\n"
3172 "Global attributes only\n")
3173{
3174 unsigned int attr_mask = 0;
3175
3176 if (argv[0][0] == 'g') /* global */
3177 attr_mask |= ATTR_TYPE_GLOBAL;
3178 else if (argv[0][0] == 'l') /* library */
3179 attr_mask |= ATTR_TYPE_LIB;
3180 else if (argv[0][0] == 'a') /* application */
3181 attr_mask |= ATTR_TYPE_APP;
3182
3183 print_attr_list(vty, attr_mask);
3184 return CMD_SUCCESS;
3185}
3186
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003187/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003188static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003189{
3190 unsigned int flag_mask = 0x00;
3191 unsigned int f, i;
3192
3193 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3194 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3195 const struct cmd_element *cmd;
3196 char flag_letter;
3197
3198 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3199 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003200 if (cmd->attr & CMD_ATTR_DEPRECATED)
3201 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003202 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003203 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003204 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003205 continue;
3206
3207 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3208 flag_letter = cmd_lib_attr_letters[f];
3209 else
3210 flag_letter = host.app_info->usr_attr_letters[f];
3211
3212 if (flag_letter == '\0')
3213 continue;
3214
3215 flag_mask |= (1 << f);
3216 break;
3217 }
3218 }
3219
3220 return flag_mask;
3221}
3222
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003223/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3224static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3225{
3226 static char char_mask[8 + 1];
3227 char *ptr = &char_mask[0];
3228
3229 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003230 if (cmd->attr & CMD_ATTR_HIDDEN)
3231 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3232 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003233 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3234 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3235 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3236 else
3237 *(ptr++) = '.';
3238
3239 *ptr = '\0';
3240
3241 return char_mask;
3242}
3243
3244/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003245static const char *cmd_flag_mask(const struct cmd_element *cmd,
3246 unsigned int flag_mask)
3247{
3248 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3249 char *ptr = &char_mask[0];
3250 char flag_letter;
3251 unsigned int f;
3252
3253 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003254 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003255 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003256 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003257 *(ptr++) = '.';
3258 continue;
3259 }
3260
3261 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3262 flag_letter = cmd_lib_attr_letters[f];
3263 else
3264 flag_letter = host.app_info->usr_attr_letters[f];
3265
3266 *(ptr++) = flag_letter ? flag_letter : '.';
3267 }
3268
3269 *ptr = '\0';
3270
3271 return char_mask;
3272}
3273
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003274/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003275gDEFUN(config_list, config_list_cmd,
3276 "list [with-flags]",
3277 "Print command list\n"
3278 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003279{
3280 unsigned int i;
3281 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003282 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003283 struct cmd_element *cmd;
3284
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003285 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003286 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003287
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003288 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3289 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3290 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003291 if (cmd->attr & CMD_ATTR_DEPRECATED)
3292 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003293 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003294 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003295 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003296 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3297 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003298 vty_out(vty, " %s %s %s%s",
3299 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003300 cmd_flag_mask(cmd, flag_mask),
3301 cmd->string, VTY_NEWLINE);
3302 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003303 }
3304
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003305 return CMD_SUCCESS;
3306}
3307
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003308static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003309{
3310 unsigned int i;
3311 int fd;
3312 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003313 char *config_file_tmp = NULL;
3314 char *config_file_sav = NULL;
3315 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003316 struct stat st;
3317
3318 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003319
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003320 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3321 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3322 * manually instead. */
3323
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003324 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003325 config_file_sav =
3326 _talloc_zero(tall_vty_cmd_ctx,
3327 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3328 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003329 if (!config_file_sav)
3330 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003331 strcpy(config_file_sav, config_file);
3332 strcat(config_file_sav, CONF_BACKUP_EXT);
3333
3334 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003335 "config_file_tmp");
3336 if (!config_file_tmp) {
3337 talloc_free(config_file_sav);
3338 return -1;
3339 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003340 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3341
3342 /* Open file to configuration write. */
3343 fd = mkstemp(config_file_tmp);
3344 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003345 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003346 talloc_free(config_file_tmp);
3347 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003348 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003349 }
3350
3351 /* Make vty for configuration file. */
3352 file_vty = vty_new();
3353 file_vty->fd = fd;
3354 file_vty->type = VTY_FILE;
3355
3356 /* Config file header print. */
3357 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003358 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003359 //vty_time_print (file_vty, 1);
3360 vty_out(file_vty, "!\n");
3361
3362 for (i = 0; i < vector_active(cmdvec); i++)
3363 if ((node = vector_slot(cmdvec, i)) && node->func) {
3364 if ((*node->func) (file_vty))
3365 vty_out(file_vty, "!\n");
3366 }
3367 vty_close(file_vty);
3368
3369 if (unlink(config_file_sav) != 0)
3370 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003371 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003372 talloc_free(config_file_sav);
3373 talloc_free(config_file_tmp);
3374 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003375 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003376 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003377
3378 /* Only link the .sav file if the original file exists */
3379 if (stat(config_file, &st) == 0) {
3380 if (link(config_file, config_file_sav) != 0) {
3381 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3382 talloc_free(config_file_sav);
3383 talloc_free(config_file_tmp);
3384 unlink(config_file_tmp);
3385 return -3;
3386 }
3387 sync();
3388 if (unlink(config_file) != 0) {
3389 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3390 talloc_free(config_file_sav);
3391 talloc_free(config_file_tmp);
3392 unlink(config_file_tmp);
3393 return -4;
3394 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003395 }
3396 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003397 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003398 talloc_free(config_file_sav);
3399 talloc_free(config_file_tmp);
3400 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003401 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003402 }
3403 unlink(config_file_tmp);
3404 sync();
3405
3406 talloc_free(config_file_sav);
3407 talloc_free(config_file_tmp);
3408
3409 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003410 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3411 return -6;
3412 }
3413
3414 return 0;
3415}
3416
3417
3418/* Write current configuration into file. */
3419DEFUN(config_write_file,
3420 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003421 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003422 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003423 "Write to configuration file\n"
3424 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003425{
3426 char *failed_file;
3427 int rc;
3428
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003429 if (host.app_info->config_is_consistent) {
3430 rc = host.app_info->config_is_consistent(vty);
3431 if (!rc) {
3432 vty_out(vty, "Configuration is not consistent%s",
3433 VTY_NEWLINE);
3434 return CMD_WARNING;
3435 }
3436 }
3437
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003438 if (argc == 1)
3439 host_config_set(argv[0]);
3440
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003441 if (host.config == NULL) {
3442 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3443 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003444 return CMD_WARNING;
3445 }
3446
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003447 rc = write_config_file(host.config, &failed_file);
3448 switch (rc) {
3449 case -1:
3450 vty_out(vty, "Can't open configuration file %s.%s",
3451 failed_file, VTY_NEWLINE);
3452 rc = CMD_WARNING;
3453 break;
3454 case -2:
3455 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3456 failed_file, VTY_NEWLINE);
3457 rc = CMD_WARNING;
3458 break;
3459 case -3:
3460 vty_out(vty, "Can't backup old configuration file %s.%s",
3461 failed_file, VTY_NEWLINE);
3462 rc = CMD_WARNING;
3463 break;
3464 case -4:
3465 vty_out(vty, "Can't unlink configuration file %s.%s",
3466 failed_file, VTY_NEWLINE);
3467 rc = CMD_WARNING;
3468 break;
3469 case -5:
3470 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3471 VTY_NEWLINE);
3472 rc = CMD_WARNING;
3473 break;
3474 case -6:
3475 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3476 failed_file, strerror(errno), errno, VTY_NEWLINE);
3477 rc = CMD_WARNING;
3478 break;
3479 default:
3480 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3481 rc = CMD_SUCCESS;
3482 break;
3483 }
3484
3485 talloc_free(failed_file);
3486 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003487}
3488
3489ALIAS(config_write_file,
3490 config_write_cmd,
3491 "write", "Write running configuration to memory, network, or terminal\n")
3492
3493 ALIAS(config_write_file,
3494 config_write_memory_cmd,
3495 "write memory",
3496 "Write running configuration to memory, network, or terminal\n"
3497 "Write configuration to the file (same as write file)\n")
3498
3499 ALIAS(config_write_file,
3500 copy_runningconfig_startupconfig_cmd,
3501 "copy running-config startup-config",
3502 "Copy configuration\n"
3503 "Copy running config to... \n"
3504 "Copy running config to startup config (same as write file)\n")
3505
3506/* Write current configuration into the terminal. */
3507 DEFUN(config_write_terminal,
3508 config_write_terminal_cmd,
3509 "write terminal",
3510 "Write running configuration to memory, network, or terminal\n"
3511 "Write to terminal\n")
3512{
3513 unsigned int i;
3514 struct cmd_node *node;
3515
3516 if (vty->type == VTY_SHELL_SERV) {
3517 for (i = 0; i < vector_active(cmdvec); i++)
3518 if ((node = vector_slot(cmdvec, i)) && node->func
3519 && node->vtysh) {
3520 if ((*node->func) (vty))
3521 vty_out(vty, "!%s", VTY_NEWLINE);
3522 }
3523 } else {
3524 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3525 VTY_NEWLINE);
3526 vty_out(vty, "!%s", VTY_NEWLINE);
3527
3528 for (i = 0; i < vector_active(cmdvec); i++)
3529 if ((node = vector_slot(cmdvec, i)) && node->func) {
3530 if ((*node->func) (vty))
3531 vty_out(vty, "!%s", VTY_NEWLINE);
3532 }
3533 vty_out(vty, "end%s", VTY_NEWLINE);
3534 }
3535 return CMD_SUCCESS;
3536}
3537
3538/* Write current configuration into the terminal. */
3539ALIAS(config_write_terminal,
3540 show_running_config_cmd,
3541 "show running-config", SHOW_STR "running configuration\n")
3542
3543/* Write startup configuration into the terminal. */
3544 DEFUN(show_startup_config,
3545 show_startup_config_cmd,
3546 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3547{
3548 char buf[BUFSIZ];
3549 FILE *confp;
3550
3551 confp = fopen(host.config, "r");
3552 if (confp == NULL) {
3553 vty_out(vty, "Can't open configuration file [%s]%s",
3554 host.config, VTY_NEWLINE);
3555 return CMD_WARNING;
3556 }
3557
3558 while (fgets(buf, BUFSIZ, confp)) {
3559 char *cp = buf;
3560
3561 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3562 cp++;
3563 *cp = '\0';
3564
3565 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3566 }
3567
3568 fclose(confp);
3569
3570 return CMD_SUCCESS;
3571}
3572
3573/* Hostname configuration */
3574DEFUN(config_hostname,
3575 hostname_cmd,
3576 "hostname WORD",
3577 "Set system's network name\n" "This system's network name\n")
3578{
3579 if (!isalpha((int)*argv[0])) {
3580 vty_out(vty, "Please specify string starting with alphabet%s",
3581 VTY_NEWLINE);
3582 return CMD_WARNING;
3583 }
3584
3585 if (host.name)
3586 talloc_free(host.name);
3587
3588 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3589 return CMD_SUCCESS;
3590}
3591
3592DEFUN(config_no_hostname,
3593 no_hostname_cmd,
3594 "no hostname [HOSTNAME]",
3595 NO_STR "Reset system's network name\n" "Host name of this router\n")
3596{
3597 if (host.name)
3598 talloc_free(host.name);
3599 host.name = NULL;
3600 return CMD_SUCCESS;
3601}
3602
3603/* VTY interface password set. */
3604DEFUN(config_password, password_cmd,
3605 "password (8|) WORD",
3606 "Assign the terminal connection password\n"
3607 "Specifies a HIDDEN password will follow\n"
3608 "dummy string \n" "The HIDDEN line password string\n")
3609{
3610 /* Argument check. */
3611 if (argc == 0) {
3612 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3613 return CMD_WARNING;
3614 }
3615
3616 if (argc == 2) {
3617 if (*argv[0] == '8') {
3618 if (host.password)
3619 talloc_free(host.password);
3620 host.password = NULL;
3621 if (host.password_encrypt)
3622 talloc_free(host.password_encrypt);
3623 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3624 return CMD_SUCCESS;
3625 } else {
3626 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3627 return CMD_WARNING;
3628 }
3629 }
3630
3631 if (!isalnum((int)*argv[0])) {
3632 vty_out(vty,
3633 "Please specify string starting with alphanumeric%s",
3634 VTY_NEWLINE);
3635 return CMD_WARNING;
3636 }
3637
3638 if (host.password)
3639 talloc_free(host.password);
3640 host.password = NULL;
3641
3642#ifdef VTY_CRYPT_PW
3643 if (host.encrypt) {
3644 if (host.password_encrypt)
3645 talloc_free(host.password_encrypt);
3646 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3647 } else
3648#endif
3649 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3650
3651 return CMD_SUCCESS;
3652}
3653
3654ALIAS(config_password, password_text_cmd,
3655 "password LINE",
3656 "Assign the terminal connection password\n"
3657 "The UNENCRYPTED (cleartext) line password\n")
3658
3659/* VTY enable password set. */
3660 DEFUN(config_enable_password, enable_password_cmd,
3661 "enable password (8|) WORD",
3662 "Modify enable password parameters\n"
3663 "Assign the privileged level password\n"
3664 "Specifies a HIDDEN password will follow\n"
3665 "dummy string \n" "The HIDDEN 'enable' password string\n")
3666{
3667 /* Argument check. */
3668 if (argc == 0) {
3669 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3670 return CMD_WARNING;
3671 }
3672
3673 /* Crypt type is specified. */
3674 if (argc == 2) {
3675 if (*argv[0] == '8') {
3676 if (host.enable)
3677 talloc_free(host.enable);
3678 host.enable = NULL;
3679
3680 if (host.enable_encrypt)
3681 talloc_free(host.enable_encrypt);
3682 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3683
3684 return CMD_SUCCESS;
3685 } else {
3686 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3687 return CMD_WARNING;
3688 }
3689 }
3690
3691 if (!isalnum((int)*argv[0])) {
3692 vty_out(vty,
3693 "Please specify string starting with alphanumeric%s",
3694 VTY_NEWLINE);
3695 return CMD_WARNING;
3696 }
3697
3698 if (host.enable)
3699 talloc_free(host.enable);
3700 host.enable = NULL;
3701
3702 /* Plain password input. */
3703#ifdef VTY_CRYPT_PW
3704 if (host.encrypt) {
3705 if (host.enable_encrypt)
3706 talloc_free(host.enable_encrypt);
3707 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3708 } else
3709#endif
3710 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3711
3712 return CMD_SUCCESS;
3713}
3714
3715ALIAS(config_enable_password,
3716 enable_password_text_cmd,
3717 "enable password LINE",
3718 "Modify enable password parameters\n"
3719 "Assign the privileged level password\n"
3720 "The UNENCRYPTED (cleartext) 'enable' password\n")
3721
3722/* VTY enable password delete. */
3723 DEFUN(no_config_enable_password, no_enable_password_cmd,
3724 "no enable password",
3725 NO_STR
3726 "Modify enable password parameters\n"
3727 "Assign the privileged level password\n")
3728{
3729 if (host.enable)
3730 talloc_free(host.enable);
3731 host.enable = NULL;
3732
3733 if (host.enable_encrypt)
3734 talloc_free(host.enable_encrypt);
3735 host.enable_encrypt = NULL;
3736
3737 return CMD_SUCCESS;
3738}
3739
3740#ifdef VTY_CRYPT_PW
3741DEFUN(service_password_encrypt,
3742 service_password_encrypt_cmd,
3743 "service password-encryption",
3744 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3745{
3746 if (host.encrypt)
3747 return CMD_SUCCESS;
3748
3749 host.encrypt = 1;
3750
3751 if (host.password) {
3752 if (host.password_encrypt)
3753 talloc_free(host.password_encrypt);
3754 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3755 }
3756 if (host.enable) {
3757 if (host.enable_encrypt)
3758 talloc_free(host.enable_encrypt);
3759 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3760 }
3761
3762 return CMD_SUCCESS;
3763}
3764
3765DEFUN(no_service_password_encrypt,
3766 no_service_password_encrypt_cmd,
3767 "no service password-encryption",
3768 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3769{
3770 if (!host.encrypt)
3771 return CMD_SUCCESS;
3772
3773 host.encrypt = 0;
3774
3775 if (host.password_encrypt)
3776 talloc_free(host.password_encrypt);
3777 host.password_encrypt = NULL;
3778
3779 if (host.enable_encrypt)
3780 talloc_free(host.enable_encrypt);
3781 host.enable_encrypt = NULL;
3782
3783 return CMD_SUCCESS;
3784}
3785#endif
3786
3787DEFUN(config_terminal_length, config_terminal_length_cmd,
3788 "terminal length <0-512>",
3789 "Set terminal line parameters\n"
3790 "Set number of lines on a screen\n"
3791 "Number of lines on screen (0 for no pausing)\n")
3792{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003793 vty->lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003794 return CMD_SUCCESS;
3795}
3796
3797DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3798 "terminal no length",
3799 "Set terminal line parameters\n"
3800 NO_STR "Set number of lines on a screen\n")
3801{
3802 vty->lines = -1;
3803 return CMD_SUCCESS;
3804}
3805
3806DEFUN(service_terminal_length, service_terminal_length_cmd,
3807 "service terminal-length <0-512>",
3808 "Set up miscellaneous service\n"
3809 "System wide terminal length configuration\n"
3810 "Number of lines of VTY (0 means no line control)\n")
3811{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003812 host.lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003813 return CMD_SUCCESS;
3814}
3815
3816DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3817 "no service terminal-length [<0-512>]",
3818 NO_STR
3819 "Set up miscellaneous service\n"
3820 "System wide terminal length configuration\n"
3821 "Number of lines of VTY (0 means no line control)\n")
3822{
3823 host.lines = -1;
3824 return CMD_SUCCESS;
3825}
3826
3827DEFUN_HIDDEN(do_echo,
3828 echo_cmd,
3829 "echo .MESSAGE",
3830 "Echo a message back to the vty\n" "The message to echo\n")
3831{
3832 char *message;
3833
3834 vty_out(vty, "%s%s",
3835 ((message =
3836 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3837 if (message)
3838 talloc_free(message);
3839 return CMD_SUCCESS;
3840}
3841
3842#if 0
3843DEFUN(config_logmsg,
3844 config_logmsg_cmd,
3845 "logmsg " LOG_LEVELS " .MESSAGE",
3846 "Send a message to enabled logging destinations\n"
3847 LOG_LEVEL_DESC "The message to send\n")
3848{
3849 int level;
3850 char *message;
3851
3852 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3853 return CMD_ERR_NO_MATCH;
3854
3855 zlog(NULL, level,
3856 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3857 if (message)
3858 talloc_free(message);
3859 return CMD_SUCCESS;
3860}
3861
3862DEFUN(show_logging,
3863 show_logging_cmd,
3864 "show logging", SHOW_STR "Show current logging configuration\n")
3865{
3866 struct zlog *zl = zlog_default;
3867
3868 vty_out(vty, "Syslog logging: ");
3869 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3870 vty_out(vty, "disabled");
3871 else
3872 vty_out(vty, "level %s, facility %s, ident %s",
3873 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3874 facility_name(zl->facility), zl->ident);
3875 vty_out(vty, "%s", VTY_NEWLINE);
3876
3877 vty_out(vty, "Stdout logging: ");
3878 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3879 vty_out(vty, "disabled");
3880 else
3881 vty_out(vty, "level %s",
3882 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3883 vty_out(vty, "%s", VTY_NEWLINE);
3884
3885 vty_out(vty, "Monitor logging: ");
3886 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3887 vty_out(vty, "disabled");
3888 else
3889 vty_out(vty, "level %s",
3890 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3891 vty_out(vty, "%s", VTY_NEWLINE);
3892
3893 vty_out(vty, "File logging: ");
3894 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3895 vty_out(vty, "disabled");
3896 else
3897 vty_out(vty, "level %s, filename %s",
3898 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3899 zl->filename);
3900 vty_out(vty, "%s", VTY_NEWLINE);
3901
3902 vty_out(vty, "Protocol name: %s%s",
3903 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3904 vty_out(vty, "Record priority: %s%s",
3905 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3906
3907 return CMD_SUCCESS;
3908}
3909
3910DEFUN(config_log_stdout,
3911 config_log_stdout_cmd,
3912 "log stdout", "Logging control\n" "Set stdout logging level\n")
3913{
3914 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3915 return CMD_SUCCESS;
3916}
3917
3918DEFUN(config_log_stdout_level,
3919 config_log_stdout_level_cmd,
3920 "log stdout " LOG_LEVELS,
3921 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3922{
3923 int level;
3924
3925 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3926 return CMD_ERR_NO_MATCH;
3927 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3928 return CMD_SUCCESS;
3929}
3930
3931DEFUN(no_config_log_stdout,
3932 no_config_log_stdout_cmd,
3933 "no log stdout [LEVEL]",
3934 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3935{
3936 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3937 return CMD_SUCCESS;
3938}
3939
3940DEFUN(config_log_monitor,
3941 config_log_monitor_cmd,
3942 "log monitor",
3943 "Logging control\n" "Set terminal line (monitor) logging level\n")
3944{
3945 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3946 return CMD_SUCCESS;
3947}
3948
3949DEFUN(config_log_monitor_level,
3950 config_log_monitor_level_cmd,
3951 "log monitor " LOG_LEVELS,
3952 "Logging control\n"
3953 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3954{
3955 int level;
3956
3957 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3958 return CMD_ERR_NO_MATCH;
3959 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3960 return CMD_SUCCESS;
3961}
3962
3963DEFUN(no_config_log_monitor,
3964 no_config_log_monitor_cmd,
3965 "no log monitor [LEVEL]",
3966 NO_STR
3967 "Logging control\n"
3968 "Disable terminal line (monitor) logging\n" "Logging level\n")
3969{
3970 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3971 return CMD_SUCCESS;
3972}
3973
3974static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3975{
3976 int ret;
3977 char *p = NULL;
3978 const char *fullpath;
3979
3980 /* Path detection. */
3981 if (!IS_DIRECTORY_SEP(*fname)) {
3982 char cwd[MAXPATHLEN + 1];
3983 cwd[MAXPATHLEN] = '\0';
3984
3985 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3986 zlog_err("config_log_file: Unable to alloc mem!");
3987 return CMD_WARNING;
3988 }
3989
3990 if ((p = _talloc_zero(tall_vcmd_ctx,
3991 strlen(cwd) + strlen(fname) + 2),
3992 "set_log_file")
3993 == NULL) {
3994 zlog_err("config_log_file: Unable to alloc mem!");
3995 return CMD_WARNING;
3996 }
3997 sprintf(p, "%s/%s", cwd, fname);
3998 fullpath = p;
3999 } else
4000 fullpath = fname;
4001
4002 ret = zlog_set_file(NULL, fullpath, loglevel);
4003
4004 if (p)
4005 talloc_free(p);
4006
4007 if (!ret) {
4008 vty_out(vty, "can't open logfile %s\n", fname);
4009 return CMD_WARNING;
4010 }
4011
4012 if (host.logfile)
4013 talloc_free(host.logfile);
4014
4015 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
4016
4017 return CMD_SUCCESS;
4018}
4019
4020DEFUN(config_log_file,
4021 config_log_file_cmd,
4022 "log file FILENAME",
4023 "Logging control\n" "Logging to file\n" "Logging filename\n")
4024{
4025 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4026}
4027
4028DEFUN(config_log_file_level,
4029 config_log_file_level_cmd,
4030 "log file FILENAME " LOG_LEVELS,
4031 "Logging control\n"
4032 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4033{
4034 int level;
4035
4036 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4037 return CMD_ERR_NO_MATCH;
4038 return set_log_file(vty, argv[0], level);
4039}
4040
4041DEFUN(no_config_log_file,
4042 no_config_log_file_cmd,
4043 "no log file [FILENAME]",
4044 NO_STR
4045 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4046{
4047 zlog_reset_file(NULL);
4048
4049 if (host.logfile)
4050 talloc_free(host.logfile);
4051
4052 host.logfile = NULL;
4053
4054 return CMD_SUCCESS;
4055}
4056
4057ALIAS(no_config_log_file,
4058 no_config_log_file_level_cmd,
4059 "no log file FILENAME LEVEL",
4060 NO_STR
4061 "Logging control\n"
4062 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4063
4064 DEFUN(config_log_syslog,
4065 config_log_syslog_cmd,
4066 "log syslog", "Logging control\n" "Set syslog logging level\n")
4067{
4068 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4069 return CMD_SUCCESS;
4070}
4071
4072DEFUN(config_log_syslog_level,
4073 config_log_syslog_level_cmd,
4074 "log syslog " LOG_LEVELS,
4075 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4076{
4077 int level;
4078
4079 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4080 return CMD_ERR_NO_MATCH;
4081 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4082 return CMD_SUCCESS;
4083}
4084
4085DEFUN_DEPRECATED(config_log_syslog_facility,
4086 config_log_syslog_facility_cmd,
4087 "log syslog facility " LOG_FACILITIES,
4088 "Logging control\n"
4089 "Logging goes to syslog\n"
4090 "(Deprecated) Facility parameter for syslog messages\n"
4091 LOG_FACILITY_DESC)
4092{
4093 int facility;
4094
4095 if ((facility = facility_match(argv[0])) < 0)
4096 return CMD_ERR_NO_MATCH;
4097
4098 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4099 zlog_default->facility = facility;
4100 return CMD_SUCCESS;
4101}
4102
4103DEFUN(no_config_log_syslog,
4104 no_config_log_syslog_cmd,
4105 "no log syslog [LEVEL]",
4106 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4107{
4108 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4109 return CMD_SUCCESS;
4110}
4111
4112ALIAS(no_config_log_syslog,
4113 no_config_log_syslog_facility_cmd,
4114 "no log syslog facility " LOG_FACILITIES,
4115 NO_STR
4116 "Logging control\n"
4117 "Logging goes to syslog\n"
4118 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4119
4120 DEFUN(config_log_facility,
4121 config_log_facility_cmd,
4122 "log facility " LOG_FACILITIES,
4123 "Logging control\n"
4124 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4125{
4126 int facility;
4127
4128 if ((facility = facility_match(argv[0])) < 0)
4129 return CMD_ERR_NO_MATCH;
4130 zlog_default->facility = facility;
4131 return CMD_SUCCESS;
4132}
4133
4134DEFUN(no_config_log_facility,
4135 no_config_log_facility_cmd,
4136 "no log facility [FACILITY]",
4137 NO_STR
4138 "Logging control\n"
4139 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4140{
4141 zlog_default->facility = LOG_DAEMON;
4142 return CMD_SUCCESS;
4143}
4144
4145DEFUN_DEPRECATED(config_log_trap,
4146 config_log_trap_cmd,
4147 "log trap " LOG_LEVELS,
4148 "Logging control\n"
4149 "(Deprecated) Set logging level and default for all destinations\n"
4150 LOG_LEVEL_DESC)
4151{
4152 int new_level;
4153 int i;
4154
4155 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4156 return CMD_ERR_NO_MATCH;
4157
4158 zlog_default->default_lvl = new_level;
4159 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4160 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4161 zlog_default->maxlvl[i] = new_level;
4162 return CMD_SUCCESS;
4163}
4164
4165DEFUN_DEPRECATED(no_config_log_trap,
4166 no_config_log_trap_cmd,
4167 "no log trap [LEVEL]",
4168 NO_STR
4169 "Logging control\n"
4170 "Permit all logging information\n" "Logging level\n")
4171{
4172 zlog_default->default_lvl = LOG_DEBUG;
4173 return CMD_SUCCESS;
4174}
4175
4176DEFUN(config_log_record_priority,
4177 config_log_record_priority_cmd,
4178 "log record-priority",
4179 "Logging control\n"
4180 "Log the priority of the message within the message\n")
4181{
4182 zlog_default->record_priority = 1;
4183 return CMD_SUCCESS;
4184}
4185
4186DEFUN(no_config_log_record_priority,
4187 no_config_log_record_priority_cmd,
4188 "no log record-priority",
4189 NO_STR
4190 "Logging control\n"
4191 "Do not log the priority of the message within the message\n")
4192{
4193 zlog_default->record_priority = 0;
4194 return CMD_SUCCESS;
4195}
4196#endif
4197
4198DEFUN(banner_motd_file,
4199 banner_motd_file_cmd,
4200 "banner motd file [FILE]",
4201 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4202{
4203 if (host.motdfile)
4204 talloc_free(host.motdfile);
4205 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4206
4207 return CMD_SUCCESS;
4208}
4209
4210DEFUN(banner_motd_default,
4211 banner_motd_default_cmd,
4212 "banner motd default",
4213 "Set banner string\n" "Strings for motd\n" "Default string\n")
4214{
4215 host.motd = default_motd;
4216 return CMD_SUCCESS;
4217}
4218
4219DEFUN(no_banner_motd,
4220 no_banner_motd_cmd,
4221 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4222{
4223 host.motd = NULL;
4224 if (host.motdfile)
4225 talloc_free(host.motdfile);
4226 host.motdfile = NULL;
4227 return CMD_SUCCESS;
4228}
4229
4230/* Set config filename. Called from vty.c */
4231void host_config_set(const char *filename)
4232{
4233 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4234}
4235
Pau Espin Pedrolebb6c1f2021-05-17 18:54:21 +02004236const char *host_config_file(void)
4237{
4238 return host.config;
4239}
4240
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004241/*! Deprecated, now happens implicitly when calling install_node().
4242 * Users of the API may still attempt to call this function, hence
4243 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004244void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004245{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004246}
4247
4248/*! Deprecated, now happens implicitly when calling install_node().
4249 * Users of the API may still attempt to call this function, hence
4250 * leave it here as a no-op. */
4251void vty_install_default(int node)
4252{
4253}
4254
4255/*! Install common commands like 'exit' and 'list'. */
4256static void install_basic_node_commands(int node)
4257{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004258 install_lib_element(node, &config_help_cmd);
4259 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004260
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004261 install_lib_element(node, &show_vty_attr_all_cmd);
4262 install_lib_element(node, &show_vty_attr_cmd);
4263
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004264 install_lib_element(node, &config_write_terminal_cmd);
4265 install_lib_element(node, &config_write_file_cmd);
4266 install_lib_element(node, &config_write_memory_cmd);
4267 install_lib_element(node, &config_write_cmd);
4268 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004269
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004270 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004271
4272 if (node >= CONFIG_NODE) {
4273 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004274 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004275 }
4276}
4277
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004278/*! Return true if a node is installed by install_basic_node_commands(), so
4279 * that we can avoid repeating them for each and every node during 'show
4280 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004281static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004282{
4283 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004284 || cmd == &show_vty_attr_all_cmd
4285 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004286 || cmd == &config_list_cmd
4287 || cmd == &config_write_terminal_cmd
4288 || cmd == &config_write_file_cmd
4289 || cmd == &config_write_memory_cmd
4290 || cmd == &config_write_cmd
4291 || cmd == &show_running_config_cmd
4292 || cmd == &config_exit_cmd
4293 || cmd == &config_end_cmd)
4294 return true;
4295 return false;
4296}
4297
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004298/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004299 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004300 * \param[in] vty the vty of the code
4301 * \param[in] filename where to store the file
4302 * \return 0 in case of success.
4303 *
4304 * If the filename already exists create a filename.sav
4305 * version with the current code.
4306 *
4307 */
4308int osmo_vty_write_config_file(const char *filename)
4309{
4310 char *failed_file;
4311 int rc;
4312
4313 rc = write_config_file(filename, &failed_file);
4314 talloc_free(failed_file);
4315 return rc;
4316}
4317
4318/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004319 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004320 * \return 0 in case of success.
4321 *
4322 * If the filename already exists create a filename.sav
4323 * version with the current code.
4324 *
4325 */
4326int osmo_vty_save_config_file(void)
4327{
4328 char *failed_file;
4329 int rc;
4330
4331 if (host.config == NULL)
4332 return -7;
4333
4334 rc = write_config_file(host.config, &failed_file);
4335 talloc_free(failed_file);
4336 return rc;
4337}
4338
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004339/* Initialize command interface. Install basic nodes and commands. */
4340void cmd_init(int terminal)
4341{
4342 /* Allocate initial top vector of commands. */
4343 cmdvec = vector_init(VECTOR_MIN_SIZE);
4344
4345 /* Default host value settings. */
4346 host.name = NULL;
4347 host.password = NULL;
4348 host.enable = NULL;
4349 host.logfile = NULL;
4350 host.config = NULL;
4351 host.lines = -1;
4352 host.motd = default_motd;
4353 host.motdfile = NULL;
4354
4355 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004356 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004357 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004358 install_node_bare(&auth_node, NULL);
4359 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004360 install_node(&config_node, config_write_host);
4361
4362 /* Each node's basic commands. */
Oliver Smith0c78bc62021-07-12 17:28:36 +02004363 install_lib_element(VIEW_NODE, &show_pid_cmd);
Oliver Smithd243c2a2021-07-09 17:19:32 +02004364 install_lib_element(VIEW_NODE, &show_uptime_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004365 install_lib_element(VIEW_NODE, &show_version_cmd);
4366 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004367 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004368 install_lib_element(VIEW_NODE, &config_list_cmd);
4369 install_lib_element(VIEW_NODE, &config_exit_cmd);
4370 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004371 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4372 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004373 install_lib_element(VIEW_NODE, &config_enable_cmd);
4374 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4375 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4376 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004377 }
4378
4379 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004380 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4381 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4382 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Oliver Smith8a482fd2021-07-12 18:18:51 +02004383 install_lib_element(ENABLE_NODE, &shutdown_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004384 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004385 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4386 install_lib_element(ENABLE_NODE, &show_version_cmd);
4387 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004388
4389 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004390 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4391 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4392 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004393 }
4394
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004395 install_lib_element(CONFIG_NODE, &hostname_cmd);
4396 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004397
4398 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004399 install_lib_element(CONFIG_NODE, &password_cmd);
4400 install_lib_element(CONFIG_NODE, &password_text_cmd);
4401 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4402 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4403 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004404
4405#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004406 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4407 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004408#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004409 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4410 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4411 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4412 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4413 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004414
4415 }
4416 srand(time(NULL));
4417}
Harald Welte7acb30c2011-08-17 17:13:48 +02004418
Oliver Smithd243c2a2021-07-09 17:19:32 +02004419static __attribute__((constructor)) void on_dso_load_starttime(void)
4420{
4421 osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
4422}
4423
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004424/* FIXME: execute this section in the unit test instead */
4425static __attribute__((constructor)) void on_dso_load(void)
4426{
4427 unsigned int i, j;
4428
4429 /* Check total number of the library specific attributes */
4430 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4431
4432 /* Check for duplicates in the list of library specific flags */
4433 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4434 if (cmd_lib_attr_letters[i] == '\0')
4435 continue;
4436
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004437 /* Some flag characters are reserved for global attributes */
4438 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4439 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4440 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4441
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004442 /* Only upper case flag letters are allowed for libraries */
4443 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4444 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4445
4446 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4447 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4448 }
4449}
4450
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004451/*! @} */