blob: 2a8942dbf8068c560d7e787860978639f409d245 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Oliver Smith8a482fd2021-07-12 18:18:51 +020041#include <signal.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020042#include <sys/time.h>
43#include <sys/stat.h>
Oliver Smith8a482fd2021-07-12 18:18:51 +020044#include <sys/types.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020045
46#include <osmocom/vty/vector.h>
47#include <osmocom/vty/vty.h>
48#include <osmocom/vty/command.h>
49
Oliver Smith8a482fd2021-07-12 18:18:51 +020050#include <osmocom/core/logging.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010051#include <osmocom/core/talloc.h>
Oliver Smithd243c2a2021-07-09 17:19:32 +020052#include <osmocom/core/timer.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010053#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020054
Ruben Undheim766f77c2018-11-18 13:02:47 +010055#ifndef MAXPATHLEN
56 #define MAXPATHLEN 4096
57#endif
58
59
Harald Weltee881b1b2011-08-17 18:52:30 +020060/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020061 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020062 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020063 *
64 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020065
Harald Welte3fb0b6f2010-05-19 19:02:52 +020066#define CONFIGFILE_MASK 022
67
68void *tall_vty_cmd_ctx;
69
Oliver Smithd243c2a2021-07-09 17:19:32 +020070/* Set by on_dso_load_starttime() for "show uptime". */
71static struct timespec starttime;
72
Harald Welte3fb0b6f2010-05-19 19:02:52 +020073/* Command vector which includes some level of command lists. Normally
74 each daemon maintains each own cmdvec. */
75vector cmdvec;
76
77/* Host information structure. */
78struct host host;
79
80/* Standard command node structures. */
81struct cmd_node auth_node = {
82 AUTH_NODE,
83 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010084 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020085};
86
87struct cmd_node view_node = {
88 VIEW_NODE,
89 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010090 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020091};
92
93struct cmd_node auth_enable_node = {
94 AUTH_ENABLE_NODE,
95 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010096 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020097};
98
99struct cmd_node enable_node = {
100 ENABLE_NODE,
101 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +0100102 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200103};
104
105struct cmd_node config_node = {
106 CONFIG_NODE,
107 "%s(config)# ",
108 1
109};
110
111/* Default motd string. */
112const char *default_motd = "";
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200115 *
116 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200117void print_version(int print_copyright)
118{
Harald Welte237f6242010-05-25 23:00:45 +0200119 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200120 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200121 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200122}
123
124/* Utility function to concatenate argv argument into a single string
125 with inserting ' ' character between each argument. */
126char *argv_concat(const char **argv, int argc, int shift)
127{
128 int i;
129 size_t len;
130 char *str;
131 char *p;
132
133 len = 0;
134 for (i = shift; i < argc; i++)
135 len += strlen(argv[i]) + 1;
136 if (!len)
137 return NULL;
138 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
139 for (i = shift; i < argc; i++) {
140 size_t arglen;
141 memcpy(p, argv[i], (arglen = strlen(argv[i])));
142 p += arglen;
143 *p++ = ' ';
144 }
145 *(p - 1) = '\0';
146 return str;
147}
148
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200149/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
150 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
151 * in turn, this name us used for XML IDs in 'show online-help'. */
152static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
153{
154 const char *pos;
155 int dest = 0;
156
157 if (!prompt || !*prompt)
158 return "";
159
160 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
161 if (pos[0] == '%' && pos[1]) {
162 /* skip "%s"; loop pos++ does the second one. */
163 pos++;
164 continue;
165 }
166 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
167 continue;
168 name_buf[dest] = pos[0];
169 dest++;
170 }
171 name_buf[dest] = '\0';
172 return name_buf;
173}
174
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200175static void install_basic_node_commands(int node);
176
177/*! Install top node of command vector, without adding basic node commands. */
178static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200179{
180 vector_set_index(cmdvec, node->node, node);
181 node->func = func;
182 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200183 if (!*node->name)
184 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200185}
186
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200187/*! Install top node of command vector. */
188void install_node(struct cmd_node *node, int (*func) (struct vty *))
189{
190 install_node_bare(node, func);
191 install_basic_node_commands(node->node);
192}
193
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200194/* Compare two command's string. Used in sort_node (). */
195static int cmp_node(const void *p, const void *q)
196{
197 struct cmd_element *a = *(struct cmd_element **)p;
198 struct cmd_element *b = *(struct cmd_element **)q;
199
200 return strcmp(a->string, b->string);
201}
202
203static int cmp_desc(const void *p, const void *q)
204{
205 struct desc *a = *(struct desc **)p;
206 struct desc *b = *(struct desc **)q;
207
208 return strcmp(a->cmd, b->cmd);
209}
210
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200211/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200212void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200213{
214 unsigned int i, j;
215 struct cmd_node *cnode;
216 vector descvec;
217 struct cmd_element *cmd_element;
218
219 for (i = 0; i < vector_active(cmdvec); i++)
220 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
221 vector cmd_vector = cnode->cmd_vector;
222 qsort(cmd_vector->index, vector_active(cmd_vector),
223 sizeof(void *), cmp_node);
224
225 for (j = 0; j < vector_active(cmd_vector); j++)
226 if ((cmd_element =
227 vector_slot(cmd_vector, j)) != NULL
228 && vector_active(cmd_element->strvec)) {
229 descvec =
230 vector_slot(cmd_element->strvec,
231 vector_active
232 (cmd_element->strvec) -
233 1);
234 qsort(descvec->index,
235 vector_active(descvec),
236 sizeof(void *), cmp_desc);
237 }
238 }
239}
240
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200241/*! Break up string in command tokens. Return leading indents.
242 * \param[in] string String to split.
243 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
244 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
245 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
246 *
247 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
248 * so that \a indent can simply return the count of leading spaces.
249 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
250 */
251int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200252{
253 const char *cp, *start;
254 char *token;
255 int strlen;
256 vector strvec;
257
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200258 *strvec_p = NULL;
259 if (indent)
260 *indent = 0;
261
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200262 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200263 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200264
265 cp = string;
266
267 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200268 while (isspace((int)*cp) && *cp != '\0') {
269 /* if we're counting indents, we need to be strict about them */
270 if (indent && (*cp != ' ') && (*cp != '\t')) {
271 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
272 if (*cp == '\n' || *cp == '\r') {
273 cp++;
274 string = cp;
275 continue;
276 }
277 return CMD_ERR_INVALID_INDENT;
278 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200279 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 }
281
282 if (indent)
283 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Return if there is only white spaces */
286 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200287 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200288
289 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200290 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200291
292 /* Prepare return vector. */
293 strvec = vector_init(VECTOR_MIN_SIZE);
294
295 /* Copy each command piece and set into vector. */
296 while (1) {
297 start = cp;
298 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
299 *cp != '\0')
300 cp++;
301 strlen = cp - start;
302 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
303 memcpy(token, start, strlen);
304 *(token + strlen) = '\0';
305 vector_set(strvec, token);
306
307 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
308 *cp != '\0')
309 cp++;
310
311 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200312 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200313 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200314
315 *strvec_p = strvec;
316 return CMD_SUCCESS;
317}
318
319/*! Breaking up string into each command piece. I assume given
320 character is separated by a space character. Return value is a
321 vector which includes char ** data element. */
322vector cmd_make_strvec(const char *string)
323{
324 vector strvec;
325 cmd_make_strvec2(string, NULL, &strvec);
326 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200327}
328
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200329/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200330void cmd_free_strvec(vector v)
331{
332 unsigned int i;
333 char *cp;
334
335 if (!v)
336 return;
337
338 for (i = 0; i < vector_active(v); i++)
339 if ((cp = vector_slot(v, i)) != NULL)
340 talloc_free(cp);
341
342 vector_free(v);
343}
344
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200345/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200346static char *cmd_desc_str(const char **string)
347{
348 const char *cp, *start;
349 char *token;
350 int strlen;
351
352 cp = *string;
353
354 if (cp == NULL)
355 return NULL;
356
357 /* Skip white spaces. */
358 while (isspace((int)*cp) && *cp != '\0')
359 cp++;
360
361 /* Return if there is only white spaces */
362 if (*cp == '\0')
363 return NULL;
364
365 start = cp;
366
367 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
368 cp++;
369
370 strlen = cp - start;
371 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
372 memcpy(token, start, strlen);
373 *(token + strlen) = '\0';
374
375 *string = cp;
376
377 return token;
378}
379
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200380/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200381static vector cmd_make_descvec(const char *string, const char *descstr)
382{
383 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100384 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200385 const char *sp;
386 char *token;
387 int len;
388 const char *cp;
389 const char *dp;
390 vector allvec;
391 vector strvec = NULL;
392 struct desc *desc;
393
394 cp = string;
395 dp = descstr;
396
397 if (cp == NULL)
398 return NULL;
399
400 allvec = vector_init(VECTOR_MIN_SIZE);
401
402 while (1) {
403 while (isspace((int)*cp) && *cp != '\0')
404 cp++;
405
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100406 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
407 if (cp[0] == '[' && cp[1] == '(') {
408 optional_brace = 1;
409 cp++;
410 }
411
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200412 if (*cp == '(') {
413 multiple = 1;
414 cp++;
415 }
416 if (*cp == ')') {
417 multiple = 0;
418 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100419 if (*cp == ']')
420 cp++;
421 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200422 }
423 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100424 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200425 cp++;
426 }
427
428 while (isspace((int)*cp) && *cp != '\0')
429 cp++;
430
431 if (*cp == '(') {
432 multiple = 1;
433 cp++;
434 }
435
436 if (*cp == '\0')
437 return allvec;
438
439 sp = cp;
440
441 while (!
442 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
443 || *cp == ')' || *cp == '|') && *cp != '\0')
444 cp++;
445
446 len = cp - sp;
447
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100448 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
449 if (optional_brace) {
450 /* Place each individual multi-choice token in its own square braces */
451 token[0] = '[';
452 memcpy(token + 1, sp, len);
453 token[1 + len] = ']';
454 token[2 + len] = '\0';
455 } else {
456 memcpy(token, sp, len);
457 *(token + len) = '\0';
458 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200459
460 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
461 desc->cmd = token;
462 desc->str = cmd_desc_str(&dp);
463
464 if (multiple) {
465 if (multiple == 1) {
466 strvec = vector_init(VECTOR_MIN_SIZE);
467 vector_set(allvec, strvec);
468 }
469 multiple++;
470 } else {
471 strvec = vector_init(VECTOR_MIN_SIZE);
472 vector_set(allvec, strvec);
473 }
474 vector_set(strvec, desc);
475 }
476}
477
478/* Count mandantory string vector size. This is to determine inputed
479 command has enough command length. */
480static int cmd_cmdsize(vector strvec)
481{
482 unsigned int i;
483 int size = 0;
484 vector descvec;
485 struct desc *desc;
486
487 for (i = 0; i < vector_active(strvec); i++)
488 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100489 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200490 && (desc = vector_slot(descvec, 0)) != NULL) {
491 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
492 return size;
493 else
494 size++;
495 } else
496 size++;
497 }
498 return size;
499}
500
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200501/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200502const char *cmd_prompt(enum node_type node)
503{
504 struct cmd_node *cnode;
505
506 cnode = vector_slot(cmdvec, node);
507 return cnode->prompt;
508}
509
Alexander Couzensad580ba2016-05-16 16:01:45 +0200510/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200511 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200512 * \param unsafe string
513 * \return a new talloc char *
514 */
515char *osmo_asciidoc_escape(const char *inp)
516{
517 int _strlen;
518 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200519 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200520
521 if (!inp)
522 return NULL;
523 _strlen = strlen(inp);
524
525 for (i = 0; i < _strlen; ++i) {
526 switch (inp[i]) {
527 case '|':
528 len += 2;
529 break;
530 default:
531 len += 1;
532 break;
533 }
534 }
535
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200536 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200537 if (!out)
538 return NULL;
539
540 out_ptr = out;
541
Alexander Couzensad580ba2016-05-16 16:01:45 +0200542 for (i = 0; i < _strlen; ++i) {
543 switch (inp[i]) {
544 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200545 /* Prepend escape character "\": */
546 *(out_ptr++) = '\\';
547 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200548 default:
549 *(out_ptr++) = inp[i];
550 break;
551 }
552 }
553
Alexander Couzensad580ba2016-05-16 16:01:45 +0200554 out_ptr[0] = '\0';
555 return out;
556}
557
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100558static char *xml_escape(const char *inp)
559{
560 int _strlen;
561 char *out, *out_ptr;
562 int len = 0, i, j;
563
564 if (!inp)
565 return NULL;
566 _strlen = strlen(inp);
567
568 for (i = 0; i < _strlen; ++i) {
569 switch (inp[i]) {
570 case '"':
571 len += 6;
572 break;
573 case '\'':
574 len += 6;
575 break;
576 case '<':
577 len += 4;
578 break;
579 case '>':
580 len += 4;
581 break;
582 case '&':
583 len += 5;
584 break;
585 default:
586 len += 1;
587 break;
588 }
589 }
590
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200591 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100592 if (!out)
593 return NULL;
594
595 out_ptr = out;
596
597#define ADD(out, str) \
598 for (j = 0; j < strlen(str); ++j) \
599 *(out++) = str[j];
600
601 for (i = 0; i < _strlen; ++i) {
602 switch (inp[i]) {
603 case '"':
604 ADD(out_ptr, "&quot;");
605 break;
606 case '\'':
607 ADD(out_ptr, "&apos;");
608 break;
609 case '<':
610 ADD(out_ptr, "&lt;");
611 break;
612 case '>':
613 ADD(out_ptr, "&gt;");
614 break;
615 case '&':
616 ADD(out_ptr, "&amp;");
617 break;
618 default:
619 *(out_ptr++) = inp[i];
620 break;
621 }
622 }
623
624#undef ADD
625
626 out_ptr[0] = '\0';
627 return out;
628}
629
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200630typedef int (*print_func_t)(void *data, const char *fmt, ...);
631
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700632static const struct value_string cmd_attr_desc[] = {
633 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700634 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700635 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700636 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700637 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700638 { 0, NULL }
639};
640
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700641/* Public attributes (to be printed in the VTY / XML reference) */
642#define CMD_ATTR_PUBLIC_MASK \
Vadim Yanitskiy67608452020-10-23 20:37:32 +0700643 (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700644
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700645/* Get a flag character for a global VTY command attribute */
646static char cmd_attr_get_flag(unsigned int attr)
647{
648 switch (attr) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700649 case CMD_ATTR_HIDDEN:
650 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700651 case CMD_ATTR_IMMEDIATE:
652 return '!';
653 case CMD_ATTR_NODE_EXIT:
654 return '@';
655 default:
656 return '.';
657 }
658}
659
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700660/* Description of attributes shared between the lib commands */
661static const char * const cmd_lib_attr_desc[32] = {
662 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
663 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200664 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
665 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200666 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
667 "This command applies on IPA link establishment",
668 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
669 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700670};
671
672/* Flag letters of attributes shared between the lib commands.
673 * NOTE: uppercase letters only, the rest is reserved for applications. */
674static const char cmd_lib_attr_letters[32] = {
675 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200676 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200677 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
678 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700679};
680
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100681/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200682 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100683 */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700684static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
685 void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100686{
687 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700688 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100689
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200690 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700691
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700692 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700693 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700694 print_func(data, " <attributes scope='global'>%s", newline);
695
696 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
697 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700698 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700699
700 if (~cmd->attr & cmd_attr_desc[i].value)
701 continue;
702
703 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700704 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700705 xml_att_desc, newline);
706 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700707
708 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
709 if (flag != '.')
710 print_func(data, " flag='%c'", flag);
711 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700712 }
713
714 print_func(data, " </attributes>%s", newline);
715 }
716
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700717 /* Print application specific attributes and their description */
718 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700719 const char * const *desc;
720 const char *letters;
721
722 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
723 print_func(data, " <attributes scope='library'>%s", newline);
724 letters = &cmd_lib_attr_letters[0];
725 desc = &cmd_lib_attr_desc[0];
726 } else {
727 print_func(data, " <attributes scope='application'>%s", newline);
728 letters = &host.app_info->usr_attr_letters[0];
729 desc = &host.app_info->usr_attr_desc[0];
730 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700731
732 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
733 char *xml_att_desc;
734 char flag;
735
736 /* Skip attribute if *not* set */
Harald Weltec296e292020-12-21 15:44:52 +0100737 if (~cmd->usrattr & ((unsigned)1 << i))
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700738 continue;
739
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700740 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700741 print_func(data, " <attribute doc='%s'", xml_att_desc);
742 talloc_free(xml_att_desc);
743
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700744 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700745 print_func(data, " flag='%c'", flag);
746 print_func(data, " />%s", newline);
747 }
748
749 print_func(data, " </attributes>%s", newline);
750 }
751
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200752 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100753
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700754 for (i = 0; i < vector_count(cmd->strvec); ++i) {
755 vector descvec = vector_slot(cmd->strvec, i);
756 int j;
757 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100758 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700759 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100760 if (desc == NULL)
761 continue;
762
763 xml_param = xml_escape(desc->cmd);
764 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200765 print_func(data, " <param name='%s' doc='%s' />%s",
766 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100767 talloc_free(xml_param);
768 talloc_free(xml_doc);
769 }
770 }
771
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200772 print_func(data, " </params>%s", newline);
773 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100774
775 talloc_free(xml_string);
776 return 0;
777}
778
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700779static bool vty_command_is_common(const struct cmd_element *cmd);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200780
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100781/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200782 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700783 *
784 * (gflag_mask, match = false) - print only those commands with non-matching flags.
785 * (gflag_mask, match = true) - print only those commands with matching flags.
786 *
787 * Some examples:
788 *
789 * Print all commands except deprecated and hidden (default mode):
790 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
791 * Print only deprecated and hidden commands:
792 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
793 * Print all commands except deprecated (expert mode):
794 * (CMD_ATTR_DEPRECATED, false)
795 * Print only hidden commands:
796 * (CMD_ATTR_HIDDEN, true)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100797 */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700798static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
799 unsigned char gflag_mask, bool match)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100800{
801 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200802 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100803
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200804 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100805
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200806 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200807 print_func(data, " <node id='_common_cmds_'>%s", newline);
808 print_func(data, " <name>Common Commands</name>%s", newline);
809 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
810 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200811 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700812 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200813 if (!cnode)
814 continue;
815 if (cnode->node != CONFIG_NODE)
816 continue;
817
818 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700819 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200820 if (!vty_command_is_common(elem))
821 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700822 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700823 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700824 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700825 continue;
826 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200827 }
828 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200829 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200830
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100831 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700832 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100833 if (!cnode)
834 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200835 if (vector_active(cnode->cmd_vector) < 1)
836 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100837
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200838 /* De-dup node IDs: how many times has this same name been used before? Count the first
839 * occurence as _1 and omit that first suffix, so that the first occurence is called
840 * 'name', the second becomes 'name_2', then 'name_3', ... */
841 same_name_count = 1;
842 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700843 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200844 if (!cnode2)
845 continue;
846 if (strcmp(cnode->name, cnode2->name) == 0)
847 same_name_count ++;
848 }
849
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200850 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200851 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200852 print_func(data, "_%d", same_name_count);
853 print_func(data, "'>%s", newline);
854 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100855
856 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700857 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200858 if (vty_command_is_common(elem))
859 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700860 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700861 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700862 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700863 continue;
864 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100865 }
866
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200867 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100868 }
869
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200870 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100871
872 return 0;
873}
874
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200875static int print_func_vty(void *data, const char *format, ...)
876{
877 struct vty *vty = data;
878 va_list args;
879 int rc;
880 va_start(args, format);
881 rc = vty_out_va(vty, format, args);
882 va_end(args);
883 return rc;
884}
885
886static int vty_dump_xml_ref_to_vty(struct vty *vty)
887{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700888 unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
889 if (!vty->expert_mode)
890 gflag_mask |= CMD_ATTR_HIDDEN;
891 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200892}
893
894static int print_func_stream(void *data, const char *format, ...)
895{
896 va_list args;
897 int rc;
898 va_start(args, format);
899 rc = vfprintf((FILE*)data, format, args);
900 va_end(args);
901 return rc;
902}
903
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700904const struct value_string vty_ref_gen_mode_names[] = {
905 { VTY_REF_GEN_MODE_DEFAULT, "default" },
906 { VTY_REF_GEN_MODE_EXPERT, "expert" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700907 { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700908 { 0, NULL }
909};
910
911const struct value_string vty_ref_gen_mode_desc[] = {
912 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
913 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700914 { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700915 { 0, NULL }
916};
917
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200918/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700919 * \param[out] stream Output stream to print the XML reference to.
920 * \param[in] mode The XML reference generation mode.
921 * \returns always 0 for now, no errors possible.
922 */
923int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
924{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700925 unsigned char gflag_mask;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700926 bool match = false;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700927
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700928 switch (mode) {
929 case VTY_REF_GEN_MODE_EXPERT:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700930 /* All commands except deprecated */
931 gflag_mask = CMD_ATTR_DEPRECATED;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700932 break;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700933 case VTY_REF_GEN_MODE_HIDDEN:
934 /* Only hidden commands */
935 gflag_mask = CMD_ATTR_HIDDEN;
936 match = true;
937 break;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700938 case VTY_REF_GEN_MODE_DEFAULT:
939 default:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700940 /* All commands except deprecated and hidden */
941 gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700942 break;
943 }
944
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700945 return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700946}
947
948/*! Print the XML reference of all VTY nodes to the given stream.
949 * \param[out] stream Output stream to print the XML reference to.
950 * \returns always 0 for now, no errors possible.
951 *
952 * NOTE: this function is deprecated because it does not allow to
953 * specify the XML reference generation mode (default mode
954 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200955 */
956int vty_dump_xml_ref(FILE *stream)
957{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700958 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200959}
960
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200961/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100962static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
963{
964 int i;
965
966 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
967 struct cmd_element *elem;
968 elem = vector_slot(cnode->cmd_vector, i);
969 if (!elem->string)
970 continue;
971 if (!strcmp(elem->string, cmdstring))
972 return 1;
973 }
974 return 0;
975}
976
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200977/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200978 * \param[in] ntype Node Type
979 * \param[cmd] element to be installed
980 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000981void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200982{
983 struct cmd_node *cnode;
984
985 cnode = vector_slot(cmdvec, ntype);
986
Harald Weltea99d45a2015-11-12 13:48:23 +0100987 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100988 /* ensure no _identical_ command has been registered at this
989 * node so far */
990 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200991
992 vector_set(cnode->cmd_vector, cmd);
993
994 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
995 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
996}
997
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700998/*! Install a library command into a node
999 * \param[in] ntype Node Type
1000 * \param[in] cmd element to be installed
1001 */
1002void install_lib_element(int ntype, struct cmd_element *cmd)
1003{
1004 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1005 install_element(ntype, cmd);
1006}
1007
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001008/* Install a command into VIEW and ENABLE node */
1009void install_element_ve(struct cmd_element *cmd)
1010{
1011 install_element(VIEW_NODE, cmd);
1012 install_element(ENABLE_NODE, cmd);
1013}
1014
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001015/* Install a library command into VIEW and ENABLE node */
1016void install_lib_element_ve(struct cmd_element *cmd)
1017{
1018 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1019 install_element_ve(cmd);
1020}
1021
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022#ifdef VTY_CRYPT_PW
1023static unsigned char itoa64[] =
1024 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1025
1026static void to64(char *s, long v, int n)
1027{
1028 while (--n >= 0) {
1029 *s++ = itoa64[v & 0x3f];
1030 v >>= 6;
1031 }
1032}
1033
1034static char *zencrypt(const char *passwd)
1035{
1036 char salt[6];
1037 struct timeval tv;
1038 char *crypt(const char *, const char *);
1039
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001040 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001041
1042 to64(&salt[0], random(), 3);
1043 to64(&salt[3], tv.tv_usec, 3);
1044 salt[5] = '\0';
1045
1046 return crypt(passwd, salt);
1047}
1048#endif
1049
1050/* This function write configuration of this host. */
1051static int config_write_host(struct vty *vty)
1052{
1053 if (host.name)
1054 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1055
1056 if (host.encrypt) {
1057 if (host.password_encrypt)
1058 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1059 VTY_NEWLINE);
1060 if (host.enable_encrypt)
1061 vty_out(vty, "enable password 8 %s%s",
1062 host.enable_encrypt, VTY_NEWLINE);
1063 } else {
1064 if (host.password)
1065 vty_out(vty, "password %s%s", host.password,
1066 VTY_NEWLINE);
1067 if (host.enable)
1068 vty_out(vty, "enable password %s%s", host.enable,
1069 VTY_NEWLINE);
1070 }
1071
1072 if (host.advanced)
1073 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1074
1075 if (host.encrypt)
1076 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1077
1078 if (host.lines >= 0)
1079 vty_out(vty, "service terminal-length %d%s", host.lines,
1080 VTY_NEWLINE);
1081
1082 if (host.motdfile)
1083 vty_out(vty, "banner motd file %s%s", host.motdfile,
1084 VTY_NEWLINE);
1085 else if (!host.motd)
1086 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1087
1088 return 1;
1089}
1090
1091/* Utility function for getting command vector. */
1092static vector cmd_node_vector(vector v, enum node_type ntype)
1093{
1094 struct cmd_node *cnode = vector_slot(v, ntype);
1095 return cnode->cmd_vector;
1096}
1097
1098/* Completion match types. */
1099enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001100 NO_MATCH = 0,
1101 ANY_MATCH,
1102 EXTEND_MATCH,
1103 IPV4_PREFIX_MATCH,
1104 IPV4_MATCH,
1105 IPV6_PREFIX_MATCH,
1106 IPV6_MATCH,
1107 RANGE_MATCH,
1108 VARARG_MATCH,
1109 PARTLY_MATCH,
1110 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111};
1112
1113static enum match_type cmd_ipv4_match(const char *str)
1114{
1115 const char *sp;
1116 int dots = 0, nums = 0;
1117 char buf[4];
1118
1119 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001120 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001121
1122 for (;;) {
1123 memset(buf, 0, sizeof(buf));
1124 sp = str;
1125 while (*str != '\0') {
1126 if (*str == '.') {
1127 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001128 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001129
1130 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001131 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001132
1133 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001134 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001135
1136 dots++;
1137 break;
1138 }
1139 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001140 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001141
1142 str++;
1143 }
1144
1145 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001146 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001147
1148 strncpy(buf, sp, str - sp);
1149 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 nums++;
1153
1154 if (*str == '\0')
1155 break;
1156
1157 str++;
1158 }
1159
1160 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001161 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001162
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001163 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001164}
1165
1166static enum match_type cmd_ipv4_prefix_match(const char *str)
1167{
1168 const char *sp;
1169 int dots = 0;
1170 char buf[4];
1171
1172 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001173 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001174
1175 for (;;) {
1176 memset(buf, 0, sizeof(buf));
1177 sp = str;
1178 while (*str != '\0' && *str != '/') {
1179 if (*str == '.') {
1180 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001181 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001182
1183 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001184 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001185
1186 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001187 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001188
1189 dots++;
1190 break;
1191 }
1192
1193 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001194 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001195
1196 str++;
1197 }
1198
1199 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001200 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001201
1202 strncpy(buf, sp, str - sp);
1203 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001204 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001205
1206 if (dots == 3) {
1207 if (*str == '/') {
1208 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001209 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001210
1211 str++;
1212 break;
1213 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001214 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001215 }
1216
1217 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001218 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001219
1220 str++;
1221 }
1222
1223 sp = str;
1224 while (*str != '\0') {
1225 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001226 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001227
1228 str++;
1229 }
1230
1231 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001232 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001234 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235}
1236
1237#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1238#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1239#define STATE_START 1
1240#define STATE_COLON 2
1241#define STATE_DOUBLE 3
1242#define STATE_ADDR 4
1243#define STATE_DOT 5
1244#define STATE_SLASH 6
1245#define STATE_MASK 7
1246
1247#ifdef HAVE_IPV6
1248
1249static enum match_type cmd_ipv6_match(const char *str)
1250{
1251 int state = STATE_START;
1252 int colons = 0, nums = 0, double_colon = 0;
1253 const char *sp = NULL;
1254 struct sockaddr_in6 sin6_dummy;
1255 int ret;
1256
1257 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001258 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259
1260 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001261 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001262
1263 /* use inet_pton that has a better support,
1264 * for example inet_pton can support the automatic addresses:
1265 * ::1.2.3.4
1266 */
1267 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1268
1269 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001270 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001271
1272 while (*str != '\0') {
1273 switch (state) {
1274 case STATE_START:
1275 if (*str == ':') {
1276 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001277 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278 colons--;
1279 state = STATE_COLON;
1280 } else {
1281 sp = str;
1282 state = STATE_ADDR;
1283 }
1284
1285 continue;
1286 case STATE_COLON:
1287 colons++;
1288 if (*(str + 1) == ':')
1289 state = STATE_DOUBLE;
1290 else {
1291 sp = str + 1;
1292 state = STATE_ADDR;
1293 }
1294 break;
1295 case STATE_DOUBLE:
1296 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001297 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001298
1299 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001300 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001301 else {
1302 if (*(str + 1) != '\0')
1303 colons++;
1304 sp = str + 1;
1305 state = STATE_ADDR;
1306 }
1307
1308 double_colon++;
1309 nums++;
1310 break;
1311 case STATE_ADDR:
1312 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1313 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001314 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001315
1316 nums++;
1317 state = STATE_COLON;
1318 }
1319 if (*(str + 1) == '.')
1320 state = STATE_DOT;
1321 break;
1322 case STATE_DOT:
1323 state = STATE_ADDR;
1324 break;
1325 default:
1326 break;
1327 }
1328
1329 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001330 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331
1332 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001333 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001334
1335 str++;
1336 }
1337
1338#if 0
1339 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001341#endif /* 0 */
1342
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001343 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001344}
1345
1346static enum match_type cmd_ipv6_prefix_match(const char *str)
1347{
1348 int state = STATE_START;
1349 int colons = 0, nums = 0, double_colon = 0;
1350 int mask;
1351 const char *sp = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001352
1353 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001354 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001355
1356 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001357 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001358
1359 while (*str != '\0' && state != STATE_MASK) {
1360 switch (state) {
1361 case STATE_START:
1362 if (*str == ':') {
1363 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001364 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001365 colons--;
1366 state = STATE_COLON;
1367 } else {
1368 sp = str;
1369 state = STATE_ADDR;
1370 }
1371
1372 continue;
1373 case STATE_COLON:
1374 colons++;
1375 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001376 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001377 else if (*(str + 1) == ':')
1378 state = STATE_DOUBLE;
1379 else {
1380 sp = str + 1;
1381 state = STATE_ADDR;
1382 }
1383 break;
1384 case STATE_DOUBLE:
1385 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001386 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001387
1388 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001389 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001390 else {
1391 if (*(str + 1) != '\0' && *(str + 1) != '/')
1392 colons++;
1393 sp = str + 1;
1394
1395 if (*(str + 1) == '/')
1396 state = STATE_SLASH;
1397 else
1398 state = STATE_ADDR;
1399 }
1400
1401 double_colon++;
1402 nums += 1;
1403 break;
1404 case STATE_ADDR:
1405 if (*(str + 1) == ':' || *(str + 1) == '.'
1406 || *(str + 1) == '\0' || *(str + 1) == '/') {
1407 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001408 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001409
1410 for (; sp <= str; sp++)
1411 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001412 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001413
1414 nums++;
1415
1416 if (*(str + 1) == ':')
1417 state = STATE_COLON;
1418 else if (*(str + 1) == '.')
1419 state = STATE_DOT;
1420 else if (*(str + 1) == '/')
1421 state = STATE_SLASH;
1422 }
1423 break;
1424 case STATE_DOT:
1425 state = STATE_ADDR;
1426 break;
1427 case STATE_SLASH:
1428 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001429 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001430
1431 state = STATE_MASK;
1432 break;
1433 default:
1434 break;
1435 }
1436
1437 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001438 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001439
1440 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001441 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001442
1443 str++;
1444 }
1445
1446 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001447 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001448
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02001449 if (osmo_str_to_int(&mask, str, 10, 0, 128))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001450 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001451
1452/* I don't know why mask < 13 makes command match partly.
1453 Forgive me to make this comments. I Want to set static default route
1454 because of lack of function to originate default in ospf6d; sorry
1455 yasu
1456 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001457 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001458*/
1459
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001460 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001461}
1462
1463#endif /* HAVE_IPV6 */
1464
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001465
1466#if ULONG_MAX == 18446744073709551615UL
1467#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1468#elif ULONG_MAX == 4294967295UL
1469#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1470#else
1471#error "ULONG_MAX not defined!"
1472#endif
1473
1474#if LONG_MAX == 9223372036854775807L
1475#define DECIMAL_STRLEN_MAX_SIGNED 19
1476#elif LONG_MAX == 2147483647L
1477#define DECIMAL_STRLEN_MAX_SIGNED 10
1478#else
1479#error "LONG_MAX not defined!"
1480#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001481
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001482int vty_cmd_range_match(const char *range, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001483{
1484 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001485 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001486 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487
1488 if (str == NULL)
1489 return 1;
1490
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001491 if (range[1] == '-') {
1492 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001493
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001494 val = strtol(str, &endptr, 10);
1495 if (*endptr != '\0')
1496 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001497
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001498 range += 2;
1499 p = strchr(range, '-');
1500 if (p == NULL)
1501 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001502 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001503 return 0;
1504 strncpy(buf, range, p - range);
1505 buf[p - range] = '\0';
1506 min = -strtol(buf, &endptr, 10);
1507 if (*endptr != '\0')
1508 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001509
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001510 range = p + 1;
1511 p = strchr(range, '>');
1512 if (p == NULL)
1513 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001514 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001515 return 0;
1516 strncpy(buf, range, p - range);
1517 buf[p - range] = '\0';
1518 max = strtol(buf, &endptr, 10);
1519 if (*endptr != '\0')
1520 return 0;
1521
1522 if (val < min || val > max)
1523 return 0;
1524 } else {
1525 unsigned long min, max, val;
1526
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001527 if (str[0] == '-')
1528 return 0;
1529
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001530 val = strtoul(str, &endptr, 10);
1531 if (*endptr != '\0')
1532 return 0;
1533
1534 range++;
1535 p = strchr(range, '-');
1536 if (p == NULL)
1537 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001538 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001539 return 0;
1540 strncpy(buf, range, p - range);
1541 buf[p - range] = '\0';
1542 min = strtoul(buf, &endptr, 10);
1543 if (*endptr != '\0')
1544 return 0;
1545
1546 range = p + 1;
1547 p = strchr(range, '>');
1548 if (p == NULL)
1549 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001550 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001551 return 0;
1552 strncpy(buf, range, p - range);
1553 buf[p - range] = '\0';
1554 max = strtoul(buf, &endptr, 10);
1555 if (*endptr != '\0')
1556 return 0;
1557
1558 if (val < min || val > max)
1559 return 0;
1560 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001561
1562 return 1;
1563}
1564
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001565/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001566static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001567{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001568 /* we've got "[blah]". We want to strip off the []s and redo the
1569 * match check for "blah"
1570 */
1571 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001572
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001573 if (len < 3)
1574 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001575
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001576 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577}
1578
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001579static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001580cmd_match(const char *str, const char *command,
1581 enum match_type min, bool recur)
1582{
1583
1584 if (recur && CMD_OPTION(str))
1585 {
1586 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001587 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001588
1589 /* this would be a bug in a command, however handle it gracefully
1590 * as it we only discover it if a user tries to run it
1591 */
1592 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001593 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001594
1595 ret = cmd_match(tmp, command, min, false);
1596
1597 talloc_free(tmp);
1598
1599 return ret;
1600 }
1601 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001602 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001603 else if (CMD_RANGE(str))
1604 {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001605 if (vty_cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001606 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001607 }
1608#ifdef HAVE_IPV6
1609 else if (CMD_IPV6(str))
1610 {
1611 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001612 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001613 }
1614 else if (CMD_IPV6_PREFIX(str))
1615 {
1616 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001617 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001618 }
1619#endif /* HAVE_IPV6 */
1620 else if (CMD_IPV4(str))
1621 {
1622 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001623 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001624 }
1625 else if (CMD_IPV4_PREFIX(str))
1626 {
1627 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001628 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001629 }
1630 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001631 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001632 else if (strncmp(command, str, strlen(command)) == 0)
1633 {
1634 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001635 return EXACT_MATCH;
1636 else if (PARTLY_MATCH >= min)
1637 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001638 }
1639
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001640 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001641}
1642
1643/* Filter vector at the specified index and by the given command string, to
1644 * the desired matching level (thus allowing part matches), and return match
1645 * type flag.
1646 */
1647static enum match_type
1648cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001649{
1650 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001651 struct cmd_element *cmd_element;
1652 enum match_type match_type;
1653 vector descvec;
1654 struct desc *desc;
1655
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001656 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001657
1658 /* If command and cmd_element string does not match set NULL to vector */
1659 for (i = 0; i < vector_active(v); i++)
1660 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001661 if (index >= vector_active(cmd_element->strvec))
1662 vector_slot(v, i) = NULL;
1663 else {
1664 unsigned int j;
1665 int matched = 0;
1666
1667 descvec =
1668 vector_slot(cmd_element->strvec, index);
1669
1670 for (j = 0; j < vector_active(descvec); j++)
1671 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001672 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001673
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001674 ret = cmd_match (desc->cmd, command, level, true);
1675
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001676 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001677 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001678
1679 if (match_type < ret)
1680 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001681 }
1682 if (!matched)
1683 vector_slot(v, i) = NULL;
1684 }
1685 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001686
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001687 if (match_type == NO_MATCH)
1688 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001689
1690 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1691 * go again and filter out commands whose argument (at this index) is
1692 * 'weaker'. E.g., if we have 2 commands:
1693 *
1694 * foo bar <1-255>
1695 * foo bar BLAH
1696 *
1697 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001698 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001699 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1700 *
1701 * If we don't do a 2nd pass and filter it out, the higher-layers will
1702 * consider this to be ambiguous.
1703 */
1704 for (i = 0; i < vector_active(v); i++)
1705 if ((cmd_element = vector_slot(v, i)) != NULL) {
1706 if (index >= vector_active(cmd_element->strvec))
1707 vector_slot(v, i) = NULL;
1708 else {
1709 unsigned int j;
1710 int matched = 0;
1711
1712 descvec =
1713 vector_slot(cmd_element->strvec, index);
1714
1715 for (j = 0; j < vector_active(descvec); j++)
1716 if ((desc = vector_slot(descvec, j))) {
1717 enum match_type ret;
1718
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001719 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001720
1721 if (ret >= match_type)
1722 matched++;
1723 }
1724 if (!matched)
1725 vector_slot(v, i) = NULL;
1726 }
1727 }
1728
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001729 return match_type;
1730}
1731
1732/* Check ambiguous match */
1733static int
1734is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1735{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001736 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001737 unsigned int i;
1738 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001739 struct cmd_element *cmd_element;
1740 const char *matched = NULL;
1741 vector descvec;
1742 struct desc *desc;
1743
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001744 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1745 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1746 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1747 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1748 * that case, the string must remain allocated until this function exits or another match comes
1749 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1750 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1751 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1752 void *cmd_deopt_ctx = NULL;
1753
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001754 for (i = 0; i < vector_active(v); i++) {
1755 cmd_element = vector_slot(v, i);
1756 if (!cmd_element)
1757 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001758
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001759 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001760
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001761 descvec = vector_slot(cmd_element->strvec, index);
1762
1763 for (j = 0; j < vector_active(descvec); j++) {
1764 desc = vector_slot(descvec, j);
1765 if (!desc)
1766 continue;
1767
1768 enum match_type mtype;
1769 const char *str = desc->cmd;
1770
1771 if (CMD_OPTION(str)) {
1772 if (!cmd_deopt_ctx)
1773 cmd_deopt_ctx =
1774 talloc_named_const(tall_vty_cmd_ctx, 0,
1775 __func__);
1776 str = cmd_deopt(cmd_deopt_ctx, str);
1777 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001778 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001779 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001780
1781 switch (type) {
1782 case EXACT_MATCH:
1783 if (!(CMD_VARIABLE (str))
1784 && strcmp(command, str) == 0)
1785 match++;
1786 break;
1787 case PARTLY_MATCH:
1788 if (!(CMD_VARIABLE (str))
1789 && strncmp(command, str, strlen (command)) == 0)
1790 {
1791 if (matched
1792 && strcmp(matched,
1793 str) != 0) {
1794 ret = 1; /* There is ambiguous match. */
1795 goto free_and_return;
1796 } else
1797 matched = str;
1798 match++;
1799 }
1800 break;
1801 case RANGE_MATCH:
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001802 if (vty_cmd_range_match
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001803 (str, command)) {
1804 if (matched
1805 && strcmp(matched,
1806 str) != 0) {
1807 ret = 1;
1808 goto free_and_return;
1809 } else
1810 matched = str;
1811 match++;
1812 }
1813 break;
1814#ifdef HAVE_IPV6
1815 case IPV6_MATCH:
1816 if (CMD_IPV6(str))
1817 match++;
1818 break;
1819 case IPV6_PREFIX_MATCH:
1820 if ((mtype =
1821 cmd_ipv6_prefix_match
1822 (command)) != NO_MATCH) {
1823 if (mtype == PARTLY_MATCH) {
1824 ret = 2; /* There is incomplete match. */
1825 goto free_and_return;
1826 }
1827
1828 match++;
1829 }
1830 break;
1831#endif /* HAVE_IPV6 */
1832 case IPV4_MATCH:
1833 if (CMD_IPV4(str))
1834 match++;
1835 break;
1836 case IPV4_PREFIX_MATCH:
1837 if ((mtype =
1838 cmd_ipv4_prefix_match
1839 (command)) != NO_MATCH) {
1840 if (mtype == PARTLY_MATCH) {
1841 ret = 2; /* There is incomplete match. */
1842 goto free_and_return;
1843 }
1844
1845 match++;
1846 }
1847 break;
1848 case EXTEND_MATCH:
1849 if (CMD_VARIABLE (str))
1850 match++;
1851 break;
1852 case NO_MATCH:
1853 default:
1854 break;
1855 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001856 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001857 if (!match)
1858 vector_slot(v, i) = NULL;
1859 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001860
1861free_and_return:
1862 if (cmd_deopt_ctx)
1863 talloc_free(cmd_deopt_ctx);
1864 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001865}
1866
1867/* If src matches dst return dst string, otherwise return NULL */
1868static const char *cmd_entry_function(const char *src, const char *dst)
1869{
1870 /* Skip variable arguments. */
1871 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1872 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1873 return NULL;
1874
1875 /* In case of 'command \t', given src is NULL string. */
1876 if (src == NULL)
1877 return dst;
1878
1879 /* Matched with input string. */
1880 if (strncmp(src, dst, strlen(src)) == 0)
1881 return dst;
1882
1883 return NULL;
1884}
1885
1886/* If src matches dst return dst string, otherwise return NULL */
1887/* This version will return the dst string always if it is
1888 CMD_VARIABLE for '?' key processing */
1889static const char *cmd_entry_function_desc(const char *src, const char *dst)
1890{
1891 if (CMD_VARARG(dst))
1892 return dst;
1893
1894 if (CMD_RANGE(dst)) {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001895 if (vty_cmd_range_match(dst, src))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001896 return dst;
1897 else
1898 return NULL;
1899 }
1900#ifdef HAVE_IPV6
1901 if (CMD_IPV6(dst)) {
1902 if (cmd_ipv6_match(src))
1903 return dst;
1904 else
1905 return NULL;
1906 }
1907
1908 if (CMD_IPV6_PREFIX(dst)) {
1909 if (cmd_ipv6_prefix_match(src))
1910 return dst;
1911 else
1912 return NULL;
1913 }
1914#endif /* HAVE_IPV6 */
1915
1916 if (CMD_IPV4(dst)) {
1917 if (cmd_ipv4_match(src))
1918 return dst;
1919 else
1920 return NULL;
1921 }
1922
1923 if (CMD_IPV4_PREFIX(dst)) {
1924 if (cmd_ipv4_prefix_match(src))
1925 return dst;
1926 else
1927 return NULL;
1928 }
1929
1930 /* Optional or variable commands always match on '?' */
1931 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1932 return dst;
1933
1934 /* In case of 'command \t', given src is NULL string. */
1935 if (src == NULL)
1936 return dst;
1937
1938 if (strncmp(src, dst, strlen(src)) == 0)
1939 return dst;
1940 else
1941 return NULL;
1942}
1943
1944/* Check same string element existence. If it isn't there return
1945 1. */
1946static int cmd_unique_string(vector v, const char *str)
1947{
1948 unsigned int i;
1949 char *match;
1950
1951 for (i = 0; i < vector_active(v); i++)
1952 if ((match = vector_slot(v, i)) != NULL)
1953 if (strcmp(match, str) == 0)
1954 return 0;
1955 return 1;
1956}
1957
1958/* Compare string to description vector. If there is same string
1959 return 1 else return 0. */
1960static int desc_unique_string(vector v, const char *str)
1961{
1962 unsigned int i;
1963 struct desc *desc;
1964
1965 for (i = 0; i < vector_active(v); i++)
1966 if ((desc = vector_slot(v, i)) != NULL)
1967 if (strcmp(desc->cmd, str) == 0)
1968 return 1;
1969 return 0;
1970}
1971
1972static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1973{
1974 if (first_word != NULL &&
1975 node != AUTH_NODE &&
1976 node != VIEW_NODE &&
1977 node != AUTH_ENABLE_NODE &&
1978 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1979 return 1;
1980 return 0;
1981}
1982
1983/* '?' describe command support. */
1984static vector
1985cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1986{
1987 unsigned int i;
1988 vector cmd_vector;
1989#define INIT_MATCHVEC_SIZE 10
1990 vector matchvec;
1991 struct cmd_element *cmd_element;
1992 unsigned int index;
1993 int ret;
1994 enum match_type match;
1995 char *command;
1996 static struct desc desc_cr = { "<cr>", "" };
1997
1998 /* Set index. */
1999 if (vector_active(vline) == 0) {
2000 *status = CMD_ERR_NO_MATCH;
2001 return NULL;
2002 } else
2003 index = vector_active(vline) - 1;
2004
2005 /* Make copy vector of current node's command vector. */
2006 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2007
2008 /* Prepare match vector */
2009 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2010
2011 /* Filter commands. */
2012 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002013 for (i = 0; i < index; i++) {
2014 command = vector_slot(vline, i);
2015 if (!command)
2016 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002017
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002018 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002019
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002020 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002021 struct cmd_element *cmd_element;
2022 vector descvec;
2023 unsigned int j, k;
2024
2025 for (j = 0; j < vector_active(cmd_vector); j++)
2026 if ((cmd_element =
2027 vector_slot(cmd_vector, j)) != NULL
2028 &&
2029 (vector_active(cmd_element->strvec))) {
2030 descvec =
2031 vector_slot(cmd_element->
2032 strvec,
2033 vector_active
2034 (cmd_element->
2035 strvec) - 1);
2036 for (k = 0;
2037 k < vector_active(descvec);
2038 k++) {
2039 struct desc *desc =
2040 vector_slot(descvec,
2041 k);
2042 vector_set(matchvec,
2043 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002044 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002045 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002046
Harald Welte80d30fe2013-02-12 11:08:57 +01002047 vector_set(matchvec, &desc_cr);
2048 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002049
Harald Welte80d30fe2013-02-12 11:08:57 +01002050 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002051 }
2052
Harald Welte80d30fe2013-02-12 11:08:57 +01002053 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2054 match)) == 1) {
2055 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002056 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002057 *status = CMD_ERR_AMBIGUOUS;
2058 return NULL;
2059 } else if (ret == 2) {
2060 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002061 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002062 *status = CMD_ERR_NO_MATCH;
2063 return NULL;
2064 }
2065 }
2066
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002067 /* Prepare match vector */
2068 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2069
2070 /* Make sure that cmd_vector is filtered based on current word */
2071 command = vector_slot(vline, index);
2072 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002073 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002074
2075 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002076 for (i = 0; i < vector_active(cmd_vector); i++) {
2077 const char *string = NULL;
2078 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002079
Harald Welte80d30fe2013-02-12 11:08:57 +01002080 cmd_element = vector_slot(cmd_vector, i);
2081 if (!cmd_element)
2082 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002083
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002084 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2085 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002086 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002087 continue;
2088
Harald Welte80d30fe2013-02-12 11:08:57 +01002089 strvec = cmd_element->strvec;
2090
2091 /* if command is NULL, index may be equal to vector_active */
2092 if (command && index >= vector_active(strvec))
2093 vector_slot(cmd_vector, i) = NULL;
2094 else {
2095 /* Check if command is completed. */
2096 if (command == NULL
2097 && index == vector_active(strvec)) {
2098 string = "<cr>";
2099 if (!desc_unique_string(matchvec, string))
2100 vector_set(matchvec, &desc_cr);
2101 } else {
2102 unsigned int j;
2103 vector descvec = vector_slot(strvec, index);
2104 struct desc *desc;
2105
2106 for (j = 0; j < vector_active(descvec); j++) {
2107 desc = vector_slot(descvec, j);
2108 if (!desc)
2109 continue;
2110 string = cmd_entry_function_desc
2111 (command, desc->cmd);
2112 if (!string)
2113 continue;
2114 /* Uniqueness check */
2115 if (!desc_unique_string(matchvec, string))
2116 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002117 }
2118 }
2119 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002120 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002121 vector_free(cmd_vector);
2122
2123 if (vector_slot(matchvec, 0) == NULL) {
2124 vector_free(matchvec);
2125 *status = CMD_ERR_NO_MATCH;
2126 } else
2127 *status = CMD_SUCCESS;
2128
2129 return matchvec;
2130}
2131
2132vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2133{
2134 vector ret;
2135
2136 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2137 enum node_type onode;
2138 vector shifted_vline;
2139 unsigned int index;
2140
2141 onode = vty->node;
2142 vty->node = ENABLE_NODE;
2143 /* We can try it on enable node, cos' the vty is authenticated */
2144
2145 shifted_vline = vector_init(vector_count(vline));
2146 /* use memcpy? */
2147 for (index = 1; index < vector_active(vline); index++) {
2148 vector_set_index(shifted_vline, index - 1,
2149 vector_lookup(vline, index));
2150 }
2151
2152 ret = cmd_describe_command_real(shifted_vline, vty, status);
2153
2154 vector_free(shifted_vline);
2155 vty->node = onode;
2156 return ret;
2157 }
2158
2159 return cmd_describe_command_real(vline, vty, status);
2160}
2161
2162/* Check LCD of matched command. */
2163static int cmd_lcd(char **matched)
2164{
2165 int i;
2166 int j;
2167 int lcd = -1;
2168 char *s1, *s2;
2169 char c1, c2;
2170
2171 if (matched[0] == NULL || matched[1] == NULL)
2172 return 0;
2173
2174 for (i = 1; matched[i] != NULL; i++) {
2175 s1 = matched[i - 1];
2176 s2 = matched[i];
2177
2178 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2179 if (c1 != c2)
2180 break;
2181
2182 if (lcd < 0)
2183 lcd = j;
2184 else {
2185 if (lcd > j)
2186 lcd = j;
2187 }
2188 }
2189 return lcd;
2190}
2191
2192/* Command line completion support. */
2193static char **cmd_complete_command_real(vector vline, struct vty *vty,
2194 int *status)
2195{
2196 unsigned int i;
2197 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2198#define INIT_MATCHVEC_SIZE 10
2199 vector matchvec;
2200 struct cmd_element *cmd_element;
2201 unsigned int index;
2202 char **match_str;
2203 struct desc *desc;
2204 vector descvec;
2205 char *command;
2206 int lcd;
2207
2208 if (vector_active(vline) == 0) {
2209 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002210 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002211 return NULL;
2212 } else
2213 index = vector_active(vline) - 1;
2214
2215 /* First, filter by preceeding command string */
2216 for (i = 0; i < index; i++)
2217 if ((command = vector_slot(vline, i))) {
2218 enum match_type match;
2219 int ret;
2220
2221 /* First try completion match, if there is exactly match return 1 */
2222 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002223 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002224
2225 /* If there is exact match then filter ambiguous match else check
2226 ambiguousness. */
2227 if ((ret =
2228 is_cmd_ambiguous(command, cmd_vector, i,
2229 match)) == 1) {
2230 vector_free(cmd_vector);
2231 *status = CMD_ERR_AMBIGUOUS;
2232 return NULL;
2233 }
2234 /*
2235 else if (ret == 2)
2236 {
2237 vector_free (cmd_vector);
2238 *status = CMD_ERR_NO_MATCH;
2239 return NULL;
2240 }
2241 */
2242 }
2243
2244 /* Prepare match vector. */
2245 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2246
2247 /* Now we got into completion */
2248 for (i = 0; i < vector_active(cmd_vector); i++)
2249 if ((cmd_element = vector_slot(cmd_vector, i))) {
2250 const char *string;
2251 vector strvec = cmd_element->strvec;
2252
2253 /* Check field length */
2254 if (index >= vector_active(strvec))
2255 vector_slot(cmd_vector, i) = NULL;
2256 else {
2257 unsigned int j;
2258
2259 descvec = vector_slot(strvec, index);
2260 for (j = 0; j < vector_active(descvec); j++)
2261 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002262 const char *cmd = desc->cmd;
2263 char *tmp = NULL;
2264
2265 if (CMD_OPTION(desc->cmd)) {
2266 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2267 cmd = tmp;
2268 }
2269 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002270 if (cmd_unique_string (matchvec, string))
2271 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002272 if (tmp)
2273 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002274 }
2275 }
2276 }
2277
2278 /* We don't need cmd_vector any more. */
2279 vector_free(cmd_vector);
2280
2281 /* No matched command */
2282 if (vector_slot(matchvec, 0) == NULL) {
2283 vector_free(matchvec);
2284
2285 /* In case of 'command \t' pattern. Do you need '?' command at
2286 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002287 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002288 *status = CMD_ERR_NOTHING_TODO;
2289 else
2290 *status = CMD_ERR_NO_MATCH;
2291 return NULL;
2292 }
2293
2294 /* Only one matched */
2295 if (vector_slot(matchvec, 1) == NULL) {
2296 match_str = (char **)matchvec->index;
2297 vector_only_wrapper_free(matchvec);
2298 *status = CMD_COMPLETE_FULL_MATCH;
2299 return match_str;
2300 }
2301 /* Make it sure last element is NULL. */
2302 vector_set(matchvec, NULL);
2303
2304 /* Check LCD of matched strings. */
2305 if (vector_slot(vline, index) != NULL) {
2306 lcd = cmd_lcd((char **)matchvec->index);
2307
2308 if (lcd) {
2309 int len = strlen(vector_slot(vline, index));
2310
2311 if (len < lcd) {
2312 char *lcdstr;
2313
2314 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2315 "complete-lcdstr");
2316 memcpy(lcdstr, matchvec->index[0], lcd);
2317 lcdstr[lcd] = '\0';
2318
2319 /* match_str = (char **) &lcdstr; */
2320
2321 /* Free matchvec. */
2322 for (i = 0; i < vector_active(matchvec); i++) {
2323 if (vector_slot(matchvec, i))
2324 talloc_free(vector_slot(matchvec, i));
2325 }
2326 vector_free(matchvec);
2327
2328 /* Make new matchvec. */
2329 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2330 vector_set(matchvec, lcdstr);
2331 match_str = (char **)matchvec->index;
2332 vector_only_wrapper_free(matchvec);
2333
2334 *status = CMD_COMPLETE_MATCH;
2335 return match_str;
2336 }
2337 }
2338 }
2339
2340 match_str = (char **)matchvec->index;
2341 vector_only_wrapper_free(matchvec);
2342 *status = CMD_COMPLETE_LIST_MATCH;
2343 return match_str;
2344}
2345
2346char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2347{
2348 char **ret;
2349
2350 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2351 enum node_type onode;
2352 vector shifted_vline;
2353 unsigned int index;
2354
2355 onode = vty->node;
2356 vty->node = ENABLE_NODE;
2357 /* We can try it on enable node, cos' the vty is authenticated */
2358
2359 shifted_vline = vector_init(vector_count(vline));
2360 /* use memcpy? */
2361 for (index = 1; index < vector_active(vline); index++) {
2362 vector_set_index(shifted_vline, index - 1,
2363 vector_lookup(vline, index));
2364 }
2365
2366 ret = cmd_complete_command_real(shifted_vline, vty, status);
2367
2368 vector_free(shifted_vline);
2369 vty->node = onode;
2370 return ret;
2371 }
2372
2373 return cmd_complete_command_real(vline, vty, status);
2374}
2375
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002376static struct vty_parent_node *vty_parent(struct vty *vty)
2377{
2378 return llist_first_entry_or_null(&vty->parent_nodes,
2379 struct vty_parent_node,
2380 entry);
2381}
2382
2383static bool vty_pop_parent(struct vty *vty)
2384{
2385 struct vty_parent_node *parent = vty_parent(vty);
2386 if (!parent)
2387 return false;
2388 llist_del(&parent->entry);
2389 vty->node = parent->node;
2390 vty->priv = parent->priv;
2391 if (vty->indent)
2392 talloc_free(vty->indent);
2393 vty->indent = parent->indent;
2394 talloc_free(parent);
2395 return true;
2396}
2397
2398static void vty_clear_parents(struct vty *vty)
2399{
2400 while (vty_pop_parent(vty));
2401}
2402
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002403/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002404/*
2405 * This function MUST eventually converge on a node when called repeatedly,
2406 * there must not be any cycles.
2407 * All 'config' nodes shall converge on CONFIG_NODE.
2408 * All other 'enable' nodes shall converge on ENABLE_NODE.
2409 * All 'view' only nodes shall converge on VIEW_NODE.
2410 * All other nodes shall converge on themselves or it must be ensured,
2411 * that the user's rights are not extended anyhow by calling this function.
2412 *
2413 * Note that these requirements also apply to all functions that are used
2414 * as go_parent_cb.
2415 * Note also that this function relies on the is_config_child callback to
2416 * recognize non-config nodes if go_parent_cb is not set.
2417 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002418int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002419{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002420 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002421 case AUTH_NODE:
2422 case VIEW_NODE:
2423 case ENABLE_NODE:
2424 case CONFIG_NODE:
2425 vty_clear_parents(vty);
2426 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002427
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002428 case AUTH_ENABLE_NODE:
2429 vty->node = VIEW_NODE;
2430 vty_clear_parents(vty);
2431 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002432
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002433 default:
2434 if (host.app_info->go_parent_cb)
2435 host.app_info->go_parent_cb(vty);
2436 vty_pop_parent(vty);
2437 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002438 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002439
2440 return vty->node;
2441}
2442
2443/* Execute command by argument vline vector. */
2444static int
2445cmd_execute_command_real(vector vline, struct vty *vty,
2446 struct cmd_element **cmd)
2447{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002448 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002449 unsigned int index;
2450 vector cmd_vector;
2451 struct cmd_element *cmd_element;
2452 struct cmd_element *matched_element;
2453 unsigned int matched_count, incomplete_count;
2454 int argc;
2455 const char *argv[CMD_ARGC_MAX];
2456 enum match_type match = 0;
2457 int varflag;
2458 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002459 int rc;
2460 /* Used for temporary storage of cmd_deopt() allocated arguments during
2461 argv[] generation */
2462 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002463
2464 /* Make copy of command elements. */
2465 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2466
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002467 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002468 if ((command = vector_slot(vline, index))) {
2469 int ret;
2470
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002471 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002472 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002473
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002474 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002475 break;
2476
2477 ret =
2478 is_cmd_ambiguous(command, cmd_vector, index, match);
2479
2480 if (ret == 1) {
2481 vector_free(cmd_vector);
2482 return CMD_ERR_AMBIGUOUS;
2483 } else if (ret == 2) {
2484 vector_free(cmd_vector);
2485 return CMD_ERR_NO_MATCH;
2486 }
2487 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002488 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002489
2490 /* Check matched count. */
2491 matched_element = NULL;
2492 matched_count = 0;
2493 incomplete_count = 0;
2494
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002495 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002496 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002497 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002498 || index >= cmd_element->cmdsize) {
2499 matched_element = cmd_element;
2500#if 0
2501 printf("DEBUG: %s\n", cmd_element->string);
2502#endif
2503 matched_count++;
2504 } else {
2505 incomplete_count++;
2506 }
2507 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002508 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002509
2510 /* Finish of using cmd_vector. */
2511 vector_free(cmd_vector);
2512
2513 /* To execute command, matched_count must be 1. */
2514 if (matched_count == 0) {
2515 if (incomplete_count)
2516 return CMD_ERR_INCOMPLETE;
2517 else
2518 return CMD_ERR_NO_MATCH;
2519 }
2520
2521 if (matched_count > 1)
2522 return CMD_ERR_AMBIGUOUS;
2523
2524 /* Argument treatment */
2525 varflag = 0;
2526 argc = 0;
2527
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002528 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2529
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002530 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002531 if (argc == CMD_ARGC_MAX) {
2532 rc = CMD_ERR_EXEED_ARGC_MAX;
2533 goto rc_free_deopt_ctx;
2534 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002535 if (varflag) {
2536 argv[argc++] = vector_slot(vline, i);
2537 continue;
2538 }
2539
2540 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002541 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002542
2543 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002544 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002545 struct desc *desc = vector_slot(descvec, 0);
2546
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002547 if (CMD_OPTION(desc->cmd)) {
2548 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2549 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2550 } else {
2551 tmp_cmd = desc->cmd;
2552 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002553
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002554 if (CMD_VARARG(tmp_cmd))
2555 varflag = 1;
2556 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002557 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002558 else if (CMD_OPTION(desc->cmd))
2559 argv[argc++] = tmp_cmd;
2560 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002561 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002562 /* multi choice argument. look up which choice
2563 the user meant (can only be one after
2564 filtering and checking for ambigous). For instance,
2565 if user typed "th" for "(two|three)" arg, we
2566 want to pass "three" in argv[]. */
2567 for (j = 0; j < vector_active(descvec); j++) {
2568 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002569 if (!desc)
2570 continue;
2571 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2572 continue;
2573 if (CMD_OPTION(desc->cmd)) {
2574 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2575 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2576 } else {
2577 tmp_cmd = desc->cmd;
2578 }
2579
2580 if(CMD_VARIABLE(tmp_cmd)) {
2581 argv[argc++] = vector_slot(vline, i);
2582 } else {
2583 argv[argc++] = tmp_cmd;
2584 }
2585 break;
2586 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002587 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002588 }
2589
2590 /* For vtysh execution. */
2591 if (cmd)
2592 *cmd = matched_element;
2593
2594 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002595 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002596 else {
2597 /* Execute matched command. */
2598 struct vty_parent_node this_node = {
2599 .node = vty->node,
2600 .priv = vty->priv,
2601 .indent = vty->indent,
2602 };
2603 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002604 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002605
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002606 /* If we have stepped down into a child node, push a parent frame.
2607 * The causality is such: we don't expect every single node entry implementation to push
2608 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2609 * a parent node. Hence if the node changed without the parent node changing, we must
2610 * have stepped into a child node. */
2611 if (vty->node != this_node.node && parent == vty_parent(vty)
2612 && vty->node > CONFIG_NODE) {
2613 /* Push the parent node. */
2614 parent = talloc_zero(vty, struct vty_parent_node);
2615 *parent = this_node;
2616 llist_add(&parent->entry, &vty->parent_nodes);
2617 }
2618 }
2619
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002620rc_free_deopt_ctx:
2621 /* Now after we called the command func, we can free temporary strings */
2622 talloc_free(cmd_deopt_ctx);
2623 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002624}
2625
2626int
2627cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2628 int vtysh)
2629{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002630 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002631 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002632
2633 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002634
2635 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2636 vector shifted_vline;
2637 unsigned int index;
2638
2639 vty->node = ENABLE_NODE;
2640 /* We can try it on enable node, cos' the vty is authenticated */
2641
2642 shifted_vline = vector_init(vector_count(vline));
2643 /* use memcpy? */
2644 for (index = 1; index < vector_active(vline); index++) {
2645 vector_set_index(shifted_vline, index - 1,
2646 vector_lookup(vline, index));
2647 }
2648
2649 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2650
2651 vector_free(shifted_vline);
2652 vty->node = onode;
2653 return ret;
2654 }
2655
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002656 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002657}
2658
2659/* Execute command by argument readline. */
2660int
2661cmd_execute_command_strict(vector vline, struct vty *vty,
2662 struct cmd_element **cmd)
2663{
2664 unsigned int i;
2665 unsigned int index;
2666 vector cmd_vector;
2667 struct cmd_element *cmd_element;
2668 struct cmd_element *matched_element;
2669 unsigned int matched_count, incomplete_count;
2670 int argc;
2671 const char *argv[CMD_ARGC_MAX];
2672 int varflag;
2673 enum match_type match = 0;
2674 char *command;
2675
2676 /* Make copy of command element */
2677 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2678
2679 for (index = 0; index < vector_active(vline); index++)
2680 if ((command = vector_slot(vline, index))) {
2681 int ret;
2682
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002683 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002684 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002685
2686 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002687 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002688 break;
2689
2690 ret =
2691 is_cmd_ambiguous(command, cmd_vector, index, match);
2692 if (ret == 1) {
2693 vector_free(cmd_vector);
2694 return CMD_ERR_AMBIGUOUS;
2695 }
2696 if (ret == 2) {
2697 vector_free(cmd_vector);
2698 return CMD_ERR_NO_MATCH;
2699 }
2700 }
2701
2702 /* Check matched count. */
2703 matched_element = NULL;
2704 matched_count = 0;
2705 incomplete_count = 0;
2706 for (i = 0; i < vector_active(cmd_vector); i++)
2707 if (vector_slot(cmd_vector, i) != NULL) {
2708 cmd_element = vector_slot(cmd_vector, i);
2709
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002710 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002711 || index >= cmd_element->cmdsize) {
2712 matched_element = cmd_element;
2713 matched_count++;
2714 } else
2715 incomplete_count++;
2716 }
2717
2718 /* Finish of using cmd_vector. */
2719 vector_free(cmd_vector);
2720
2721 /* To execute command, matched_count must be 1. */
2722 if (matched_count == 0) {
2723 if (incomplete_count)
2724 return CMD_ERR_INCOMPLETE;
2725 else
2726 return CMD_ERR_NO_MATCH;
2727 }
2728
2729 if (matched_count > 1)
2730 return CMD_ERR_AMBIGUOUS;
2731
2732 /* Argument treatment */
2733 varflag = 0;
2734 argc = 0;
2735
2736 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002737 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002738 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002739 if (varflag) {
2740 argv[argc++] = vector_slot(vline, i);
2741 continue;
2742 }
2743
2744 vector descvec = vector_slot(matched_element->strvec, i);
2745
2746 if (vector_active(descvec) == 1) {
2747 struct desc *desc = vector_slot(descvec, 0);
2748
2749 if (CMD_VARARG(desc->cmd))
2750 varflag = 1;
2751
2752 if (varflag || CMD_VARIABLE(desc->cmd)
2753 || CMD_OPTION(desc->cmd))
2754 argv[argc++] = vector_slot(vline, i);
2755 } else {
2756 argv[argc++] = vector_slot(vline, i);
2757 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002758 }
2759
2760 /* For vtysh execution. */
2761 if (cmd)
2762 *cmd = matched_element;
2763
2764 if (matched_element->daemon)
2765 return CMD_SUCCESS_DAEMON;
2766
2767 /* Now execute matched command */
2768 return (*matched_element->func) (matched_element, vty, argc, argv);
2769}
2770
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002771static inline size_t len(const char *str)
2772{
2773 return str? strlen(str) : 0;
2774}
2775
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002776/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2777 * is longer than b, a must start with exactly b, and vice versa.
2778 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2779 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002780static int indent_cmp(const char *a, const char *b)
2781{
2782 size_t al, bl;
2783 al = len(a);
2784 bl = len(b);
2785 if (al > bl) {
2786 if (bl && strncmp(a, b, bl) != 0)
2787 return EINVAL;
2788 return 1;
2789 }
2790 /* al <= bl */
2791 if (al && strncmp(a, b, al) != 0)
2792 return EINVAL;
2793 return (al < bl)? -1 : 0;
2794}
2795
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002796/* Configration make from file. */
2797int config_from_file(struct vty *vty, FILE * fp)
2798{
2799 int ret;
2800 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002801 char *indent;
2802 int cmp;
2803 struct vty_parent_node this_node;
2804 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002805
2806 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002807 indent = NULL;
2808 vline = NULL;
2809 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002810
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002811 if (ret != CMD_SUCCESS)
2812 goto return_invalid_indent;
2813
2814 /* In case of comment or empty line */
2815 if (vline == NULL) {
2816 if (indent) {
2817 talloc_free(indent);
2818 indent = NULL;
2819 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002820 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002821 }
2822
Neels Hofmeyr43063632017-09-19 23:54:01 +02002823 /* We have a nonempty line. */
2824 if (!vty->indent) {
2825 /* We have just entered a node and expecting the first child to come up; but we
2826 * may also skip right back to a parent or ancestor level. */
2827 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002828
Neels Hofmeyr43063632017-09-19 23:54:01 +02002829 /* If there is no parent, record any indentation we encounter. */
2830 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2831
2832 if (cmp == EINVAL)
2833 goto return_invalid_indent;
2834
2835 if (cmp <= 0) {
2836 /* We have gone right back to the parent level or higher, we are skipping
2837 * this child node level entirely. Pop the parent to go back to a node
2838 * that was actually there (to reinstate vty->indent) and re-use below
2839 * go-parent while-loop to find an accurate match of indent in the node
2840 * ancestry. */
2841 vty_go_parent(vty);
2842 } else {
2843 /* The indent is deeper than the just entered parent, record the new
2844 * indentation characters. */
2845 vty->indent = talloc_strdup(vty, indent);
2846 /* This *is* the new indentation. */
2847 cmp = 0;
2848 }
2849 } else {
2850 /* There is a known indentation for this node level, validate and detect node
2851 * exits. */
2852 cmp = indent_cmp(indent, vty->indent);
2853 if (cmp == EINVAL)
2854 goto return_invalid_indent;
2855 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002856
2857 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2858 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2859 while (cmp < 0) {
2860 vty_go_parent(vty);
2861 cmp = indent_cmp(indent, vty->indent);
2862 if (cmp == EINVAL)
2863 goto return_invalid_indent;
2864 }
2865
2866 /* More indent without having entered a child node level? Either the parent node's indent
2867 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2868 * or the indentation increased even though the vty command didn't enter a child. */
2869 if (cmp > 0)
2870 goto return_invalid_indent;
2871
2872 /* Remember the current node before the command possibly changes it. */
2873 this_node = (struct vty_parent_node){
2874 .node = vty->node,
2875 .priv = vty->priv,
2876 .indent = vty->indent,
2877 };
2878
2879 parent = vty_parent(vty);
2880 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002881 cmd_free_strvec(vline);
2882
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002883 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002884 if (indent) {
2885 talloc_free(indent);
2886 indent = NULL;
2887 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002888 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002889 }
2890
2891 /* If we have stepped down into a child node, push a parent frame.
2892 * The causality is such: we don't expect every single node entry implementation to push
2893 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2894 * a parent node. Hence if the node changed without the parent node changing, we must
2895 * have stepped into a child node (and now expect a deeper indent). */
2896 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2897 /* Push the parent node. */
2898 parent = talloc_zero(vty, struct vty_parent_node);
2899 *parent = this_node;
2900 llist_add(&parent->entry, &vty->parent_nodes);
2901
2902 /* The current talloc'ed vty->indent string will now be owned by this parent
2903 * struct. Indicate that we don't know what deeper indent characters the user
2904 * will choose. */
2905 vty->indent = NULL;
2906 }
2907
2908 if (indent) {
2909 talloc_free(indent);
2910 indent = NULL;
2911 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002912 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002913 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2914 while (vty_parent(vty))
2915 vty_go_parent(vty);
2916
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002917 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002918
2919return_invalid_indent:
2920 if (vline)
2921 cmd_free_strvec(vline);
2922 if (indent) {
2923 talloc_free(indent);
2924 indent = NULL;
2925 }
2926 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002927}
2928
2929/* Configration from terminal */
2930DEFUN(config_terminal,
2931 config_terminal_cmd,
2932 "configure terminal",
2933 "Configuration from vty interface\n" "Configuration terminal\n")
2934{
2935 if (vty_config_lock(vty))
2936 vty->node = CONFIG_NODE;
2937 else {
2938 vty_out(vty, "VTY configuration is locked by other VTY%s",
2939 VTY_NEWLINE);
2940 return CMD_WARNING;
2941 }
2942 return CMD_SUCCESS;
2943}
2944
2945/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002946DEFUN(enable, config_enable_cmd,
2947 "enable [expert-mode]",
2948 "Turn on privileged mode command\n"
2949 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002950{
2951 /* If enable password is NULL, change to ENABLE_NODE */
2952 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2953 vty->type == VTY_SHELL_SERV)
2954 vty->node = ENABLE_NODE;
2955 else
2956 vty->node = AUTH_ENABLE_NODE;
2957
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002958 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002959
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002960 return CMD_SUCCESS;
2961}
2962
2963/* Disable command */
2964DEFUN(disable,
2965 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2966{
2967 if (vty->node == ENABLE_NODE)
2968 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002969
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002970 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002971
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002972 return CMD_SUCCESS;
2973}
2974
2975/* Down vty node level. */
2976gDEFUN(config_exit,
2977 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2978{
2979 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002980 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002981 case VIEW_NODE:
2982 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002983 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002984 break;
2985 case CONFIG_NODE:
2986 vty->node = ENABLE_NODE;
2987 vty_config_unlock(vty);
2988 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002989 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002990 if (vty->node > CONFIG_NODE)
2991 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002992 break;
2993 }
2994 return CMD_SUCCESS;
2995}
2996
2997/* End of configuration. */
2998 gDEFUN(config_end,
2999 config_end_cmd, "end", "End current mode and change to enable mode.")
3000{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003001 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02003002 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003003
3004 /* Repeatedly call go_parent until a top node is reached. */
3005 while (vty->node > CONFIG_NODE) {
3006 if (vty->node == last_node) {
3007 /* Ensure termination, this shouldn't happen. */
3008 break;
3009 }
3010 last_node = vty->node;
3011 vty_go_parent(vty);
3012 }
3013
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003014 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003015 if (vty->node > ENABLE_NODE)
3016 vty->node = ENABLE_NODE;
3017 vty->index = NULL;
3018 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003019 }
3020 return CMD_SUCCESS;
3021}
3022
Oliver Smith8a482fd2021-07-12 18:18:51 +02003023DEFUN(shutdown,
3024 shutdown_cmd, "shutdown", "Request a shutdown of the program\n")
3025{
3026 LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n");
3027 vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE);
3028
3029 /* Same exit path as if it was killed by the service manager */
3030 kill(getpid(), SIGTERM);
3031
3032 return CMD_SUCCESS;
3033}
3034
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003035/* Show version. */
3036DEFUN(show_version,
3037 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3038{
Harald Welte237f6242010-05-25 23:00:45 +02003039 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3040 host.app_info->version,
3041 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3042 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003043
3044 return CMD_SUCCESS;
3045}
3046
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003047DEFUN(show_online_help,
3048 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3049{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003050 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003051 return CMD_SUCCESS;
3052}
3053
Oliver Smith0c78bc62021-07-12 17:28:36 +02003054DEFUN(show_pid,
3055 show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
3056{
3057 vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
3058 return CMD_SUCCESS;
3059}
3060
Oliver Smithd243c2a2021-07-09 17:19:32 +02003061DEFUN(show_uptime,
3062 show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
3063{
Alexander Couzens06929162021-09-05 23:08:59 +02003064 vty_out(vty, "%s has been running for ", host.app_info->name);
3065 vty_out_uptime(vty, &starttime);
3066 vty_out_newline(vty);
Oliver Smithd243c2a2021-07-09 17:19:32 +02003067
3068 return CMD_SUCCESS;
3069}
3070
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003071/* Help display function for all node. */
3072gDEFUN(config_help,
3073 config_help_cmd, "help", "Description of the interactive help system\n")
3074{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003075 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3076 "anytime at the command line please press '?'.%s%s"
3077 "If nothing matches, the help list will be empty and you must backup%s"
3078 " until entering a '?' shows the available options.%s"
3079 "Two styles of help are provided:%s"
3080 "1. Full help is available when you are ready to enter a%s"
3081 "command argument (e.g. 'show ?') and describes each possible%s"
3082 "argument.%s"
3083 "2. Partial help is provided when an abbreviated argument is entered%s"
3084 " and you want to know what arguments match the input%s"
3085 " (e.g. 'show me?'.)%s%s",
3086 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3087 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3088 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3089 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003090 return CMD_SUCCESS;
3091}
3092
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003093enum {
3094 ATTR_TYPE_GLOBAL = (1 << 0),
3095 ATTR_TYPE_LIB = (1 << 1),
3096 ATTR_TYPE_APP = (1 << 2),
3097};
3098
3099static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3100{
3101 const char *desc;
3102 unsigned int i;
3103 bool found;
3104 char flag;
3105
3106 if (attr_mask & ATTR_TYPE_GLOBAL) {
3107 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3108
3109 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003110 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003111 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003112
3113 /* Skip attributes without flags */
3114 if (flag != '.')
3115 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003116 }
3117 }
3118
3119 if (attr_mask & ATTR_TYPE_LIB) {
3120 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3121
3122 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3123 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3124 continue;
3125 found = true;
3126
3127 flag = cmd_lib_attr_letters[i];
3128 if (flag == '\0')
3129 flag = '.';
3130
3131 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3132 }
3133
3134 if (!found)
3135 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3136 }
3137
3138 if (attr_mask & ATTR_TYPE_APP) {
3139 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3140
3141 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3142 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3143 continue;
3144 found = true;
3145
3146 flag = host.app_info->usr_attr_letters[i];
3147 if (flag == '\0')
3148 flag = '.';
3149
3150 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3151 }
3152
3153 if (!found)
3154 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3155 }
3156}
3157
3158gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3159 "show vty-attributes",
3160 SHOW_STR "List of VTY attributes\n")
3161{
3162 print_attr_list(vty, 0xff);
3163 return CMD_SUCCESS;
3164}
3165
3166gDEFUN(show_vty_attr, show_vty_attr_cmd,
3167 "show vty-attributes (application|library|global)",
3168 SHOW_STR "List of VTY attributes\n"
3169 "Application specific attributes only\n"
3170 "Library specific attributes only\n"
3171 "Global attributes only\n")
3172{
3173 unsigned int attr_mask = 0;
3174
3175 if (argv[0][0] == 'g') /* global */
3176 attr_mask |= ATTR_TYPE_GLOBAL;
3177 else if (argv[0][0] == 'l') /* library */
3178 attr_mask |= ATTR_TYPE_LIB;
3179 else if (argv[0][0] == 'a') /* application */
3180 attr_mask |= ATTR_TYPE_APP;
3181
3182 print_attr_list(vty, attr_mask);
3183 return CMD_SUCCESS;
3184}
3185
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003186/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003187static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003188{
3189 unsigned int flag_mask = 0x00;
3190 unsigned int f, i;
3191
3192 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3193 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3194 const struct cmd_element *cmd;
3195 char flag_letter;
3196
3197 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3198 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003199 if (cmd->attr & CMD_ATTR_DEPRECATED)
3200 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003201 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003202 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003203 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003204 continue;
3205
3206 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3207 flag_letter = cmd_lib_attr_letters[f];
3208 else
3209 flag_letter = host.app_info->usr_attr_letters[f];
3210
3211 if (flag_letter == '\0')
3212 continue;
3213
3214 flag_mask |= (1 << f);
3215 break;
3216 }
3217 }
3218
3219 return flag_mask;
3220}
3221
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003222/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3223static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3224{
3225 static char char_mask[8 + 1];
3226 char *ptr = &char_mask[0];
3227
3228 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003229 if (cmd->attr & CMD_ATTR_HIDDEN)
3230 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3231 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003232 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3233 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3234 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3235 else
3236 *(ptr++) = '.';
3237
3238 *ptr = '\0';
3239
3240 return char_mask;
3241}
3242
3243/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003244static const char *cmd_flag_mask(const struct cmd_element *cmd,
3245 unsigned int flag_mask)
3246{
3247 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3248 char *ptr = &char_mask[0];
3249 char flag_letter;
3250 unsigned int f;
3251
3252 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003253 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003254 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003255 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003256 *(ptr++) = '.';
3257 continue;
3258 }
3259
3260 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3261 flag_letter = cmd_lib_attr_letters[f];
3262 else
3263 flag_letter = host.app_info->usr_attr_letters[f];
3264
3265 *(ptr++) = flag_letter ? flag_letter : '.';
3266 }
3267
3268 *ptr = '\0';
3269
3270 return char_mask;
3271}
3272
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003273/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003274gDEFUN(config_list, config_list_cmd,
3275 "list [with-flags]",
3276 "Print command list\n"
3277 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003278{
3279 unsigned int i;
3280 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003281 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003282 struct cmd_element *cmd;
3283
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003284 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003285 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003286
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003287 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3288 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3289 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003290 if (cmd->attr & CMD_ATTR_DEPRECATED)
3291 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003292 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003293 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003294 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003295 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3296 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003297 vty_out(vty, " %s %s %s%s",
3298 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003299 cmd_flag_mask(cmd, flag_mask),
3300 cmd->string, VTY_NEWLINE);
3301 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003302 }
3303
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003304 return CMD_SUCCESS;
3305}
3306
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003307static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003308{
3309 unsigned int i;
3310 int fd;
3311 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003312 char *config_file_tmp = NULL;
3313 char *config_file_sav = NULL;
3314 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003315 struct stat st;
3316
3317 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003318
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003319 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3320 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3321 * manually instead. */
3322
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003323 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003324 config_file_sav =
3325 _talloc_zero(tall_vty_cmd_ctx,
3326 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3327 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003328 if (!config_file_sav)
3329 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003330 strcpy(config_file_sav, config_file);
3331 strcat(config_file_sav, CONF_BACKUP_EXT);
3332
3333 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003334 "config_file_tmp");
3335 if (!config_file_tmp) {
3336 talloc_free(config_file_sav);
3337 return -1;
3338 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003339 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3340
3341 /* Open file to configuration write. */
3342 fd = mkstemp(config_file_tmp);
3343 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003344 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003345 talloc_free(config_file_tmp);
3346 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003347 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003348 }
3349
3350 /* Make vty for configuration file. */
3351 file_vty = vty_new();
3352 file_vty->fd = fd;
3353 file_vty->type = VTY_FILE;
3354
3355 /* Config file header print. */
3356 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003357 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003358 //vty_time_print (file_vty, 1);
3359 vty_out(file_vty, "!\n");
3360
3361 for (i = 0; i < vector_active(cmdvec); i++)
3362 if ((node = vector_slot(cmdvec, i)) && node->func) {
3363 if ((*node->func) (file_vty))
3364 vty_out(file_vty, "!\n");
3365 }
3366 vty_close(file_vty);
3367
3368 if (unlink(config_file_sav) != 0)
3369 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003370 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003371 talloc_free(config_file_sav);
3372 talloc_free(config_file_tmp);
3373 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003374 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003375 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003376
3377 /* Only link the .sav file if the original file exists */
3378 if (stat(config_file, &st) == 0) {
3379 if (link(config_file, config_file_sav) != 0) {
3380 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3381 talloc_free(config_file_sav);
3382 talloc_free(config_file_tmp);
3383 unlink(config_file_tmp);
3384 return -3;
3385 }
3386 sync();
3387 if (unlink(config_file) != 0) {
3388 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3389 talloc_free(config_file_sav);
3390 talloc_free(config_file_tmp);
3391 unlink(config_file_tmp);
3392 return -4;
3393 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003394 }
3395 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003396 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003397 talloc_free(config_file_sav);
3398 talloc_free(config_file_tmp);
3399 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003400 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003401 }
3402 unlink(config_file_tmp);
3403 sync();
3404
3405 talloc_free(config_file_sav);
3406 talloc_free(config_file_tmp);
3407
3408 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003409 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3410 return -6;
3411 }
3412
3413 return 0;
3414}
3415
3416
3417/* Write current configuration into file. */
3418DEFUN(config_write_file,
3419 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003420 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003421 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003422 "Write to configuration file\n"
3423 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003424{
3425 char *failed_file;
3426 int rc;
3427
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003428 if (host.app_info->config_is_consistent) {
3429 rc = host.app_info->config_is_consistent(vty);
3430 if (!rc) {
3431 vty_out(vty, "Configuration is not consistent%s",
3432 VTY_NEWLINE);
3433 return CMD_WARNING;
3434 }
3435 }
3436
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003437 if (argc == 1)
3438 host_config_set(argv[0]);
3439
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003440 if (host.config == NULL) {
3441 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3442 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003443 return CMD_WARNING;
3444 }
3445
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003446 rc = write_config_file(host.config, &failed_file);
3447 switch (rc) {
3448 case -1:
3449 vty_out(vty, "Can't open configuration file %s.%s",
3450 failed_file, VTY_NEWLINE);
3451 rc = CMD_WARNING;
3452 break;
3453 case -2:
3454 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3455 failed_file, VTY_NEWLINE);
3456 rc = CMD_WARNING;
3457 break;
3458 case -3:
3459 vty_out(vty, "Can't backup old configuration file %s.%s",
3460 failed_file, VTY_NEWLINE);
3461 rc = CMD_WARNING;
3462 break;
3463 case -4:
3464 vty_out(vty, "Can't unlink configuration file %s.%s",
3465 failed_file, VTY_NEWLINE);
3466 rc = CMD_WARNING;
3467 break;
3468 case -5:
3469 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3470 VTY_NEWLINE);
3471 rc = CMD_WARNING;
3472 break;
3473 case -6:
3474 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3475 failed_file, strerror(errno), errno, VTY_NEWLINE);
3476 rc = CMD_WARNING;
3477 break;
3478 default:
3479 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3480 rc = CMD_SUCCESS;
3481 break;
3482 }
3483
3484 talloc_free(failed_file);
3485 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003486}
3487
3488ALIAS(config_write_file,
3489 config_write_cmd,
3490 "write", "Write running configuration to memory, network, or terminal\n")
3491
3492 ALIAS(config_write_file,
3493 config_write_memory_cmd,
3494 "write memory",
3495 "Write running configuration to memory, network, or terminal\n"
3496 "Write configuration to the file (same as write file)\n")
3497
3498 ALIAS(config_write_file,
3499 copy_runningconfig_startupconfig_cmd,
3500 "copy running-config startup-config",
3501 "Copy configuration\n"
3502 "Copy running config to... \n"
3503 "Copy running config to startup config (same as write file)\n")
3504
3505/* Write current configuration into the terminal. */
3506 DEFUN(config_write_terminal,
3507 config_write_terminal_cmd,
3508 "write terminal",
3509 "Write running configuration to memory, network, or terminal\n"
3510 "Write to terminal\n")
3511{
3512 unsigned int i;
3513 struct cmd_node *node;
3514
3515 if (vty->type == VTY_SHELL_SERV) {
3516 for (i = 0; i < vector_active(cmdvec); i++)
3517 if ((node = vector_slot(cmdvec, i)) && node->func
3518 && node->vtysh) {
3519 if ((*node->func) (vty))
3520 vty_out(vty, "!%s", VTY_NEWLINE);
3521 }
3522 } else {
3523 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3524 VTY_NEWLINE);
3525 vty_out(vty, "!%s", VTY_NEWLINE);
3526
3527 for (i = 0; i < vector_active(cmdvec); i++)
3528 if ((node = vector_slot(cmdvec, i)) && node->func) {
3529 if ((*node->func) (vty))
3530 vty_out(vty, "!%s", VTY_NEWLINE);
3531 }
3532 vty_out(vty, "end%s", VTY_NEWLINE);
3533 }
3534 return CMD_SUCCESS;
3535}
3536
3537/* Write current configuration into the terminal. */
3538ALIAS(config_write_terminal,
3539 show_running_config_cmd,
3540 "show running-config", SHOW_STR "running configuration\n")
3541
3542/* Write startup configuration into the terminal. */
3543 DEFUN(show_startup_config,
3544 show_startup_config_cmd,
3545 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3546{
3547 char buf[BUFSIZ];
3548 FILE *confp;
3549
3550 confp = fopen(host.config, "r");
3551 if (confp == NULL) {
3552 vty_out(vty, "Can't open configuration file [%s]%s",
3553 host.config, VTY_NEWLINE);
3554 return CMD_WARNING;
3555 }
3556
3557 while (fgets(buf, BUFSIZ, confp)) {
3558 char *cp = buf;
3559
3560 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3561 cp++;
3562 *cp = '\0';
3563
3564 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3565 }
3566
3567 fclose(confp);
3568
3569 return CMD_SUCCESS;
3570}
3571
3572/* Hostname configuration */
3573DEFUN(config_hostname,
3574 hostname_cmd,
3575 "hostname WORD",
3576 "Set system's network name\n" "This system's network name\n")
3577{
3578 if (!isalpha((int)*argv[0])) {
3579 vty_out(vty, "Please specify string starting with alphabet%s",
3580 VTY_NEWLINE);
3581 return CMD_WARNING;
3582 }
3583
3584 if (host.name)
3585 talloc_free(host.name);
3586
3587 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3588 return CMD_SUCCESS;
3589}
3590
3591DEFUN(config_no_hostname,
3592 no_hostname_cmd,
3593 "no hostname [HOSTNAME]",
3594 NO_STR "Reset system's network name\n" "Host name of this router\n")
3595{
3596 if (host.name)
3597 talloc_free(host.name);
3598 host.name = NULL;
3599 return CMD_SUCCESS;
3600}
3601
3602/* VTY interface password set. */
3603DEFUN(config_password, password_cmd,
3604 "password (8|) WORD",
3605 "Assign the terminal connection password\n"
3606 "Specifies a HIDDEN password will follow\n"
3607 "dummy string \n" "The HIDDEN line password string\n")
3608{
3609 /* Argument check. */
3610 if (argc == 0) {
3611 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3612 return CMD_WARNING;
3613 }
3614
3615 if (argc == 2) {
3616 if (*argv[0] == '8') {
3617 if (host.password)
3618 talloc_free(host.password);
3619 host.password = NULL;
3620 if (host.password_encrypt)
3621 talloc_free(host.password_encrypt);
3622 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3623 return CMD_SUCCESS;
3624 } else {
3625 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3626 return CMD_WARNING;
3627 }
3628 }
3629
3630 if (!isalnum((int)*argv[0])) {
3631 vty_out(vty,
3632 "Please specify string starting with alphanumeric%s",
3633 VTY_NEWLINE);
3634 return CMD_WARNING;
3635 }
3636
3637 if (host.password)
3638 talloc_free(host.password);
3639 host.password = NULL;
3640
3641#ifdef VTY_CRYPT_PW
3642 if (host.encrypt) {
3643 if (host.password_encrypt)
3644 talloc_free(host.password_encrypt);
3645 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3646 } else
3647#endif
3648 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3649
3650 return CMD_SUCCESS;
3651}
3652
3653ALIAS(config_password, password_text_cmd,
3654 "password LINE",
3655 "Assign the terminal connection password\n"
3656 "The UNENCRYPTED (cleartext) line password\n")
3657
3658/* VTY enable password set. */
3659 DEFUN(config_enable_password, enable_password_cmd,
3660 "enable password (8|) WORD",
3661 "Modify enable password parameters\n"
3662 "Assign the privileged level password\n"
3663 "Specifies a HIDDEN password will follow\n"
3664 "dummy string \n" "The HIDDEN 'enable' password string\n")
3665{
3666 /* Argument check. */
3667 if (argc == 0) {
3668 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3669 return CMD_WARNING;
3670 }
3671
3672 /* Crypt type is specified. */
3673 if (argc == 2) {
3674 if (*argv[0] == '8') {
3675 if (host.enable)
3676 talloc_free(host.enable);
3677 host.enable = NULL;
3678
3679 if (host.enable_encrypt)
3680 talloc_free(host.enable_encrypt);
3681 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3682
3683 return CMD_SUCCESS;
3684 } else {
3685 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3686 return CMD_WARNING;
3687 }
3688 }
3689
3690 if (!isalnum((int)*argv[0])) {
3691 vty_out(vty,
3692 "Please specify string starting with alphanumeric%s",
3693 VTY_NEWLINE);
3694 return CMD_WARNING;
3695 }
3696
3697 if (host.enable)
3698 talloc_free(host.enable);
3699 host.enable = NULL;
3700
3701 /* Plain password input. */
3702#ifdef VTY_CRYPT_PW
3703 if (host.encrypt) {
3704 if (host.enable_encrypt)
3705 talloc_free(host.enable_encrypt);
3706 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3707 } else
3708#endif
3709 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3710
3711 return CMD_SUCCESS;
3712}
3713
3714ALIAS(config_enable_password,
3715 enable_password_text_cmd,
3716 "enable password LINE",
3717 "Modify enable password parameters\n"
3718 "Assign the privileged level password\n"
3719 "The UNENCRYPTED (cleartext) 'enable' password\n")
3720
3721/* VTY enable password delete. */
3722 DEFUN(no_config_enable_password, no_enable_password_cmd,
3723 "no enable password",
3724 NO_STR
3725 "Modify enable password parameters\n"
3726 "Assign the privileged level password\n")
3727{
3728 if (host.enable)
3729 talloc_free(host.enable);
3730 host.enable = NULL;
3731
3732 if (host.enable_encrypt)
3733 talloc_free(host.enable_encrypt);
3734 host.enable_encrypt = NULL;
3735
3736 return CMD_SUCCESS;
3737}
3738
3739#ifdef VTY_CRYPT_PW
3740DEFUN(service_password_encrypt,
3741 service_password_encrypt_cmd,
3742 "service password-encryption",
3743 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3744{
3745 if (host.encrypt)
3746 return CMD_SUCCESS;
3747
3748 host.encrypt = 1;
3749
3750 if (host.password) {
3751 if (host.password_encrypt)
3752 talloc_free(host.password_encrypt);
3753 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3754 }
3755 if (host.enable) {
3756 if (host.enable_encrypt)
3757 talloc_free(host.enable_encrypt);
3758 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3759 }
3760
3761 return CMD_SUCCESS;
3762}
3763
3764DEFUN(no_service_password_encrypt,
3765 no_service_password_encrypt_cmd,
3766 "no service password-encryption",
3767 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3768{
3769 if (!host.encrypt)
3770 return CMD_SUCCESS;
3771
3772 host.encrypt = 0;
3773
3774 if (host.password_encrypt)
3775 talloc_free(host.password_encrypt);
3776 host.password_encrypt = NULL;
3777
3778 if (host.enable_encrypt)
3779 talloc_free(host.enable_encrypt);
3780 host.enable_encrypt = NULL;
3781
3782 return CMD_SUCCESS;
3783}
3784#endif
3785
3786DEFUN(config_terminal_length, config_terminal_length_cmd,
3787 "terminal length <0-512>",
3788 "Set terminal line parameters\n"
3789 "Set number of lines on a screen\n"
3790 "Number of lines on screen (0 for no pausing)\n")
3791{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003792 vty->lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003793 return CMD_SUCCESS;
3794}
3795
3796DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3797 "terminal no length",
3798 "Set terminal line parameters\n"
3799 NO_STR "Set number of lines on a screen\n")
3800{
3801 vty->lines = -1;
3802 return CMD_SUCCESS;
3803}
3804
3805DEFUN(service_terminal_length, service_terminal_length_cmd,
3806 "service terminal-length <0-512>",
3807 "Set up miscellaneous service\n"
3808 "System wide terminal length configuration\n"
3809 "Number of lines of VTY (0 means no line control)\n")
3810{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003811 host.lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003812 return CMD_SUCCESS;
3813}
3814
3815DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3816 "no service terminal-length [<0-512>]",
3817 NO_STR
3818 "Set up miscellaneous service\n"
3819 "System wide terminal length configuration\n"
3820 "Number of lines of VTY (0 means no line control)\n")
3821{
3822 host.lines = -1;
3823 return CMD_SUCCESS;
3824}
3825
3826DEFUN_HIDDEN(do_echo,
3827 echo_cmd,
3828 "echo .MESSAGE",
3829 "Echo a message back to the vty\n" "The message to echo\n")
3830{
3831 char *message;
3832
3833 vty_out(vty, "%s%s",
3834 ((message =
3835 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3836 if (message)
3837 talloc_free(message);
3838 return CMD_SUCCESS;
3839}
3840
3841#if 0
3842DEFUN(config_logmsg,
3843 config_logmsg_cmd,
3844 "logmsg " LOG_LEVELS " .MESSAGE",
3845 "Send a message to enabled logging destinations\n"
3846 LOG_LEVEL_DESC "The message to send\n")
3847{
3848 int level;
3849 char *message;
3850
3851 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3852 return CMD_ERR_NO_MATCH;
3853
3854 zlog(NULL, level,
3855 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3856 if (message)
3857 talloc_free(message);
3858 return CMD_SUCCESS;
3859}
3860
3861DEFUN(show_logging,
3862 show_logging_cmd,
3863 "show logging", SHOW_STR "Show current logging configuration\n")
3864{
3865 struct zlog *zl = zlog_default;
3866
3867 vty_out(vty, "Syslog logging: ");
3868 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3869 vty_out(vty, "disabled");
3870 else
3871 vty_out(vty, "level %s, facility %s, ident %s",
3872 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3873 facility_name(zl->facility), zl->ident);
3874 vty_out(vty, "%s", VTY_NEWLINE);
3875
3876 vty_out(vty, "Stdout logging: ");
3877 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3878 vty_out(vty, "disabled");
3879 else
3880 vty_out(vty, "level %s",
3881 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3882 vty_out(vty, "%s", VTY_NEWLINE);
3883
3884 vty_out(vty, "Monitor logging: ");
3885 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3886 vty_out(vty, "disabled");
3887 else
3888 vty_out(vty, "level %s",
3889 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3890 vty_out(vty, "%s", VTY_NEWLINE);
3891
3892 vty_out(vty, "File logging: ");
3893 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3894 vty_out(vty, "disabled");
3895 else
3896 vty_out(vty, "level %s, filename %s",
3897 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3898 zl->filename);
3899 vty_out(vty, "%s", VTY_NEWLINE);
3900
3901 vty_out(vty, "Protocol name: %s%s",
3902 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3903 vty_out(vty, "Record priority: %s%s",
3904 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3905
3906 return CMD_SUCCESS;
3907}
3908
3909DEFUN(config_log_stdout,
3910 config_log_stdout_cmd,
3911 "log stdout", "Logging control\n" "Set stdout logging level\n")
3912{
3913 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3914 return CMD_SUCCESS;
3915}
3916
3917DEFUN(config_log_stdout_level,
3918 config_log_stdout_level_cmd,
3919 "log stdout " LOG_LEVELS,
3920 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3921{
3922 int level;
3923
3924 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3925 return CMD_ERR_NO_MATCH;
3926 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3927 return CMD_SUCCESS;
3928}
3929
3930DEFUN(no_config_log_stdout,
3931 no_config_log_stdout_cmd,
3932 "no log stdout [LEVEL]",
3933 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3934{
3935 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3936 return CMD_SUCCESS;
3937}
3938
3939DEFUN(config_log_monitor,
3940 config_log_monitor_cmd,
3941 "log monitor",
3942 "Logging control\n" "Set terminal line (monitor) logging level\n")
3943{
3944 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3945 return CMD_SUCCESS;
3946}
3947
3948DEFUN(config_log_monitor_level,
3949 config_log_monitor_level_cmd,
3950 "log monitor " LOG_LEVELS,
3951 "Logging control\n"
3952 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3953{
3954 int level;
3955
3956 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3957 return CMD_ERR_NO_MATCH;
3958 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3959 return CMD_SUCCESS;
3960}
3961
3962DEFUN(no_config_log_monitor,
3963 no_config_log_monitor_cmd,
3964 "no log monitor [LEVEL]",
3965 NO_STR
3966 "Logging control\n"
3967 "Disable terminal line (monitor) logging\n" "Logging level\n")
3968{
3969 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3970 return CMD_SUCCESS;
3971}
3972
3973static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3974{
3975 int ret;
3976 char *p = NULL;
3977 const char *fullpath;
3978
3979 /* Path detection. */
3980 if (!IS_DIRECTORY_SEP(*fname)) {
3981 char cwd[MAXPATHLEN + 1];
3982 cwd[MAXPATHLEN] = '\0';
3983
3984 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3985 zlog_err("config_log_file: Unable to alloc mem!");
3986 return CMD_WARNING;
3987 }
3988
3989 if ((p = _talloc_zero(tall_vcmd_ctx,
3990 strlen(cwd) + strlen(fname) + 2),
3991 "set_log_file")
3992 == NULL) {
3993 zlog_err("config_log_file: Unable to alloc mem!");
3994 return CMD_WARNING;
3995 }
3996 sprintf(p, "%s/%s", cwd, fname);
3997 fullpath = p;
3998 } else
3999 fullpath = fname;
4000
4001 ret = zlog_set_file(NULL, fullpath, loglevel);
4002
4003 if (p)
4004 talloc_free(p);
4005
4006 if (!ret) {
4007 vty_out(vty, "can't open logfile %s\n", fname);
4008 return CMD_WARNING;
4009 }
4010
4011 if (host.logfile)
4012 talloc_free(host.logfile);
4013
4014 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
4015
4016 return CMD_SUCCESS;
4017}
4018
4019DEFUN(config_log_file,
4020 config_log_file_cmd,
4021 "log file FILENAME",
4022 "Logging control\n" "Logging to file\n" "Logging filename\n")
4023{
4024 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4025}
4026
4027DEFUN(config_log_file_level,
4028 config_log_file_level_cmd,
4029 "log file FILENAME " LOG_LEVELS,
4030 "Logging control\n"
4031 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4032{
4033 int level;
4034
4035 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4036 return CMD_ERR_NO_MATCH;
4037 return set_log_file(vty, argv[0], level);
4038}
4039
4040DEFUN(no_config_log_file,
4041 no_config_log_file_cmd,
4042 "no log file [FILENAME]",
4043 NO_STR
4044 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4045{
4046 zlog_reset_file(NULL);
4047
4048 if (host.logfile)
4049 talloc_free(host.logfile);
4050
4051 host.logfile = NULL;
4052
4053 return CMD_SUCCESS;
4054}
4055
4056ALIAS(no_config_log_file,
4057 no_config_log_file_level_cmd,
4058 "no log file FILENAME LEVEL",
4059 NO_STR
4060 "Logging control\n"
4061 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4062
4063 DEFUN(config_log_syslog,
4064 config_log_syslog_cmd,
4065 "log syslog", "Logging control\n" "Set syslog logging level\n")
4066{
4067 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4068 return CMD_SUCCESS;
4069}
4070
4071DEFUN(config_log_syslog_level,
4072 config_log_syslog_level_cmd,
4073 "log syslog " LOG_LEVELS,
4074 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4075{
4076 int level;
4077
4078 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4079 return CMD_ERR_NO_MATCH;
4080 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4081 return CMD_SUCCESS;
4082}
4083
4084DEFUN_DEPRECATED(config_log_syslog_facility,
4085 config_log_syslog_facility_cmd,
4086 "log syslog facility " LOG_FACILITIES,
4087 "Logging control\n"
4088 "Logging goes to syslog\n"
4089 "(Deprecated) Facility parameter for syslog messages\n"
4090 LOG_FACILITY_DESC)
4091{
4092 int facility;
4093
4094 if ((facility = facility_match(argv[0])) < 0)
4095 return CMD_ERR_NO_MATCH;
4096
4097 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4098 zlog_default->facility = facility;
4099 return CMD_SUCCESS;
4100}
4101
4102DEFUN(no_config_log_syslog,
4103 no_config_log_syslog_cmd,
4104 "no log syslog [LEVEL]",
4105 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4106{
4107 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4108 return CMD_SUCCESS;
4109}
4110
4111ALIAS(no_config_log_syslog,
4112 no_config_log_syslog_facility_cmd,
4113 "no log syslog facility " LOG_FACILITIES,
4114 NO_STR
4115 "Logging control\n"
4116 "Logging goes to syslog\n"
4117 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4118
4119 DEFUN(config_log_facility,
4120 config_log_facility_cmd,
4121 "log facility " LOG_FACILITIES,
4122 "Logging control\n"
4123 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4124{
4125 int facility;
4126
4127 if ((facility = facility_match(argv[0])) < 0)
4128 return CMD_ERR_NO_MATCH;
4129 zlog_default->facility = facility;
4130 return CMD_SUCCESS;
4131}
4132
4133DEFUN(no_config_log_facility,
4134 no_config_log_facility_cmd,
4135 "no log facility [FACILITY]",
4136 NO_STR
4137 "Logging control\n"
4138 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4139{
4140 zlog_default->facility = LOG_DAEMON;
4141 return CMD_SUCCESS;
4142}
4143
4144DEFUN_DEPRECATED(config_log_trap,
4145 config_log_trap_cmd,
4146 "log trap " LOG_LEVELS,
4147 "Logging control\n"
4148 "(Deprecated) Set logging level and default for all destinations\n"
4149 LOG_LEVEL_DESC)
4150{
4151 int new_level;
4152 int i;
4153
4154 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4155 return CMD_ERR_NO_MATCH;
4156
4157 zlog_default->default_lvl = new_level;
4158 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4159 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4160 zlog_default->maxlvl[i] = new_level;
4161 return CMD_SUCCESS;
4162}
4163
4164DEFUN_DEPRECATED(no_config_log_trap,
4165 no_config_log_trap_cmd,
4166 "no log trap [LEVEL]",
4167 NO_STR
4168 "Logging control\n"
4169 "Permit all logging information\n" "Logging level\n")
4170{
4171 zlog_default->default_lvl = LOG_DEBUG;
4172 return CMD_SUCCESS;
4173}
4174
4175DEFUN(config_log_record_priority,
4176 config_log_record_priority_cmd,
4177 "log record-priority",
4178 "Logging control\n"
4179 "Log the priority of the message within the message\n")
4180{
4181 zlog_default->record_priority = 1;
4182 return CMD_SUCCESS;
4183}
4184
4185DEFUN(no_config_log_record_priority,
4186 no_config_log_record_priority_cmd,
4187 "no log record-priority",
4188 NO_STR
4189 "Logging control\n"
4190 "Do not log the priority of the message within the message\n")
4191{
4192 zlog_default->record_priority = 0;
4193 return CMD_SUCCESS;
4194}
4195#endif
4196
4197DEFUN(banner_motd_file,
4198 banner_motd_file_cmd,
4199 "banner motd file [FILE]",
4200 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4201{
4202 if (host.motdfile)
4203 talloc_free(host.motdfile);
4204 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4205
4206 return CMD_SUCCESS;
4207}
4208
4209DEFUN(banner_motd_default,
4210 banner_motd_default_cmd,
4211 "banner motd default",
4212 "Set banner string\n" "Strings for motd\n" "Default string\n")
4213{
4214 host.motd = default_motd;
4215 return CMD_SUCCESS;
4216}
4217
4218DEFUN(no_banner_motd,
4219 no_banner_motd_cmd,
4220 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4221{
4222 host.motd = NULL;
4223 if (host.motdfile)
4224 talloc_free(host.motdfile);
4225 host.motdfile = NULL;
4226 return CMD_SUCCESS;
4227}
4228
4229/* Set config filename. Called from vty.c */
4230void host_config_set(const char *filename)
4231{
4232 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4233}
4234
Pau Espin Pedrolebb6c1f2021-05-17 18:54:21 +02004235const char *host_config_file(void)
4236{
4237 return host.config;
4238}
4239
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004240/*! Deprecated, now happens implicitly when calling install_node().
4241 * Users of the API may still attempt to call this function, hence
4242 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004243void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004244{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004245}
4246
4247/*! Deprecated, now happens implicitly when calling install_node().
4248 * Users of the API may still attempt to call this function, hence
4249 * leave it here as a no-op. */
4250void vty_install_default(int node)
4251{
4252}
4253
4254/*! Install common commands like 'exit' and 'list'. */
4255static void install_basic_node_commands(int node)
4256{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004257 install_lib_element(node, &config_help_cmd);
4258 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004259
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004260 install_lib_element(node, &show_vty_attr_all_cmd);
4261 install_lib_element(node, &show_vty_attr_cmd);
4262
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004263 install_lib_element(node, &config_write_terminal_cmd);
4264 install_lib_element(node, &config_write_file_cmd);
4265 install_lib_element(node, &config_write_memory_cmd);
4266 install_lib_element(node, &config_write_cmd);
4267 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004268
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004269 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004270
4271 if (node >= CONFIG_NODE) {
4272 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004273 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004274 }
4275}
4276
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004277/*! Return true if a node is installed by install_basic_node_commands(), so
4278 * that we can avoid repeating them for each and every node during 'show
4279 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004280static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004281{
4282 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004283 || cmd == &show_vty_attr_all_cmd
4284 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004285 || cmd == &config_list_cmd
4286 || cmd == &config_write_terminal_cmd
4287 || cmd == &config_write_file_cmd
4288 || cmd == &config_write_memory_cmd
4289 || cmd == &config_write_cmd
4290 || cmd == &show_running_config_cmd
4291 || cmd == &config_exit_cmd
4292 || cmd == &config_end_cmd)
4293 return true;
4294 return false;
4295}
4296
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004297/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004298 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004299 * \param[in] vty the vty of the code
4300 * \param[in] filename where to store the file
4301 * \return 0 in case of success.
4302 *
4303 * If the filename already exists create a filename.sav
4304 * version with the current code.
4305 *
4306 */
4307int osmo_vty_write_config_file(const char *filename)
4308{
4309 char *failed_file;
4310 int rc;
4311
4312 rc = write_config_file(filename, &failed_file);
4313 talloc_free(failed_file);
4314 return rc;
4315}
4316
4317/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004318 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004319 * \return 0 in case of success.
4320 *
4321 * If the filename already exists create a filename.sav
4322 * version with the current code.
4323 *
4324 */
4325int osmo_vty_save_config_file(void)
4326{
4327 char *failed_file;
4328 int rc;
4329
4330 if (host.config == NULL)
4331 return -7;
4332
4333 rc = write_config_file(host.config, &failed_file);
4334 talloc_free(failed_file);
4335 return rc;
4336}
4337
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004338/* Initialize command interface. Install basic nodes and commands. */
4339void cmd_init(int terminal)
4340{
4341 /* Allocate initial top vector of commands. */
4342 cmdvec = vector_init(VECTOR_MIN_SIZE);
4343
4344 /* Default host value settings. */
4345 host.name = NULL;
4346 host.password = NULL;
4347 host.enable = NULL;
4348 host.logfile = NULL;
4349 host.config = NULL;
4350 host.lines = -1;
4351 host.motd = default_motd;
4352 host.motdfile = NULL;
4353
4354 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004355 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004356 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004357 install_node_bare(&auth_node, NULL);
4358 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004359 install_node(&config_node, config_write_host);
4360
4361 /* Each node's basic commands. */
Oliver Smith0c78bc62021-07-12 17:28:36 +02004362 install_lib_element(VIEW_NODE, &show_pid_cmd);
Oliver Smithd243c2a2021-07-09 17:19:32 +02004363 install_lib_element(VIEW_NODE, &show_uptime_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004364 install_lib_element(VIEW_NODE, &show_version_cmd);
4365 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004366 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004367 install_lib_element(VIEW_NODE, &config_list_cmd);
4368 install_lib_element(VIEW_NODE, &config_exit_cmd);
4369 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004370 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4371 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004372 install_lib_element(VIEW_NODE, &config_enable_cmd);
4373 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4374 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4375 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004376 }
4377
4378 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004379 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4380 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4381 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Oliver Smith8a482fd2021-07-12 18:18:51 +02004382 install_lib_element(ENABLE_NODE, &shutdown_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004383 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004384 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4385 install_lib_element(ENABLE_NODE, &show_version_cmd);
4386 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004387
4388 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004389 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4390 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4391 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004392 }
4393
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004394 install_lib_element(CONFIG_NODE, &hostname_cmd);
4395 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004396
4397 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004398 install_lib_element(CONFIG_NODE, &password_cmd);
4399 install_lib_element(CONFIG_NODE, &password_text_cmd);
4400 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4401 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4402 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004403
4404#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004405 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4406 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004407#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004408 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4409 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4410 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4411 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4412 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004413
4414 }
4415 srand(time(NULL));
4416}
Harald Welte7acb30c2011-08-17 17:13:48 +02004417
Oliver Smithd243c2a2021-07-09 17:19:32 +02004418static __attribute__((constructor)) void on_dso_load_starttime(void)
4419{
4420 osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
4421}
4422
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004423/* FIXME: execute this section in the unit test instead */
4424static __attribute__((constructor)) void on_dso_load(void)
4425{
4426 unsigned int i, j;
4427
4428 /* Check total number of the library specific attributes */
4429 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4430
4431 /* Check for duplicates in the list of library specific flags */
4432 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4433 if (cmd_lib_attr_letters[i] == '\0')
4434 continue;
4435
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004436 /* Some flag characters are reserved for global attributes */
4437 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4438 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4439 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4440
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004441 /* Only upper case flag letters are allowed for libraries */
4442 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4443 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4444
4445 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4446 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4447 }
4448}
4449
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004450/*! @} */