blob: a60b544747567e136388d671db885043d35994b4 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Oliver Smith8a482fd2021-07-12 18:18:51 +020041#include <signal.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020042#include <sys/time.h>
43#include <sys/stat.h>
Oliver Smith8a482fd2021-07-12 18:18:51 +020044#include <sys/types.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020045
46#include <osmocom/vty/vector.h>
47#include <osmocom/vty/vty.h>
48#include <osmocom/vty/command.h>
49
Oliver Smith8a482fd2021-07-12 18:18:51 +020050#include <osmocom/core/logging.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010051#include <osmocom/core/talloc.h>
Oliver Smithd243c2a2021-07-09 17:19:32 +020052#include <osmocom/core/timer.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010053#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020054
Ruben Undheim766f77c2018-11-18 13:02:47 +010055#ifndef MAXPATHLEN
56 #define MAXPATHLEN 4096
57#endif
58
59
Harald Weltee881b1b2011-08-17 18:52:30 +020060/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020061 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020062 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020063 *
64 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020065
Harald Welte3fb0b6f2010-05-19 19:02:52 +020066#define CONFIGFILE_MASK 022
67
68void *tall_vty_cmd_ctx;
69
Oliver Smithd243c2a2021-07-09 17:19:32 +020070/* Set by on_dso_load_starttime() for "show uptime". */
71static struct timespec starttime;
72
Harald Welte3fb0b6f2010-05-19 19:02:52 +020073/* Command vector which includes some level of command lists. Normally
74 each daemon maintains each own cmdvec. */
75vector cmdvec;
76
77/* Host information structure. */
78struct host host;
79
80/* Standard command node structures. */
81struct cmd_node auth_node = {
82 AUTH_NODE,
83 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010084 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020085};
86
87struct cmd_node view_node = {
88 VIEW_NODE,
89 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010090 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020091};
92
93struct cmd_node auth_enable_node = {
94 AUTH_ENABLE_NODE,
95 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010096 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020097};
98
99struct cmd_node enable_node = {
100 ENABLE_NODE,
101 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +0100102 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200103};
104
105struct cmd_node config_node = {
106 CONFIG_NODE,
107 "%s(config)# ",
108 1
109};
110
111/* Default motd string. */
112const char *default_motd = "";
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200115 *
116 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200117void print_version(int print_copyright)
118{
Harald Welte237f6242010-05-25 23:00:45 +0200119 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200120 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200121 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200122}
123
124/* Utility function to concatenate argv argument into a single string
125 with inserting ' ' character between each argument. */
126char *argv_concat(const char **argv, int argc, int shift)
127{
128 int i;
129 size_t len;
130 char *str;
131 char *p;
132
133 len = 0;
134 for (i = shift; i < argc; i++)
135 len += strlen(argv[i]) + 1;
136 if (!len)
137 return NULL;
138 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
139 for (i = shift; i < argc; i++) {
140 size_t arglen;
141 memcpy(p, argv[i], (arglen = strlen(argv[i])));
142 p += arglen;
143 *p++ = ' ';
144 }
145 *(p - 1) = '\0';
146 return str;
147}
148
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200149/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
150 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
151 * in turn, this name us used for XML IDs in 'show online-help'. */
152static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
153{
154 const char *pos;
155 int dest = 0;
156
157 if (!prompt || !*prompt)
158 return "";
159
160 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
161 if (pos[0] == '%' && pos[1]) {
162 /* skip "%s"; loop pos++ does the second one. */
163 pos++;
164 continue;
165 }
166 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
167 continue;
168 name_buf[dest] = pos[0];
169 dest++;
170 }
171 name_buf[dest] = '\0';
172 return name_buf;
173}
174
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200175static void install_basic_node_commands(int node);
176
177/*! Install top node of command vector, without adding basic node commands. */
178static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200179{
180 vector_set_index(cmdvec, node->node, node);
181 node->func = func;
182 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200183 if (!*node->name)
184 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200185}
186
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200187/*! Install top node of command vector. */
188void install_node(struct cmd_node *node, int (*func) (struct vty *))
189{
190 install_node_bare(node, func);
191 install_basic_node_commands(node->node);
192}
193
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200194/* Compare two command's string. Used in sort_node (). */
195static int cmp_node(const void *p, const void *q)
196{
197 struct cmd_element *a = *(struct cmd_element **)p;
198 struct cmd_element *b = *(struct cmd_element **)q;
199
200 return strcmp(a->string, b->string);
201}
202
203static int cmp_desc(const void *p, const void *q)
204{
205 struct desc *a = *(struct desc **)p;
206 struct desc *b = *(struct desc **)q;
207
208 return strcmp(a->cmd, b->cmd);
209}
210
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200211/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200212void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200213{
214 unsigned int i, j;
215 struct cmd_node *cnode;
216 vector descvec;
217 struct cmd_element *cmd_element;
218
219 for (i = 0; i < vector_active(cmdvec); i++)
220 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
221 vector cmd_vector = cnode->cmd_vector;
222 qsort(cmd_vector->index, vector_active(cmd_vector),
223 sizeof(void *), cmp_node);
224
225 for (j = 0; j < vector_active(cmd_vector); j++)
226 if ((cmd_element =
227 vector_slot(cmd_vector, j)) != NULL
228 && vector_active(cmd_element->strvec)) {
229 descvec =
230 vector_slot(cmd_element->strvec,
231 vector_active
232 (cmd_element->strvec) -
233 1);
234 qsort(descvec->index,
235 vector_active(descvec),
236 sizeof(void *), cmp_desc);
237 }
238 }
239}
240
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200241/*! Break up string in command tokens. Return leading indents.
242 * \param[in] string String to split.
243 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
244 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
245 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
246 *
247 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
248 * so that \a indent can simply return the count of leading spaces.
249 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
250 */
251int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200252{
253 const char *cp, *start;
254 char *token;
255 int strlen;
256 vector strvec;
257
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200258 *strvec_p = NULL;
259 if (indent)
260 *indent = 0;
261
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200262 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200263 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200264
265 cp = string;
266
267 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200268 while (isspace((int)*cp) && *cp != '\0') {
269 /* if we're counting indents, we need to be strict about them */
270 if (indent && (*cp != ' ') && (*cp != '\t')) {
271 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
272 if (*cp == '\n' || *cp == '\r') {
273 cp++;
274 string = cp;
275 continue;
276 }
277 return CMD_ERR_INVALID_INDENT;
278 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200279 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 }
281
282 if (indent)
283 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Return if there is only white spaces */
286 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200287 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200288
289 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200290 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200291
292 /* Prepare return vector. */
293 strvec = vector_init(VECTOR_MIN_SIZE);
294
295 /* Copy each command piece and set into vector. */
296 while (1) {
297 start = cp;
298 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
299 *cp != '\0')
300 cp++;
301 strlen = cp - start;
302 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
303 memcpy(token, start, strlen);
304 *(token + strlen) = '\0';
305 vector_set(strvec, token);
306
307 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
308 *cp != '\0')
309 cp++;
310
311 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200312 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200313 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200314
315 *strvec_p = strvec;
316 return CMD_SUCCESS;
317}
318
319/*! Breaking up string into each command piece. I assume given
320 character is separated by a space character. Return value is a
321 vector which includes char ** data element. */
322vector cmd_make_strvec(const char *string)
323{
324 vector strvec;
325 cmd_make_strvec2(string, NULL, &strvec);
326 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200327}
328
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200329/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200330void cmd_free_strvec(vector v)
331{
332 unsigned int i;
333 char *cp;
334
335 if (!v)
336 return;
337
338 for (i = 0; i < vector_active(v); i++)
339 if ((cp = vector_slot(v, i)) != NULL)
340 talloc_free(cp);
341
342 vector_free(v);
343}
344
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200345/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200346static char *cmd_desc_str(const char **string)
347{
348 const char *cp, *start;
349 char *token;
350 int strlen;
351
352 cp = *string;
353
354 if (cp == NULL)
355 return NULL;
356
357 /* Skip white spaces. */
358 while (isspace((int)*cp) && *cp != '\0')
359 cp++;
360
361 /* Return if there is only white spaces */
362 if (*cp == '\0')
363 return NULL;
364
365 start = cp;
366
367 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
368 cp++;
369
370 strlen = cp - start;
371 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
372 memcpy(token, start, strlen);
373 *(token + strlen) = '\0';
374
375 *string = cp;
376
377 return token;
378}
379
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200380/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200381static vector cmd_make_descvec(const char *string, const char *descstr)
382{
383 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100384 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200385 const char *sp;
386 char *token;
387 int len;
388 const char *cp;
389 const char *dp;
390 vector allvec;
391 vector strvec = NULL;
392 struct desc *desc;
393
394 cp = string;
395 dp = descstr;
396
397 if (cp == NULL)
398 return NULL;
399
400 allvec = vector_init(VECTOR_MIN_SIZE);
401
402 while (1) {
403 while (isspace((int)*cp) && *cp != '\0')
404 cp++;
405
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100406 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
407 if (cp[0] == '[' && cp[1] == '(') {
408 optional_brace = 1;
409 cp++;
410 }
411
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200412 if (*cp == '(') {
413 multiple = 1;
414 cp++;
415 }
416 if (*cp == ')') {
417 multiple = 0;
418 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100419 if (*cp == ']')
420 cp++;
421 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200422 }
423 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100424 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200425 cp++;
426 }
427
428 while (isspace((int)*cp) && *cp != '\0')
429 cp++;
430
431 if (*cp == '(') {
432 multiple = 1;
433 cp++;
434 }
435
436 if (*cp == '\0')
437 return allvec;
438
439 sp = cp;
440
441 while (!
442 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
443 || *cp == ')' || *cp == '|') && *cp != '\0')
444 cp++;
445
446 len = cp - sp;
447
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100448 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
449 if (optional_brace) {
450 /* Place each individual multi-choice token in its own square braces */
451 token[0] = '[';
452 memcpy(token + 1, sp, len);
453 token[1 + len] = ']';
454 token[2 + len] = '\0';
455 } else {
456 memcpy(token, sp, len);
457 *(token + len) = '\0';
458 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200459
460 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
461 desc->cmd = token;
462 desc->str = cmd_desc_str(&dp);
463
464 if (multiple) {
465 if (multiple == 1) {
466 strvec = vector_init(VECTOR_MIN_SIZE);
467 vector_set(allvec, strvec);
468 }
469 multiple++;
470 } else {
471 strvec = vector_init(VECTOR_MIN_SIZE);
472 vector_set(allvec, strvec);
473 }
474 vector_set(strvec, desc);
475 }
476}
477
478/* Count mandantory string vector size. This is to determine inputed
479 command has enough command length. */
480static int cmd_cmdsize(vector strvec)
481{
482 unsigned int i;
483 int size = 0;
484 vector descvec;
485 struct desc *desc;
486
487 for (i = 0; i < vector_active(strvec); i++)
488 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100489 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200490 && (desc = vector_slot(descvec, 0)) != NULL) {
491 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
492 return size;
493 else
494 size++;
495 } else
496 size++;
497 }
498 return size;
499}
500
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200501/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200502const char *cmd_prompt(enum node_type node)
503{
504 struct cmd_node *cnode;
505
506 cnode = vector_slot(cmdvec, node);
507 return cnode->prompt;
508}
509
Alexander Couzensad580ba2016-05-16 16:01:45 +0200510/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200511 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200512 * \param unsafe string
513 * \return a new talloc char *
514 */
515char *osmo_asciidoc_escape(const char *inp)
516{
517 int _strlen;
518 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200519 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200520
521 if (!inp)
522 return NULL;
523 _strlen = strlen(inp);
524
525 for (i = 0; i < _strlen; ++i) {
526 switch (inp[i]) {
527 case '|':
528 len += 2;
529 break;
530 default:
531 len += 1;
532 break;
533 }
534 }
535
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200536 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200537 if (!out)
538 return NULL;
539
540 out_ptr = out;
541
Alexander Couzensad580ba2016-05-16 16:01:45 +0200542 for (i = 0; i < _strlen; ++i) {
543 switch (inp[i]) {
544 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200545 /* Prepend escape character "\": */
546 *(out_ptr++) = '\\';
547 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200548 default:
549 *(out_ptr++) = inp[i];
550 break;
551 }
552 }
553
Alexander Couzensad580ba2016-05-16 16:01:45 +0200554 out_ptr[0] = '\0';
555 return out;
556}
557
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100558static char *xml_escape(const char *inp)
559{
560 int _strlen;
561 char *out, *out_ptr;
562 int len = 0, i, j;
563
564 if (!inp)
565 return NULL;
566 _strlen = strlen(inp);
567
568 for (i = 0; i < _strlen; ++i) {
569 switch (inp[i]) {
570 case '"':
571 len += 6;
572 break;
573 case '\'':
574 len += 6;
575 break;
576 case '<':
577 len += 4;
578 break;
579 case '>':
580 len += 4;
581 break;
582 case '&':
583 len += 5;
584 break;
585 default:
586 len += 1;
587 break;
588 }
589 }
590
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200591 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100592 if (!out)
593 return NULL;
594
595 out_ptr = out;
596
597#define ADD(out, str) \
598 for (j = 0; j < strlen(str); ++j) \
599 *(out++) = str[j];
600
601 for (i = 0; i < _strlen; ++i) {
602 switch (inp[i]) {
603 case '"':
604 ADD(out_ptr, "&quot;");
605 break;
606 case '\'':
607 ADD(out_ptr, "&apos;");
608 break;
609 case '<':
610 ADD(out_ptr, "&lt;");
611 break;
612 case '>':
613 ADD(out_ptr, "&gt;");
614 break;
615 case '&':
616 ADD(out_ptr, "&amp;");
617 break;
618 default:
619 *(out_ptr++) = inp[i];
620 break;
621 }
622 }
623
624#undef ADD
625
626 out_ptr[0] = '\0';
627 return out;
628}
629
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200630typedef int (*print_func_t)(void *data, const char *fmt, ...);
631
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700632static const struct value_string cmd_attr_desc[] = {
633 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700634 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700635 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700636 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700637 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700638 { 0, NULL }
639};
640
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700641/* Public attributes (to be printed in the VTY / XML reference) */
642#define CMD_ATTR_PUBLIC_MASK \
Vadim Yanitskiy67608452020-10-23 20:37:32 +0700643 (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700644
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700645/* Get a flag character for a global VTY command attribute */
646static char cmd_attr_get_flag(unsigned int attr)
647{
648 switch (attr) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700649 case CMD_ATTR_HIDDEN:
650 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700651 case CMD_ATTR_IMMEDIATE:
652 return '!';
653 case CMD_ATTR_NODE_EXIT:
654 return '@';
655 default:
656 return '.';
657 }
658}
659
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700660/* Description of attributes shared between the lib commands */
661static const char * const cmd_lib_attr_desc[32] = {
662 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
663 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200664 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
665 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200666 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
667 "This command applies on IPA link establishment",
668 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
669 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700670};
671
672/* Flag letters of attributes shared between the lib commands.
673 * NOTE: uppercase letters only, the rest is reserved for applications. */
674static const char cmd_lib_attr_letters[32] = {
675 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200676 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200677 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
678 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700679};
680
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100681/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200682 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100683 */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700684static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
685 void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100686{
687 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700688 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100689
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200690 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700691
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700692 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700693 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700694 print_func(data, " <attributes scope='global'>%s", newline);
695
696 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
697 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700698 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700699
700 if (~cmd->attr & cmd_attr_desc[i].value)
701 continue;
702
703 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700704 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700705 xml_att_desc, newline);
706 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700707
708 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
709 if (flag != '.')
710 print_func(data, " flag='%c'", flag);
711 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700712 }
713
714 print_func(data, " </attributes>%s", newline);
715 }
716
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700717 /* Print application specific attributes and their description */
718 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700719 const char * const *desc;
720 const char *letters;
721
722 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
723 print_func(data, " <attributes scope='library'>%s", newline);
724 letters = &cmd_lib_attr_letters[0];
725 desc = &cmd_lib_attr_desc[0];
726 } else {
727 print_func(data, " <attributes scope='application'>%s", newline);
728 letters = &host.app_info->usr_attr_letters[0];
729 desc = &host.app_info->usr_attr_desc[0];
730 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700731
732 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
733 char *xml_att_desc;
734 char flag;
735
736 /* Skip attribute if *not* set */
Harald Weltec296e292020-12-21 15:44:52 +0100737 if (~cmd->usrattr & ((unsigned)1 << i))
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700738 continue;
739
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700740 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700741 print_func(data, " <attribute doc='%s'", xml_att_desc);
742 talloc_free(xml_att_desc);
743
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700744 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700745 print_func(data, " flag='%c'", flag);
746 print_func(data, " />%s", newline);
747 }
748
749 print_func(data, " </attributes>%s", newline);
750 }
751
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200752 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100753
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700754 for (i = 0; i < vector_count(cmd->strvec); ++i) {
755 vector descvec = vector_slot(cmd->strvec, i);
756 int j;
757 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100758 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700759 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100760 if (desc == NULL)
761 continue;
762
763 xml_param = xml_escape(desc->cmd);
764 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200765 print_func(data, " <param name='%s' doc='%s' />%s",
766 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100767 talloc_free(xml_param);
768 talloc_free(xml_doc);
769 }
770 }
771
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200772 print_func(data, " </params>%s", newline);
773 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100774
775 talloc_free(xml_string);
776 return 0;
777}
778
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700779static bool vty_command_is_common(const struct cmd_element *cmd);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200780
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100781/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200782 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700783 *
784 * (gflag_mask, match = false) - print only those commands with non-matching flags.
785 * (gflag_mask, match = true) - print only those commands with matching flags.
786 *
787 * Some examples:
788 *
789 * Print all commands except deprecated and hidden (default mode):
790 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
791 * Print only deprecated and hidden commands:
792 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
793 * Print all commands except deprecated (expert mode):
794 * (CMD_ATTR_DEPRECATED, false)
795 * Print only hidden commands:
796 * (CMD_ATTR_HIDDEN, true)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100797 */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700798static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
799 unsigned char gflag_mask, bool match)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100800{
801 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200802 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100803
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200804 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100805
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200806 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200807 print_func(data, " <node id='_common_cmds_'>%s", newline);
808 print_func(data, " <name>Common Commands</name>%s", newline);
809 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
810 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200811 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700812 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200813 if (!cnode)
814 continue;
815 if (cnode->node != CONFIG_NODE)
816 continue;
817
818 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700819 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200820 if (!vty_command_is_common(elem))
821 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700822 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700823 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700824 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700825 continue;
826 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200827 }
828 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200829 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200830
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100831 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700832 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100833 if (!cnode)
834 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200835 if (vector_active(cnode->cmd_vector) < 1)
836 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100837
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200838 /* De-dup node IDs: how many times has this same name been used before? Count the first
839 * occurence as _1 and omit that first suffix, so that the first occurence is called
840 * 'name', the second becomes 'name_2', then 'name_3', ... */
841 same_name_count = 1;
842 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700843 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200844 if (!cnode2)
845 continue;
846 if (strcmp(cnode->name, cnode2->name) == 0)
847 same_name_count ++;
848 }
849
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200850 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200851 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200852 print_func(data, "_%d", same_name_count);
853 print_func(data, "'>%s", newline);
854 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100855
856 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700857 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200858 if (vty_command_is_common(elem))
859 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700860 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700861 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700862 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700863 continue;
864 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100865 }
866
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200867 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100868 }
869
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200870 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100871
872 return 0;
873}
874
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200875static int print_func_vty(void *data, const char *format, ...)
876{
877 struct vty *vty = data;
878 va_list args;
879 int rc;
880 va_start(args, format);
881 rc = vty_out_va(vty, format, args);
882 va_end(args);
883 return rc;
884}
885
886static int vty_dump_xml_ref_to_vty(struct vty *vty)
887{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700888 unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
889 if (!vty->expert_mode)
890 gflag_mask |= CMD_ATTR_HIDDEN;
891 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200892}
893
894static int print_func_stream(void *data, const char *format, ...)
895{
896 va_list args;
897 int rc;
898 va_start(args, format);
899 rc = vfprintf((FILE*)data, format, args);
900 va_end(args);
901 return rc;
902}
903
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700904const struct value_string vty_ref_gen_mode_names[] = {
905 { VTY_REF_GEN_MODE_DEFAULT, "default" },
906 { VTY_REF_GEN_MODE_EXPERT, "expert" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700907 { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700908 { 0, NULL }
909};
910
911const struct value_string vty_ref_gen_mode_desc[] = {
912 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
913 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700914 { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700915 { 0, NULL }
916};
917
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200918/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700919 * \param[out] stream Output stream to print the XML reference to.
920 * \param[in] mode The XML reference generation mode.
921 * \returns always 0 for now, no errors possible.
922 */
923int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
924{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700925 unsigned char gflag_mask;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700926 bool match = false;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700927
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700928 switch (mode) {
929 case VTY_REF_GEN_MODE_EXPERT:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700930 /* All commands except deprecated */
931 gflag_mask = CMD_ATTR_DEPRECATED;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700932 break;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700933 case VTY_REF_GEN_MODE_HIDDEN:
934 /* Only hidden commands */
935 gflag_mask = CMD_ATTR_HIDDEN;
936 match = true;
937 break;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700938 case VTY_REF_GEN_MODE_DEFAULT:
939 default:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700940 /* All commands except deprecated and hidden */
941 gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700942 break;
943 }
944
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700945 return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700946}
947
948/*! Print the XML reference of all VTY nodes to the given stream.
949 * \param[out] stream Output stream to print the XML reference to.
950 * \returns always 0 for now, no errors possible.
951 *
952 * NOTE: this function is deprecated because it does not allow to
953 * specify the XML reference generation mode (default mode
954 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200955 */
956int vty_dump_xml_ref(FILE *stream)
957{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700958 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200959}
960
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200961/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100962static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
963{
964 int i;
965
966 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
967 struct cmd_element *elem;
968 elem = vector_slot(cnode->cmd_vector, i);
969 if (!elem->string)
970 continue;
971 if (!strcmp(elem->string, cmdstring))
972 return 1;
973 }
974 return 0;
975}
976
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200977/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200978 * \param[in] ntype Node Type
979 * \param[cmd] element to be installed
980 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000981void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200982{
983 struct cmd_node *cnode;
984
985 cnode = vector_slot(cmdvec, ntype);
986
Harald Weltea99d45a2015-11-12 13:48:23 +0100987 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100988 /* ensure no _identical_ command has been registered at this
989 * node so far */
990 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200991
992 vector_set(cnode->cmd_vector, cmd);
993
994 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
995 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
996}
997
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700998/*! Install a library command into a node
999 * \param[in] ntype Node Type
1000 * \param[in] cmd element to be installed
1001 */
1002void install_lib_element(int ntype, struct cmd_element *cmd)
1003{
1004 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1005 install_element(ntype, cmd);
1006}
1007
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001008/* Install a command into VIEW and ENABLE node */
1009void install_element_ve(struct cmd_element *cmd)
1010{
1011 install_element(VIEW_NODE, cmd);
1012 install_element(ENABLE_NODE, cmd);
1013}
1014
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001015/* Install a library command into VIEW and ENABLE node */
1016void install_lib_element_ve(struct cmd_element *cmd)
1017{
1018 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1019 install_element_ve(cmd);
1020}
1021
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022#ifdef VTY_CRYPT_PW
1023static unsigned char itoa64[] =
1024 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1025
1026static void to64(char *s, long v, int n)
1027{
1028 while (--n >= 0) {
1029 *s++ = itoa64[v & 0x3f];
1030 v >>= 6;
1031 }
1032}
1033
1034static char *zencrypt(const char *passwd)
1035{
1036 char salt[6];
1037 struct timeval tv;
1038 char *crypt(const char *, const char *);
1039
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001040 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001041
1042 to64(&salt[0], random(), 3);
1043 to64(&salt[3], tv.tv_usec, 3);
1044 salt[5] = '\0';
1045
1046 return crypt(passwd, salt);
1047}
1048#endif
1049
1050/* This function write configuration of this host. */
1051static int config_write_host(struct vty *vty)
1052{
1053 if (host.name)
1054 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1055
1056 if (host.encrypt) {
1057 if (host.password_encrypt)
1058 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1059 VTY_NEWLINE);
1060 if (host.enable_encrypt)
1061 vty_out(vty, "enable password 8 %s%s",
1062 host.enable_encrypt, VTY_NEWLINE);
1063 } else {
1064 if (host.password)
1065 vty_out(vty, "password %s%s", host.password,
1066 VTY_NEWLINE);
1067 if (host.enable)
1068 vty_out(vty, "enable password %s%s", host.enable,
1069 VTY_NEWLINE);
1070 }
1071
1072 if (host.advanced)
1073 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1074
1075 if (host.encrypt)
1076 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1077
1078 if (host.lines >= 0)
1079 vty_out(vty, "service terminal-length %d%s", host.lines,
1080 VTY_NEWLINE);
1081
1082 if (host.motdfile)
1083 vty_out(vty, "banner motd file %s%s", host.motdfile,
1084 VTY_NEWLINE);
1085 else if (!host.motd)
1086 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1087
1088 return 1;
1089}
1090
1091/* Utility function for getting command vector. */
1092static vector cmd_node_vector(vector v, enum node_type ntype)
1093{
1094 struct cmd_node *cnode = vector_slot(v, ntype);
Pau Espin Pedrol079149e2022-06-16 17:08:34 +02001095 OSMO_ASSERT(cnode != NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001096 return cnode->cmd_vector;
1097}
1098
1099/* Completion match types. */
1100enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001101 NO_MATCH = 0,
1102 ANY_MATCH,
1103 EXTEND_MATCH,
1104 IPV4_PREFIX_MATCH,
1105 IPV4_MATCH,
1106 IPV6_PREFIX_MATCH,
1107 IPV6_MATCH,
1108 RANGE_MATCH,
1109 VARARG_MATCH,
1110 PARTLY_MATCH,
1111 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001112};
1113
1114static enum match_type cmd_ipv4_match(const char *str)
1115{
1116 const char *sp;
1117 int dots = 0, nums = 0;
1118 char buf[4];
1119
1120 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001121 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001122
1123 for (;;) {
1124 memset(buf, 0, sizeof(buf));
1125 sp = str;
1126 while (*str != '\0') {
1127 if (*str == '.') {
1128 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001129 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130
1131 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001132 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001133
1134 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001135 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001136
1137 dots++;
1138 break;
1139 }
1140 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001141 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001142
1143 str++;
1144 }
1145
1146 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 strncpy(buf, sp, str - sp);
1150 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001151 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001152
1153 nums++;
1154
1155 if (*str == '\0')
1156 break;
1157
1158 str++;
1159 }
1160
1161 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001162 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001163
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001164 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001165}
1166
1167static enum match_type cmd_ipv4_prefix_match(const char *str)
1168{
1169 const char *sp;
1170 int dots = 0;
1171 char buf[4];
1172
1173 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001174 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001175
1176 for (;;) {
1177 memset(buf, 0, sizeof(buf));
1178 sp = str;
1179 while (*str != '\0' && *str != '/') {
1180 if (*str == '.') {
1181 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001182 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001183
1184 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001185 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186
1187 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001188 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001189
1190 dots++;
1191 break;
1192 }
1193
1194 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001195 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001196
1197 str++;
1198 }
1199
1200 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001201 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001202
1203 strncpy(buf, sp, str - sp);
1204 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001205 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001206
1207 if (dots == 3) {
1208 if (*str == '/') {
1209 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001210 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001211
1212 str++;
1213 break;
1214 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001215 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001216 }
1217
1218 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001219 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001220
1221 str++;
1222 }
1223
1224 sp = str;
1225 while (*str != '\0') {
1226 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001227 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001228
1229 str++;
1230 }
1231
1232 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001233 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001234
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001235 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001236}
1237
1238#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1239#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1240#define STATE_START 1
1241#define STATE_COLON 2
1242#define STATE_DOUBLE 3
1243#define STATE_ADDR 4
1244#define STATE_DOT 5
1245#define STATE_SLASH 6
1246#define STATE_MASK 7
1247
1248#ifdef HAVE_IPV6
1249
1250static enum match_type cmd_ipv6_match(const char *str)
1251{
1252 int state = STATE_START;
1253 int colons = 0, nums = 0, double_colon = 0;
1254 const char *sp = NULL;
1255 struct sockaddr_in6 sin6_dummy;
1256 int ret;
1257
1258 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001259 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001260
1261 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001262 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001263
1264 /* use inet_pton that has a better support,
1265 * for example inet_pton can support the automatic addresses:
1266 * ::1.2.3.4
1267 */
1268 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1269
1270 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001271 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001272
1273 while (*str != '\0') {
1274 switch (state) {
1275 case STATE_START:
1276 if (*str == ':') {
1277 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001278 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001279 colons--;
1280 state = STATE_COLON;
1281 } else {
1282 sp = str;
1283 state = STATE_ADDR;
1284 }
1285
1286 continue;
1287 case STATE_COLON:
1288 colons++;
1289 if (*(str + 1) == ':')
1290 state = STATE_DOUBLE;
1291 else {
1292 sp = str + 1;
1293 state = STATE_ADDR;
1294 }
1295 break;
1296 case STATE_DOUBLE:
1297 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001298 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001299
1300 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001301 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302 else {
1303 if (*(str + 1) != '\0')
1304 colons++;
1305 sp = str + 1;
1306 state = STATE_ADDR;
1307 }
1308
1309 double_colon++;
1310 nums++;
1311 break;
1312 case STATE_ADDR:
1313 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1314 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001315 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001316
1317 nums++;
1318 state = STATE_COLON;
1319 }
1320 if (*(str + 1) == '.')
1321 state = STATE_DOT;
1322 break;
1323 case STATE_DOT:
1324 state = STATE_ADDR;
1325 break;
1326 default:
1327 break;
1328 }
1329
1330 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001331 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001332
1333 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001334 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001335
1336 str++;
1337 }
1338
1339#if 0
1340 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001341 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001342#endif /* 0 */
1343
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001344 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001345}
1346
1347static enum match_type cmd_ipv6_prefix_match(const char *str)
1348{
1349 int state = STATE_START;
1350 int colons = 0, nums = 0, double_colon = 0;
1351 int mask;
1352 const char *sp = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001353
1354 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001355 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001356
1357 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001358 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001359
1360 while (*str != '\0' && state != STATE_MASK) {
1361 switch (state) {
1362 case STATE_START:
1363 if (*str == ':') {
1364 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001365 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001366 colons--;
1367 state = STATE_COLON;
1368 } else {
1369 sp = str;
1370 state = STATE_ADDR;
1371 }
1372
1373 continue;
1374 case STATE_COLON:
1375 colons++;
1376 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001377 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378 else if (*(str + 1) == ':')
1379 state = STATE_DOUBLE;
1380 else {
1381 sp = str + 1;
1382 state = STATE_ADDR;
1383 }
1384 break;
1385 case STATE_DOUBLE:
1386 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001387 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001388
1389 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001390 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001391 else {
1392 if (*(str + 1) != '\0' && *(str + 1) != '/')
1393 colons++;
1394 sp = str + 1;
1395
1396 if (*(str + 1) == '/')
1397 state = STATE_SLASH;
1398 else
1399 state = STATE_ADDR;
1400 }
1401
1402 double_colon++;
1403 nums += 1;
1404 break;
1405 case STATE_ADDR:
1406 if (*(str + 1) == ':' || *(str + 1) == '.'
1407 || *(str + 1) == '\0' || *(str + 1) == '/') {
1408 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001409 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001410
1411 for (; sp <= str; sp++)
1412 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001413 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001414
1415 nums++;
1416
1417 if (*(str + 1) == ':')
1418 state = STATE_COLON;
1419 else if (*(str + 1) == '.')
1420 state = STATE_DOT;
1421 else if (*(str + 1) == '/')
1422 state = STATE_SLASH;
1423 }
1424 break;
1425 case STATE_DOT:
1426 state = STATE_ADDR;
1427 break;
1428 case STATE_SLASH:
1429 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001430 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001431
1432 state = STATE_MASK;
1433 break;
1434 default:
1435 break;
1436 }
1437
1438 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001439 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001440
1441 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001442 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001443
1444 str++;
1445 }
1446
1447 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001448 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001449
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02001450 if (osmo_str_to_int(&mask, str, 10, 0, 128))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001451 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001452
1453/* I don't know why mask < 13 makes command match partly.
1454 Forgive me to make this comments. I Want to set static default route
1455 because of lack of function to originate default in ospf6d; sorry
1456 yasu
1457 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001458 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459*/
1460
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001461 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001462}
1463
1464#endif /* HAVE_IPV6 */
1465
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001466
1467#if ULONG_MAX == 18446744073709551615UL
1468#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1469#elif ULONG_MAX == 4294967295UL
1470#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1471#else
1472#error "ULONG_MAX not defined!"
1473#endif
1474
1475#if LONG_MAX == 9223372036854775807L
1476#define DECIMAL_STRLEN_MAX_SIGNED 19
1477#elif LONG_MAX == 2147483647L
1478#define DECIMAL_STRLEN_MAX_SIGNED 10
1479#else
1480#error "LONG_MAX not defined!"
1481#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001482
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001483/* This function is aimed at quickly guessing & filtering the numeric base a
1484 * string can contain, by no means validates the entire value.
1485 * Returns 16 if string follows pattern "({+,-}0x[digits])"
1486 * Returns 10 if string follows pattern "({+,-}[digits])"
1487 * Returns a negative value if something other is detected (error)
1488*/
1489static int check_base(const char *str)
1490{
1491 const char *ptr = str;
1492 /* Skip any space */
1493 while (isspace(*ptr)) ptr++;
1494
1495 /* Skip optional sign: */
1496 if (*ptr == '+' || *ptr == '-')
1497 ptr++;
1498 if (*ptr == '0') {
1499 ptr++;
1500 if (*ptr == '\0' || isdigit(*ptr))
1501 return 10;
1502 if (!(*ptr == 'x' || *ptr == 'X'))
1503 return -1;
1504 ptr++;
1505 if (isxdigit(*ptr))
1506 return 16;
1507 return -1;
1508 }
1509 return 10;
1510}
1511
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001512int vty_cmd_range_match(const char *range, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001513{
1514 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001515 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001516 char *endptr = NULL;
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001517 int min_base, max_base, val_base;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001518
1519 if (str == NULL)
1520 return 1;
1521
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001522 if ((val_base = check_base(str)) < 0)
1523 return 0;
1524
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001525 if (range[1] == '-') {
1526 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001527
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001528 val = strtol(str, &endptr, val_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001529 if (*endptr != '\0')
1530 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001531
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001532 range += 2;
1533 p = strchr(range, '-');
1534 if (p == NULL)
1535 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001536 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001537 return 0;
1538 strncpy(buf, range, p - range);
1539 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001540 if ((min_base = check_base(buf)) < 0)
1541 return 0;
1542 min = -strtol(buf, &endptr, min_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001543 if (*endptr != '\0')
1544 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001545
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001546 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_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001551 return 0;
1552 strncpy(buf, range, p - range);
1553 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001554 if ((max_base = check_base(buf)) < 0)
1555 return 0;
1556 max = strtol(buf, &endptr, max_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001557 if (*endptr != '\0')
1558 return 0;
1559
1560 if (val < min || val > max)
1561 return 0;
1562 } else {
1563 unsigned long min, max, val;
1564
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001565 if (str[0] == '-')
1566 return 0;
1567
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001568 val = strtoul(str, &endptr, val_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001569 if (*endptr != '\0')
1570 return 0;
1571
1572 range++;
1573 p = strchr(range, '-');
1574 if (p == NULL)
1575 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001576 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001577 return 0;
1578 strncpy(buf, range, p - range);
1579 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001580 if ((min_base = check_base(buf)) < 0)
1581 return 0;
1582 min = strtoul(buf, &endptr, min_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001583 if (*endptr != '\0')
1584 return 0;
1585
1586 range = p + 1;
1587 p = strchr(range, '>');
1588 if (p == NULL)
1589 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001590 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001591 return 0;
1592 strncpy(buf, range, p - range);
1593 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001594 if ((max_base = check_base(buf)) < 0)
1595 return 0;
1596 max = strtoul(buf, &endptr, max_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001597 if (*endptr != '\0')
1598 return 0;
1599
1600 if (val < min || val > max)
1601 return 0;
1602 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001603
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001604 /* Don't allow ranges specified by min and max with different bases */
1605 if (min_base != max_base)
1606 return 0;
1607 /* arg value passed must match the base of the range */
1608 if (min_base != val_base)
1609 return 0;
1610
1611 /* Everything's fine */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001612 return 1;
1613}
1614
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001615/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001616static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001617{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001618 /* we've got "[blah]". We want to strip off the []s and redo the
1619 * match check for "blah"
1620 */
1621 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001622
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001623 if (len < 3)
1624 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001625
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001626 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001627}
1628
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001629static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001630cmd_match(const char *str, const char *command,
1631 enum match_type min, bool recur)
1632{
1633
1634 if (recur && CMD_OPTION(str))
1635 {
1636 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001637 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001638
1639 /* this would be a bug in a command, however handle it gracefully
1640 * as it we only discover it if a user tries to run it
1641 */
1642 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001643 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001644
1645 ret = cmd_match(tmp, command, min, false);
1646
1647 talloc_free(tmp);
1648
1649 return ret;
1650 }
1651 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001652 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001653 else if (CMD_RANGE(str))
1654 {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001655 if (vty_cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001656 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001657 }
1658#ifdef HAVE_IPV6
1659 else if (CMD_IPV6(str))
1660 {
1661 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001662 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001663 }
1664 else if (CMD_IPV6_PREFIX(str))
1665 {
1666 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001667 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001668 }
1669#endif /* HAVE_IPV6 */
1670 else if (CMD_IPV4(str))
1671 {
1672 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001673 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001674 }
1675 else if (CMD_IPV4_PREFIX(str))
1676 {
1677 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001678 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001679 }
1680 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001681 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001682 else if (strncmp(command, str, strlen(command)) == 0)
1683 {
1684 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001685 return EXACT_MATCH;
1686 else if (PARTLY_MATCH >= min)
1687 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001688 }
1689
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001690 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001691}
1692
1693/* Filter vector at the specified index and by the given command string, to
1694 * the desired matching level (thus allowing part matches), and return match
1695 * type flag.
1696 */
1697static enum match_type
1698cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001699{
1700 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001701 struct cmd_element *cmd_element;
1702 enum match_type match_type;
1703 vector descvec;
1704 struct desc *desc;
1705
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001706 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001707
1708 /* If command and cmd_element string does not match set NULL to vector */
1709 for (i = 0; i < vector_active(v); i++)
1710 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001711 if (index >= vector_active(cmd_element->strvec))
1712 vector_slot(v, i) = NULL;
1713 else {
1714 unsigned int j;
1715 int matched = 0;
1716
1717 descvec =
1718 vector_slot(cmd_element->strvec, index);
1719
1720 for (j = 0; j < vector_active(descvec); j++)
1721 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001722 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001723
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001724 ret = cmd_match (desc->cmd, command, level, true);
1725
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001726 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001727 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001728
1729 if (match_type < ret)
1730 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001731 }
1732 if (!matched)
1733 vector_slot(v, i) = NULL;
1734 }
1735 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001736
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001737 if (match_type == NO_MATCH)
1738 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001739
1740 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1741 * go again and filter out commands whose argument (at this index) is
1742 * 'weaker'. E.g., if we have 2 commands:
1743 *
1744 * foo bar <1-255>
1745 * foo bar BLAH
1746 *
1747 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001748 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001749 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1750 *
1751 * If we don't do a 2nd pass and filter it out, the higher-layers will
1752 * consider this to be ambiguous.
1753 */
1754 for (i = 0; i < vector_active(v); i++)
1755 if ((cmd_element = vector_slot(v, i)) != NULL) {
1756 if (index >= vector_active(cmd_element->strvec))
1757 vector_slot(v, i) = NULL;
1758 else {
1759 unsigned int j;
1760 int matched = 0;
1761
1762 descvec =
1763 vector_slot(cmd_element->strvec, index);
1764
1765 for (j = 0; j < vector_active(descvec); j++)
1766 if ((desc = vector_slot(descvec, j))) {
1767 enum match_type ret;
1768
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001769 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001770
1771 if (ret >= match_type)
1772 matched++;
1773 }
1774 if (!matched)
1775 vector_slot(v, i) = NULL;
1776 }
1777 }
1778
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001779 return match_type;
1780}
1781
1782/* Check ambiguous match */
1783static int
1784is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1785{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001786 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001787 unsigned int i;
1788 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001789 struct cmd_element *cmd_element;
1790 const char *matched = NULL;
1791 vector descvec;
1792 struct desc *desc;
1793
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001794 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1795 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1796 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1797 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1798 * that case, the string must remain allocated until this function exits or another match comes
1799 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1800 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1801 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1802 void *cmd_deopt_ctx = NULL;
1803
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001804 for (i = 0; i < vector_active(v); i++) {
1805 cmd_element = vector_slot(v, i);
1806 if (!cmd_element)
1807 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001808
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001809 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001810
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001811 descvec = vector_slot(cmd_element->strvec, index);
1812
1813 for (j = 0; j < vector_active(descvec); j++) {
1814 desc = vector_slot(descvec, j);
1815 if (!desc)
1816 continue;
1817
1818 enum match_type mtype;
1819 const char *str = desc->cmd;
1820
1821 if (CMD_OPTION(str)) {
1822 if (!cmd_deopt_ctx)
1823 cmd_deopt_ctx =
1824 talloc_named_const(tall_vty_cmd_ctx, 0,
1825 __func__);
1826 str = cmd_deopt(cmd_deopt_ctx, str);
1827 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001828 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001829 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001830
1831 switch (type) {
1832 case EXACT_MATCH:
1833 if (!(CMD_VARIABLE (str))
1834 && strcmp(command, str) == 0)
1835 match++;
1836 break;
1837 case PARTLY_MATCH:
1838 if (!(CMD_VARIABLE (str))
1839 && strncmp(command, str, strlen (command)) == 0)
1840 {
1841 if (matched
1842 && strcmp(matched,
1843 str) != 0) {
1844 ret = 1; /* There is ambiguous match. */
1845 goto free_and_return;
1846 } else
1847 matched = str;
1848 match++;
1849 }
1850 break;
1851 case RANGE_MATCH:
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001852 if (vty_cmd_range_match
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001853 (str, command)) {
1854 if (matched
1855 && strcmp(matched,
1856 str) != 0) {
1857 ret = 1;
1858 goto free_and_return;
1859 } else
1860 matched = str;
1861 match++;
1862 }
1863 break;
1864#ifdef HAVE_IPV6
1865 case IPV6_MATCH:
1866 if (CMD_IPV6(str))
1867 match++;
1868 break;
1869 case IPV6_PREFIX_MATCH:
1870 if ((mtype =
1871 cmd_ipv6_prefix_match
1872 (command)) != NO_MATCH) {
1873 if (mtype == PARTLY_MATCH) {
1874 ret = 2; /* There is incomplete match. */
1875 goto free_and_return;
1876 }
1877
1878 match++;
1879 }
1880 break;
1881#endif /* HAVE_IPV6 */
1882 case IPV4_MATCH:
1883 if (CMD_IPV4(str))
1884 match++;
1885 break;
1886 case IPV4_PREFIX_MATCH:
1887 if ((mtype =
1888 cmd_ipv4_prefix_match
1889 (command)) != NO_MATCH) {
1890 if (mtype == PARTLY_MATCH) {
1891 ret = 2; /* There is incomplete match. */
1892 goto free_and_return;
1893 }
1894
1895 match++;
1896 }
1897 break;
1898 case EXTEND_MATCH:
1899 if (CMD_VARIABLE (str))
1900 match++;
1901 break;
1902 case NO_MATCH:
1903 default:
1904 break;
1905 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001906 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001907 if (!match)
1908 vector_slot(v, i) = NULL;
1909 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001910
1911free_and_return:
1912 if (cmd_deopt_ctx)
1913 talloc_free(cmd_deopt_ctx);
1914 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001915}
1916
1917/* If src matches dst return dst string, otherwise return NULL */
1918static const char *cmd_entry_function(const char *src, const char *dst)
1919{
1920 /* Skip variable arguments. */
1921 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1922 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1923 return NULL;
1924
1925 /* In case of 'command \t', given src is NULL string. */
1926 if (src == NULL)
1927 return dst;
1928
1929 /* Matched with input string. */
1930 if (strncmp(src, dst, strlen(src)) == 0)
1931 return dst;
1932
1933 return NULL;
1934}
1935
1936/* If src matches dst return dst string, otherwise return NULL */
1937/* This version will return the dst string always if it is
1938 CMD_VARIABLE for '?' key processing */
1939static const char *cmd_entry_function_desc(const char *src, const char *dst)
1940{
1941 if (CMD_VARARG(dst))
1942 return dst;
1943
1944 if (CMD_RANGE(dst)) {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001945 if (vty_cmd_range_match(dst, src))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001946 return dst;
1947 else
1948 return NULL;
1949 }
1950#ifdef HAVE_IPV6
1951 if (CMD_IPV6(dst)) {
1952 if (cmd_ipv6_match(src))
1953 return dst;
1954 else
1955 return NULL;
1956 }
1957
1958 if (CMD_IPV6_PREFIX(dst)) {
1959 if (cmd_ipv6_prefix_match(src))
1960 return dst;
1961 else
1962 return NULL;
1963 }
1964#endif /* HAVE_IPV6 */
1965
1966 if (CMD_IPV4(dst)) {
1967 if (cmd_ipv4_match(src))
1968 return dst;
1969 else
1970 return NULL;
1971 }
1972
1973 if (CMD_IPV4_PREFIX(dst)) {
1974 if (cmd_ipv4_prefix_match(src))
1975 return dst;
1976 else
1977 return NULL;
1978 }
1979
1980 /* Optional or variable commands always match on '?' */
1981 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1982 return dst;
1983
1984 /* In case of 'command \t', given src is NULL string. */
1985 if (src == NULL)
1986 return dst;
1987
1988 if (strncmp(src, dst, strlen(src)) == 0)
1989 return dst;
1990 else
1991 return NULL;
1992}
1993
1994/* Check same string element existence. If it isn't there return
1995 1. */
1996static int cmd_unique_string(vector v, const char *str)
1997{
1998 unsigned int i;
1999 char *match;
2000
2001 for (i = 0; i < vector_active(v); i++)
2002 if ((match = vector_slot(v, i)) != NULL)
2003 if (strcmp(match, str) == 0)
2004 return 0;
2005 return 1;
2006}
2007
2008/* Compare string to description vector. If there is same string
2009 return 1 else return 0. */
2010static int desc_unique_string(vector v, const char *str)
2011{
2012 unsigned int i;
2013 struct desc *desc;
2014
2015 for (i = 0; i < vector_active(v); i++)
2016 if ((desc = vector_slot(v, i)) != NULL)
2017 if (strcmp(desc->cmd, str) == 0)
2018 return 1;
2019 return 0;
2020}
2021
2022static int cmd_try_do_shortcut(enum node_type node, char *first_word)
2023{
2024 if (first_word != NULL &&
2025 node != AUTH_NODE &&
2026 node != VIEW_NODE &&
2027 node != AUTH_ENABLE_NODE &&
2028 node != ENABLE_NODE && 0 == strcmp("do", first_word))
2029 return 1;
2030 return 0;
2031}
2032
2033/* '?' describe command support. */
2034static vector
2035cmd_describe_command_real(vector vline, struct vty *vty, int *status)
2036{
2037 unsigned int i;
2038 vector cmd_vector;
2039#define INIT_MATCHVEC_SIZE 10
2040 vector matchvec;
2041 struct cmd_element *cmd_element;
2042 unsigned int index;
2043 int ret;
2044 enum match_type match;
2045 char *command;
2046 static struct desc desc_cr = { "<cr>", "" };
2047
2048 /* Set index. */
2049 if (vector_active(vline) == 0) {
2050 *status = CMD_ERR_NO_MATCH;
2051 return NULL;
2052 } else
2053 index = vector_active(vline) - 1;
2054
2055 /* Make copy vector of current node's command vector. */
2056 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2057
2058 /* Prepare match vector */
2059 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2060
2061 /* Filter commands. */
2062 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002063 for (i = 0; i < index; i++) {
2064 command = vector_slot(vline, i);
2065 if (!command)
2066 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002067
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002068 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002069
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002070 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002071 struct cmd_element *cmd_element;
2072 vector descvec;
2073 unsigned int j, k;
2074
2075 for (j = 0; j < vector_active(cmd_vector); j++)
2076 if ((cmd_element =
2077 vector_slot(cmd_vector, j)) != NULL
2078 &&
2079 (vector_active(cmd_element->strvec))) {
2080 descvec =
2081 vector_slot(cmd_element->
2082 strvec,
2083 vector_active
2084 (cmd_element->
2085 strvec) - 1);
2086 for (k = 0;
2087 k < vector_active(descvec);
2088 k++) {
2089 struct desc *desc =
2090 vector_slot(descvec,
2091 k);
2092 vector_set(matchvec,
2093 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002094 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002095 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002096
Harald Welte80d30fe2013-02-12 11:08:57 +01002097 vector_set(matchvec, &desc_cr);
2098 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002099
Harald Welte80d30fe2013-02-12 11:08:57 +01002100 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002101 }
2102
Harald Welte80d30fe2013-02-12 11:08:57 +01002103 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2104 match)) == 1) {
2105 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002106 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002107 *status = CMD_ERR_AMBIGUOUS;
2108 return NULL;
2109 } else if (ret == 2) {
2110 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002111 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002112 *status = CMD_ERR_NO_MATCH;
2113 return NULL;
2114 }
2115 }
2116
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002117 /* Prepare match vector */
2118 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2119
2120 /* Make sure that cmd_vector is filtered based on current word */
2121 command = vector_slot(vline, index);
2122 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002123 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002124
2125 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002126 for (i = 0; i < vector_active(cmd_vector); i++) {
2127 const char *string = NULL;
2128 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002129
Harald Welte80d30fe2013-02-12 11:08:57 +01002130 cmd_element = vector_slot(cmd_vector, i);
2131 if (!cmd_element)
2132 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002133
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002134 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2135 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002136 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002137 continue;
2138
Harald Welte80d30fe2013-02-12 11:08:57 +01002139 strvec = cmd_element->strvec;
2140
2141 /* if command is NULL, index may be equal to vector_active */
2142 if (command && index >= vector_active(strvec))
2143 vector_slot(cmd_vector, i) = NULL;
2144 else {
2145 /* Check if command is completed. */
2146 if (command == NULL
2147 && index == vector_active(strvec)) {
2148 string = "<cr>";
2149 if (!desc_unique_string(matchvec, string))
2150 vector_set(matchvec, &desc_cr);
2151 } else {
2152 unsigned int j;
2153 vector descvec = vector_slot(strvec, index);
2154 struct desc *desc;
2155
2156 for (j = 0; j < vector_active(descvec); j++) {
2157 desc = vector_slot(descvec, j);
2158 if (!desc)
2159 continue;
2160 string = cmd_entry_function_desc
2161 (command, desc->cmd);
2162 if (!string)
2163 continue;
2164 /* Uniqueness check */
2165 if (!desc_unique_string(matchvec, string))
2166 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002167 }
2168 }
2169 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002170 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002171 vector_free(cmd_vector);
2172
2173 if (vector_slot(matchvec, 0) == NULL) {
2174 vector_free(matchvec);
2175 *status = CMD_ERR_NO_MATCH;
2176 } else
2177 *status = CMD_SUCCESS;
2178
2179 return matchvec;
2180}
2181
2182vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2183{
2184 vector ret;
2185
2186 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2187 enum node_type onode;
2188 vector shifted_vline;
2189 unsigned int index;
2190
2191 onode = vty->node;
2192 vty->node = ENABLE_NODE;
2193 /* We can try it on enable node, cos' the vty is authenticated */
2194
2195 shifted_vline = vector_init(vector_count(vline));
2196 /* use memcpy? */
2197 for (index = 1; index < vector_active(vline); index++) {
2198 vector_set_index(shifted_vline, index - 1,
2199 vector_lookup(vline, index));
2200 }
2201
2202 ret = cmd_describe_command_real(shifted_vline, vty, status);
2203
2204 vector_free(shifted_vline);
2205 vty->node = onode;
2206 return ret;
2207 }
2208
2209 return cmd_describe_command_real(vline, vty, status);
2210}
2211
2212/* Check LCD of matched command. */
2213static int cmd_lcd(char **matched)
2214{
2215 int i;
2216 int j;
2217 int lcd = -1;
2218 char *s1, *s2;
2219 char c1, c2;
2220
2221 if (matched[0] == NULL || matched[1] == NULL)
2222 return 0;
2223
2224 for (i = 1; matched[i] != NULL; i++) {
2225 s1 = matched[i - 1];
2226 s2 = matched[i];
2227
2228 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2229 if (c1 != c2)
2230 break;
2231
2232 if (lcd < 0)
2233 lcd = j;
2234 else {
2235 if (lcd > j)
2236 lcd = j;
2237 }
2238 }
2239 return lcd;
2240}
2241
2242/* Command line completion support. */
2243static char **cmd_complete_command_real(vector vline, struct vty *vty,
2244 int *status)
2245{
2246 unsigned int i;
2247 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2248#define INIT_MATCHVEC_SIZE 10
2249 vector matchvec;
2250 struct cmd_element *cmd_element;
2251 unsigned int index;
2252 char **match_str;
2253 struct desc *desc;
2254 vector descvec;
2255 char *command;
2256 int lcd;
2257
2258 if (vector_active(vline) == 0) {
2259 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002260 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002261 return NULL;
2262 } else
2263 index = vector_active(vline) - 1;
2264
2265 /* First, filter by preceeding command string */
2266 for (i = 0; i < index; i++)
2267 if ((command = vector_slot(vline, i))) {
2268 enum match_type match;
2269 int ret;
2270
2271 /* First try completion match, if there is exactly match return 1 */
2272 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002273 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002274
2275 /* If there is exact match then filter ambiguous match else check
2276 ambiguousness. */
2277 if ((ret =
2278 is_cmd_ambiguous(command, cmd_vector, i,
2279 match)) == 1) {
2280 vector_free(cmd_vector);
2281 *status = CMD_ERR_AMBIGUOUS;
2282 return NULL;
2283 }
2284 /*
2285 else if (ret == 2)
2286 {
2287 vector_free (cmd_vector);
2288 *status = CMD_ERR_NO_MATCH;
2289 return NULL;
2290 }
2291 */
2292 }
2293
2294 /* Prepare match vector. */
2295 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2296
2297 /* Now we got into completion */
2298 for (i = 0; i < vector_active(cmd_vector); i++)
2299 if ((cmd_element = vector_slot(cmd_vector, i))) {
2300 const char *string;
2301 vector strvec = cmd_element->strvec;
2302
2303 /* Check field length */
2304 if (index >= vector_active(strvec))
2305 vector_slot(cmd_vector, i) = NULL;
2306 else {
2307 unsigned int j;
2308
2309 descvec = vector_slot(strvec, index);
2310 for (j = 0; j < vector_active(descvec); j++)
2311 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002312 const char *cmd = desc->cmd;
2313 char *tmp = NULL;
2314
2315 if (CMD_OPTION(desc->cmd)) {
2316 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2317 cmd = tmp;
2318 }
2319 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002320 if (cmd_unique_string (matchvec, string))
2321 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002322 if (tmp)
2323 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002324 }
2325 }
2326 }
2327
2328 /* We don't need cmd_vector any more. */
2329 vector_free(cmd_vector);
2330
2331 /* No matched command */
2332 if (vector_slot(matchvec, 0) == NULL) {
2333 vector_free(matchvec);
2334
2335 /* In case of 'command \t' pattern. Do you need '?' command at
2336 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002337 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002338 *status = CMD_ERR_NOTHING_TODO;
2339 else
2340 *status = CMD_ERR_NO_MATCH;
2341 return NULL;
2342 }
2343
2344 /* Only one matched */
2345 if (vector_slot(matchvec, 1) == NULL) {
2346 match_str = (char **)matchvec->index;
2347 vector_only_wrapper_free(matchvec);
2348 *status = CMD_COMPLETE_FULL_MATCH;
2349 return match_str;
2350 }
2351 /* Make it sure last element is NULL. */
2352 vector_set(matchvec, NULL);
2353
2354 /* Check LCD of matched strings. */
2355 if (vector_slot(vline, index) != NULL) {
2356 lcd = cmd_lcd((char **)matchvec->index);
2357
2358 if (lcd) {
2359 int len = strlen(vector_slot(vline, index));
2360
2361 if (len < lcd) {
2362 char *lcdstr;
2363
2364 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2365 "complete-lcdstr");
2366 memcpy(lcdstr, matchvec->index[0], lcd);
2367 lcdstr[lcd] = '\0';
2368
2369 /* match_str = (char **) &lcdstr; */
2370
2371 /* Free matchvec. */
2372 for (i = 0; i < vector_active(matchvec); i++) {
2373 if (vector_slot(matchvec, i))
2374 talloc_free(vector_slot(matchvec, i));
2375 }
2376 vector_free(matchvec);
2377
2378 /* Make new matchvec. */
2379 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2380 vector_set(matchvec, lcdstr);
2381 match_str = (char **)matchvec->index;
2382 vector_only_wrapper_free(matchvec);
2383
2384 *status = CMD_COMPLETE_MATCH;
2385 return match_str;
2386 }
2387 }
2388 }
2389
2390 match_str = (char **)matchvec->index;
2391 vector_only_wrapper_free(matchvec);
2392 *status = CMD_COMPLETE_LIST_MATCH;
2393 return match_str;
2394}
2395
2396char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2397{
2398 char **ret;
2399
2400 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2401 enum node_type onode;
2402 vector shifted_vline;
2403 unsigned int index;
2404
2405 onode = vty->node;
2406 vty->node = ENABLE_NODE;
2407 /* We can try it on enable node, cos' the vty is authenticated */
2408
2409 shifted_vline = vector_init(vector_count(vline));
2410 /* use memcpy? */
2411 for (index = 1; index < vector_active(vline); index++) {
2412 vector_set_index(shifted_vline, index - 1,
2413 vector_lookup(vline, index));
2414 }
2415
2416 ret = cmd_complete_command_real(shifted_vline, vty, status);
2417
2418 vector_free(shifted_vline);
2419 vty->node = onode;
2420 return ret;
2421 }
2422
2423 return cmd_complete_command_real(vline, vty, status);
2424}
2425
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002426static struct vty_parent_node *vty_parent(struct vty *vty)
2427{
2428 return llist_first_entry_or_null(&vty->parent_nodes,
2429 struct vty_parent_node,
2430 entry);
2431}
2432
2433static bool vty_pop_parent(struct vty *vty)
2434{
2435 struct vty_parent_node *parent = vty_parent(vty);
2436 if (!parent)
2437 return false;
2438 llist_del(&parent->entry);
2439 vty->node = parent->node;
2440 vty->priv = parent->priv;
Neels Hofmeyr67d84d22023-05-01 02:59:01 +02002441 vty->index = parent->index;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002442 if (vty->indent)
2443 talloc_free(vty->indent);
2444 vty->indent = parent->indent;
2445 talloc_free(parent);
2446 return true;
2447}
2448
2449static void vty_clear_parents(struct vty *vty)
2450{
2451 while (vty_pop_parent(vty));
2452}
2453
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002454/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002455/*
2456 * This function MUST eventually converge on a node when called repeatedly,
2457 * there must not be any cycles.
2458 * All 'config' nodes shall converge on CONFIG_NODE.
2459 * All other 'enable' nodes shall converge on ENABLE_NODE.
2460 * All 'view' only nodes shall converge on VIEW_NODE.
2461 * All other nodes shall converge on themselves or it must be ensured,
2462 * that the user's rights are not extended anyhow by calling this function.
2463 *
2464 * Note that these requirements also apply to all functions that are used
2465 * as go_parent_cb.
2466 * Note also that this function relies on the is_config_child callback to
2467 * recognize non-config nodes if go_parent_cb is not set.
2468 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002469int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002470{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002471 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002472 case AUTH_NODE:
2473 case VIEW_NODE:
2474 case ENABLE_NODE:
2475 case CONFIG_NODE:
2476 vty_clear_parents(vty);
2477 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002478
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002479 case AUTH_ENABLE_NODE:
2480 vty->node = VIEW_NODE;
2481 vty_clear_parents(vty);
2482 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002483
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002484 default:
2485 if (host.app_info->go_parent_cb)
2486 host.app_info->go_parent_cb(vty);
2487 vty_pop_parent(vty);
2488 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002489 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002490
2491 return vty->node;
2492}
2493
2494/* Execute command by argument vline vector. */
2495static int
2496cmd_execute_command_real(vector vline, struct vty *vty,
2497 struct cmd_element **cmd)
2498{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002499 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002500 unsigned int index;
2501 vector cmd_vector;
2502 struct cmd_element *cmd_element;
2503 struct cmd_element *matched_element;
2504 unsigned int matched_count, incomplete_count;
2505 int argc;
2506 const char *argv[CMD_ARGC_MAX];
2507 enum match_type match = 0;
2508 int varflag;
2509 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002510 int rc;
2511 /* Used for temporary storage of cmd_deopt() allocated arguments during
2512 argv[] generation */
2513 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002514
2515 /* Make copy of command elements. */
2516 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2517
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002518 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002519 if ((command = vector_slot(vline, index))) {
2520 int ret;
2521
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002522 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002523 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002524
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002525 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002526 break;
2527
2528 ret =
2529 is_cmd_ambiguous(command, cmd_vector, index, match);
2530
2531 if (ret == 1) {
2532 vector_free(cmd_vector);
2533 return CMD_ERR_AMBIGUOUS;
2534 } else if (ret == 2) {
2535 vector_free(cmd_vector);
2536 return CMD_ERR_NO_MATCH;
2537 }
2538 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002539 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002540
2541 /* Check matched count. */
2542 matched_element = NULL;
2543 matched_count = 0;
2544 incomplete_count = 0;
2545
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002546 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002547 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002548 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002549 || index >= cmd_element->cmdsize) {
2550 matched_element = cmd_element;
2551#if 0
2552 printf("DEBUG: %s\n", cmd_element->string);
2553#endif
2554 matched_count++;
2555 } else {
2556 incomplete_count++;
2557 }
2558 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002559 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002560
2561 /* Finish of using cmd_vector. */
2562 vector_free(cmd_vector);
2563
2564 /* To execute command, matched_count must be 1. */
2565 if (matched_count == 0) {
2566 if (incomplete_count)
2567 return CMD_ERR_INCOMPLETE;
2568 else
2569 return CMD_ERR_NO_MATCH;
2570 }
2571
2572 if (matched_count > 1)
2573 return CMD_ERR_AMBIGUOUS;
2574
2575 /* Argument treatment */
2576 varflag = 0;
2577 argc = 0;
2578
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002579 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2580
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002581 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002582 if (argc == CMD_ARGC_MAX) {
2583 rc = CMD_ERR_EXEED_ARGC_MAX;
2584 goto rc_free_deopt_ctx;
2585 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002586 if (varflag) {
2587 argv[argc++] = vector_slot(vline, i);
2588 continue;
2589 }
2590
2591 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002592 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002593
2594 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002595 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002596 struct desc *desc = vector_slot(descvec, 0);
2597
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002598 if (CMD_OPTION(desc->cmd)) {
2599 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2600 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2601 } else {
2602 tmp_cmd = desc->cmd;
2603 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002604
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002605 if (CMD_VARARG(tmp_cmd))
2606 varflag = 1;
2607 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002608 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002609 else if (CMD_OPTION(desc->cmd))
2610 argv[argc++] = tmp_cmd;
2611 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002612 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002613 /* multi choice argument. look up which choice
2614 the user meant (can only be one after
2615 filtering and checking for ambigous). For instance,
2616 if user typed "th" for "(two|three)" arg, we
2617 want to pass "three" in argv[]. */
2618 for (j = 0; j < vector_active(descvec); j++) {
2619 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002620 if (!desc)
2621 continue;
2622 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2623 continue;
2624 if (CMD_OPTION(desc->cmd)) {
2625 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2626 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2627 } else {
2628 tmp_cmd = desc->cmd;
2629 }
2630
2631 if(CMD_VARIABLE(tmp_cmd)) {
2632 argv[argc++] = vector_slot(vline, i);
2633 } else {
2634 argv[argc++] = tmp_cmd;
2635 }
2636 break;
2637 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002638 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002639 }
2640
2641 /* For vtysh execution. */
2642 if (cmd)
2643 *cmd = matched_element;
2644
2645 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002646 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002647 else {
2648 /* Execute matched command. */
2649 struct vty_parent_node this_node = {
2650 .node = vty->node,
2651 .priv = vty->priv,
Neels Hofmeyr67d84d22023-05-01 02:59:01 +02002652 .index = vty->index,
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002653 .indent = vty->indent,
2654 };
2655 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002656 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002657
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002658 /* If we have stepped down into a child node, push a parent frame.
2659 * The causality is such: we don't expect every single node entry implementation to push
2660 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2661 * a parent node. Hence if the node changed without the parent node changing, we must
2662 * have stepped into a child node. */
2663 if (vty->node != this_node.node && parent == vty_parent(vty)
2664 && vty->node > CONFIG_NODE) {
2665 /* Push the parent node. */
2666 parent = talloc_zero(vty, struct vty_parent_node);
2667 *parent = this_node;
2668 llist_add(&parent->entry, &vty->parent_nodes);
2669 }
2670 }
2671
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002672rc_free_deopt_ctx:
2673 /* Now after we called the command func, we can free temporary strings */
2674 talloc_free(cmd_deopt_ctx);
2675 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002676}
2677
2678int
2679cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2680 int vtysh)
2681{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002682 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002683 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002684
2685 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002686
2687 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2688 vector shifted_vline;
2689 unsigned int index;
2690
2691 vty->node = ENABLE_NODE;
2692 /* We can try it on enable node, cos' the vty is authenticated */
2693
2694 shifted_vline = vector_init(vector_count(vline));
2695 /* use memcpy? */
2696 for (index = 1; index < vector_active(vline); index++) {
2697 vector_set_index(shifted_vline, index - 1,
2698 vector_lookup(vline, index));
2699 }
2700
2701 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2702
2703 vector_free(shifted_vline);
2704 vty->node = onode;
2705 return ret;
2706 }
2707
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002708 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002709}
2710
2711/* Execute command by argument readline. */
2712int
2713cmd_execute_command_strict(vector vline, struct vty *vty,
2714 struct cmd_element **cmd)
2715{
2716 unsigned int i;
2717 unsigned int index;
2718 vector cmd_vector;
2719 struct cmd_element *cmd_element;
2720 struct cmd_element *matched_element;
2721 unsigned int matched_count, incomplete_count;
2722 int argc;
2723 const char *argv[CMD_ARGC_MAX];
2724 int varflag;
2725 enum match_type match = 0;
2726 char *command;
2727
2728 /* Make copy of command element */
2729 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2730
2731 for (index = 0; index < vector_active(vline); index++)
2732 if ((command = vector_slot(vline, index))) {
2733 int ret;
2734
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002735 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002736 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002737
2738 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002739 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002740 break;
2741
2742 ret =
2743 is_cmd_ambiguous(command, cmd_vector, index, match);
2744 if (ret == 1) {
2745 vector_free(cmd_vector);
2746 return CMD_ERR_AMBIGUOUS;
2747 }
2748 if (ret == 2) {
2749 vector_free(cmd_vector);
2750 return CMD_ERR_NO_MATCH;
2751 }
2752 }
2753
2754 /* Check matched count. */
2755 matched_element = NULL;
2756 matched_count = 0;
2757 incomplete_count = 0;
2758 for (i = 0; i < vector_active(cmd_vector); i++)
2759 if (vector_slot(cmd_vector, i) != NULL) {
2760 cmd_element = vector_slot(cmd_vector, i);
2761
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002762 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002763 || index >= cmd_element->cmdsize) {
2764 matched_element = cmd_element;
2765 matched_count++;
2766 } else
2767 incomplete_count++;
2768 }
2769
2770 /* Finish of using cmd_vector. */
2771 vector_free(cmd_vector);
2772
2773 /* To execute command, matched_count must be 1. */
2774 if (matched_count == 0) {
2775 if (incomplete_count)
2776 return CMD_ERR_INCOMPLETE;
2777 else
2778 return CMD_ERR_NO_MATCH;
2779 }
2780
2781 if (matched_count > 1)
2782 return CMD_ERR_AMBIGUOUS;
2783
2784 /* Argument treatment */
2785 varflag = 0;
2786 argc = 0;
2787
2788 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002789 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002790 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002791 if (varflag) {
2792 argv[argc++] = vector_slot(vline, i);
2793 continue;
2794 }
2795
2796 vector descvec = vector_slot(matched_element->strvec, i);
2797
2798 if (vector_active(descvec) == 1) {
2799 struct desc *desc = vector_slot(descvec, 0);
2800
2801 if (CMD_VARARG(desc->cmd))
2802 varflag = 1;
2803
2804 if (varflag || CMD_VARIABLE(desc->cmd)
2805 || CMD_OPTION(desc->cmd))
2806 argv[argc++] = vector_slot(vline, i);
2807 } else {
2808 argv[argc++] = vector_slot(vline, i);
2809 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002810 }
2811
2812 /* For vtysh execution. */
2813 if (cmd)
2814 *cmd = matched_element;
2815
2816 if (matched_element->daemon)
2817 return CMD_SUCCESS_DAEMON;
2818
2819 /* Now execute matched command */
2820 return (*matched_element->func) (matched_element, vty, argc, argv);
2821}
2822
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002823static inline size_t len(const char *str)
2824{
2825 return str? strlen(str) : 0;
2826}
2827
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002828/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2829 * is longer than b, a must start with exactly b, and vice versa.
2830 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2831 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002832static int indent_cmp(const char *a, const char *b)
2833{
2834 size_t al, bl;
2835 al = len(a);
2836 bl = len(b);
2837 if (al > bl) {
2838 if (bl && strncmp(a, b, bl) != 0)
2839 return EINVAL;
2840 return 1;
2841 }
2842 /* al <= bl */
2843 if (al && strncmp(a, b, al) != 0)
2844 return EINVAL;
2845 return (al < bl)? -1 : 0;
2846}
2847
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002848/* Configration make from file. */
2849int config_from_file(struct vty *vty, FILE * fp)
2850{
2851 int ret;
2852 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002853 char *indent;
2854 int cmp;
2855 struct vty_parent_node this_node;
2856 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002857
2858 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002859 indent = NULL;
2860 vline = NULL;
2861 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002862
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002863 if (ret != CMD_SUCCESS)
2864 goto return_invalid_indent;
2865
2866 /* In case of comment or empty line */
2867 if (vline == NULL) {
2868 if (indent) {
2869 talloc_free(indent);
2870 indent = NULL;
2871 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002872 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002873 }
2874
Neels Hofmeyr43063632017-09-19 23:54:01 +02002875 /* We have a nonempty line. */
2876 if (!vty->indent) {
2877 /* We have just entered a node and expecting the first child to come up; but we
2878 * may also skip right back to a parent or ancestor level. */
2879 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002880
Neels Hofmeyr43063632017-09-19 23:54:01 +02002881 /* If there is no parent, record any indentation we encounter. */
2882 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2883
2884 if (cmp == EINVAL)
2885 goto return_invalid_indent;
2886
2887 if (cmp <= 0) {
2888 /* We have gone right back to the parent level or higher, we are skipping
2889 * this child node level entirely. Pop the parent to go back to a node
2890 * that was actually there (to reinstate vty->indent) and re-use below
2891 * go-parent while-loop to find an accurate match of indent in the node
2892 * ancestry. */
2893 vty_go_parent(vty);
2894 } else {
2895 /* The indent is deeper than the just entered parent, record the new
2896 * indentation characters. */
2897 vty->indent = talloc_strdup(vty, indent);
2898 /* This *is* the new indentation. */
2899 cmp = 0;
2900 }
2901 } else {
2902 /* There is a known indentation for this node level, validate and detect node
2903 * exits. */
2904 cmp = indent_cmp(indent, vty->indent);
2905 if (cmp == EINVAL)
2906 goto return_invalid_indent;
2907 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002908
2909 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2910 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2911 while (cmp < 0) {
2912 vty_go_parent(vty);
2913 cmp = indent_cmp(indent, vty->indent);
2914 if (cmp == EINVAL)
2915 goto return_invalid_indent;
2916 }
2917
2918 /* More indent without having entered a child node level? Either the parent node's indent
2919 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2920 * or the indentation increased even though the vty command didn't enter a child. */
2921 if (cmp > 0)
2922 goto return_invalid_indent;
2923
2924 /* Remember the current node before the command possibly changes it. */
2925 this_node = (struct vty_parent_node){
2926 .node = vty->node,
2927 .priv = vty->priv,
Neels Hofmeyr67d84d22023-05-01 02:59:01 +02002928 .index = vty->index,
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002929 .indent = vty->indent,
2930 };
2931
2932 parent = vty_parent(vty);
2933 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002934 cmd_free_strvec(vline);
2935
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002936 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002937 if (indent) {
2938 talloc_free(indent);
2939 indent = NULL;
2940 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002941 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002942 }
2943
2944 /* If we have stepped down into a child node, push a parent frame.
2945 * The causality is such: we don't expect every single node entry implementation to push
2946 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2947 * a parent node. Hence if the node changed without the parent node changing, we must
2948 * have stepped into a child node (and now expect a deeper indent). */
2949 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2950 /* Push the parent node. */
2951 parent = talloc_zero(vty, struct vty_parent_node);
2952 *parent = this_node;
2953 llist_add(&parent->entry, &vty->parent_nodes);
2954
2955 /* The current talloc'ed vty->indent string will now be owned by this parent
2956 * struct. Indicate that we don't know what deeper indent characters the user
2957 * will choose. */
2958 vty->indent = NULL;
2959 }
2960
2961 if (indent) {
2962 talloc_free(indent);
2963 indent = NULL;
2964 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002965 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002966 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2967 while (vty_parent(vty))
2968 vty_go_parent(vty);
2969
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002970 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002971
2972return_invalid_indent:
2973 if (vline)
2974 cmd_free_strvec(vline);
2975 if (indent) {
2976 talloc_free(indent);
2977 indent = NULL;
2978 }
2979 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002980}
2981
2982/* Configration from terminal */
2983DEFUN(config_terminal,
2984 config_terminal_cmd,
2985 "configure terminal",
2986 "Configuration from vty interface\n" "Configuration terminal\n")
2987{
2988 if (vty_config_lock(vty))
2989 vty->node = CONFIG_NODE;
2990 else {
2991 vty_out(vty, "VTY configuration is locked by other VTY%s",
2992 VTY_NEWLINE);
2993 return CMD_WARNING;
2994 }
2995 return CMD_SUCCESS;
2996}
2997
2998/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002999DEFUN(enable, config_enable_cmd,
3000 "enable [expert-mode]",
3001 "Turn on privileged mode command\n"
3002 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003003{
3004 /* If enable password is NULL, change to ENABLE_NODE */
3005 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
3006 vty->type == VTY_SHELL_SERV)
3007 vty->node = ENABLE_NODE;
3008 else
3009 vty->node = AUTH_ENABLE_NODE;
3010
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003011 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003012
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003013 return CMD_SUCCESS;
3014}
3015
3016/* Disable command */
3017DEFUN(disable,
3018 config_disable_cmd, "disable", "Turn off privileged mode command\n")
3019{
3020 if (vty->node == ENABLE_NODE)
3021 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003022
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003023 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003024
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003025 return CMD_SUCCESS;
3026}
3027
3028/* Down vty node level. */
3029gDEFUN(config_exit,
3030 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
3031{
3032 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02003033 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003034 case VIEW_NODE:
3035 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01003036 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003037 break;
3038 case CONFIG_NODE:
3039 vty->node = ENABLE_NODE;
3040 vty_config_unlock(vty);
3041 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003042 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003043 if (vty->node > CONFIG_NODE)
3044 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003045 break;
3046 }
3047 return CMD_SUCCESS;
3048}
3049
3050/* End of configuration. */
3051 gDEFUN(config_end,
3052 config_end_cmd, "end", "End current mode and change to enable mode.")
3053{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003054 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02003055 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003056
3057 /* Repeatedly call go_parent until a top node is reached. */
3058 while (vty->node > CONFIG_NODE) {
3059 if (vty->node == last_node) {
3060 /* Ensure termination, this shouldn't happen. */
3061 break;
3062 }
3063 last_node = vty->node;
3064 vty_go_parent(vty);
3065 }
3066
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003067 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003068 if (vty->node > ENABLE_NODE)
3069 vty->node = ENABLE_NODE;
3070 vty->index = NULL;
3071 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003072 }
3073 return CMD_SUCCESS;
3074}
3075
Oliver Smith8a482fd2021-07-12 18:18:51 +02003076DEFUN(shutdown,
3077 shutdown_cmd, "shutdown", "Request a shutdown of the program\n")
3078{
3079 LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n");
3080 vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE);
3081
3082 /* Same exit path as if it was killed by the service manager */
3083 kill(getpid(), SIGTERM);
3084
3085 return CMD_SUCCESS;
3086}
3087
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003088/* Show version. */
3089DEFUN(show_version,
3090 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3091{
Harald Welte237f6242010-05-25 23:00:45 +02003092 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3093 host.app_info->version,
3094 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3095 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003096
3097 return CMD_SUCCESS;
3098}
3099
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003100DEFUN(show_online_help,
3101 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3102{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003103 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003104 return CMD_SUCCESS;
3105}
3106
Oliver Smith0c78bc62021-07-12 17:28:36 +02003107DEFUN(show_pid,
3108 show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
3109{
3110 vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
3111 return CMD_SUCCESS;
3112}
3113
Oliver Smithd243c2a2021-07-09 17:19:32 +02003114DEFUN(show_uptime,
3115 show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
3116{
Alexander Couzens06929162021-09-05 23:08:59 +02003117 vty_out(vty, "%s has been running for ", host.app_info->name);
3118 vty_out_uptime(vty, &starttime);
3119 vty_out_newline(vty);
Oliver Smithd243c2a2021-07-09 17:19:32 +02003120
3121 return CMD_SUCCESS;
3122}
3123
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003124/* Help display function for all node. */
3125gDEFUN(config_help,
3126 config_help_cmd, "help", "Description of the interactive help system\n")
3127{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003128 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3129 "anytime at the command line please press '?'.%s%s"
3130 "If nothing matches, the help list will be empty and you must backup%s"
3131 " until entering a '?' shows the available options.%s"
3132 "Two styles of help are provided:%s"
3133 "1. Full help is available when you are ready to enter a%s"
3134 "command argument (e.g. 'show ?') and describes each possible%s"
3135 "argument.%s"
3136 "2. Partial help is provided when an abbreviated argument is entered%s"
3137 " and you want to know what arguments match the input%s"
3138 " (e.g. 'show me?'.)%s%s",
3139 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3140 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3141 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3142 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003143 return CMD_SUCCESS;
3144}
3145
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003146enum {
3147 ATTR_TYPE_GLOBAL = (1 << 0),
3148 ATTR_TYPE_LIB = (1 << 1),
3149 ATTR_TYPE_APP = (1 << 2),
3150};
3151
3152static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3153{
3154 const char *desc;
3155 unsigned int i;
3156 bool found;
3157 char flag;
3158
3159 if (attr_mask & ATTR_TYPE_GLOBAL) {
3160 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3161
3162 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003163 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003164 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003165
3166 /* Skip attributes without flags */
3167 if (flag != '.')
3168 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003169 }
3170 }
3171
3172 if (attr_mask & ATTR_TYPE_LIB) {
3173 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3174
3175 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3176 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3177 continue;
3178 found = true;
3179
3180 flag = cmd_lib_attr_letters[i];
3181 if (flag == '\0')
3182 flag = '.';
3183
3184 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3185 }
3186
3187 if (!found)
3188 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3189 }
3190
3191 if (attr_mask & ATTR_TYPE_APP) {
3192 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3193
3194 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3195 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3196 continue;
3197 found = true;
3198
3199 flag = host.app_info->usr_attr_letters[i];
3200 if (flag == '\0')
3201 flag = '.';
3202
3203 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3204 }
3205
3206 if (!found)
3207 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3208 }
3209}
3210
3211gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3212 "show vty-attributes",
3213 SHOW_STR "List of VTY attributes\n")
3214{
3215 print_attr_list(vty, 0xff);
3216 return CMD_SUCCESS;
3217}
3218
3219gDEFUN(show_vty_attr, show_vty_attr_cmd,
3220 "show vty-attributes (application|library|global)",
3221 SHOW_STR "List of VTY attributes\n"
3222 "Application specific attributes only\n"
3223 "Library specific attributes only\n"
3224 "Global attributes only\n")
3225{
3226 unsigned int attr_mask = 0;
3227
3228 if (argv[0][0] == 'g') /* global */
3229 attr_mask |= ATTR_TYPE_GLOBAL;
3230 else if (argv[0][0] == 'l') /* library */
3231 attr_mask |= ATTR_TYPE_LIB;
3232 else if (argv[0][0] == 'a') /* application */
3233 attr_mask |= ATTR_TYPE_APP;
3234
3235 print_attr_list(vty, attr_mask);
3236 return CMD_SUCCESS;
3237}
3238
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003239/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003240static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003241{
3242 unsigned int flag_mask = 0x00;
3243 unsigned int f, i;
3244
3245 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3246 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3247 const struct cmd_element *cmd;
3248 char flag_letter;
3249
3250 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3251 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003252 if (cmd->attr & CMD_ATTR_DEPRECATED)
3253 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003254 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003255 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003256 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003257 continue;
3258
3259 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3260 flag_letter = cmd_lib_attr_letters[f];
3261 else
3262 flag_letter = host.app_info->usr_attr_letters[f];
3263
3264 if (flag_letter == '\0')
3265 continue;
3266
3267 flag_mask |= (1 << f);
3268 break;
3269 }
3270 }
3271
3272 return flag_mask;
3273}
3274
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003275/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3276static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3277{
3278 static char char_mask[8 + 1];
3279 char *ptr = &char_mask[0];
3280
3281 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003282 if (cmd->attr & CMD_ATTR_HIDDEN)
3283 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3284 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003285 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3286 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3287 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3288 else
3289 *(ptr++) = '.';
3290
3291 *ptr = '\0';
3292
3293 return char_mask;
3294}
3295
3296/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003297static const char *cmd_flag_mask(const struct cmd_element *cmd,
3298 unsigned int flag_mask)
3299{
3300 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3301 char *ptr = &char_mask[0];
3302 char flag_letter;
3303 unsigned int f;
3304
3305 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003306 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003307 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003308 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003309 *(ptr++) = '.';
3310 continue;
3311 }
3312
3313 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3314 flag_letter = cmd_lib_attr_letters[f];
3315 else
3316 flag_letter = host.app_info->usr_attr_letters[f];
3317
3318 *(ptr++) = flag_letter ? flag_letter : '.';
3319 }
3320
3321 *ptr = '\0';
3322
3323 return char_mask;
3324}
3325
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003326/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003327gDEFUN(config_list, config_list_cmd,
3328 "list [with-flags]",
3329 "Print command list\n"
3330 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003331{
3332 unsigned int i;
3333 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003334 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003335 struct cmd_element *cmd;
3336
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003337 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003338 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003339
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003340 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3341 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3342 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003343 if (cmd->attr & CMD_ATTR_DEPRECATED)
3344 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003345 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003346 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003347 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003348 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3349 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003350 vty_out(vty, " %s %s %s%s",
3351 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003352 cmd_flag_mask(cmd, flag_mask),
3353 cmd->string, VTY_NEWLINE);
3354 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003355 }
3356
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003357 return CMD_SUCCESS;
3358}
3359
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003360static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003361{
3362 unsigned int i;
3363 int fd;
3364 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003365 char *config_file_tmp = NULL;
3366 char *config_file_sav = NULL;
3367 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003368 struct stat st;
3369
3370 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003371
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003372 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3373 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3374 * manually instead. */
3375
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003376 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003377 config_file_sav =
3378 _talloc_zero(tall_vty_cmd_ctx,
3379 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3380 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003381 if (!config_file_sav)
3382 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003383 strcpy(config_file_sav, config_file);
3384 strcat(config_file_sav, CONF_BACKUP_EXT);
3385
3386 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003387 "config_file_tmp");
3388 if (!config_file_tmp) {
3389 talloc_free(config_file_sav);
3390 return -1;
3391 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003392 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3393
3394 /* Open file to configuration write. */
3395 fd = mkstemp(config_file_tmp);
3396 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003397 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003398 talloc_free(config_file_tmp);
3399 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003400 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003401 }
3402
3403 /* Make vty for configuration file. */
3404 file_vty = vty_new();
3405 file_vty->fd = fd;
3406 file_vty->type = VTY_FILE;
3407
3408 /* Config file header print. */
3409 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003410 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003411 //vty_time_print (file_vty, 1);
3412 vty_out(file_vty, "!\n");
3413
3414 for (i = 0; i < vector_active(cmdvec); i++)
3415 if ((node = vector_slot(cmdvec, i)) && node->func) {
3416 if ((*node->func) (file_vty))
3417 vty_out(file_vty, "!\n");
3418 }
3419 vty_close(file_vty);
3420
3421 if (unlink(config_file_sav) != 0)
3422 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003423 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003424 talloc_free(config_file_sav);
3425 talloc_free(config_file_tmp);
3426 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003427 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003428 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003429
3430 /* Only link the .sav file if the original file exists */
3431 if (stat(config_file, &st) == 0) {
3432 if (link(config_file, config_file_sav) != 0) {
3433 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3434 talloc_free(config_file_sav);
3435 talloc_free(config_file_tmp);
3436 unlink(config_file_tmp);
3437 return -3;
3438 }
3439 sync();
3440 if (unlink(config_file) != 0) {
3441 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3442 talloc_free(config_file_sav);
3443 talloc_free(config_file_tmp);
3444 unlink(config_file_tmp);
3445 return -4;
3446 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003447 }
3448 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003449 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003450 talloc_free(config_file_sav);
3451 talloc_free(config_file_tmp);
3452 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003453 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003454 }
3455 unlink(config_file_tmp);
3456 sync();
3457
3458 talloc_free(config_file_sav);
3459 talloc_free(config_file_tmp);
3460
3461 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003462 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3463 return -6;
3464 }
3465
3466 return 0;
3467}
3468
3469
3470/* Write current configuration into file. */
3471DEFUN(config_write_file,
3472 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003473 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003474 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003475 "Write to configuration file\n"
3476 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003477{
3478 char *failed_file;
3479 int rc;
3480
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003481 if (host.app_info->config_is_consistent) {
3482 rc = host.app_info->config_is_consistent(vty);
3483 if (!rc) {
3484 vty_out(vty, "Configuration is not consistent%s",
3485 VTY_NEWLINE);
3486 return CMD_WARNING;
3487 }
3488 }
3489
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003490 if (argc == 1)
3491 host_config_set(argv[0]);
3492
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003493 if (host.config == NULL) {
3494 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3495 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003496 return CMD_WARNING;
3497 }
3498
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003499 rc = write_config_file(host.config, &failed_file);
3500 switch (rc) {
3501 case -1:
3502 vty_out(vty, "Can't open configuration file %s.%s",
3503 failed_file, VTY_NEWLINE);
3504 rc = CMD_WARNING;
3505 break;
3506 case -2:
3507 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3508 failed_file, VTY_NEWLINE);
3509 rc = CMD_WARNING;
3510 break;
3511 case -3:
3512 vty_out(vty, "Can't backup old configuration file %s.%s",
3513 failed_file, VTY_NEWLINE);
3514 rc = CMD_WARNING;
3515 break;
3516 case -4:
3517 vty_out(vty, "Can't unlink configuration file %s.%s",
3518 failed_file, VTY_NEWLINE);
3519 rc = CMD_WARNING;
3520 break;
3521 case -5:
3522 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3523 VTY_NEWLINE);
3524 rc = CMD_WARNING;
3525 break;
3526 case -6:
3527 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3528 failed_file, strerror(errno), errno, VTY_NEWLINE);
3529 rc = CMD_WARNING;
3530 break;
3531 default:
3532 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3533 rc = CMD_SUCCESS;
3534 break;
3535 }
3536
3537 talloc_free(failed_file);
3538 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003539}
3540
3541ALIAS(config_write_file,
3542 config_write_cmd,
3543 "write", "Write running configuration to memory, network, or terminal\n")
3544
3545 ALIAS(config_write_file,
3546 config_write_memory_cmd,
3547 "write memory",
3548 "Write running configuration to memory, network, or terminal\n"
3549 "Write configuration to the file (same as write file)\n")
3550
3551 ALIAS(config_write_file,
3552 copy_runningconfig_startupconfig_cmd,
3553 "copy running-config startup-config",
3554 "Copy configuration\n"
3555 "Copy running config to... \n"
3556 "Copy running config to startup config (same as write file)\n")
3557
3558/* Write current configuration into the terminal. */
3559 DEFUN(config_write_terminal,
3560 config_write_terminal_cmd,
3561 "write terminal",
3562 "Write running configuration to memory, network, or terminal\n"
3563 "Write to terminal\n")
3564{
3565 unsigned int i;
3566 struct cmd_node *node;
3567
3568 if (vty->type == VTY_SHELL_SERV) {
3569 for (i = 0; i < vector_active(cmdvec); i++)
3570 if ((node = vector_slot(cmdvec, i)) && node->func
3571 && node->vtysh) {
3572 if ((*node->func) (vty))
3573 vty_out(vty, "!%s", VTY_NEWLINE);
3574 }
3575 } else {
3576 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3577 VTY_NEWLINE);
3578 vty_out(vty, "!%s", VTY_NEWLINE);
3579
3580 for (i = 0; i < vector_active(cmdvec); i++)
3581 if ((node = vector_slot(cmdvec, i)) && node->func) {
3582 if ((*node->func) (vty))
3583 vty_out(vty, "!%s", VTY_NEWLINE);
3584 }
3585 vty_out(vty, "end%s", VTY_NEWLINE);
3586 }
3587 return CMD_SUCCESS;
3588}
3589
3590/* Write current configuration into the terminal. */
3591ALIAS(config_write_terminal,
3592 show_running_config_cmd,
3593 "show running-config", SHOW_STR "running configuration\n")
3594
3595/* Write startup configuration into the terminal. */
3596 DEFUN(show_startup_config,
3597 show_startup_config_cmd,
3598 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3599{
3600 char buf[BUFSIZ];
3601 FILE *confp;
3602
3603 confp = fopen(host.config, "r");
3604 if (confp == NULL) {
3605 vty_out(vty, "Can't open configuration file [%s]%s",
3606 host.config, VTY_NEWLINE);
3607 return CMD_WARNING;
3608 }
3609
3610 while (fgets(buf, BUFSIZ, confp)) {
3611 char *cp = buf;
3612
3613 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3614 cp++;
3615 *cp = '\0';
3616
3617 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3618 }
3619
3620 fclose(confp);
3621
3622 return CMD_SUCCESS;
3623}
3624
3625/* Hostname configuration */
3626DEFUN(config_hostname,
3627 hostname_cmd,
3628 "hostname WORD",
3629 "Set system's network name\n" "This system's network name\n")
3630{
3631 if (!isalpha((int)*argv[0])) {
3632 vty_out(vty, "Please specify string starting with alphabet%s",
3633 VTY_NEWLINE);
3634 return CMD_WARNING;
3635 }
3636
3637 if (host.name)
3638 talloc_free(host.name);
3639
3640 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3641 return CMD_SUCCESS;
3642}
3643
3644DEFUN(config_no_hostname,
3645 no_hostname_cmd,
3646 "no hostname [HOSTNAME]",
3647 NO_STR "Reset system's network name\n" "Host name of this router\n")
3648{
3649 if (host.name)
3650 talloc_free(host.name);
3651 host.name = NULL;
3652 return CMD_SUCCESS;
3653}
3654
3655/* VTY interface password set. */
3656DEFUN(config_password, password_cmd,
3657 "password (8|) WORD",
3658 "Assign the terminal connection password\n"
3659 "Specifies a HIDDEN password will follow\n"
3660 "dummy string \n" "The HIDDEN line password string\n")
3661{
3662 /* Argument check. */
3663 if (argc == 0) {
3664 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3665 return CMD_WARNING;
3666 }
3667
3668 if (argc == 2) {
3669 if (*argv[0] == '8') {
3670 if (host.password)
3671 talloc_free(host.password);
3672 host.password = NULL;
3673 if (host.password_encrypt)
3674 talloc_free(host.password_encrypt);
3675 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3676 return CMD_SUCCESS;
3677 } else {
3678 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3679 return CMD_WARNING;
3680 }
3681 }
3682
3683 if (!isalnum((int)*argv[0])) {
3684 vty_out(vty,
3685 "Please specify string starting with alphanumeric%s",
3686 VTY_NEWLINE);
3687 return CMD_WARNING;
3688 }
3689
3690 if (host.password)
3691 talloc_free(host.password);
3692 host.password = NULL;
3693
3694#ifdef VTY_CRYPT_PW
3695 if (host.encrypt) {
3696 if (host.password_encrypt)
3697 talloc_free(host.password_encrypt);
3698 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3699 } else
3700#endif
3701 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3702
3703 return CMD_SUCCESS;
3704}
3705
3706ALIAS(config_password, password_text_cmd,
3707 "password LINE",
3708 "Assign the terminal connection password\n"
3709 "The UNENCRYPTED (cleartext) line password\n")
3710
3711/* VTY enable password set. */
3712 DEFUN(config_enable_password, enable_password_cmd,
3713 "enable password (8|) WORD",
3714 "Modify enable password parameters\n"
3715 "Assign the privileged level password\n"
3716 "Specifies a HIDDEN password will follow\n"
3717 "dummy string \n" "The HIDDEN 'enable' password string\n")
3718{
3719 /* Argument check. */
3720 if (argc == 0) {
3721 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3722 return CMD_WARNING;
3723 }
3724
3725 /* Crypt type is specified. */
3726 if (argc == 2) {
3727 if (*argv[0] == '8') {
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 = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3735
3736 return CMD_SUCCESS;
3737 } else {
3738 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3739 return CMD_WARNING;
3740 }
3741 }
3742
3743 if (!isalnum((int)*argv[0])) {
3744 vty_out(vty,
3745 "Please specify string starting with alphanumeric%s",
3746 VTY_NEWLINE);
3747 return CMD_WARNING;
3748 }
3749
3750 if (host.enable)
3751 talloc_free(host.enable);
3752 host.enable = NULL;
3753
3754 /* Plain password input. */
3755#ifdef VTY_CRYPT_PW
3756 if (host.encrypt) {
3757 if (host.enable_encrypt)
3758 talloc_free(host.enable_encrypt);
3759 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3760 } else
3761#endif
3762 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3763
3764 return CMD_SUCCESS;
3765}
3766
3767ALIAS(config_enable_password,
3768 enable_password_text_cmd,
3769 "enable password LINE",
3770 "Modify enable password parameters\n"
3771 "Assign the privileged level password\n"
3772 "The UNENCRYPTED (cleartext) 'enable' password\n")
3773
3774/* VTY enable password delete. */
3775 DEFUN(no_config_enable_password, no_enable_password_cmd,
3776 "no enable password",
3777 NO_STR
3778 "Modify enable password parameters\n"
3779 "Assign the privileged level password\n")
3780{
3781 if (host.enable)
3782 talloc_free(host.enable);
3783 host.enable = NULL;
3784
3785 if (host.enable_encrypt)
3786 talloc_free(host.enable_encrypt);
3787 host.enable_encrypt = NULL;
3788
3789 return CMD_SUCCESS;
3790}
3791
3792#ifdef VTY_CRYPT_PW
3793DEFUN(service_password_encrypt,
3794 service_password_encrypt_cmd,
3795 "service password-encryption",
3796 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3797{
3798 if (host.encrypt)
3799 return CMD_SUCCESS;
3800
3801 host.encrypt = 1;
3802
3803 if (host.password) {
3804 if (host.password_encrypt)
3805 talloc_free(host.password_encrypt);
3806 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3807 }
3808 if (host.enable) {
3809 if (host.enable_encrypt)
3810 talloc_free(host.enable_encrypt);
3811 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3812 }
3813
3814 return CMD_SUCCESS;
3815}
3816
3817DEFUN(no_service_password_encrypt,
3818 no_service_password_encrypt_cmd,
3819 "no service password-encryption",
3820 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3821{
3822 if (!host.encrypt)
3823 return CMD_SUCCESS;
3824
3825 host.encrypt = 0;
3826
3827 if (host.password_encrypt)
3828 talloc_free(host.password_encrypt);
3829 host.password_encrypt = NULL;
3830
3831 if (host.enable_encrypt)
3832 talloc_free(host.enable_encrypt);
3833 host.enable_encrypt = NULL;
3834
3835 return CMD_SUCCESS;
3836}
3837#endif
3838
3839DEFUN(config_terminal_length, config_terminal_length_cmd,
3840 "terminal length <0-512>",
3841 "Set terminal line parameters\n"
3842 "Set number of lines on a screen\n"
3843 "Number of lines on screen (0 for no pausing)\n")
3844{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003845 vty->lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003846 return CMD_SUCCESS;
3847}
3848
3849DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3850 "terminal no length",
3851 "Set terminal line parameters\n"
3852 NO_STR "Set number of lines on a screen\n")
3853{
3854 vty->lines = -1;
3855 return CMD_SUCCESS;
3856}
3857
3858DEFUN(service_terminal_length, service_terminal_length_cmd,
3859 "service terminal-length <0-512>",
3860 "Set up miscellaneous service\n"
3861 "System wide terminal length configuration\n"
3862 "Number of lines of VTY (0 means no line control)\n")
3863{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003864 host.lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003865 return CMD_SUCCESS;
3866}
3867
3868DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3869 "no service terminal-length [<0-512>]",
3870 NO_STR
3871 "Set up miscellaneous service\n"
3872 "System wide terminal length configuration\n"
3873 "Number of lines of VTY (0 means no line control)\n")
3874{
3875 host.lines = -1;
3876 return CMD_SUCCESS;
3877}
3878
3879DEFUN_HIDDEN(do_echo,
3880 echo_cmd,
3881 "echo .MESSAGE",
3882 "Echo a message back to the vty\n" "The message to echo\n")
3883{
3884 char *message;
3885
3886 vty_out(vty, "%s%s",
3887 ((message =
3888 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3889 if (message)
3890 talloc_free(message);
3891 return CMD_SUCCESS;
3892}
3893
3894#if 0
3895DEFUN(config_logmsg,
3896 config_logmsg_cmd,
3897 "logmsg " LOG_LEVELS " .MESSAGE",
3898 "Send a message to enabled logging destinations\n"
3899 LOG_LEVEL_DESC "The message to send\n")
3900{
3901 int level;
3902 char *message;
3903
3904 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3905 return CMD_ERR_NO_MATCH;
3906
3907 zlog(NULL, level,
3908 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3909 if (message)
3910 talloc_free(message);
3911 return CMD_SUCCESS;
3912}
3913
3914DEFUN(show_logging,
3915 show_logging_cmd,
3916 "show logging", SHOW_STR "Show current logging configuration\n")
3917{
3918 struct zlog *zl = zlog_default;
3919
3920 vty_out(vty, "Syslog logging: ");
3921 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3922 vty_out(vty, "disabled");
3923 else
3924 vty_out(vty, "level %s, facility %s, ident %s",
3925 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3926 facility_name(zl->facility), zl->ident);
3927 vty_out(vty, "%s", VTY_NEWLINE);
3928
3929 vty_out(vty, "Stdout logging: ");
3930 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3931 vty_out(vty, "disabled");
3932 else
3933 vty_out(vty, "level %s",
3934 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3935 vty_out(vty, "%s", VTY_NEWLINE);
3936
3937 vty_out(vty, "Monitor logging: ");
3938 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3939 vty_out(vty, "disabled");
3940 else
3941 vty_out(vty, "level %s",
3942 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3943 vty_out(vty, "%s", VTY_NEWLINE);
3944
3945 vty_out(vty, "File logging: ");
3946 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3947 vty_out(vty, "disabled");
3948 else
3949 vty_out(vty, "level %s, filename %s",
3950 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3951 zl->filename);
3952 vty_out(vty, "%s", VTY_NEWLINE);
3953
3954 vty_out(vty, "Protocol name: %s%s",
3955 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3956 vty_out(vty, "Record priority: %s%s",
3957 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3958
3959 return CMD_SUCCESS;
3960}
3961
3962DEFUN(config_log_stdout,
3963 config_log_stdout_cmd,
3964 "log stdout", "Logging control\n" "Set stdout logging level\n")
3965{
3966 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3967 return CMD_SUCCESS;
3968}
3969
3970DEFUN(config_log_stdout_level,
3971 config_log_stdout_level_cmd,
3972 "log stdout " LOG_LEVELS,
3973 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3974{
3975 int level;
3976
3977 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3978 return CMD_ERR_NO_MATCH;
3979 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3980 return CMD_SUCCESS;
3981}
3982
3983DEFUN(no_config_log_stdout,
3984 no_config_log_stdout_cmd,
3985 "no log stdout [LEVEL]",
3986 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3987{
3988 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3989 return CMD_SUCCESS;
3990}
3991
3992DEFUN(config_log_monitor,
3993 config_log_monitor_cmd,
3994 "log monitor",
3995 "Logging control\n" "Set terminal line (monitor) logging level\n")
3996{
3997 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3998 return CMD_SUCCESS;
3999}
4000
4001DEFUN(config_log_monitor_level,
4002 config_log_monitor_level_cmd,
4003 "log monitor " LOG_LEVELS,
4004 "Logging control\n"
4005 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
4006{
4007 int level;
4008
4009 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4010 return CMD_ERR_NO_MATCH;
4011 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
4012 return CMD_SUCCESS;
4013}
4014
4015DEFUN(no_config_log_monitor,
4016 no_config_log_monitor_cmd,
4017 "no log monitor [LEVEL]",
4018 NO_STR
4019 "Logging control\n"
4020 "Disable terminal line (monitor) logging\n" "Logging level\n")
4021{
4022 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
4023 return CMD_SUCCESS;
4024}
4025
4026static int set_log_file(struct vty *vty, const char *fname, int loglevel)
4027{
4028 int ret;
4029 char *p = NULL;
4030 const char *fullpath;
4031
4032 /* Path detection. */
4033 if (!IS_DIRECTORY_SEP(*fname)) {
4034 char cwd[MAXPATHLEN + 1];
4035 cwd[MAXPATHLEN] = '\0';
4036
4037 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4038 zlog_err("config_log_file: Unable to alloc mem!");
4039 return CMD_WARNING;
4040 }
4041
4042 if ((p = _talloc_zero(tall_vcmd_ctx,
4043 strlen(cwd) + strlen(fname) + 2),
4044 "set_log_file")
4045 == NULL) {
4046 zlog_err("config_log_file: Unable to alloc mem!");
4047 return CMD_WARNING;
4048 }
4049 sprintf(p, "%s/%s", cwd, fname);
4050 fullpath = p;
4051 } else
4052 fullpath = fname;
4053
4054 ret = zlog_set_file(NULL, fullpath, loglevel);
4055
4056 if (p)
4057 talloc_free(p);
4058
4059 if (!ret) {
4060 vty_out(vty, "can't open logfile %s\n", fname);
4061 return CMD_WARNING;
4062 }
4063
4064 if (host.logfile)
4065 talloc_free(host.logfile);
4066
4067 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
4068
4069 return CMD_SUCCESS;
4070}
4071
4072DEFUN(config_log_file,
4073 config_log_file_cmd,
4074 "log file FILENAME",
4075 "Logging control\n" "Logging to file\n" "Logging filename\n")
4076{
4077 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4078}
4079
4080DEFUN(config_log_file_level,
4081 config_log_file_level_cmd,
4082 "log file FILENAME " LOG_LEVELS,
4083 "Logging control\n"
4084 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4085{
4086 int level;
4087
4088 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4089 return CMD_ERR_NO_MATCH;
4090 return set_log_file(vty, argv[0], level);
4091}
4092
4093DEFUN(no_config_log_file,
4094 no_config_log_file_cmd,
4095 "no log file [FILENAME]",
4096 NO_STR
4097 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4098{
4099 zlog_reset_file(NULL);
4100
4101 if (host.logfile)
4102 talloc_free(host.logfile);
4103
4104 host.logfile = NULL;
4105
4106 return CMD_SUCCESS;
4107}
4108
4109ALIAS(no_config_log_file,
4110 no_config_log_file_level_cmd,
4111 "no log file FILENAME LEVEL",
4112 NO_STR
4113 "Logging control\n"
4114 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4115
4116 DEFUN(config_log_syslog,
4117 config_log_syslog_cmd,
4118 "log syslog", "Logging control\n" "Set syslog logging level\n")
4119{
4120 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4121 return CMD_SUCCESS;
4122}
4123
4124DEFUN(config_log_syslog_level,
4125 config_log_syslog_level_cmd,
4126 "log syslog " LOG_LEVELS,
4127 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4128{
4129 int level;
4130
4131 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4132 return CMD_ERR_NO_MATCH;
4133 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4134 return CMD_SUCCESS;
4135}
4136
4137DEFUN_DEPRECATED(config_log_syslog_facility,
4138 config_log_syslog_facility_cmd,
4139 "log syslog facility " LOG_FACILITIES,
4140 "Logging control\n"
4141 "Logging goes to syslog\n"
4142 "(Deprecated) Facility parameter for syslog messages\n"
4143 LOG_FACILITY_DESC)
4144{
4145 int facility;
4146
4147 if ((facility = facility_match(argv[0])) < 0)
4148 return CMD_ERR_NO_MATCH;
4149
4150 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4151 zlog_default->facility = facility;
4152 return CMD_SUCCESS;
4153}
4154
4155DEFUN(no_config_log_syslog,
4156 no_config_log_syslog_cmd,
4157 "no log syslog [LEVEL]",
4158 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4159{
4160 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4161 return CMD_SUCCESS;
4162}
4163
4164ALIAS(no_config_log_syslog,
4165 no_config_log_syslog_facility_cmd,
4166 "no log syslog facility " LOG_FACILITIES,
4167 NO_STR
4168 "Logging control\n"
4169 "Logging goes to syslog\n"
4170 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4171
4172 DEFUN(config_log_facility,
4173 config_log_facility_cmd,
4174 "log facility " LOG_FACILITIES,
4175 "Logging control\n"
4176 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4177{
4178 int facility;
4179
4180 if ((facility = facility_match(argv[0])) < 0)
4181 return CMD_ERR_NO_MATCH;
4182 zlog_default->facility = facility;
4183 return CMD_SUCCESS;
4184}
4185
4186DEFUN(no_config_log_facility,
4187 no_config_log_facility_cmd,
4188 "no log facility [FACILITY]",
4189 NO_STR
4190 "Logging control\n"
4191 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4192{
4193 zlog_default->facility = LOG_DAEMON;
4194 return CMD_SUCCESS;
4195}
4196
4197DEFUN_DEPRECATED(config_log_trap,
4198 config_log_trap_cmd,
4199 "log trap " LOG_LEVELS,
4200 "Logging control\n"
4201 "(Deprecated) Set logging level and default for all destinations\n"
4202 LOG_LEVEL_DESC)
4203{
4204 int new_level;
4205 int i;
4206
4207 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4208 return CMD_ERR_NO_MATCH;
4209
4210 zlog_default->default_lvl = new_level;
4211 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4212 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4213 zlog_default->maxlvl[i] = new_level;
4214 return CMD_SUCCESS;
4215}
4216
4217DEFUN_DEPRECATED(no_config_log_trap,
4218 no_config_log_trap_cmd,
4219 "no log trap [LEVEL]",
4220 NO_STR
4221 "Logging control\n"
4222 "Permit all logging information\n" "Logging level\n")
4223{
4224 zlog_default->default_lvl = LOG_DEBUG;
4225 return CMD_SUCCESS;
4226}
4227
4228DEFUN(config_log_record_priority,
4229 config_log_record_priority_cmd,
4230 "log record-priority",
4231 "Logging control\n"
4232 "Log the priority of the message within the message\n")
4233{
4234 zlog_default->record_priority = 1;
4235 return CMD_SUCCESS;
4236}
4237
4238DEFUN(no_config_log_record_priority,
4239 no_config_log_record_priority_cmd,
4240 "no log record-priority",
4241 NO_STR
4242 "Logging control\n"
4243 "Do not log the priority of the message within the message\n")
4244{
4245 zlog_default->record_priority = 0;
4246 return CMD_SUCCESS;
4247}
4248#endif
4249
4250DEFUN(banner_motd_file,
4251 banner_motd_file_cmd,
4252 "banner motd file [FILE]",
4253 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4254{
4255 if (host.motdfile)
4256 talloc_free(host.motdfile);
4257 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4258
4259 return CMD_SUCCESS;
4260}
4261
4262DEFUN(banner_motd_default,
4263 banner_motd_default_cmd,
4264 "banner motd default",
4265 "Set banner string\n" "Strings for motd\n" "Default string\n")
4266{
4267 host.motd = default_motd;
4268 return CMD_SUCCESS;
4269}
4270
4271DEFUN(no_banner_motd,
4272 no_banner_motd_cmd,
4273 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4274{
4275 host.motd = NULL;
4276 if (host.motdfile)
4277 talloc_free(host.motdfile);
4278 host.motdfile = NULL;
4279 return CMD_SUCCESS;
4280}
4281
4282/* Set config filename. Called from vty.c */
4283void host_config_set(const char *filename)
4284{
4285 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4286}
4287
Pau Espin Pedrolebb6c1f2021-05-17 18:54:21 +02004288const char *host_config_file(void)
4289{
4290 return host.config;
4291}
4292
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004293/*! Deprecated, now happens implicitly when calling install_node().
4294 * Users of the API may still attempt to call this function, hence
4295 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004296void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004297{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004298}
4299
4300/*! Deprecated, now happens implicitly when calling install_node().
4301 * Users of the API may still attempt to call this function, hence
4302 * leave it here as a no-op. */
4303void vty_install_default(int node)
4304{
4305}
4306
4307/*! Install common commands like 'exit' and 'list'. */
4308static void install_basic_node_commands(int node)
4309{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004310 install_lib_element(node, &config_help_cmd);
4311 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004312
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004313 install_lib_element(node, &show_vty_attr_all_cmd);
4314 install_lib_element(node, &show_vty_attr_cmd);
4315
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004316 install_lib_element(node, &config_write_terminal_cmd);
4317 install_lib_element(node, &config_write_file_cmd);
4318 install_lib_element(node, &config_write_memory_cmd);
4319 install_lib_element(node, &config_write_cmd);
4320 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004321
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004322 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004323
4324 if (node >= CONFIG_NODE) {
4325 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004326 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004327 }
4328}
4329
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004330/*! Return true if a node is installed by install_basic_node_commands(), so
4331 * that we can avoid repeating them for each and every node during 'show
4332 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004333static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004334{
4335 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004336 || cmd == &show_vty_attr_all_cmd
4337 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004338 || cmd == &config_list_cmd
4339 || cmd == &config_write_terminal_cmd
4340 || cmd == &config_write_file_cmd
4341 || cmd == &config_write_memory_cmd
4342 || cmd == &config_write_cmd
4343 || cmd == &show_running_config_cmd
4344 || cmd == &config_exit_cmd
4345 || cmd == &config_end_cmd)
4346 return true;
4347 return false;
4348}
4349
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004350/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004351 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004352 * \param[in] vty the vty of the code
4353 * \param[in] filename where to store the file
4354 * \return 0 in case of success.
4355 *
4356 * If the filename already exists create a filename.sav
4357 * version with the current code.
4358 *
4359 */
4360int osmo_vty_write_config_file(const char *filename)
4361{
4362 char *failed_file;
4363 int rc;
4364
4365 rc = write_config_file(filename, &failed_file);
4366 talloc_free(failed_file);
4367 return rc;
4368}
4369
4370/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004371 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004372 * \return 0 in case of success.
4373 *
4374 * If the filename already exists create a filename.sav
4375 * version with the current code.
4376 *
4377 */
4378int osmo_vty_save_config_file(void)
4379{
4380 char *failed_file;
4381 int rc;
4382
4383 if (host.config == NULL)
4384 return -7;
4385
4386 rc = write_config_file(host.config, &failed_file);
4387 talloc_free(failed_file);
4388 return rc;
4389}
4390
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004391/* Initialize command interface. Install basic nodes and commands. */
4392void cmd_init(int terminal)
4393{
4394 /* Allocate initial top vector of commands. */
4395 cmdvec = vector_init(VECTOR_MIN_SIZE);
4396
4397 /* Default host value settings. */
4398 host.name = NULL;
4399 host.password = NULL;
4400 host.enable = NULL;
4401 host.logfile = NULL;
4402 host.config = NULL;
4403 host.lines = -1;
4404 host.motd = default_motd;
4405 host.motdfile = NULL;
4406
4407 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004408 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004409 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004410 install_node_bare(&auth_node, NULL);
4411 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004412 install_node(&config_node, config_write_host);
4413
4414 /* Each node's basic commands. */
Oliver Smith0c78bc62021-07-12 17:28:36 +02004415 install_lib_element(VIEW_NODE, &show_pid_cmd);
Oliver Smithd243c2a2021-07-09 17:19:32 +02004416 install_lib_element(VIEW_NODE, &show_uptime_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004417 install_lib_element(VIEW_NODE, &show_version_cmd);
4418 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004419 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004420 install_lib_element(VIEW_NODE, &config_list_cmd);
4421 install_lib_element(VIEW_NODE, &config_exit_cmd);
4422 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004423 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4424 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004425 install_lib_element(VIEW_NODE, &config_enable_cmd);
4426 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4427 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4428 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004429 }
4430
4431 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004432 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4433 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4434 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Oliver Smith8a482fd2021-07-12 18:18:51 +02004435 install_lib_element(ENABLE_NODE, &shutdown_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004436 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004437 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4438 install_lib_element(ENABLE_NODE, &show_version_cmd);
4439 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004440
4441 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004442 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4443 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4444 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004445 }
4446
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004447 install_lib_element(CONFIG_NODE, &hostname_cmd);
4448 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004449
4450 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004451 install_lib_element(CONFIG_NODE, &password_cmd);
4452 install_lib_element(CONFIG_NODE, &password_text_cmd);
4453 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4454 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4455 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004456
4457#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004458 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4459 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004460#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004461 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4462 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4463 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4464 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4465 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004466
4467 }
4468 srand(time(NULL));
4469}
Harald Welte7acb30c2011-08-17 17:13:48 +02004470
Oliver Smithd243c2a2021-07-09 17:19:32 +02004471static __attribute__((constructor)) void on_dso_load_starttime(void)
4472{
4473 osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
4474}
4475
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004476/* FIXME: execute this section in the unit test instead */
4477static __attribute__((constructor)) void on_dso_load(void)
4478{
4479 unsigned int i, j;
4480
4481 /* Check total number of the library specific attributes */
4482 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4483
4484 /* Check for duplicates in the list of library specific flags */
4485 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4486 if (cmd_lib_attr_letters[i] == '\0')
4487 continue;
4488
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004489 /* Some flag characters are reserved for global attributes */
4490 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4491 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4492 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4493
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004494 /* Only upper case flag letters are allowed for libraries */
4495 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4496 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4497
4498 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4499 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4500 }
4501}
4502
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004503/*! @} */