blob: a3e0e36bf529ad670368ef7cf2213adecb557625 [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
Neels Hofmeyre4b84732023-05-03 02:31:16 +020080struct vty_parent_node {
81 struct llist_head entry;
82
83 /*! private data, specified by creator */
84 void *priv;
85 void *index;
86
87 /*! Node status of this vty */
88 int node;
89
90 /*! When reading from a config file, these are the indenting characters expected for children of
91 * this VTY node. */
92 char *indent;
93};
94
Harald Welte3fb0b6f2010-05-19 19:02:52 +020095/* Standard command node structures. */
96struct cmd_node auth_node = {
97 AUTH_NODE,
98 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010099 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200100};
101
102struct cmd_node view_node = {
103 VIEW_NODE,
104 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +0100105 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200106};
107
108struct cmd_node auth_enable_node = {
109 AUTH_ENABLE_NODE,
110 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +0100111 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200112};
113
114struct cmd_node enable_node = {
115 ENABLE_NODE,
116 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +0100117 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200118};
119
120struct cmd_node config_node = {
121 CONFIG_NODE,
122 "%s(config)# ",
123 1
124};
125
126/* Default motd string. */
127const char *default_motd = "";
128
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200129/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200130 *
131 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200132void print_version(int print_copyright)
133{
Harald Welte237f6242010-05-25 23:00:45 +0200134 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200135 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200136 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200137}
138
139/* Utility function to concatenate argv argument into a single string
140 with inserting ' ' character between each argument. */
141char *argv_concat(const char **argv, int argc, int shift)
142{
143 int i;
144 size_t len;
145 char *str;
146 char *p;
147
148 len = 0;
149 for (i = shift; i < argc; i++)
150 len += strlen(argv[i]) + 1;
151 if (!len)
152 return NULL;
153 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
154 for (i = shift; i < argc; i++) {
155 size_t arglen;
156 memcpy(p, argv[i], (arglen = strlen(argv[i])));
157 p += arglen;
158 *p++ = ' ';
159 }
160 *(p - 1) = '\0';
161 return str;
162}
163
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200164/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
165 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
166 * in turn, this name us used for XML IDs in 'show online-help'. */
167static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
168{
169 const char *pos;
170 int dest = 0;
171
172 if (!prompt || !*prompt)
173 return "";
174
175 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
176 if (pos[0] == '%' && pos[1]) {
177 /* skip "%s"; loop pos++ does the second one. */
178 pos++;
179 continue;
180 }
181 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
182 continue;
183 name_buf[dest] = pos[0];
184 dest++;
185 }
186 name_buf[dest] = '\0';
187 return name_buf;
188}
189
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200190static void install_basic_node_commands(int node);
191
192/*! Install top node of command vector, without adding basic node commands. */
193static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200194{
195 vector_set_index(cmdvec, node->node, node);
196 node->func = func;
197 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200198 if (!*node->name)
199 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200200}
201
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200202/*! Install top node of command vector. */
203void install_node(struct cmd_node *node, int (*func) (struct vty *))
204{
205 install_node_bare(node, func);
206 install_basic_node_commands(node->node);
207}
208
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200209/* Compare two command's string. Used in sort_node (). */
210static int cmp_node(const void *p, const void *q)
211{
212 struct cmd_element *a = *(struct cmd_element **)p;
213 struct cmd_element *b = *(struct cmd_element **)q;
214
215 return strcmp(a->string, b->string);
216}
217
218static int cmp_desc(const void *p, const void *q)
219{
220 struct desc *a = *(struct desc **)p;
221 struct desc *b = *(struct desc **)q;
222
223 return strcmp(a->cmd, b->cmd);
224}
225
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200226/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200227void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200228{
229 unsigned int i, j;
230 struct cmd_node *cnode;
231 vector descvec;
232 struct cmd_element *cmd_element;
233
234 for (i = 0; i < vector_active(cmdvec); i++)
235 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
236 vector cmd_vector = cnode->cmd_vector;
237 qsort(cmd_vector->index, vector_active(cmd_vector),
238 sizeof(void *), cmp_node);
239
240 for (j = 0; j < vector_active(cmd_vector); j++)
241 if ((cmd_element =
242 vector_slot(cmd_vector, j)) != NULL
243 && vector_active(cmd_element->strvec)) {
244 descvec =
245 vector_slot(cmd_element->strvec,
246 vector_active
247 (cmd_element->strvec) -
248 1);
249 qsort(descvec->index,
250 vector_active(descvec),
251 sizeof(void *), cmp_desc);
252 }
253 }
254}
255
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256/*! Break up string in command tokens. Return leading indents.
257 * \param[in] string String to split.
258 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
259 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
260 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
261 *
262 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
263 * so that \a indent can simply return the count of leading spaces.
264 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
265 */
266int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200267{
268 const char *cp, *start;
269 char *token;
270 int strlen;
271 vector strvec;
272
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 *strvec_p = NULL;
274 if (indent)
275 *indent = 0;
276
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200278 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200279
280 cp = string;
281
282 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 while (isspace((int)*cp) && *cp != '\0') {
284 /* if we're counting indents, we need to be strict about them */
285 if (indent && (*cp != ' ') && (*cp != '\t')) {
286 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
287 if (*cp == '\n' || *cp == '\r') {
288 cp++;
289 string = cp;
290 continue;
291 }
292 return CMD_ERR_INVALID_INDENT;
293 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200294 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200295 }
296
297 if (indent)
298 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200299
300 /* Return if there is only white spaces */
301 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200302 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200303
304 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306
307 /* Prepare return vector. */
308 strvec = vector_init(VECTOR_MIN_SIZE);
309
310 /* Copy each command piece and set into vector. */
311 while (1) {
312 start = cp;
313 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
314 *cp != '\0')
315 cp++;
316 strlen = cp - start;
317 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
318 memcpy(token, start, strlen);
319 *(token + strlen) = '\0';
320 vector_set(strvec, token);
321
322 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
323 *cp != '\0')
324 cp++;
325
326 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200327 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200328 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200329
330 *strvec_p = strvec;
331 return CMD_SUCCESS;
332}
333
334/*! Breaking up string into each command piece. I assume given
335 character is separated by a space character. Return value is a
336 vector which includes char ** data element. */
337vector cmd_make_strvec(const char *string)
338{
339 vector strvec;
340 cmd_make_strvec2(string, NULL, &strvec);
341 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200342}
343
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200344/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200345void cmd_free_strvec(vector v)
346{
347 unsigned int i;
348 char *cp;
349
350 if (!v)
351 return;
352
353 for (i = 0; i < vector_active(v); i++)
354 if ((cp = vector_slot(v, i)) != NULL)
355 talloc_free(cp);
356
357 vector_free(v);
358}
359
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200360/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200361static char *cmd_desc_str(const char **string)
362{
363 const char *cp, *start;
364 char *token;
365 int strlen;
366
367 cp = *string;
368
369 if (cp == NULL)
370 return NULL;
371
372 /* Skip white spaces. */
373 while (isspace((int)*cp) && *cp != '\0')
374 cp++;
375
376 /* Return if there is only white spaces */
377 if (*cp == '\0')
378 return NULL;
379
380 start = cp;
381
382 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
383 cp++;
384
385 strlen = cp - start;
386 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
387 memcpy(token, start, strlen);
388 *(token + strlen) = '\0';
389
390 *string = cp;
391
392 return token;
393}
394
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200395/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200396static vector cmd_make_descvec(const char *string, const char *descstr)
397{
398 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200400 const char *sp;
401 char *token;
402 int len;
403 const char *cp;
404 const char *dp;
405 vector allvec;
406 vector strvec = NULL;
407 struct desc *desc;
408
409 cp = string;
410 dp = descstr;
411
412 if (cp == NULL)
413 return NULL;
414
415 allvec = vector_init(VECTOR_MIN_SIZE);
416
417 while (1) {
418 while (isspace((int)*cp) && *cp != '\0')
419 cp++;
420
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100421 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
422 if (cp[0] == '[' && cp[1] == '(') {
423 optional_brace = 1;
424 cp++;
425 }
426
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200427 if (*cp == '(') {
428 multiple = 1;
429 cp++;
430 }
431 if (*cp == ')') {
432 multiple = 0;
433 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100434 if (*cp == ']')
435 cp++;
436 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200437 }
438 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100439 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200440 cp++;
441 }
442
443 while (isspace((int)*cp) && *cp != '\0')
444 cp++;
445
446 if (*cp == '(') {
447 multiple = 1;
448 cp++;
449 }
450
451 if (*cp == '\0')
452 return allvec;
453
454 sp = cp;
455
456 while (!
457 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
458 || *cp == ')' || *cp == '|') && *cp != '\0')
459 cp++;
460
461 len = cp - sp;
462
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100463 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
464 if (optional_brace) {
465 /* Place each individual multi-choice token in its own square braces */
466 token[0] = '[';
467 memcpy(token + 1, sp, len);
468 token[1 + len] = ']';
469 token[2 + len] = '\0';
470 } else {
471 memcpy(token, sp, len);
472 *(token + len) = '\0';
473 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200474
475 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
476 desc->cmd = token;
477 desc->str = cmd_desc_str(&dp);
478
479 if (multiple) {
480 if (multiple == 1) {
481 strvec = vector_init(VECTOR_MIN_SIZE);
482 vector_set(allvec, strvec);
483 }
484 multiple++;
485 } else {
486 strvec = vector_init(VECTOR_MIN_SIZE);
487 vector_set(allvec, strvec);
488 }
489 vector_set(strvec, desc);
490 }
491}
492
493/* Count mandantory string vector size. This is to determine inputed
494 command has enough command length. */
495static int cmd_cmdsize(vector strvec)
496{
497 unsigned int i;
498 int size = 0;
499 vector descvec;
500 struct desc *desc;
501
502 for (i = 0; i < vector_active(strvec); i++)
503 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100504 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200505 && (desc = vector_slot(descvec, 0)) != NULL) {
506 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
507 return size;
508 else
509 size++;
510 } else
511 size++;
512 }
513 return size;
514}
515
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200516/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200517const char *cmd_prompt(enum node_type node)
518{
519 struct cmd_node *cnode;
520
521 cnode = vector_slot(cmdvec, node);
522 return cnode->prompt;
523}
524
Alexander Couzensad580ba2016-05-16 16:01:45 +0200525/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200526 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200527 * \param unsafe string
528 * \return a new talloc char *
529 */
530char *osmo_asciidoc_escape(const char *inp)
531{
532 int _strlen;
533 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200534 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535
536 if (!inp)
537 return NULL;
538 _strlen = strlen(inp);
539
540 for (i = 0; i < _strlen; ++i) {
541 switch (inp[i]) {
542 case '|':
543 len += 2;
544 break;
545 default:
546 len += 1;
547 break;
548 }
549 }
550
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200551 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200552 if (!out)
553 return NULL;
554
555 out_ptr = out;
556
Alexander Couzensad580ba2016-05-16 16:01:45 +0200557 for (i = 0; i < _strlen; ++i) {
558 switch (inp[i]) {
559 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200560 /* Prepend escape character "\": */
561 *(out_ptr++) = '\\';
562 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200563 default:
564 *(out_ptr++) = inp[i];
565 break;
566 }
567 }
568
Alexander Couzensad580ba2016-05-16 16:01:45 +0200569 out_ptr[0] = '\0';
570 return out;
571}
572
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100573static char *xml_escape(const char *inp)
574{
575 int _strlen;
576 char *out, *out_ptr;
577 int len = 0, i, j;
578
579 if (!inp)
580 return NULL;
581 _strlen = strlen(inp);
582
583 for (i = 0; i < _strlen; ++i) {
584 switch (inp[i]) {
585 case '"':
586 len += 6;
587 break;
588 case '\'':
589 len += 6;
590 break;
591 case '<':
592 len += 4;
593 break;
594 case '>':
595 len += 4;
596 break;
597 case '&':
598 len += 5;
599 break;
600 default:
601 len += 1;
602 break;
603 }
604 }
605
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200606 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100607 if (!out)
608 return NULL;
609
610 out_ptr = out;
611
612#define ADD(out, str) \
613 for (j = 0; j < strlen(str); ++j) \
614 *(out++) = str[j];
615
616 for (i = 0; i < _strlen; ++i) {
617 switch (inp[i]) {
618 case '"':
619 ADD(out_ptr, "&quot;");
620 break;
621 case '\'':
622 ADD(out_ptr, "&apos;");
623 break;
624 case '<':
625 ADD(out_ptr, "&lt;");
626 break;
627 case '>':
628 ADD(out_ptr, "&gt;");
629 break;
630 case '&':
631 ADD(out_ptr, "&amp;");
632 break;
633 default:
634 *(out_ptr++) = inp[i];
635 break;
636 }
637 }
638
639#undef ADD
640
641 out_ptr[0] = '\0';
642 return out;
643}
644
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200645typedef int (*print_func_t)(void *data, const char *fmt, ...);
646
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700647static const struct value_string cmd_attr_desc[] = {
648 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700649 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700650 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700651 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700652 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700653 { 0, NULL }
654};
655
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700656/* Public attributes (to be printed in the VTY / XML reference) */
657#define CMD_ATTR_PUBLIC_MASK \
Vadim Yanitskiy67608452020-10-23 20:37:32 +0700658 (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700659
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700660/* Get a flag character for a global VTY command attribute */
661static char cmd_attr_get_flag(unsigned int attr)
662{
663 switch (attr) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700664 case CMD_ATTR_HIDDEN:
665 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700666 case CMD_ATTR_IMMEDIATE:
667 return '!';
668 case CMD_ATTR_NODE_EXIT:
669 return '@';
670 default:
671 return '.';
672 }
673}
674
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700675/* Description of attributes shared between the lib commands */
676static const char * const cmd_lib_attr_desc[32] = {
677 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
678 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200679 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
680 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200681 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
682 "This command applies on IPA link establishment",
683 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
684 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700685};
686
687/* Flag letters of attributes shared between the lib commands.
688 * NOTE: uppercase letters only, the rest is reserved for applications. */
689static const char cmd_lib_attr_letters[32] = {
690 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200691 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200692 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
693 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700694};
695
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100696/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200697 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100698 */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700699static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
700 void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100701{
702 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700703 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100704
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200705 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700706
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700707 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700708 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700709 print_func(data, " <attributes scope='global'>%s", newline);
710
711 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
712 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700713 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700714
715 if (~cmd->attr & cmd_attr_desc[i].value)
716 continue;
717
718 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700719 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700720 xml_att_desc, newline);
721 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700722
723 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
724 if (flag != '.')
725 print_func(data, " flag='%c'", flag);
726 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700727 }
728
729 print_func(data, " </attributes>%s", newline);
730 }
731
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700732 /* Print application specific attributes and their description */
733 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700734 const char * const *desc;
735 const char *letters;
736
737 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
738 print_func(data, " <attributes scope='library'>%s", newline);
739 letters = &cmd_lib_attr_letters[0];
740 desc = &cmd_lib_attr_desc[0];
741 } else {
742 print_func(data, " <attributes scope='application'>%s", newline);
743 letters = &host.app_info->usr_attr_letters[0];
744 desc = &host.app_info->usr_attr_desc[0];
745 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700746
747 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
748 char *xml_att_desc;
749 char flag;
750
751 /* Skip attribute if *not* set */
Harald Weltec296e292020-12-21 15:44:52 +0100752 if (~cmd->usrattr & ((unsigned)1 << i))
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700753 continue;
754
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700755 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700756 print_func(data, " <attribute doc='%s'", xml_att_desc);
757 talloc_free(xml_att_desc);
758
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700759 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700760 print_func(data, " flag='%c'", flag);
761 print_func(data, " />%s", newline);
762 }
763
764 print_func(data, " </attributes>%s", newline);
765 }
766
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200767 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100768
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700769 for (i = 0; i < vector_count(cmd->strvec); ++i) {
770 vector descvec = vector_slot(cmd->strvec, i);
771 int j;
772 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100773 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700774 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100775 if (desc == NULL)
776 continue;
777
778 xml_param = xml_escape(desc->cmd);
779 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200780 print_func(data, " <param name='%s' doc='%s' />%s",
781 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100782 talloc_free(xml_param);
783 talloc_free(xml_doc);
784 }
785 }
786
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200787 print_func(data, " </params>%s", newline);
788 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100789
790 talloc_free(xml_string);
791 return 0;
792}
793
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700794static bool vty_command_is_common(const struct cmd_element *cmd);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200795
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100796/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200797 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700798 *
799 * (gflag_mask, match = false) - print only those commands with non-matching flags.
800 * (gflag_mask, match = true) - print only those commands with matching flags.
801 *
802 * Some examples:
803 *
804 * Print all commands except deprecated and hidden (default mode):
805 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
806 * Print only deprecated and hidden commands:
807 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
808 * Print all commands except deprecated (expert mode):
809 * (CMD_ATTR_DEPRECATED, false)
810 * Print only hidden commands:
811 * (CMD_ATTR_HIDDEN, true)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100812 */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700813static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
814 unsigned char gflag_mask, bool match)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100815{
816 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200817 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100818
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200819 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100820
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200821 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200822 print_func(data, " <node id='_common_cmds_'>%s", newline);
823 print_func(data, " <name>Common Commands</name>%s", newline);
824 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
825 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200826 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700827 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200828 if (!cnode)
829 continue;
830 if (cnode->node != CONFIG_NODE)
831 continue;
832
833 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700834 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200835 if (!vty_command_is_common(elem))
836 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700837 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700838 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700839 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700840 continue;
841 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200842 }
843 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200844 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200845
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100846 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700847 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100848 if (!cnode)
849 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200850 if (vector_active(cnode->cmd_vector) < 1)
851 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100852
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200853 /* De-dup node IDs: how many times has this same name been used before? Count the first
854 * occurence as _1 and omit that first suffix, so that the first occurence is called
855 * 'name', the second becomes 'name_2', then 'name_3', ... */
856 same_name_count = 1;
857 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700858 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200859 if (!cnode2)
860 continue;
861 if (strcmp(cnode->name, cnode2->name) == 0)
862 same_name_count ++;
863 }
864
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200865 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200866 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200867 print_func(data, "_%d", same_name_count);
868 print_func(data, "'>%s", newline);
869 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100870
871 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700872 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200873 if (vty_command_is_common(elem))
874 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700875 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700876 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700877 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700878 continue;
879 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100880 }
881
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200882 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100883 }
884
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200885 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100886
887 return 0;
888}
889
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200890static int print_func_vty(void *data, const char *format, ...)
891{
892 struct vty *vty = data;
893 va_list args;
894 int rc;
895 va_start(args, format);
896 rc = vty_out_va(vty, format, args);
897 va_end(args);
898 return rc;
899}
900
901static int vty_dump_xml_ref_to_vty(struct vty *vty)
902{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700903 unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
904 if (!vty->expert_mode)
905 gflag_mask |= CMD_ATTR_HIDDEN;
906 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200907}
908
909static int print_func_stream(void *data, const char *format, ...)
910{
911 va_list args;
912 int rc;
913 va_start(args, format);
914 rc = vfprintf((FILE*)data, format, args);
915 va_end(args);
916 return rc;
917}
918
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700919const struct value_string vty_ref_gen_mode_names[] = {
920 { VTY_REF_GEN_MODE_DEFAULT, "default" },
921 { VTY_REF_GEN_MODE_EXPERT, "expert" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700922 { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700923 { 0, NULL }
924};
925
926const struct value_string vty_ref_gen_mode_desc[] = {
927 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
928 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700929 { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700930 { 0, NULL }
931};
932
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200933/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700934 * \param[out] stream Output stream to print the XML reference to.
935 * \param[in] mode The XML reference generation mode.
936 * \returns always 0 for now, no errors possible.
937 */
938int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
939{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700940 unsigned char gflag_mask;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700941 bool match = false;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700942
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700943 switch (mode) {
944 case VTY_REF_GEN_MODE_EXPERT:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700945 /* All commands except deprecated */
946 gflag_mask = CMD_ATTR_DEPRECATED;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700947 break;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700948 case VTY_REF_GEN_MODE_HIDDEN:
949 /* Only hidden commands */
950 gflag_mask = CMD_ATTR_HIDDEN;
951 match = true;
952 break;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700953 case VTY_REF_GEN_MODE_DEFAULT:
954 default:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700955 /* All commands except deprecated and hidden */
956 gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700957 break;
958 }
959
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700960 return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700961}
962
963/*! Print the XML reference of all VTY nodes to the given stream.
964 * \param[out] stream Output stream to print the XML reference to.
965 * \returns always 0 for now, no errors possible.
966 *
967 * NOTE: this function is deprecated because it does not allow to
968 * specify the XML reference generation mode (default mode
969 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200970 */
971int vty_dump_xml_ref(FILE *stream)
972{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700973 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200974}
975
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200976/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100977static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
978{
979 int i;
980
981 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
982 struct cmd_element *elem;
983 elem = vector_slot(cnode->cmd_vector, i);
984 if (!elem->string)
985 continue;
986 if (!strcmp(elem->string, cmdstring))
987 return 1;
988 }
989 return 0;
990}
991
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200992/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200993 * \param[in] ntype Node Type
994 * \param[cmd] element to be installed
995 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000996void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200997{
998 struct cmd_node *cnode;
999
1000 cnode = vector_slot(cmdvec, ntype);
1001
Harald Weltea99d45a2015-11-12 13:48:23 +01001002 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +01001003 /* ensure no _identical_ command has been registered at this
1004 * node so far */
1005 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001006
1007 vector_set(cnode->cmd_vector, cmd);
1008
1009 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
1010 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
1011}
1012
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001013/*! Install a library command into a node
1014 * \param[in] ntype Node Type
1015 * \param[in] cmd element to be installed
1016 */
1017void install_lib_element(int ntype, struct cmd_element *cmd)
1018{
1019 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1020 install_element(ntype, cmd);
1021}
1022
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001023/* Install a command into VIEW and ENABLE node */
1024void install_element_ve(struct cmd_element *cmd)
1025{
1026 install_element(VIEW_NODE, cmd);
1027 install_element(ENABLE_NODE, cmd);
1028}
1029
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001030/* Install a library command into VIEW and ENABLE node */
1031void install_lib_element_ve(struct cmd_element *cmd)
1032{
1033 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1034 install_element_ve(cmd);
1035}
1036
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001037#ifdef VTY_CRYPT_PW
1038static unsigned char itoa64[] =
1039 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1040
1041static void to64(char *s, long v, int n)
1042{
1043 while (--n >= 0) {
1044 *s++ = itoa64[v & 0x3f];
1045 v >>= 6;
1046 }
1047}
1048
1049static char *zencrypt(const char *passwd)
1050{
1051 char salt[6];
1052 struct timeval tv;
1053 char *crypt(const char *, const char *);
1054
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001055 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001056
1057 to64(&salt[0], random(), 3);
1058 to64(&salt[3], tv.tv_usec, 3);
1059 salt[5] = '\0';
1060
1061 return crypt(passwd, salt);
1062}
1063#endif
1064
1065/* This function write configuration of this host. */
1066static int config_write_host(struct vty *vty)
1067{
1068 if (host.name)
1069 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1070
1071 if (host.encrypt) {
1072 if (host.password_encrypt)
1073 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1074 VTY_NEWLINE);
1075 if (host.enable_encrypt)
1076 vty_out(vty, "enable password 8 %s%s",
1077 host.enable_encrypt, VTY_NEWLINE);
1078 } else {
1079 if (host.password)
1080 vty_out(vty, "password %s%s", host.password,
1081 VTY_NEWLINE);
1082 if (host.enable)
1083 vty_out(vty, "enable password %s%s", host.enable,
1084 VTY_NEWLINE);
1085 }
1086
1087 if (host.advanced)
1088 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1089
1090 if (host.encrypt)
1091 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1092
1093 if (host.lines >= 0)
1094 vty_out(vty, "service terminal-length %d%s", host.lines,
1095 VTY_NEWLINE);
1096
1097 if (host.motdfile)
1098 vty_out(vty, "banner motd file %s%s", host.motdfile,
1099 VTY_NEWLINE);
1100 else if (!host.motd)
1101 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1102
1103 return 1;
1104}
1105
1106/* Utility function for getting command vector. */
1107static vector cmd_node_vector(vector v, enum node_type ntype)
1108{
1109 struct cmd_node *cnode = vector_slot(v, ntype);
Pau Espin Pedrol079149e2022-06-16 17:08:34 +02001110 OSMO_ASSERT(cnode != NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111 return cnode->cmd_vector;
1112}
1113
1114/* Completion match types. */
1115enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001116 NO_MATCH = 0,
1117 ANY_MATCH,
1118 EXTEND_MATCH,
1119 IPV4_PREFIX_MATCH,
1120 IPV4_MATCH,
1121 IPV6_PREFIX_MATCH,
1122 IPV6_MATCH,
1123 RANGE_MATCH,
1124 VARARG_MATCH,
1125 PARTLY_MATCH,
1126 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001127};
1128
1129static enum match_type cmd_ipv4_match(const char *str)
1130{
1131 const char *sp;
1132 int dots = 0, nums = 0;
1133 char buf[4];
1134
1135 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001136 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001137
1138 for (;;) {
1139 memset(buf, 0, sizeof(buf));
1140 sp = str;
1141 while (*str != '\0') {
1142 if (*str == '.') {
1143 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001144 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001145
1146 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 dots++;
1153 break;
1154 }
1155 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001156 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001157
1158 str++;
1159 }
1160
1161 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001162 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001163
1164 strncpy(buf, sp, str - sp);
1165 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001166 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001167
1168 nums++;
1169
1170 if (*str == '\0')
1171 break;
1172
1173 str++;
1174 }
1175
1176 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001177 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001178
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001179 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001180}
1181
1182static enum match_type cmd_ipv4_prefix_match(const char *str)
1183{
1184 const char *sp;
1185 int dots = 0;
1186 char buf[4];
1187
1188 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001189 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190
1191 for (;;) {
1192 memset(buf, 0, sizeof(buf));
1193 sp = str;
1194 while (*str != '\0' && *str != '/') {
1195 if (*str == '.') {
1196 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001197 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001198
1199 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001200 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001201
1202 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001203 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001204
1205 dots++;
1206 break;
1207 }
1208
1209 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001210 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001211
1212 str++;
1213 }
1214
1215 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001216 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001217
1218 strncpy(buf, sp, str - sp);
1219 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001220 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001221
1222 if (dots == 3) {
1223 if (*str == '/') {
1224 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226
1227 str++;
1228 break;
1229 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001230 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001231 }
1232
1233 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001234 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235
1236 str++;
1237 }
1238
1239 sp = str;
1240 while (*str != '\0') {
1241 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001242 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001243
1244 str++;
1245 }
1246
1247 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001248 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001249
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001250 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001251}
1252
1253#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1254#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1255#define STATE_START 1
1256#define STATE_COLON 2
1257#define STATE_DOUBLE 3
1258#define STATE_ADDR 4
1259#define STATE_DOT 5
1260#define STATE_SLASH 6
1261#define STATE_MASK 7
1262
1263#ifdef HAVE_IPV6
1264
1265static enum match_type cmd_ipv6_match(const char *str)
1266{
1267 int state = STATE_START;
1268 int colons = 0, nums = 0, double_colon = 0;
1269 const char *sp = NULL;
1270 struct sockaddr_in6 sin6_dummy;
1271 int ret;
1272
1273 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001274 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001275
1276 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001277 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278
1279 /* use inet_pton that has a better support,
1280 * for example inet_pton can support the automatic addresses:
1281 * ::1.2.3.4
1282 */
1283 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1284
1285 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001286 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001287
1288 while (*str != '\0') {
1289 switch (state) {
1290 case STATE_START:
1291 if (*str == ':') {
1292 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001293 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001294 colons--;
1295 state = STATE_COLON;
1296 } else {
1297 sp = str;
1298 state = STATE_ADDR;
1299 }
1300
1301 continue;
1302 case STATE_COLON:
1303 colons++;
1304 if (*(str + 1) == ':')
1305 state = STATE_DOUBLE;
1306 else {
1307 sp = str + 1;
1308 state = STATE_ADDR;
1309 }
1310 break;
1311 case STATE_DOUBLE:
1312 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001313 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001314
1315 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001316 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001317 else {
1318 if (*(str + 1) != '\0')
1319 colons++;
1320 sp = str + 1;
1321 state = STATE_ADDR;
1322 }
1323
1324 double_colon++;
1325 nums++;
1326 break;
1327 case STATE_ADDR:
1328 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1329 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001330 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331
1332 nums++;
1333 state = STATE_COLON;
1334 }
1335 if (*(str + 1) == '.')
1336 state = STATE_DOT;
1337 break;
1338 case STATE_DOT:
1339 state = STATE_ADDR;
1340 break;
1341 default:
1342 break;
1343 }
1344
1345 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001346 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001347
1348 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001349 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001350
1351 str++;
1352 }
1353
1354#if 0
1355 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001356 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001357#endif /* 0 */
1358
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001359 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001360}
1361
1362static enum match_type cmd_ipv6_prefix_match(const char *str)
1363{
1364 int state = STATE_START;
1365 int colons = 0, nums = 0, double_colon = 0;
1366 int mask;
1367 const char *sp = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001368
1369 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001370 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001371
1372 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001373 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001374
1375 while (*str != '\0' && state != STATE_MASK) {
1376 switch (state) {
1377 case STATE_START:
1378 if (*str == ':') {
1379 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001380 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001381 colons--;
1382 state = STATE_COLON;
1383 } else {
1384 sp = str;
1385 state = STATE_ADDR;
1386 }
1387
1388 continue;
1389 case STATE_COLON:
1390 colons++;
1391 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001392 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001393 else if (*(str + 1) == ':')
1394 state = STATE_DOUBLE;
1395 else {
1396 sp = str + 1;
1397 state = STATE_ADDR;
1398 }
1399 break;
1400 case STATE_DOUBLE:
1401 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001402 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001403
1404 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001405 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001406 else {
1407 if (*(str + 1) != '\0' && *(str + 1) != '/')
1408 colons++;
1409 sp = str + 1;
1410
1411 if (*(str + 1) == '/')
1412 state = STATE_SLASH;
1413 else
1414 state = STATE_ADDR;
1415 }
1416
1417 double_colon++;
1418 nums += 1;
1419 break;
1420 case STATE_ADDR:
1421 if (*(str + 1) == ':' || *(str + 1) == '.'
1422 || *(str + 1) == '\0' || *(str + 1) == '/') {
1423 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001424 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001425
1426 for (; sp <= str; sp++)
1427 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001428 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001429
1430 nums++;
1431
1432 if (*(str + 1) == ':')
1433 state = STATE_COLON;
1434 else if (*(str + 1) == '.')
1435 state = STATE_DOT;
1436 else if (*(str + 1) == '/')
1437 state = STATE_SLASH;
1438 }
1439 break;
1440 case STATE_DOT:
1441 state = STATE_ADDR;
1442 break;
1443 case STATE_SLASH:
1444 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001445 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001446
1447 state = STATE_MASK;
1448 break;
1449 default:
1450 break;
1451 }
1452
1453 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001454 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001455
1456 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001457 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001458
1459 str++;
1460 }
1461
1462 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001463 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001464
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02001465 if (osmo_str_to_int(&mask, str, 10, 0, 128))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001466 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001467
1468/* I don't know why mask < 13 makes command match partly.
1469 Forgive me to make this comments. I Want to set static default route
1470 because of lack of function to originate default in ospf6d; sorry
1471 yasu
1472 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001473 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001474*/
1475
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001476 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001477}
1478
1479#endif /* HAVE_IPV6 */
1480
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001481
1482#if ULONG_MAX == 18446744073709551615UL
1483#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1484#elif ULONG_MAX == 4294967295UL
1485#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1486#else
1487#error "ULONG_MAX not defined!"
1488#endif
1489
1490#if LONG_MAX == 9223372036854775807L
1491#define DECIMAL_STRLEN_MAX_SIGNED 19
1492#elif LONG_MAX == 2147483647L
1493#define DECIMAL_STRLEN_MAX_SIGNED 10
1494#else
1495#error "LONG_MAX not defined!"
1496#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001497
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001498/* This function is aimed at quickly guessing & filtering the numeric base a
1499 * string can contain, by no means validates the entire value.
1500 * Returns 16 if string follows pattern "({+,-}0x[digits])"
1501 * Returns 10 if string follows pattern "({+,-}[digits])"
1502 * Returns a negative value if something other is detected (error)
1503*/
1504static int check_base(const char *str)
1505{
1506 const char *ptr = str;
1507 /* Skip any space */
1508 while (isspace(*ptr)) ptr++;
1509
1510 /* Skip optional sign: */
1511 if (*ptr == '+' || *ptr == '-')
1512 ptr++;
1513 if (*ptr == '0') {
1514 ptr++;
1515 if (*ptr == '\0' || isdigit(*ptr))
1516 return 10;
1517 if (!(*ptr == 'x' || *ptr == 'X'))
1518 return -1;
1519 ptr++;
1520 if (isxdigit(*ptr))
1521 return 16;
1522 return -1;
1523 }
1524 return 10;
1525}
1526
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001527int vty_cmd_range_match(const char *range, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001528{
1529 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001530 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001531 char *endptr = NULL;
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001532 int min_base, max_base, val_base;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001533
1534 if (str == NULL)
1535 return 1;
1536
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001537 if ((val_base = check_base(str)) < 0)
1538 return 0;
1539
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001540 if (range[1] == '-') {
1541 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001542
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001543 val = strtol(str, &endptr, val_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001544 if (*endptr != '\0')
1545 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001546
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001547 range += 2;
1548 p = strchr(range, '-');
1549 if (p == NULL)
1550 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001551 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001552 return 0;
1553 strncpy(buf, range, p - range);
1554 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001555 if ((min_base = check_base(buf)) < 0)
1556 return 0;
1557 min = -strtol(buf, &endptr, min_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001558 if (*endptr != '\0')
1559 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001560
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001561 range = p + 1;
1562 p = strchr(range, '>');
1563 if (p == NULL)
1564 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001565 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001566 return 0;
1567 strncpy(buf, range, p - range);
1568 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001569 if ((max_base = check_base(buf)) < 0)
1570 return 0;
1571 max = strtol(buf, &endptr, max_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001572 if (*endptr != '\0')
1573 return 0;
1574
1575 if (val < min || val > max)
1576 return 0;
1577 } else {
1578 unsigned long min, max, val;
1579
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001580 if (str[0] == '-')
1581 return 0;
1582
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001583 val = strtoul(str, &endptr, val_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001584 if (*endptr != '\0')
1585 return 0;
1586
1587 range++;
1588 p = strchr(range, '-');
1589 if (p == NULL)
1590 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001591 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001592 return 0;
1593 strncpy(buf, range, p - range);
1594 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001595 if ((min_base = check_base(buf)) < 0)
1596 return 0;
1597 min = strtoul(buf, &endptr, min_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001598 if (*endptr != '\0')
1599 return 0;
1600
1601 range = p + 1;
1602 p = strchr(range, '>');
1603 if (p == NULL)
1604 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001605 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001606 return 0;
1607 strncpy(buf, range, p - range);
1608 buf[p - range] = '\0';
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001609 if ((max_base = check_base(buf)) < 0)
1610 return 0;
1611 max = strtoul(buf, &endptr, max_base);
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001612 if (*endptr != '\0')
1613 return 0;
1614
1615 if (val < min || val > max)
1616 return 0;
1617 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001618
Pau Espin Pedrolc3b904a2020-07-28 15:37:02 +02001619 /* Don't allow ranges specified by min and max with different bases */
1620 if (min_base != max_base)
1621 return 0;
1622 /* arg value passed must match the base of the range */
1623 if (min_base != val_base)
1624 return 0;
1625
1626 /* Everything's fine */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001627 return 1;
1628}
1629
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001630/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001631static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001632{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001633 /* we've got "[blah]". We want to strip off the []s and redo the
1634 * match check for "blah"
1635 */
1636 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001637
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001638 if (len < 3)
1639 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001640
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001641 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001642}
1643
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001644static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001645cmd_match(const char *str, const char *command,
1646 enum match_type min, bool recur)
1647{
1648
1649 if (recur && CMD_OPTION(str))
1650 {
1651 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001652 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001653
1654 /* this would be a bug in a command, however handle it gracefully
1655 * as it we only discover it if a user tries to run it
1656 */
1657 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001658 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001659
1660 ret = cmd_match(tmp, command, min, false);
1661
1662 talloc_free(tmp);
1663
1664 return ret;
1665 }
1666 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001667 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001668 else if (CMD_RANGE(str))
1669 {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001670 if (vty_cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001671 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001672 }
1673#ifdef HAVE_IPV6
1674 else if (CMD_IPV6(str))
1675 {
1676 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001677 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001678 }
1679 else if (CMD_IPV6_PREFIX(str))
1680 {
1681 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001682 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001683 }
1684#endif /* HAVE_IPV6 */
1685 else if (CMD_IPV4(str))
1686 {
1687 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001688 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001689 }
1690 else if (CMD_IPV4_PREFIX(str))
1691 {
1692 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001693 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001694 }
1695 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001696 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001697 else if (strncmp(command, str, strlen(command)) == 0)
1698 {
1699 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001700 return EXACT_MATCH;
1701 else if (PARTLY_MATCH >= min)
1702 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001703 }
1704
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001705 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001706}
1707
1708/* Filter vector at the specified index and by the given command string, to
1709 * the desired matching level (thus allowing part matches), and return match
1710 * type flag.
1711 */
1712static enum match_type
1713cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001714{
1715 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001716 struct cmd_element *cmd_element;
1717 enum match_type match_type;
1718 vector descvec;
1719 struct desc *desc;
1720
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001721 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001722
1723 /* If command and cmd_element string does not match set NULL to vector */
1724 for (i = 0; i < vector_active(v); i++)
1725 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001726 if (index >= vector_active(cmd_element->strvec))
1727 vector_slot(v, i) = NULL;
1728 else {
1729 unsigned int j;
1730 int matched = 0;
1731
1732 descvec =
1733 vector_slot(cmd_element->strvec, index);
1734
1735 for (j = 0; j < vector_active(descvec); j++)
1736 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001737 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001738
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001739 ret = cmd_match (desc->cmd, command, level, true);
1740
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001741 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001742 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001743
1744 if (match_type < ret)
1745 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001746 }
1747 if (!matched)
1748 vector_slot(v, i) = NULL;
1749 }
1750 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001751
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001752 if (match_type == NO_MATCH)
1753 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001754
1755 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1756 * go again and filter out commands whose argument (at this index) is
1757 * 'weaker'. E.g., if we have 2 commands:
1758 *
1759 * foo bar <1-255>
1760 * foo bar BLAH
1761 *
1762 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001763 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001764 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1765 *
1766 * If we don't do a 2nd pass and filter it out, the higher-layers will
1767 * consider this to be ambiguous.
1768 */
1769 for (i = 0; i < vector_active(v); i++)
1770 if ((cmd_element = vector_slot(v, i)) != NULL) {
1771 if (index >= vector_active(cmd_element->strvec))
1772 vector_slot(v, i) = NULL;
1773 else {
1774 unsigned int j;
1775 int matched = 0;
1776
1777 descvec =
1778 vector_slot(cmd_element->strvec, index);
1779
1780 for (j = 0; j < vector_active(descvec); j++)
1781 if ((desc = vector_slot(descvec, j))) {
1782 enum match_type ret;
1783
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001784 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001785
1786 if (ret >= match_type)
1787 matched++;
1788 }
1789 if (!matched)
1790 vector_slot(v, i) = NULL;
1791 }
1792 }
1793
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001794 return match_type;
1795}
1796
1797/* Check ambiguous match */
1798static int
1799is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1800{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001801 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001802 unsigned int i;
1803 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001804 struct cmd_element *cmd_element;
1805 const char *matched = NULL;
1806 vector descvec;
1807 struct desc *desc;
1808
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001809 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1810 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1811 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1812 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1813 * that case, the string must remain allocated until this function exits or another match comes
1814 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1815 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1816 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1817 void *cmd_deopt_ctx = NULL;
1818
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001819 for (i = 0; i < vector_active(v); i++) {
1820 cmd_element = vector_slot(v, i);
1821 if (!cmd_element)
1822 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001823
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001824 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001825
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001826 descvec = vector_slot(cmd_element->strvec, index);
1827
1828 for (j = 0; j < vector_active(descvec); j++) {
1829 desc = vector_slot(descvec, j);
1830 if (!desc)
1831 continue;
1832
1833 enum match_type mtype;
1834 const char *str = desc->cmd;
1835
1836 if (CMD_OPTION(str)) {
1837 if (!cmd_deopt_ctx)
1838 cmd_deopt_ctx =
1839 talloc_named_const(tall_vty_cmd_ctx, 0,
1840 __func__);
1841 str = cmd_deopt(cmd_deopt_ctx, str);
1842 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001843 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001844 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001845
1846 switch (type) {
1847 case EXACT_MATCH:
1848 if (!(CMD_VARIABLE (str))
1849 && strcmp(command, str) == 0)
1850 match++;
1851 break;
1852 case PARTLY_MATCH:
1853 if (!(CMD_VARIABLE (str))
1854 && strncmp(command, str, strlen (command)) == 0)
1855 {
1856 if (matched
1857 && strcmp(matched,
1858 str) != 0) {
1859 ret = 1; /* There is ambiguous match. */
1860 goto free_and_return;
1861 } else
1862 matched = str;
1863 match++;
1864 }
1865 break;
1866 case RANGE_MATCH:
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001867 if (vty_cmd_range_match
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001868 (str, command)) {
1869 if (matched
1870 && strcmp(matched,
1871 str) != 0) {
1872 ret = 1;
1873 goto free_and_return;
1874 } else
1875 matched = str;
1876 match++;
1877 }
1878 break;
1879#ifdef HAVE_IPV6
1880 case IPV6_MATCH:
1881 if (CMD_IPV6(str))
1882 match++;
1883 break;
1884 case IPV6_PREFIX_MATCH:
1885 if ((mtype =
1886 cmd_ipv6_prefix_match
1887 (command)) != NO_MATCH) {
1888 if (mtype == PARTLY_MATCH) {
1889 ret = 2; /* There is incomplete match. */
1890 goto free_and_return;
1891 }
1892
1893 match++;
1894 }
1895 break;
1896#endif /* HAVE_IPV6 */
1897 case IPV4_MATCH:
1898 if (CMD_IPV4(str))
1899 match++;
1900 break;
1901 case IPV4_PREFIX_MATCH:
1902 if ((mtype =
1903 cmd_ipv4_prefix_match
1904 (command)) != NO_MATCH) {
1905 if (mtype == PARTLY_MATCH) {
1906 ret = 2; /* There is incomplete match. */
1907 goto free_and_return;
1908 }
1909
1910 match++;
1911 }
1912 break;
1913 case EXTEND_MATCH:
1914 if (CMD_VARIABLE (str))
1915 match++;
1916 break;
1917 case NO_MATCH:
1918 default:
1919 break;
1920 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001921 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001922 if (!match)
1923 vector_slot(v, i) = NULL;
1924 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001925
1926free_and_return:
1927 if (cmd_deopt_ctx)
1928 talloc_free(cmd_deopt_ctx);
1929 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001930}
1931
1932/* If src matches dst return dst string, otherwise return NULL */
1933static const char *cmd_entry_function(const char *src, const char *dst)
1934{
1935 /* Skip variable arguments. */
1936 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1937 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1938 return NULL;
1939
1940 /* In case of 'command \t', given src is NULL string. */
1941 if (src == NULL)
1942 return dst;
1943
1944 /* Matched with input string. */
1945 if (strncmp(src, dst, strlen(src)) == 0)
1946 return dst;
1947
1948 return NULL;
1949}
1950
1951/* If src matches dst return dst string, otherwise return NULL */
1952/* This version will return the dst string always if it is
1953 CMD_VARIABLE for '?' key processing */
1954static const char *cmd_entry_function_desc(const char *src, const char *dst)
1955{
1956 if (CMD_VARARG(dst))
1957 return dst;
1958
1959 if (CMD_RANGE(dst)) {
Philipp Maiereabc6fd2021-06-15 11:08:23 +02001960 if (vty_cmd_range_match(dst, src))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001961 return dst;
1962 else
1963 return NULL;
1964 }
1965#ifdef HAVE_IPV6
1966 if (CMD_IPV6(dst)) {
1967 if (cmd_ipv6_match(src))
1968 return dst;
1969 else
1970 return NULL;
1971 }
1972
1973 if (CMD_IPV6_PREFIX(dst)) {
1974 if (cmd_ipv6_prefix_match(src))
1975 return dst;
1976 else
1977 return NULL;
1978 }
1979#endif /* HAVE_IPV6 */
1980
1981 if (CMD_IPV4(dst)) {
1982 if (cmd_ipv4_match(src))
1983 return dst;
1984 else
1985 return NULL;
1986 }
1987
1988 if (CMD_IPV4_PREFIX(dst)) {
1989 if (cmd_ipv4_prefix_match(src))
1990 return dst;
1991 else
1992 return NULL;
1993 }
1994
1995 /* Optional or variable commands always match on '?' */
1996 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1997 return dst;
1998
1999 /* In case of 'command \t', given src is NULL string. */
2000 if (src == NULL)
2001 return dst;
2002
2003 if (strncmp(src, dst, strlen(src)) == 0)
2004 return dst;
2005 else
2006 return NULL;
2007}
2008
2009/* Check same string element existence. If it isn't there return
2010 1. */
2011static int cmd_unique_string(vector v, const char *str)
2012{
2013 unsigned int i;
2014 char *match;
2015
2016 for (i = 0; i < vector_active(v); i++)
2017 if ((match = vector_slot(v, i)) != NULL)
2018 if (strcmp(match, str) == 0)
2019 return 0;
2020 return 1;
2021}
2022
2023/* Compare string to description vector. If there is same string
2024 return 1 else return 0. */
2025static int desc_unique_string(vector v, const char *str)
2026{
2027 unsigned int i;
2028 struct desc *desc;
2029
2030 for (i = 0; i < vector_active(v); i++)
2031 if ((desc = vector_slot(v, i)) != NULL)
2032 if (strcmp(desc->cmd, str) == 0)
2033 return 1;
2034 return 0;
2035}
2036
2037static int cmd_try_do_shortcut(enum node_type node, char *first_word)
2038{
2039 if (first_word != NULL &&
2040 node != AUTH_NODE &&
2041 node != VIEW_NODE &&
2042 node != AUTH_ENABLE_NODE &&
2043 node != ENABLE_NODE && 0 == strcmp("do", first_word))
2044 return 1;
2045 return 0;
2046}
2047
2048/* '?' describe command support. */
2049static vector
2050cmd_describe_command_real(vector vline, struct vty *vty, int *status)
2051{
2052 unsigned int i;
2053 vector cmd_vector;
2054#define INIT_MATCHVEC_SIZE 10
2055 vector matchvec;
2056 struct cmd_element *cmd_element;
2057 unsigned int index;
2058 int ret;
2059 enum match_type match;
2060 char *command;
2061 static struct desc desc_cr = { "<cr>", "" };
2062
2063 /* Set index. */
2064 if (vector_active(vline) == 0) {
2065 *status = CMD_ERR_NO_MATCH;
2066 return NULL;
2067 } else
2068 index = vector_active(vline) - 1;
2069
2070 /* Make copy vector of current node's command vector. */
2071 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2072
2073 /* Prepare match vector */
2074 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2075
2076 /* Filter commands. */
2077 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002078 for (i = 0; i < index; i++) {
2079 command = vector_slot(vline, i);
2080 if (!command)
2081 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002082
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002083 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002084
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002085 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002086 struct cmd_element *cmd_element;
2087 vector descvec;
2088 unsigned int j, k;
2089
2090 for (j = 0; j < vector_active(cmd_vector); j++)
2091 if ((cmd_element =
2092 vector_slot(cmd_vector, j)) != NULL
2093 &&
2094 (vector_active(cmd_element->strvec))) {
2095 descvec =
2096 vector_slot(cmd_element->
2097 strvec,
2098 vector_active
2099 (cmd_element->
2100 strvec) - 1);
2101 for (k = 0;
2102 k < vector_active(descvec);
2103 k++) {
2104 struct desc *desc =
2105 vector_slot(descvec,
2106 k);
2107 vector_set(matchvec,
2108 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002109 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002110 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002111
Harald Welte80d30fe2013-02-12 11:08:57 +01002112 vector_set(matchvec, &desc_cr);
2113 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002114
Harald Welte80d30fe2013-02-12 11:08:57 +01002115 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002116 }
2117
Harald Welte80d30fe2013-02-12 11:08:57 +01002118 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2119 match)) == 1) {
2120 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002121 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002122 *status = CMD_ERR_AMBIGUOUS;
2123 return NULL;
2124 } else if (ret == 2) {
2125 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002126 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002127 *status = CMD_ERR_NO_MATCH;
2128 return NULL;
2129 }
2130 }
2131
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002132 /* Prepare match vector */
2133 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2134
2135 /* Make sure that cmd_vector is filtered based on current word */
2136 command = vector_slot(vline, index);
2137 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002138 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002139
2140 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002141 for (i = 0; i < vector_active(cmd_vector); i++) {
2142 const char *string = NULL;
2143 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002144
Harald Welte80d30fe2013-02-12 11:08:57 +01002145 cmd_element = vector_slot(cmd_vector, i);
2146 if (!cmd_element)
2147 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002148
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002149 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2150 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002151 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002152 continue;
2153
Harald Welte80d30fe2013-02-12 11:08:57 +01002154 strvec = cmd_element->strvec;
2155
2156 /* if command is NULL, index may be equal to vector_active */
2157 if (command && index >= vector_active(strvec))
2158 vector_slot(cmd_vector, i) = NULL;
2159 else {
2160 /* Check if command is completed. */
2161 if (command == NULL
2162 && index == vector_active(strvec)) {
2163 string = "<cr>";
2164 if (!desc_unique_string(matchvec, string))
2165 vector_set(matchvec, &desc_cr);
2166 } else {
2167 unsigned int j;
2168 vector descvec = vector_slot(strvec, index);
2169 struct desc *desc;
2170
2171 for (j = 0; j < vector_active(descvec); j++) {
2172 desc = vector_slot(descvec, j);
2173 if (!desc)
2174 continue;
2175 string = cmd_entry_function_desc
2176 (command, desc->cmd);
2177 if (!string)
2178 continue;
2179 /* Uniqueness check */
2180 if (!desc_unique_string(matchvec, string))
2181 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002182 }
2183 }
2184 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002185 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002186 vector_free(cmd_vector);
2187
2188 if (vector_slot(matchvec, 0) == NULL) {
2189 vector_free(matchvec);
2190 *status = CMD_ERR_NO_MATCH;
2191 } else
2192 *status = CMD_SUCCESS;
2193
2194 return matchvec;
2195}
2196
2197vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2198{
2199 vector ret;
2200
2201 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2202 enum node_type onode;
2203 vector shifted_vline;
2204 unsigned int index;
2205
2206 onode = vty->node;
2207 vty->node = ENABLE_NODE;
2208 /* We can try it on enable node, cos' the vty is authenticated */
2209
2210 shifted_vline = vector_init(vector_count(vline));
2211 /* use memcpy? */
2212 for (index = 1; index < vector_active(vline); index++) {
2213 vector_set_index(shifted_vline, index - 1,
2214 vector_lookup(vline, index));
2215 }
2216
2217 ret = cmd_describe_command_real(shifted_vline, vty, status);
2218
2219 vector_free(shifted_vline);
2220 vty->node = onode;
2221 return ret;
2222 }
2223
2224 return cmd_describe_command_real(vline, vty, status);
2225}
2226
2227/* Check LCD of matched command. */
2228static int cmd_lcd(char **matched)
2229{
2230 int i;
2231 int j;
2232 int lcd = -1;
2233 char *s1, *s2;
2234 char c1, c2;
2235
2236 if (matched[0] == NULL || matched[1] == NULL)
2237 return 0;
2238
2239 for (i = 1; matched[i] != NULL; i++) {
2240 s1 = matched[i - 1];
2241 s2 = matched[i];
2242
2243 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2244 if (c1 != c2)
2245 break;
2246
2247 if (lcd < 0)
2248 lcd = j;
2249 else {
2250 if (lcd > j)
2251 lcd = j;
2252 }
2253 }
2254 return lcd;
2255}
2256
2257/* Command line completion support. */
2258static char **cmd_complete_command_real(vector vline, struct vty *vty,
2259 int *status)
2260{
2261 unsigned int i;
2262 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2263#define INIT_MATCHVEC_SIZE 10
2264 vector matchvec;
2265 struct cmd_element *cmd_element;
2266 unsigned int index;
2267 char **match_str;
2268 struct desc *desc;
2269 vector descvec;
2270 char *command;
2271 int lcd;
2272
2273 if (vector_active(vline) == 0) {
2274 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002275 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002276 return NULL;
2277 } else
2278 index = vector_active(vline) - 1;
2279
2280 /* First, filter by preceeding command string */
2281 for (i = 0; i < index; i++)
2282 if ((command = vector_slot(vline, i))) {
2283 enum match_type match;
2284 int ret;
2285
2286 /* First try completion match, if there is exactly match return 1 */
2287 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002288 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002289
2290 /* If there is exact match then filter ambiguous match else check
2291 ambiguousness. */
2292 if ((ret =
2293 is_cmd_ambiguous(command, cmd_vector, i,
2294 match)) == 1) {
2295 vector_free(cmd_vector);
2296 *status = CMD_ERR_AMBIGUOUS;
2297 return NULL;
2298 }
2299 /*
2300 else if (ret == 2)
2301 {
2302 vector_free (cmd_vector);
2303 *status = CMD_ERR_NO_MATCH;
2304 return NULL;
2305 }
2306 */
2307 }
2308
2309 /* Prepare match vector. */
2310 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2311
2312 /* Now we got into completion */
2313 for (i = 0; i < vector_active(cmd_vector); i++)
2314 if ((cmd_element = vector_slot(cmd_vector, i))) {
2315 const char *string;
2316 vector strvec = cmd_element->strvec;
2317
2318 /* Check field length */
2319 if (index >= vector_active(strvec))
2320 vector_slot(cmd_vector, i) = NULL;
2321 else {
2322 unsigned int j;
2323
2324 descvec = vector_slot(strvec, index);
2325 for (j = 0; j < vector_active(descvec); j++)
2326 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002327 const char *cmd = desc->cmd;
2328 char *tmp = NULL;
2329
2330 if (CMD_OPTION(desc->cmd)) {
2331 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2332 cmd = tmp;
2333 }
2334 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002335 if (cmd_unique_string (matchvec, string))
2336 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002337 if (tmp)
2338 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002339 }
2340 }
2341 }
2342
2343 /* We don't need cmd_vector any more. */
2344 vector_free(cmd_vector);
2345
2346 /* No matched command */
2347 if (vector_slot(matchvec, 0) == NULL) {
2348 vector_free(matchvec);
2349
2350 /* In case of 'command \t' pattern. Do you need '?' command at
2351 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002352 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002353 *status = CMD_ERR_NOTHING_TODO;
2354 else
2355 *status = CMD_ERR_NO_MATCH;
2356 return NULL;
2357 }
2358
2359 /* Only one matched */
2360 if (vector_slot(matchvec, 1) == NULL) {
2361 match_str = (char **)matchvec->index;
2362 vector_only_wrapper_free(matchvec);
2363 *status = CMD_COMPLETE_FULL_MATCH;
2364 return match_str;
2365 }
2366 /* Make it sure last element is NULL. */
2367 vector_set(matchvec, NULL);
2368
2369 /* Check LCD of matched strings. */
2370 if (vector_slot(vline, index) != NULL) {
2371 lcd = cmd_lcd((char **)matchvec->index);
2372
2373 if (lcd) {
2374 int len = strlen(vector_slot(vline, index));
2375
2376 if (len < lcd) {
2377 char *lcdstr;
2378
2379 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2380 "complete-lcdstr");
2381 memcpy(lcdstr, matchvec->index[0], lcd);
2382 lcdstr[lcd] = '\0';
2383
2384 /* match_str = (char **) &lcdstr; */
2385
2386 /* Free matchvec. */
2387 for (i = 0; i < vector_active(matchvec); i++) {
2388 if (vector_slot(matchvec, i))
2389 talloc_free(vector_slot(matchvec, i));
2390 }
2391 vector_free(matchvec);
2392
2393 /* Make new matchvec. */
2394 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2395 vector_set(matchvec, lcdstr);
2396 match_str = (char **)matchvec->index;
2397 vector_only_wrapper_free(matchvec);
2398
2399 *status = CMD_COMPLETE_MATCH;
2400 return match_str;
2401 }
2402 }
2403 }
2404
2405 match_str = (char **)matchvec->index;
2406 vector_only_wrapper_free(matchvec);
2407 *status = CMD_COMPLETE_LIST_MATCH;
2408 return match_str;
2409}
2410
2411char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2412{
2413 char **ret;
2414
2415 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2416 enum node_type onode;
2417 vector shifted_vline;
2418 unsigned int index;
2419
2420 onode = vty->node;
2421 vty->node = ENABLE_NODE;
2422 /* We can try it on enable node, cos' the vty is authenticated */
2423
2424 shifted_vline = vector_init(vector_count(vline));
2425 /* use memcpy? */
2426 for (index = 1; index < vector_active(vline); index++) {
2427 vector_set_index(shifted_vline, index - 1,
2428 vector_lookup(vline, index));
2429 }
2430
2431 ret = cmd_complete_command_real(shifted_vline, vty, status);
2432
2433 vector_free(shifted_vline);
2434 vty->node = onode;
2435 return ret;
2436 }
2437
2438 return cmd_complete_command_real(vline, vty, status);
2439}
2440
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002441static struct vty_parent_node *vty_parent(struct vty *vty)
2442{
2443 return llist_first_entry_or_null(&vty->parent_nodes,
2444 struct vty_parent_node,
2445 entry);
2446}
2447
2448static bool vty_pop_parent(struct vty *vty)
2449{
2450 struct vty_parent_node *parent = vty_parent(vty);
2451 if (!parent)
2452 return false;
2453 llist_del(&parent->entry);
2454 vty->node = parent->node;
2455 vty->priv = parent->priv;
Neels Hofmeyr67d84d22023-05-01 02:59:01 +02002456 vty->index = parent->index;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002457 if (vty->indent)
2458 talloc_free(vty->indent);
2459 vty->indent = parent->indent;
2460 talloc_free(parent);
2461 return true;
2462}
2463
2464static void vty_clear_parents(struct vty *vty)
2465{
2466 while (vty_pop_parent(vty));
2467}
2468
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002469/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002470/*
2471 * This function MUST eventually converge on a node when called repeatedly,
2472 * there must not be any cycles.
2473 * All 'config' nodes shall converge on CONFIG_NODE.
2474 * All other 'enable' nodes shall converge on ENABLE_NODE.
2475 * All 'view' only nodes shall converge on VIEW_NODE.
2476 * All other nodes shall converge on themselves or it must be ensured,
2477 * that the user's rights are not extended anyhow by calling this function.
2478 *
2479 * Note that these requirements also apply to all functions that are used
2480 * as go_parent_cb.
2481 * Note also that this function relies on the is_config_child callback to
2482 * recognize non-config nodes if go_parent_cb is not set.
2483 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002484int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002485{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002486 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002487 case AUTH_NODE:
2488 case VIEW_NODE:
2489 case ENABLE_NODE:
2490 case CONFIG_NODE:
2491 vty_clear_parents(vty);
2492 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002493
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002494 case AUTH_ENABLE_NODE:
2495 vty->node = VIEW_NODE;
2496 vty_clear_parents(vty);
2497 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002498
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002499 default:
2500 if (host.app_info->go_parent_cb)
2501 host.app_info->go_parent_cb(vty);
2502 vty_pop_parent(vty);
2503 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002504 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002505
2506 return vty->node;
2507}
2508
2509/* Execute command by argument vline vector. */
2510static int
2511cmd_execute_command_real(vector vline, struct vty *vty,
2512 struct cmd_element **cmd)
2513{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002514 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002515 unsigned int index;
2516 vector cmd_vector;
2517 struct cmd_element *cmd_element;
2518 struct cmd_element *matched_element;
2519 unsigned int matched_count, incomplete_count;
2520 int argc;
2521 const char *argv[CMD_ARGC_MAX];
2522 enum match_type match = 0;
2523 int varflag;
2524 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002525 int rc;
2526 /* Used for temporary storage of cmd_deopt() allocated arguments during
2527 argv[] generation */
2528 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002529
2530 /* Make copy of command elements. */
2531 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2532
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002533 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002534 if ((command = vector_slot(vline, index))) {
2535 int ret;
2536
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002537 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002538 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002539
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002540 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002541 break;
2542
2543 ret =
2544 is_cmd_ambiguous(command, cmd_vector, index, match);
2545
2546 if (ret == 1) {
2547 vector_free(cmd_vector);
2548 return CMD_ERR_AMBIGUOUS;
2549 } else if (ret == 2) {
2550 vector_free(cmd_vector);
2551 return CMD_ERR_NO_MATCH;
2552 }
2553 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002554 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002555
2556 /* Check matched count. */
2557 matched_element = NULL;
2558 matched_count = 0;
2559 incomplete_count = 0;
2560
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002561 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002562 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002563 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002564 || index >= cmd_element->cmdsize) {
2565 matched_element = cmd_element;
2566#if 0
2567 printf("DEBUG: %s\n", cmd_element->string);
2568#endif
2569 matched_count++;
2570 } else {
2571 incomplete_count++;
2572 }
2573 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002574 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002575
2576 /* Finish of using cmd_vector. */
2577 vector_free(cmd_vector);
2578
2579 /* To execute command, matched_count must be 1. */
2580 if (matched_count == 0) {
2581 if (incomplete_count)
2582 return CMD_ERR_INCOMPLETE;
2583 else
2584 return CMD_ERR_NO_MATCH;
2585 }
2586
2587 if (matched_count > 1)
2588 return CMD_ERR_AMBIGUOUS;
2589
2590 /* Argument treatment */
2591 varflag = 0;
2592 argc = 0;
2593
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002594 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2595
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002596 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002597 if (argc == CMD_ARGC_MAX) {
2598 rc = CMD_ERR_EXEED_ARGC_MAX;
2599 goto rc_free_deopt_ctx;
2600 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002601 if (varflag) {
2602 argv[argc++] = vector_slot(vline, i);
2603 continue;
2604 }
2605
2606 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002607 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002608
2609 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002610 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002611 struct desc *desc = vector_slot(descvec, 0);
2612
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002613 if (CMD_OPTION(desc->cmd)) {
2614 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2615 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2616 } else {
2617 tmp_cmd = desc->cmd;
2618 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002619
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002620 if (CMD_VARARG(tmp_cmd))
2621 varflag = 1;
2622 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002623 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002624 else if (CMD_OPTION(desc->cmd))
2625 argv[argc++] = tmp_cmd;
2626 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002627 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002628 /* multi choice argument. look up which choice
2629 the user meant (can only be one after
2630 filtering and checking for ambigous). For instance,
2631 if user typed "th" for "(two|three)" arg, we
2632 want to pass "three" in argv[]. */
2633 for (j = 0; j < vector_active(descvec); j++) {
2634 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002635 if (!desc)
2636 continue;
2637 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2638 continue;
2639 if (CMD_OPTION(desc->cmd)) {
2640 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2641 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2642 } else {
2643 tmp_cmd = desc->cmd;
2644 }
2645
2646 if(CMD_VARIABLE(tmp_cmd)) {
2647 argv[argc++] = vector_slot(vline, i);
2648 } else {
2649 argv[argc++] = tmp_cmd;
2650 }
2651 break;
2652 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002653 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002654 }
2655
2656 /* For vtysh execution. */
2657 if (cmd)
2658 *cmd = matched_element;
2659
2660 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002661 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002662 else {
2663 /* Execute matched command. */
2664 struct vty_parent_node this_node = {
2665 .node = vty->node,
2666 .priv = vty->priv,
Neels Hofmeyr67d84d22023-05-01 02:59:01 +02002667 .index = vty->index,
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002668 .indent = vty->indent,
2669 };
2670 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002671 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002672
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002673 /* If we have stepped down into a child node, push a parent frame.
2674 * The causality is such: we don't expect every single node entry implementation to push
2675 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2676 * a parent node. Hence if the node changed without the parent node changing, we must
2677 * have stepped into a child node. */
2678 if (vty->node != this_node.node && parent == vty_parent(vty)
2679 && vty->node > CONFIG_NODE) {
2680 /* Push the parent node. */
2681 parent = talloc_zero(vty, struct vty_parent_node);
2682 *parent = this_node;
2683 llist_add(&parent->entry, &vty->parent_nodes);
2684 }
2685 }
2686
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002687rc_free_deopt_ctx:
2688 /* Now after we called the command func, we can free temporary strings */
2689 talloc_free(cmd_deopt_ctx);
2690 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002691}
2692
2693int
2694cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2695 int vtysh)
2696{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002697 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002698 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002699
2700 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002701
2702 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2703 vector shifted_vline;
2704 unsigned int index;
2705
2706 vty->node = ENABLE_NODE;
2707 /* We can try it on enable node, cos' the vty is authenticated */
2708
2709 shifted_vline = vector_init(vector_count(vline));
2710 /* use memcpy? */
2711 for (index = 1; index < vector_active(vline); index++) {
2712 vector_set_index(shifted_vline, index - 1,
2713 vector_lookup(vline, index));
2714 }
2715
2716 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2717
2718 vector_free(shifted_vline);
2719 vty->node = onode;
2720 return ret;
2721 }
2722
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002723 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002724}
2725
2726/* Execute command by argument readline. */
2727int
2728cmd_execute_command_strict(vector vline, struct vty *vty,
2729 struct cmd_element **cmd)
2730{
2731 unsigned int i;
2732 unsigned int index;
2733 vector cmd_vector;
2734 struct cmd_element *cmd_element;
2735 struct cmd_element *matched_element;
2736 unsigned int matched_count, incomplete_count;
2737 int argc;
2738 const char *argv[CMD_ARGC_MAX];
2739 int varflag;
2740 enum match_type match = 0;
2741 char *command;
2742
2743 /* Make copy of command element */
2744 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2745
2746 for (index = 0; index < vector_active(vline); index++)
2747 if ((command = vector_slot(vline, index))) {
2748 int ret;
2749
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002750 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002751 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002752
2753 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002754 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002755 break;
2756
2757 ret =
2758 is_cmd_ambiguous(command, cmd_vector, index, match);
2759 if (ret == 1) {
2760 vector_free(cmd_vector);
2761 return CMD_ERR_AMBIGUOUS;
2762 }
2763 if (ret == 2) {
2764 vector_free(cmd_vector);
2765 return CMD_ERR_NO_MATCH;
2766 }
2767 }
2768
2769 /* Check matched count. */
2770 matched_element = NULL;
2771 matched_count = 0;
2772 incomplete_count = 0;
2773 for (i = 0; i < vector_active(cmd_vector); i++)
2774 if (vector_slot(cmd_vector, i) != NULL) {
2775 cmd_element = vector_slot(cmd_vector, i);
2776
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002777 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002778 || index >= cmd_element->cmdsize) {
2779 matched_element = cmd_element;
2780 matched_count++;
2781 } else
2782 incomplete_count++;
2783 }
2784
2785 /* Finish of using cmd_vector. */
2786 vector_free(cmd_vector);
2787
2788 /* To execute command, matched_count must be 1. */
2789 if (matched_count == 0) {
2790 if (incomplete_count)
2791 return CMD_ERR_INCOMPLETE;
2792 else
2793 return CMD_ERR_NO_MATCH;
2794 }
2795
2796 if (matched_count > 1)
2797 return CMD_ERR_AMBIGUOUS;
2798
2799 /* Argument treatment */
2800 varflag = 0;
2801 argc = 0;
2802
2803 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002804 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002805 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002806 if (varflag) {
2807 argv[argc++] = vector_slot(vline, i);
2808 continue;
2809 }
2810
2811 vector descvec = vector_slot(matched_element->strvec, i);
2812
2813 if (vector_active(descvec) == 1) {
2814 struct desc *desc = vector_slot(descvec, 0);
2815
2816 if (CMD_VARARG(desc->cmd))
2817 varflag = 1;
2818
2819 if (varflag || CMD_VARIABLE(desc->cmd)
2820 || CMD_OPTION(desc->cmd))
2821 argv[argc++] = vector_slot(vline, i);
2822 } else {
2823 argv[argc++] = vector_slot(vline, i);
2824 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002825 }
2826
2827 /* For vtysh execution. */
2828 if (cmd)
2829 *cmd = matched_element;
2830
2831 if (matched_element->daemon)
2832 return CMD_SUCCESS_DAEMON;
2833
2834 /* Now execute matched command */
2835 return (*matched_element->func) (matched_element, vty, argc, argv);
2836}
2837
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002838static inline size_t len(const char *str)
2839{
2840 return str? strlen(str) : 0;
2841}
2842
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002843/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2844 * is longer than b, a must start with exactly b, and vice versa.
2845 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2846 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002847static int indent_cmp(const char *a, const char *b)
2848{
2849 size_t al, bl;
2850 al = len(a);
2851 bl = len(b);
2852 if (al > bl) {
2853 if (bl && strncmp(a, b, bl) != 0)
2854 return EINVAL;
2855 return 1;
2856 }
2857 /* al <= bl */
2858 if (al && strncmp(a, b, al) != 0)
2859 return EINVAL;
2860 return (al < bl)? -1 : 0;
2861}
2862
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002863/* Configration make from file. */
2864int config_from_file(struct vty *vty, FILE * fp)
2865{
2866 int ret;
2867 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002868 char *indent;
2869 int cmp;
2870 struct vty_parent_node this_node;
2871 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002872
2873 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002874 indent = NULL;
2875 vline = NULL;
2876 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002877
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002878 if (ret != CMD_SUCCESS)
2879 goto return_invalid_indent;
2880
2881 /* In case of comment or empty line */
2882 if (vline == NULL) {
2883 if (indent) {
2884 talloc_free(indent);
2885 indent = NULL;
2886 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002887 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002888 }
2889
Neels Hofmeyr43063632017-09-19 23:54:01 +02002890 /* We have a nonempty line. */
2891 if (!vty->indent) {
2892 /* We have just entered a node and expecting the first child to come up; but we
2893 * may also skip right back to a parent or ancestor level. */
2894 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002895
Neels Hofmeyr43063632017-09-19 23:54:01 +02002896 /* If there is no parent, record any indentation we encounter. */
2897 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2898
2899 if (cmp == EINVAL)
2900 goto return_invalid_indent;
2901
2902 if (cmp <= 0) {
2903 /* We have gone right back to the parent level or higher, we are skipping
2904 * this child node level entirely. Pop the parent to go back to a node
2905 * that was actually there (to reinstate vty->indent) and re-use below
2906 * go-parent while-loop to find an accurate match of indent in the node
2907 * ancestry. */
2908 vty_go_parent(vty);
2909 } else {
2910 /* The indent is deeper than the just entered parent, record the new
2911 * indentation characters. */
2912 vty->indent = talloc_strdup(vty, indent);
2913 /* This *is* the new indentation. */
2914 cmp = 0;
2915 }
2916 } else {
2917 /* There is a known indentation for this node level, validate and detect node
2918 * exits. */
2919 cmp = indent_cmp(indent, vty->indent);
2920 if (cmp == EINVAL)
2921 goto return_invalid_indent;
2922 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002923
2924 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2925 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2926 while (cmp < 0) {
2927 vty_go_parent(vty);
2928 cmp = indent_cmp(indent, vty->indent);
2929 if (cmp == EINVAL)
2930 goto return_invalid_indent;
2931 }
2932
2933 /* More indent without having entered a child node level? Either the parent node's indent
2934 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2935 * or the indentation increased even though the vty command didn't enter a child. */
2936 if (cmp > 0)
2937 goto return_invalid_indent;
2938
2939 /* Remember the current node before the command possibly changes it. */
2940 this_node = (struct vty_parent_node){
2941 .node = vty->node,
2942 .priv = vty->priv,
Neels Hofmeyr67d84d22023-05-01 02:59:01 +02002943 .index = vty->index,
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002944 .indent = vty->indent,
2945 };
2946
2947 parent = vty_parent(vty);
2948 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002949 cmd_free_strvec(vline);
2950
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002951 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002952 if (indent) {
2953 talloc_free(indent);
2954 indent = NULL;
2955 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002956 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002957 }
2958
2959 /* If we have stepped down into a child node, push a parent frame.
2960 * The causality is such: we don't expect every single node entry implementation to push
2961 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2962 * a parent node. Hence if the node changed without the parent node changing, we must
2963 * have stepped into a child node (and now expect a deeper indent). */
2964 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2965 /* Push the parent node. */
2966 parent = talloc_zero(vty, struct vty_parent_node);
2967 *parent = this_node;
2968 llist_add(&parent->entry, &vty->parent_nodes);
2969
2970 /* The current talloc'ed vty->indent string will now be owned by this parent
2971 * struct. Indicate that we don't know what deeper indent characters the user
2972 * will choose. */
2973 vty->indent = NULL;
2974 }
2975
2976 if (indent) {
2977 talloc_free(indent);
2978 indent = NULL;
2979 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002980 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002981 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2982 while (vty_parent(vty))
2983 vty_go_parent(vty);
2984
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002985 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002986
2987return_invalid_indent:
2988 if (vline)
2989 cmd_free_strvec(vline);
2990 if (indent) {
2991 talloc_free(indent);
2992 indent = NULL;
2993 }
2994 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002995}
2996
2997/* Configration from terminal */
2998DEFUN(config_terminal,
2999 config_terminal_cmd,
Andreas Eversberg6e7739f2023-06-17 16:25:02 +02003000 "configure [terminal]",
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003001 "Configuration from vty interface\n" "Configuration terminal\n")
3002{
3003 if (vty_config_lock(vty))
3004 vty->node = CONFIG_NODE;
3005 else {
3006 vty_out(vty, "VTY configuration is locked by other VTY%s",
3007 VTY_NEWLINE);
3008 return CMD_WARNING;
3009 }
3010 return CMD_SUCCESS;
3011}
3012
3013/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003014DEFUN(enable, config_enable_cmd,
3015 "enable [expert-mode]",
3016 "Turn on privileged mode command\n"
3017 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003018{
3019 /* If enable password is NULL, change to ENABLE_NODE */
3020 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
3021 vty->type == VTY_SHELL_SERV)
3022 vty->node = ENABLE_NODE;
3023 else
3024 vty->node = AUTH_ENABLE_NODE;
3025
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003026 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003027
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003028 return CMD_SUCCESS;
3029}
3030
3031/* Disable command */
3032DEFUN(disable,
3033 config_disable_cmd, "disable", "Turn off privileged mode command\n")
3034{
3035 if (vty->node == ENABLE_NODE)
3036 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003037
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003038 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003039
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003040 return CMD_SUCCESS;
3041}
3042
3043/* Down vty node level. */
3044gDEFUN(config_exit,
3045 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
3046{
3047 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02003048 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003049 case VIEW_NODE:
3050 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01003051 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003052 break;
3053 case CONFIG_NODE:
3054 vty->node = ENABLE_NODE;
3055 vty_config_unlock(vty);
3056 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003057 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003058 if (vty->node > CONFIG_NODE)
3059 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003060 break;
3061 }
3062 return CMD_SUCCESS;
3063}
3064
3065/* End of configuration. */
3066 gDEFUN(config_end,
3067 config_end_cmd, "end", "End current mode and change to enable mode.")
3068{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003069 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02003070 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003071
3072 /* Repeatedly call go_parent until a top node is reached. */
3073 while (vty->node > CONFIG_NODE) {
3074 if (vty->node == last_node) {
3075 /* Ensure termination, this shouldn't happen. */
3076 break;
3077 }
3078 last_node = vty->node;
3079 vty_go_parent(vty);
3080 }
3081
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003082 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003083 if (vty->node > ENABLE_NODE)
3084 vty->node = ENABLE_NODE;
3085 vty->index = NULL;
3086 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003087 }
3088 return CMD_SUCCESS;
3089}
3090
Oliver Smith8a482fd2021-07-12 18:18:51 +02003091DEFUN(shutdown,
3092 shutdown_cmd, "shutdown", "Request a shutdown of the program\n")
3093{
3094 LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n");
3095 vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE);
3096
3097 /* Same exit path as if it was killed by the service manager */
3098 kill(getpid(), SIGTERM);
3099
3100 return CMD_SUCCESS;
3101}
3102
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003103/* Show version. */
3104DEFUN(show_version,
3105 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3106{
Harald Welte237f6242010-05-25 23:00:45 +02003107 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3108 host.app_info->version,
3109 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3110 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003111
3112 return CMD_SUCCESS;
3113}
3114
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003115DEFUN(show_online_help,
3116 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3117{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003118 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003119 return CMD_SUCCESS;
3120}
3121
Oliver Smith0c78bc62021-07-12 17:28:36 +02003122DEFUN(show_pid,
3123 show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
3124{
3125 vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
3126 return CMD_SUCCESS;
3127}
3128
Oliver Smithd243c2a2021-07-09 17:19:32 +02003129DEFUN(show_uptime,
3130 show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
3131{
Alexander Couzens06929162021-09-05 23:08:59 +02003132 vty_out(vty, "%s has been running for ", host.app_info->name);
3133 vty_out_uptime(vty, &starttime);
3134 vty_out_newline(vty);
Oliver Smithd243c2a2021-07-09 17:19:32 +02003135
3136 return CMD_SUCCESS;
3137}
3138
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003139/* Help display function for all node. */
3140gDEFUN(config_help,
3141 config_help_cmd, "help", "Description of the interactive help system\n")
3142{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003143 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3144 "anytime at the command line please press '?'.%s%s"
3145 "If nothing matches, the help list will be empty and you must backup%s"
3146 " until entering a '?' shows the available options.%s"
3147 "Two styles of help are provided:%s"
3148 "1. Full help is available when you are ready to enter a%s"
3149 "command argument (e.g. 'show ?') and describes each possible%s"
3150 "argument.%s"
3151 "2. Partial help is provided when an abbreviated argument is entered%s"
3152 " and you want to know what arguments match the input%s"
3153 " (e.g. 'show me?'.)%s%s",
3154 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3155 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3156 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3157 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003158 return CMD_SUCCESS;
3159}
3160
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003161enum {
3162 ATTR_TYPE_GLOBAL = (1 << 0),
3163 ATTR_TYPE_LIB = (1 << 1),
3164 ATTR_TYPE_APP = (1 << 2),
3165};
3166
3167static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3168{
3169 const char *desc;
3170 unsigned int i;
3171 bool found;
3172 char flag;
3173
3174 if (attr_mask & ATTR_TYPE_GLOBAL) {
3175 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3176
3177 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003178 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003179 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003180
3181 /* Skip attributes without flags */
3182 if (flag != '.')
3183 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003184 }
3185 }
3186
3187 if (attr_mask & ATTR_TYPE_LIB) {
3188 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3189
3190 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3191 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3192 continue;
3193 found = true;
3194
3195 flag = cmd_lib_attr_letters[i];
3196 if (flag == '\0')
3197 flag = '.';
3198
3199 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3200 }
3201
3202 if (!found)
3203 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3204 }
3205
3206 if (attr_mask & ATTR_TYPE_APP) {
3207 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3208
3209 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3210 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3211 continue;
3212 found = true;
3213
3214 flag = host.app_info->usr_attr_letters[i];
3215 if (flag == '\0')
3216 flag = '.';
3217
3218 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3219 }
3220
3221 if (!found)
3222 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3223 }
3224}
3225
3226gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3227 "show vty-attributes",
3228 SHOW_STR "List of VTY attributes\n")
3229{
3230 print_attr_list(vty, 0xff);
3231 return CMD_SUCCESS;
3232}
3233
3234gDEFUN(show_vty_attr, show_vty_attr_cmd,
3235 "show vty-attributes (application|library|global)",
3236 SHOW_STR "List of VTY attributes\n"
3237 "Application specific attributes only\n"
3238 "Library specific attributes only\n"
3239 "Global attributes only\n")
3240{
3241 unsigned int attr_mask = 0;
3242
3243 if (argv[0][0] == 'g') /* global */
3244 attr_mask |= ATTR_TYPE_GLOBAL;
3245 else if (argv[0][0] == 'l') /* library */
3246 attr_mask |= ATTR_TYPE_LIB;
3247 else if (argv[0][0] == 'a') /* application */
3248 attr_mask |= ATTR_TYPE_APP;
3249
3250 print_attr_list(vty, attr_mask);
3251 return CMD_SUCCESS;
3252}
3253
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003254/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003255static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003256{
3257 unsigned int flag_mask = 0x00;
3258 unsigned int f, i;
3259
3260 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3261 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3262 const struct cmd_element *cmd;
3263 char flag_letter;
3264
3265 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3266 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003267 if (cmd->attr & CMD_ATTR_DEPRECATED)
3268 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003269 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003270 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003271 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003272 continue;
3273
3274 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3275 flag_letter = cmd_lib_attr_letters[f];
3276 else
3277 flag_letter = host.app_info->usr_attr_letters[f];
3278
3279 if (flag_letter == '\0')
3280 continue;
3281
3282 flag_mask |= (1 << f);
3283 break;
3284 }
3285 }
3286
3287 return flag_mask;
3288}
3289
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003290/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3291static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3292{
3293 static char char_mask[8 + 1];
3294 char *ptr = &char_mask[0];
3295
3296 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003297 if (cmd->attr & CMD_ATTR_HIDDEN)
3298 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3299 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003300 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3301 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3302 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3303 else
3304 *(ptr++) = '.';
3305
3306 *ptr = '\0';
3307
3308 return char_mask;
3309}
3310
3311/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003312static const char *cmd_flag_mask(const struct cmd_element *cmd,
3313 unsigned int flag_mask)
3314{
3315 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3316 char *ptr = &char_mask[0];
3317 char flag_letter;
3318 unsigned int f;
3319
3320 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003321 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003322 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003323 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003324 *(ptr++) = '.';
3325 continue;
3326 }
3327
3328 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3329 flag_letter = cmd_lib_attr_letters[f];
3330 else
3331 flag_letter = host.app_info->usr_attr_letters[f];
3332
3333 *(ptr++) = flag_letter ? flag_letter : '.';
3334 }
3335
3336 *ptr = '\0';
3337
3338 return char_mask;
3339}
3340
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003341/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003342gDEFUN(config_list, config_list_cmd,
3343 "list [with-flags]",
3344 "Print command list\n"
3345 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003346{
3347 unsigned int i;
3348 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003349 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003350 struct cmd_element *cmd;
3351
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003352 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003353 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003354
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003355 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3356 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3357 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003358 if (cmd->attr & CMD_ATTR_DEPRECATED)
3359 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003360 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003361 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003362 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003363 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3364 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003365 vty_out(vty, " %s %s %s%s",
3366 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003367 cmd_flag_mask(cmd, flag_mask),
3368 cmd->string, VTY_NEWLINE);
3369 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003370 }
3371
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003372 return CMD_SUCCESS;
3373}
3374
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003375static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003376{
3377 unsigned int i;
3378 int fd;
3379 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003380 char *config_file_tmp = NULL;
3381 char *config_file_sav = NULL;
3382 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003383 struct stat st;
3384
3385 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003386
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003387 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3388 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3389 * manually instead. */
3390
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003391 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003392 config_file_sav =
3393 _talloc_zero(tall_vty_cmd_ctx,
3394 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3395 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003396 if (!config_file_sav)
3397 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003398 strcpy(config_file_sav, config_file);
3399 strcat(config_file_sav, CONF_BACKUP_EXT);
3400
3401 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003402 "config_file_tmp");
3403 if (!config_file_tmp) {
3404 talloc_free(config_file_sav);
3405 return -1;
3406 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003407 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3408
3409 /* Open file to configuration write. */
3410 fd = mkstemp(config_file_tmp);
3411 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003412 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003413 talloc_free(config_file_tmp);
3414 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003415 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003416 }
3417
3418 /* Make vty for configuration file. */
3419 file_vty = vty_new();
3420 file_vty->fd = fd;
3421 file_vty->type = VTY_FILE;
3422
3423 /* Config file header print. */
3424 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003425 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003426 //vty_time_print (file_vty, 1);
3427 vty_out(file_vty, "!\n");
3428
3429 for (i = 0; i < vector_active(cmdvec); i++)
3430 if ((node = vector_slot(cmdvec, i)) && node->func) {
3431 if ((*node->func) (file_vty))
3432 vty_out(file_vty, "!\n");
3433 }
3434 vty_close(file_vty);
3435
3436 if (unlink(config_file_sav) != 0)
3437 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003438 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003439 talloc_free(config_file_sav);
3440 talloc_free(config_file_tmp);
3441 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003442 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003443 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003444
3445 /* Only link the .sav file if the original file exists */
3446 if (stat(config_file, &st) == 0) {
3447 if (link(config_file, config_file_sav) != 0) {
3448 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3449 talloc_free(config_file_sav);
3450 talloc_free(config_file_tmp);
3451 unlink(config_file_tmp);
3452 return -3;
3453 }
3454 sync();
3455 if (unlink(config_file) != 0) {
3456 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3457 talloc_free(config_file_sav);
3458 talloc_free(config_file_tmp);
3459 unlink(config_file_tmp);
3460 return -4;
3461 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003462 }
3463 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003464 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003465 talloc_free(config_file_sav);
3466 talloc_free(config_file_tmp);
3467 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003468 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003469 }
3470 unlink(config_file_tmp);
3471 sync();
3472
3473 talloc_free(config_file_sav);
3474 talloc_free(config_file_tmp);
3475
3476 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003477 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3478 return -6;
3479 }
3480
3481 return 0;
3482}
3483
3484
3485/* Write current configuration into file. */
3486DEFUN(config_write_file,
3487 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003488 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003489 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003490 "Write to configuration file\n"
3491 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003492{
3493 char *failed_file;
3494 int rc;
3495
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003496 if (host.app_info->config_is_consistent) {
3497 rc = host.app_info->config_is_consistent(vty);
3498 if (!rc) {
3499 vty_out(vty, "Configuration is not consistent%s",
3500 VTY_NEWLINE);
3501 return CMD_WARNING;
3502 }
3503 }
3504
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003505 if (argc == 1)
3506 host_config_set(argv[0]);
3507
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003508 if (host.config == NULL) {
3509 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3510 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003511 return CMD_WARNING;
3512 }
3513
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003514 rc = write_config_file(host.config, &failed_file);
3515 switch (rc) {
3516 case -1:
3517 vty_out(vty, "Can't open configuration file %s.%s",
3518 failed_file, VTY_NEWLINE);
3519 rc = CMD_WARNING;
3520 break;
3521 case -2:
3522 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3523 failed_file, VTY_NEWLINE);
3524 rc = CMD_WARNING;
3525 break;
3526 case -3:
3527 vty_out(vty, "Can't backup old configuration file %s.%s",
3528 failed_file, VTY_NEWLINE);
3529 rc = CMD_WARNING;
3530 break;
3531 case -4:
3532 vty_out(vty, "Can't unlink configuration file %s.%s",
3533 failed_file, VTY_NEWLINE);
3534 rc = CMD_WARNING;
3535 break;
3536 case -5:
3537 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3538 VTY_NEWLINE);
3539 rc = CMD_WARNING;
3540 break;
3541 case -6:
3542 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3543 failed_file, strerror(errno), errno, VTY_NEWLINE);
3544 rc = CMD_WARNING;
3545 break;
3546 default:
3547 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3548 rc = CMD_SUCCESS;
3549 break;
3550 }
3551
3552 talloc_free(failed_file);
3553 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003554}
3555
3556ALIAS(config_write_file,
3557 config_write_cmd,
3558 "write", "Write running configuration to memory, network, or terminal\n")
3559
3560 ALIAS(config_write_file,
3561 config_write_memory_cmd,
3562 "write memory",
3563 "Write running configuration to memory, network, or terminal\n"
3564 "Write configuration to the file (same as write file)\n")
3565
3566 ALIAS(config_write_file,
3567 copy_runningconfig_startupconfig_cmd,
3568 "copy running-config startup-config",
3569 "Copy configuration\n"
3570 "Copy running config to... \n"
3571 "Copy running config to startup config (same as write file)\n")
3572
3573/* Write current configuration into the terminal. */
3574 DEFUN(config_write_terminal,
3575 config_write_terminal_cmd,
3576 "write terminal",
3577 "Write running configuration to memory, network, or terminal\n"
3578 "Write to terminal\n")
3579{
3580 unsigned int i;
3581 struct cmd_node *node;
3582
3583 if (vty->type == VTY_SHELL_SERV) {
3584 for (i = 0; i < vector_active(cmdvec); i++)
3585 if ((node = vector_slot(cmdvec, i)) && node->func
3586 && node->vtysh) {
3587 if ((*node->func) (vty))
3588 vty_out(vty, "!%s", VTY_NEWLINE);
3589 }
3590 } else {
3591 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3592 VTY_NEWLINE);
3593 vty_out(vty, "!%s", VTY_NEWLINE);
3594
3595 for (i = 0; i < vector_active(cmdvec); i++)
3596 if ((node = vector_slot(cmdvec, i)) && node->func) {
3597 if ((*node->func) (vty))
3598 vty_out(vty, "!%s", VTY_NEWLINE);
3599 }
3600 vty_out(vty, "end%s", VTY_NEWLINE);
3601 }
3602 return CMD_SUCCESS;
3603}
3604
3605/* Write current configuration into the terminal. */
3606ALIAS(config_write_terminal,
3607 show_running_config_cmd,
3608 "show running-config", SHOW_STR "running configuration\n")
3609
3610/* Write startup configuration into the terminal. */
3611 DEFUN(show_startup_config,
3612 show_startup_config_cmd,
3613 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3614{
3615 char buf[BUFSIZ];
3616 FILE *confp;
3617
3618 confp = fopen(host.config, "r");
3619 if (confp == NULL) {
3620 vty_out(vty, "Can't open configuration file [%s]%s",
3621 host.config, VTY_NEWLINE);
3622 return CMD_WARNING;
3623 }
3624
3625 while (fgets(buf, BUFSIZ, confp)) {
3626 char *cp = buf;
3627
3628 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3629 cp++;
3630 *cp = '\0';
3631
3632 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3633 }
3634
3635 fclose(confp);
3636
3637 return CMD_SUCCESS;
3638}
3639
3640/* Hostname configuration */
3641DEFUN(config_hostname,
3642 hostname_cmd,
3643 "hostname WORD",
3644 "Set system's network name\n" "This system's network name\n")
3645{
3646 if (!isalpha((int)*argv[0])) {
3647 vty_out(vty, "Please specify string starting with alphabet%s",
3648 VTY_NEWLINE);
3649 return CMD_WARNING;
3650 }
3651
3652 if (host.name)
3653 talloc_free(host.name);
3654
3655 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3656 return CMD_SUCCESS;
3657}
3658
3659DEFUN(config_no_hostname,
3660 no_hostname_cmd,
3661 "no hostname [HOSTNAME]",
3662 NO_STR "Reset system's network name\n" "Host name of this router\n")
3663{
3664 if (host.name)
3665 talloc_free(host.name);
3666 host.name = NULL;
3667 return CMD_SUCCESS;
3668}
3669
3670/* VTY interface password set. */
3671DEFUN(config_password, password_cmd,
3672 "password (8|) WORD",
3673 "Assign the terminal connection password\n"
3674 "Specifies a HIDDEN password will follow\n"
3675 "dummy string \n" "The HIDDEN line password string\n")
3676{
3677 /* Argument check. */
3678 if (argc == 0) {
3679 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3680 return CMD_WARNING;
3681 }
3682
3683 if (argc == 2) {
3684 if (*argv[0] == '8') {
3685 if (host.password)
3686 talloc_free(host.password);
3687 host.password = NULL;
3688 if (host.password_encrypt)
3689 talloc_free(host.password_encrypt);
3690 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3691 return CMD_SUCCESS;
3692 } else {
3693 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3694 return CMD_WARNING;
3695 }
3696 }
3697
3698 if (!isalnum((int)*argv[0])) {
3699 vty_out(vty,
3700 "Please specify string starting with alphanumeric%s",
3701 VTY_NEWLINE);
3702 return CMD_WARNING;
3703 }
3704
3705 if (host.password)
3706 talloc_free(host.password);
3707 host.password = NULL;
3708
3709#ifdef VTY_CRYPT_PW
3710 if (host.encrypt) {
3711 if (host.password_encrypt)
3712 talloc_free(host.password_encrypt);
3713 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3714 } else
3715#endif
3716 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3717
3718 return CMD_SUCCESS;
3719}
3720
3721ALIAS(config_password, password_text_cmd,
3722 "password LINE",
3723 "Assign the terminal connection password\n"
3724 "The UNENCRYPTED (cleartext) line password\n")
3725
3726/* VTY enable password set. */
3727 DEFUN(config_enable_password, enable_password_cmd,
3728 "enable password (8|) WORD",
3729 "Modify enable password parameters\n"
3730 "Assign the privileged level password\n"
3731 "Specifies a HIDDEN password will follow\n"
3732 "dummy string \n" "The HIDDEN 'enable' password string\n")
3733{
3734 /* Argument check. */
3735 if (argc == 0) {
3736 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3737 return CMD_WARNING;
3738 }
3739
3740 /* Crypt type is specified. */
3741 if (argc == 2) {
3742 if (*argv[0] == '8') {
3743 if (host.enable)
3744 talloc_free(host.enable);
3745 host.enable = NULL;
3746
3747 if (host.enable_encrypt)
3748 talloc_free(host.enable_encrypt);
3749 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3750
3751 return CMD_SUCCESS;
3752 } else {
3753 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3754 return CMD_WARNING;
3755 }
3756 }
3757
3758 if (!isalnum((int)*argv[0])) {
3759 vty_out(vty,
3760 "Please specify string starting with alphanumeric%s",
3761 VTY_NEWLINE);
3762 return CMD_WARNING;
3763 }
3764
3765 if (host.enable)
3766 talloc_free(host.enable);
3767 host.enable = NULL;
3768
3769 /* Plain password input. */
3770#ifdef VTY_CRYPT_PW
3771 if (host.encrypt) {
3772 if (host.enable_encrypt)
3773 talloc_free(host.enable_encrypt);
3774 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3775 } else
3776#endif
3777 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3778
3779 return CMD_SUCCESS;
3780}
3781
3782ALIAS(config_enable_password,
3783 enable_password_text_cmd,
3784 "enable password LINE",
3785 "Modify enable password parameters\n"
3786 "Assign the privileged level password\n"
3787 "The UNENCRYPTED (cleartext) 'enable' password\n")
3788
3789/* VTY enable password delete. */
3790 DEFUN(no_config_enable_password, no_enable_password_cmd,
3791 "no enable password",
3792 NO_STR
3793 "Modify enable password parameters\n"
3794 "Assign the privileged level password\n")
3795{
3796 if (host.enable)
3797 talloc_free(host.enable);
3798 host.enable = NULL;
3799
3800 if (host.enable_encrypt)
3801 talloc_free(host.enable_encrypt);
3802 host.enable_encrypt = NULL;
3803
3804 return CMD_SUCCESS;
3805}
3806
3807#ifdef VTY_CRYPT_PW
3808DEFUN(service_password_encrypt,
3809 service_password_encrypt_cmd,
3810 "service password-encryption",
3811 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3812{
3813 if (host.encrypt)
3814 return CMD_SUCCESS;
3815
3816 host.encrypt = 1;
3817
3818 if (host.password) {
3819 if (host.password_encrypt)
3820 talloc_free(host.password_encrypt);
3821 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3822 }
3823 if (host.enable) {
3824 if (host.enable_encrypt)
3825 talloc_free(host.enable_encrypt);
3826 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3827 }
3828
3829 return CMD_SUCCESS;
3830}
3831
3832DEFUN(no_service_password_encrypt,
3833 no_service_password_encrypt_cmd,
3834 "no service password-encryption",
3835 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3836{
3837 if (!host.encrypt)
3838 return CMD_SUCCESS;
3839
3840 host.encrypt = 0;
3841
3842 if (host.password_encrypt)
3843 talloc_free(host.password_encrypt);
3844 host.password_encrypt = NULL;
3845
3846 if (host.enable_encrypt)
3847 talloc_free(host.enable_encrypt);
3848 host.enable_encrypt = NULL;
3849
3850 return CMD_SUCCESS;
3851}
3852#endif
3853
3854DEFUN(config_terminal_length, config_terminal_length_cmd,
3855 "terminal length <0-512>",
3856 "Set terminal line parameters\n"
3857 "Set number of lines on a screen\n"
3858 "Number of lines on screen (0 for no pausing)\n")
3859{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003860 vty->lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003861 return CMD_SUCCESS;
3862}
3863
3864DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3865 "terminal no length",
3866 "Set terminal line parameters\n"
3867 NO_STR "Set number of lines on a screen\n")
3868{
3869 vty->lines = -1;
3870 return CMD_SUCCESS;
3871}
3872
3873DEFUN(service_terminal_length, service_terminal_length_cmd,
3874 "service terminal-length <0-512>",
3875 "Set up miscellaneous service\n"
3876 "System wide terminal length configuration\n"
3877 "Number of lines of VTY (0 means no line control)\n")
3878{
Neels Hofmeyr34907fe2021-09-05 19:50:34 +02003879 host.lines = atoi(argv[0]);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003880 return CMD_SUCCESS;
3881}
3882
3883DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3884 "no service terminal-length [<0-512>]",
3885 NO_STR
3886 "Set up miscellaneous service\n"
3887 "System wide terminal length configuration\n"
3888 "Number of lines of VTY (0 means no line control)\n")
3889{
3890 host.lines = -1;
3891 return CMD_SUCCESS;
3892}
3893
3894DEFUN_HIDDEN(do_echo,
3895 echo_cmd,
3896 "echo .MESSAGE",
3897 "Echo a message back to the vty\n" "The message to echo\n")
3898{
3899 char *message;
3900
3901 vty_out(vty, "%s%s",
3902 ((message =
3903 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3904 if (message)
3905 talloc_free(message);
3906 return CMD_SUCCESS;
3907}
3908
3909#if 0
3910DEFUN(config_logmsg,
3911 config_logmsg_cmd,
3912 "logmsg " LOG_LEVELS " .MESSAGE",
3913 "Send a message to enabled logging destinations\n"
3914 LOG_LEVEL_DESC "The message to send\n")
3915{
3916 int level;
3917 char *message;
3918
3919 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3920 return CMD_ERR_NO_MATCH;
3921
3922 zlog(NULL, level,
3923 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3924 if (message)
3925 talloc_free(message);
3926 return CMD_SUCCESS;
3927}
3928
3929DEFUN(show_logging,
3930 show_logging_cmd,
3931 "show logging", SHOW_STR "Show current logging configuration\n")
3932{
3933 struct zlog *zl = zlog_default;
3934
3935 vty_out(vty, "Syslog logging: ");
3936 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3937 vty_out(vty, "disabled");
3938 else
3939 vty_out(vty, "level %s, facility %s, ident %s",
3940 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3941 facility_name(zl->facility), zl->ident);
3942 vty_out(vty, "%s", VTY_NEWLINE);
3943
3944 vty_out(vty, "Stdout logging: ");
3945 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3946 vty_out(vty, "disabled");
3947 else
3948 vty_out(vty, "level %s",
3949 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3950 vty_out(vty, "%s", VTY_NEWLINE);
3951
3952 vty_out(vty, "Monitor logging: ");
3953 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3954 vty_out(vty, "disabled");
3955 else
3956 vty_out(vty, "level %s",
3957 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3958 vty_out(vty, "%s", VTY_NEWLINE);
3959
3960 vty_out(vty, "File logging: ");
3961 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3962 vty_out(vty, "disabled");
3963 else
3964 vty_out(vty, "level %s, filename %s",
3965 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3966 zl->filename);
3967 vty_out(vty, "%s", VTY_NEWLINE);
3968
3969 vty_out(vty, "Protocol name: %s%s",
3970 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3971 vty_out(vty, "Record priority: %s%s",
3972 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3973
3974 return CMD_SUCCESS;
3975}
3976
3977DEFUN(config_log_stdout,
3978 config_log_stdout_cmd,
3979 "log stdout", "Logging control\n" "Set stdout logging level\n")
3980{
3981 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3982 return CMD_SUCCESS;
3983}
3984
3985DEFUN(config_log_stdout_level,
3986 config_log_stdout_level_cmd,
3987 "log stdout " LOG_LEVELS,
3988 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3989{
3990 int level;
3991
3992 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3993 return CMD_ERR_NO_MATCH;
3994 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3995 return CMD_SUCCESS;
3996}
3997
3998DEFUN(no_config_log_stdout,
3999 no_config_log_stdout_cmd,
4000 "no log stdout [LEVEL]",
4001 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
4002{
4003 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
4004 return CMD_SUCCESS;
4005}
4006
4007DEFUN(config_log_monitor,
4008 config_log_monitor_cmd,
4009 "log monitor",
4010 "Logging control\n" "Set terminal line (monitor) logging level\n")
4011{
4012 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
4013 return CMD_SUCCESS;
4014}
4015
4016DEFUN(config_log_monitor_level,
4017 config_log_monitor_level_cmd,
4018 "log monitor " LOG_LEVELS,
4019 "Logging control\n"
4020 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
4021{
4022 int level;
4023
4024 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4025 return CMD_ERR_NO_MATCH;
4026 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
4027 return CMD_SUCCESS;
4028}
4029
4030DEFUN(no_config_log_monitor,
4031 no_config_log_monitor_cmd,
4032 "no log monitor [LEVEL]",
4033 NO_STR
4034 "Logging control\n"
4035 "Disable terminal line (monitor) logging\n" "Logging level\n")
4036{
4037 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
4038 return CMD_SUCCESS;
4039}
4040
4041static int set_log_file(struct vty *vty, const char *fname, int loglevel)
4042{
4043 int ret;
4044 char *p = NULL;
4045 const char *fullpath;
4046
4047 /* Path detection. */
4048 if (!IS_DIRECTORY_SEP(*fname)) {
4049 char cwd[MAXPATHLEN + 1];
4050 cwd[MAXPATHLEN] = '\0';
4051
4052 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4053 zlog_err("config_log_file: Unable to alloc mem!");
4054 return CMD_WARNING;
4055 }
4056
4057 if ((p = _talloc_zero(tall_vcmd_ctx,
4058 strlen(cwd) + strlen(fname) + 2),
4059 "set_log_file")
4060 == NULL) {
4061 zlog_err("config_log_file: Unable to alloc mem!");
4062 return CMD_WARNING;
4063 }
4064 sprintf(p, "%s/%s", cwd, fname);
4065 fullpath = p;
4066 } else
4067 fullpath = fname;
4068
4069 ret = zlog_set_file(NULL, fullpath, loglevel);
4070
4071 if (p)
4072 talloc_free(p);
4073
4074 if (!ret) {
4075 vty_out(vty, "can't open logfile %s\n", fname);
4076 return CMD_WARNING;
4077 }
4078
4079 if (host.logfile)
4080 talloc_free(host.logfile);
4081
4082 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
4083
4084 return CMD_SUCCESS;
4085}
4086
4087DEFUN(config_log_file,
4088 config_log_file_cmd,
4089 "log file FILENAME",
4090 "Logging control\n" "Logging to file\n" "Logging filename\n")
4091{
4092 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4093}
4094
4095DEFUN(config_log_file_level,
4096 config_log_file_level_cmd,
4097 "log file FILENAME " LOG_LEVELS,
4098 "Logging control\n"
4099 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4100{
4101 int level;
4102
4103 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4104 return CMD_ERR_NO_MATCH;
4105 return set_log_file(vty, argv[0], level);
4106}
4107
4108DEFUN(no_config_log_file,
4109 no_config_log_file_cmd,
4110 "no log file [FILENAME]",
4111 NO_STR
4112 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4113{
4114 zlog_reset_file(NULL);
4115
4116 if (host.logfile)
4117 talloc_free(host.logfile);
4118
4119 host.logfile = NULL;
4120
4121 return CMD_SUCCESS;
4122}
4123
4124ALIAS(no_config_log_file,
4125 no_config_log_file_level_cmd,
4126 "no log file FILENAME LEVEL",
4127 NO_STR
4128 "Logging control\n"
4129 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4130
4131 DEFUN(config_log_syslog,
4132 config_log_syslog_cmd,
4133 "log syslog", "Logging control\n" "Set syslog logging level\n")
4134{
4135 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4136 return CMD_SUCCESS;
4137}
4138
4139DEFUN(config_log_syslog_level,
4140 config_log_syslog_level_cmd,
4141 "log syslog " LOG_LEVELS,
4142 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4143{
4144 int level;
4145
4146 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4147 return CMD_ERR_NO_MATCH;
4148 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4149 return CMD_SUCCESS;
4150}
4151
4152DEFUN_DEPRECATED(config_log_syslog_facility,
4153 config_log_syslog_facility_cmd,
4154 "log syslog facility " LOG_FACILITIES,
4155 "Logging control\n"
4156 "Logging goes to syslog\n"
4157 "(Deprecated) Facility parameter for syslog messages\n"
4158 LOG_FACILITY_DESC)
4159{
4160 int facility;
4161
4162 if ((facility = facility_match(argv[0])) < 0)
4163 return CMD_ERR_NO_MATCH;
4164
4165 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4166 zlog_default->facility = facility;
4167 return CMD_SUCCESS;
4168}
4169
4170DEFUN(no_config_log_syslog,
4171 no_config_log_syslog_cmd,
4172 "no log syslog [LEVEL]",
4173 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4174{
4175 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4176 return CMD_SUCCESS;
4177}
4178
4179ALIAS(no_config_log_syslog,
4180 no_config_log_syslog_facility_cmd,
4181 "no log syslog facility " LOG_FACILITIES,
4182 NO_STR
4183 "Logging control\n"
4184 "Logging goes to syslog\n"
4185 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4186
4187 DEFUN(config_log_facility,
4188 config_log_facility_cmd,
4189 "log facility " LOG_FACILITIES,
4190 "Logging control\n"
4191 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4192{
4193 int facility;
4194
4195 if ((facility = facility_match(argv[0])) < 0)
4196 return CMD_ERR_NO_MATCH;
4197 zlog_default->facility = facility;
4198 return CMD_SUCCESS;
4199}
4200
4201DEFUN(no_config_log_facility,
4202 no_config_log_facility_cmd,
4203 "no log facility [FACILITY]",
4204 NO_STR
4205 "Logging control\n"
4206 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4207{
4208 zlog_default->facility = LOG_DAEMON;
4209 return CMD_SUCCESS;
4210}
4211
4212DEFUN_DEPRECATED(config_log_trap,
4213 config_log_trap_cmd,
4214 "log trap " LOG_LEVELS,
4215 "Logging control\n"
4216 "(Deprecated) Set logging level and default for all destinations\n"
4217 LOG_LEVEL_DESC)
4218{
4219 int new_level;
4220 int i;
4221
4222 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4223 return CMD_ERR_NO_MATCH;
4224
4225 zlog_default->default_lvl = new_level;
4226 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4227 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4228 zlog_default->maxlvl[i] = new_level;
4229 return CMD_SUCCESS;
4230}
4231
4232DEFUN_DEPRECATED(no_config_log_trap,
4233 no_config_log_trap_cmd,
4234 "no log trap [LEVEL]",
4235 NO_STR
4236 "Logging control\n"
4237 "Permit all logging information\n" "Logging level\n")
4238{
4239 zlog_default->default_lvl = LOG_DEBUG;
4240 return CMD_SUCCESS;
4241}
4242
4243DEFUN(config_log_record_priority,
4244 config_log_record_priority_cmd,
4245 "log record-priority",
4246 "Logging control\n"
4247 "Log the priority of the message within the message\n")
4248{
4249 zlog_default->record_priority = 1;
4250 return CMD_SUCCESS;
4251}
4252
4253DEFUN(no_config_log_record_priority,
4254 no_config_log_record_priority_cmd,
4255 "no log record-priority",
4256 NO_STR
4257 "Logging control\n"
4258 "Do not log the priority of the message within the message\n")
4259{
4260 zlog_default->record_priority = 0;
4261 return CMD_SUCCESS;
4262}
4263#endif
4264
4265DEFUN(banner_motd_file,
4266 banner_motd_file_cmd,
4267 "banner motd file [FILE]",
4268 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4269{
4270 if (host.motdfile)
4271 talloc_free(host.motdfile);
4272 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4273
4274 return CMD_SUCCESS;
4275}
4276
4277DEFUN(banner_motd_default,
4278 banner_motd_default_cmd,
4279 "banner motd default",
4280 "Set banner string\n" "Strings for motd\n" "Default string\n")
4281{
4282 host.motd = default_motd;
4283 return CMD_SUCCESS;
4284}
4285
4286DEFUN(no_banner_motd,
4287 no_banner_motd_cmd,
4288 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4289{
4290 host.motd = NULL;
4291 if (host.motdfile)
4292 talloc_free(host.motdfile);
4293 host.motdfile = NULL;
4294 return CMD_SUCCESS;
4295}
4296
4297/* Set config filename. Called from vty.c */
4298void host_config_set(const char *filename)
4299{
4300 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4301}
4302
Pau Espin Pedrolebb6c1f2021-05-17 18:54:21 +02004303const char *host_config_file(void)
4304{
4305 return host.config;
4306}
4307
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004308/*! Deprecated, now happens implicitly when calling install_node().
4309 * Users of the API may still attempt to call this function, hence
4310 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004311void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004312{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004313}
4314
4315/*! Deprecated, now happens implicitly when calling install_node().
4316 * Users of the API may still attempt to call this function, hence
4317 * leave it here as a no-op. */
4318void vty_install_default(int node)
4319{
4320}
4321
4322/*! Install common commands like 'exit' and 'list'. */
4323static void install_basic_node_commands(int node)
4324{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004325 install_lib_element(node, &config_help_cmd);
4326 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004327
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004328 install_lib_element(node, &show_vty_attr_all_cmd);
4329 install_lib_element(node, &show_vty_attr_cmd);
4330
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004331 install_lib_element(node, &config_write_terminal_cmd);
4332 install_lib_element(node, &config_write_file_cmd);
4333 install_lib_element(node, &config_write_memory_cmd);
4334 install_lib_element(node, &config_write_cmd);
4335 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004336
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004337 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004338
4339 if (node >= CONFIG_NODE) {
4340 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004341 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004342 }
4343}
4344
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004345/*! Return true if a node is installed by install_basic_node_commands(), so
4346 * that we can avoid repeating them for each and every node during 'show
4347 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004348static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004349{
4350 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004351 || cmd == &show_vty_attr_all_cmd
4352 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004353 || cmd == &config_list_cmd
4354 || cmd == &config_write_terminal_cmd
4355 || cmd == &config_write_file_cmd
4356 || cmd == &config_write_memory_cmd
4357 || cmd == &config_write_cmd
4358 || cmd == &show_running_config_cmd
4359 || cmd == &config_exit_cmd
4360 || cmd == &config_end_cmd)
4361 return true;
4362 return false;
4363}
4364
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004365/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004366 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004367 * \param[in] vty the vty of the code
4368 * \param[in] filename where to store the file
4369 * \return 0 in case of success.
4370 *
4371 * If the filename already exists create a filename.sav
4372 * version with the current code.
4373 *
4374 */
4375int osmo_vty_write_config_file(const char *filename)
4376{
4377 char *failed_file;
4378 int rc;
4379
4380 rc = write_config_file(filename, &failed_file);
4381 talloc_free(failed_file);
4382 return rc;
4383}
4384
4385/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004386 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004387 * \return 0 in case of success.
4388 *
4389 * If the filename already exists create a filename.sav
4390 * version with the current code.
4391 *
4392 */
4393int osmo_vty_save_config_file(void)
4394{
4395 char *failed_file;
4396 int rc;
4397
4398 if (host.config == NULL)
4399 return -7;
4400
4401 rc = write_config_file(host.config, &failed_file);
4402 talloc_free(failed_file);
4403 return rc;
4404}
4405
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004406/* Initialize command interface. Install basic nodes and commands. */
4407void cmd_init(int terminal)
4408{
4409 /* Allocate initial top vector of commands. */
4410 cmdvec = vector_init(VECTOR_MIN_SIZE);
4411
4412 /* Default host value settings. */
4413 host.name = NULL;
4414 host.password = NULL;
4415 host.enable = NULL;
4416 host.logfile = NULL;
4417 host.config = NULL;
4418 host.lines = -1;
4419 host.motd = default_motd;
4420 host.motdfile = NULL;
4421
4422 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004423 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004424 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004425 install_node_bare(&auth_node, NULL);
4426 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004427 install_node(&config_node, config_write_host);
4428
4429 /* Each node's basic commands. */
Oliver Smith0c78bc62021-07-12 17:28:36 +02004430 install_lib_element(VIEW_NODE, &show_pid_cmd);
Oliver Smithd243c2a2021-07-09 17:19:32 +02004431 install_lib_element(VIEW_NODE, &show_uptime_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004432 install_lib_element(VIEW_NODE, &show_version_cmd);
4433 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004434 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004435 install_lib_element(VIEW_NODE, &config_list_cmd);
4436 install_lib_element(VIEW_NODE, &config_exit_cmd);
4437 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004438 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4439 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004440 install_lib_element(VIEW_NODE, &config_enable_cmd);
4441 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4442 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4443 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004444 }
4445
4446 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004447 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4448 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4449 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Oliver Smith8a482fd2021-07-12 18:18:51 +02004450 install_lib_element(ENABLE_NODE, &shutdown_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004451 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004452 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4453 install_lib_element(ENABLE_NODE, &show_version_cmd);
4454 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004455
4456 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004457 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4458 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4459 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004460 }
4461
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004462 install_lib_element(CONFIG_NODE, &hostname_cmd);
4463 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004464
4465 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004466 install_lib_element(CONFIG_NODE, &password_cmd);
4467 install_lib_element(CONFIG_NODE, &password_text_cmd);
4468 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4469 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4470 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004471
4472#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004473 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4474 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004475#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004476 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4477 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4478 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4479 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4480 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004481
4482 }
4483 srand(time(NULL));
4484}
Harald Welte7acb30c2011-08-17 17:13:48 +02004485
Oliver Smithd243c2a2021-07-09 17:19:32 +02004486static __attribute__((constructor)) void on_dso_load_starttime(void)
4487{
4488 osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
4489}
4490
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004491/* FIXME: execute this section in the unit test instead */
4492static __attribute__((constructor)) void on_dso_load(void)
4493{
4494 unsigned int i, j;
4495
4496 /* Check total number of the library specific attributes */
4497 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4498
4499 /* Check for duplicates in the list of library specific flags */
4500 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4501 if (cmd_lib_attr_letters[i] == '\0')
4502 continue;
4503
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004504 /* Some flag characters are reserved for global attributes */
4505 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4506 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4507 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4508
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004509 /* Only upper case flag letters are allowed for libraries */
4510 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4511 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4512
4513 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4514 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4515 }
4516}
4517
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004518/*! @} */