blob: 6d63d84107dff90eeff1dd92c11d56bc452404d3 [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>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020041#include <sys/time.h>
42#include <sys/stat.h>
43
44#include <osmocom/vty/vector.h>
45#include <osmocom/vty/vty.h>
46#include <osmocom/vty/command.h>
47
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010048#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010049#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050
Ruben Undheim766f77c2018-11-18 13:02:47 +010051#ifndef MAXPATHLEN
52 #define MAXPATHLEN 4096
53#endif
54
55
Harald Weltee881b1b2011-08-17 18:52:30 +020056/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020057 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020059 *
60 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020061
Harald Welte3fb0b6f2010-05-19 19:02:52 +020062#define CONFIGFILE_MASK 022
63
64void *tall_vty_cmd_ctx;
65
66/* Command vector which includes some level of command lists. Normally
67 each daemon maintains each own cmdvec. */
68vector cmdvec;
69
70/* Host information structure. */
71struct host host;
72
73/* Standard command node structures. */
74struct cmd_node auth_node = {
75 AUTH_NODE,
76 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node view_node = {
81 VIEW_NODE,
82 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node auth_enable_node = {
87 AUTH_ENABLE_NODE,
88 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node enable_node = {
93 ENABLE_NODE,
94 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010095 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096};
97
98struct cmd_node config_node = {
99 CONFIG_NODE,
100 "%s(config)# ",
101 1
102};
103
104/* Default motd string. */
105const char *default_motd = "";
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200108 *
109 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110void print_version(int print_copyright)
111{
Harald Welte237f6242010-05-25 23:00:45 +0200112 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200114 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115}
116
117/* Utility function to concatenate argv argument into a single string
118 with inserting ' ' character between each argument. */
119char *argv_concat(const char **argv, int argc, int shift)
120{
121 int i;
122 size_t len;
123 char *str;
124 char *p;
125
126 len = 0;
127 for (i = shift; i < argc; i++)
128 len += strlen(argv[i]) + 1;
129 if (!len)
130 return NULL;
131 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
132 for (i = shift; i < argc; i++) {
133 size_t arglen;
134 memcpy(p, argv[i], (arglen = strlen(argv[i])));
135 p += arglen;
136 *p++ = ' ';
137 }
138 *(p - 1) = '\0';
139 return str;
140}
141
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200142/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
143 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
144 * in turn, this name us used for XML IDs in 'show online-help'. */
145static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
146{
147 const char *pos;
148 int dest = 0;
149
150 if (!prompt || !*prompt)
151 return "";
152
153 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
154 if (pos[0] == '%' && pos[1]) {
155 /* skip "%s"; loop pos++ does the second one. */
156 pos++;
157 continue;
158 }
159 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
160 continue;
161 name_buf[dest] = pos[0];
162 dest++;
163 }
164 name_buf[dest] = '\0';
165 return name_buf;
166}
167
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200168static void install_basic_node_commands(int node);
169
170/*! Install top node of command vector, without adding basic node commands. */
171static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172{
173 vector_set_index(cmdvec, node->node, node);
174 node->func = func;
175 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200176 if (!*node->name)
177 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200178}
179
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200180/*! Install top node of command vector. */
181void install_node(struct cmd_node *node, int (*func) (struct vty *))
182{
183 install_node_bare(node, func);
184 install_basic_node_commands(node->node);
185}
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187/* Compare two command's string. Used in sort_node (). */
188static int cmp_node(const void *p, const void *q)
189{
190 struct cmd_element *a = *(struct cmd_element **)p;
191 struct cmd_element *b = *(struct cmd_element **)q;
192
193 return strcmp(a->string, b->string);
194}
195
196static int cmp_desc(const void *p, const void *q)
197{
198 struct desc *a = *(struct desc **)p;
199 struct desc *b = *(struct desc **)q;
200
201 return strcmp(a->cmd, b->cmd);
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200205void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200206{
207 unsigned int i, j;
208 struct cmd_node *cnode;
209 vector descvec;
210 struct cmd_element *cmd_element;
211
212 for (i = 0; i < vector_active(cmdvec); i++)
213 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
214 vector cmd_vector = cnode->cmd_vector;
215 qsort(cmd_vector->index, vector_active(cmd_vector),
216 sizeof(void *), cmp_node);
217
218 for (j = 0; j < vector_active(cmd_vector); j++)
219 if ((cmd_element =
220 vector_slot(cmd_vector, j)) != NULL
221 && vector_active(cmd_element->strvec)) {
222 descvec =
223 vector_slot(cmd_element->strvec,
224 vector_active
225 (cmd_element->strvec) -
226 1);
227 qsort(descvec->index,
228 vector_active(descvec),
229 sizeof(void *), cmp_desc);
230 }
231 }
232}
233
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200234/*! Break up string in command tokens. Return leading indents.
235 * \param[in] string String to split.
236 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
237 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
238 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
239 *
240 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
241 * so that \a indent can simply return the count of leading spaces.
242 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
243 */
244int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245{
246 const char *cp, *start;
247 char *token;
248 int strlen;
249 vector strvec;
250
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200251 *strvec_p = NULL;
252 if (indent)
253 *indent = 0;
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200257
258 cp = string;
259
260 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200261 while (isspace((int)*cp) && *cp != '\0') {
262 /* if we're counting indents, we need to be strict about them */
263 if (indent && (*cp != ' ') && (*cp != '\t')) {
264 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
265 if (*cp == '\n' || *cp == '\r') {
266 cp++;
267 string = cp;
268 continue;
269 }
270 return CMD_ERR_INVALID_INDENT;
271 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 }
274
275 if (indent)
276 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277
278 /* Return if there is only white spaces */
279 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Prepare return vector. */
286 strvec = vector_init(VECTOR_MIN_SIZE);
287
288 /* Copy each command piece and set into vector. */
289 while (1) {
290 start = cp;
291 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
292 *cp != '\0')
293 cp++;
294 strlen = cp - start;
295 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
296 memcpy(token, start, strlen);
297 *(token + strlen) = '\0';
298 vector_set(strvec, token);
299
300 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
301 *cp != '\0')
302 cp++;
303
304 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200307
308 *strvec_p = strvec;
309 return CMD_SUCCESS;
310}
311
312/*! Breaking up string into each command piece. I assume given
313 character is separated by a space character. Return value is a
314 vector which includes char ** data element. */
315vector cmd_make_strvec(const char *string)
316{
317 vector strvec;
318 cmd_make_strvec2(string, NULL, &strvec);
319 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200320}
321
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200322/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200323void cmd_free_strvec(vector v)
324{
325 unsigned int i;
326 char *cp;
327
328 if (!v)
329 return;
330
331 for (i = 0; i < vector_active(v); i++)
332 if ((cp = vector_slot(v, i)) != NULL)
333 talloc_free(cp);
334
335 vector_free(v);
336}
337
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200338/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200339static char *cmd_desc_str(const char **string)
340{
341 const char *cp, *start;
342 char *token;
343 int strlen;
344
345 cp = *string;
346
347 if (cp == NULL)
348 return NULL;
349
350 /* Skip white spaces. */
351 while (isspace((int)*cp) && *cp != '\0')
352 cp++;
353
354 /* Return if there is only white spaces */
355 if (*cp == '\0')
356 return NULL;
357
358 start = cp;
359
360 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
361 cp++;
362
363 strlen = cp - start;
364 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
365 memcpy(token, start, strlen);
366 *(token + strlen) = '\0';
367
368 *string = cp;
369
370 return token;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200374static vector cmd_make_descvec(const char *string, const char *descstr)
375{
376 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100377 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378 const char *sp;
379 char *token;
380 int len;
381 const char *cp;
382 const char *dp;
383 vector allvec;
384 vector strvec = NULL;
385 struct desc *desc;
386
387 cp = string;
388 dp = descstr;
389
390 if (cp == NULL)
391 return NULL;
392
393 allvec = vector_init(VECTOR_MIN_SIZE);
394
395 while (1) {
396 while (isspace((int)*cp) && *cp != '\0')
397 cp++;
398
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
400 if (cp[0] == '[' && cp[1] == '(') {
401 optional_brace = 1;
402 cp++;
403 }
404
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200405 if (*cp == '(') {
406 multiple = 1;
407 cp++;
408 }
409 if (*cp == ')') {
410 multiple = 0;
411 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100412 if (*cp == ']')
413 cp++;
414 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 }
416 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100417 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 cp++;
419 }
420
421 while (isspace((int)*cp) && *cp != '\0')
422 cp++;
423
424 if (*cp == '(') {
425 multiple = 1;
426 cp++;
427 }
428
429 if (*cp == '\0')
430 return allvec;
431
432 sp = cp;
433
434 while (!
435 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
436 || *cp == ')' || *cp == '|') && *cp != '\0')
437 cp++;
438
439 len = cp - sp;
440
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100441 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
442 if (optional_brace) {
443 /* Place each individual multi-choice token in its own square braces */
444 token[0] = '[';
445 memcpy(token + 1, sp, len);
446 token[1 + len] = ']';
447 token[2 + len] = '\0';
448 } else {
449 memcpy(token, sp, len);
450 *(token + len) = '\0';
451 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200452
453 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
454 desc->cmd = token;
455 desc->str = cmd_desc_str(&dp);
456
457 if (multiple) {
458 if (multiple == 1) {
459 strvec = vector_init(VECTOR_MIN_SIZE);
460 vector_set(allvec, strvec);
461 }
462 multiple++;
463 } else {
464 strvec = vector_init(VECTOR_MIN_SIZE);
465 vector_set(allvec, strvec);
466 }
467 vector_set(strvec, desc);
468 }
469}
470
471/* Count mandantory string vector size. This is to determine inputed
472 command has enough command length. */
473static int cmd_cmdsize(vector strvec)
474{
475 unsigned int i;
476 int size = 0;
477 vector descvec;
478 struct desc *desc;
479
480 for (i = 0; i < vector_active(strvec); i++)
481 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100482 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483 && (desc = vector_slot(descvec, 0)) != NULL) {
484 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
485 return size;
486 else
487 size++;
488 } else
489 size++;
490 }
491 return size;
492}
493
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200494/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200495const char *cmd_prompt(enum node_type node)
496{
497 struct cmd_node *cnode;
498
499 cnode = vector_slot(cmdvec, node);
500 return cnode->prompt;
501}
502
Alexander Couzensad580ba2016-05-16 16:01:45 +0200503/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200505 * \param unsafe string
506 * \return a new talloc char *
507 */
508char *osmo_asciidoc_escape(const char *inp)
509{
510 int _strlen;
511 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200512 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200513
514 if (!inp)
515 return NULL;
516 _strlen = strlen(inp);
517
518 for (i = 0; i < _strlen; ++i) {
519 switch (inp[i]) {
520 case '|':
521 len += 2;
522 break;
523 default:
524 len += 1;
525 break;
526 }
527 }
528
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200529 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200530 if (!out)
531 return NULL;
532
533 out_ptr = out;
534
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200538 /* Prepend escape character "\": */
539 *(out_ptr++) = '\\';
540 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 default:
542 *(out_ptr++) = inp[i];
543 break;
544 }
545 }
546
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 out_ptr[0] = '\0';
548 return out;
549}
550
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100551static char *xml_escape(const char *inp)
552{
553 int _strlen;
554 char *out, *out_ptr;
555 int len = 0, i, j;
556
557 if (!inp)
558 return NULL;
559 _strlen = strlen(inp);
560
561 for (i = 0; i < _strlen; ++i) {
562 switch (inp[i]) {
563 case '"':
564 len += 6;
565 break;
566 case '\'':
567 len += 6;
568 break;
569 case '<':
570 len += 4;
571 break;
572 case '>':
573 len += 4;
574 break;
575 case '&':
576 len += 5;
577 break;
578 default:
579 len += 1;
580 break;
581 }
582 }
583
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200584 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100585 if (!out)
586 return NULL;
587
588 out_ptr = out;
589
590#define ADD(out, str) \
591 for (j = 0; j < strlen(str); ++j) \
592 *(out++) = str[j];
593
594 for (i = 0; i < _strlen; ++i) {
595 switch (inp[i]) {
596 case '"':
597 ADD(out_ptr, "&quot;");
598 break;
599 case '\'':
600 ADD(out_ptr, "&apos;");
601 break;
602 case '<':
603 ADD(out_ptr, "&lt;");
604 break;
605 case '>':
606 ADD(out_ptr, "&gt;");
607 break;
608 case '&':
609 ADD(out_ptr, "&amp;");
610 break;
611 default:
612 *(out_ptr++) = inp[i];
613 break;
614 }
615 }
616
617#undef ADD
618
619 out_ptr[0] = '\0';
620 return out;
621}
622
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200623typedef int (*print_func_t)(void *data, const char *fmt, ...);
624
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700625static const struct value_string cmd_attr_desc[] = {
626 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700627 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700628 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700629 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700630 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700631 { 0, NULL }
632};
633
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700634/* Public attributes (to be printed in the VTY / XML reference) */
635#define CMD_ATTR_PUBLIC_MASK \
Vadim Yanitskiy67608452020-10-23 20:37:32 +0700636 (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700637
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700638/* Get a flag character for a global VTY command attribute */
639static char cmd_attr_get_flag(unsigned int attr)
640{
641 switch (attr) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700642 case CMD_ATTR_HIDDEN:
643 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700644 case CMD_ATTR_IMMEDIATE:
645 return '!';
646 case CMD_ATTR_NODE_EXIT:
647 return '@';
648 default:
649 return '.';
650 }
651}
652
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700653/* Description of attributes shared between the lib commands */
654static const char * const cmd_lib_attr_desc[32] = {
655 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
656 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200657 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
658 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200659 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
660 "This command applies on IPA link establishment",
661 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
662 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700663};
664
665/* Flag letters of attributes shared between the lib commands.
666 * NOTE: uppercase letters only, the rest is reserved for applications. */
667static const char cmd_lib_attr_letters[32] = {
668 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200669 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200670 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
671 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700672};
673
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100674/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200675 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100676 */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700677static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
678 void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100679{
680 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700681 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100682
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200683 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700684
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700685 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700686 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700687 print_func(data, " <attributes scope='global'>%s", newline);
688
689 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
690 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700691 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700692
693 if (~cmd->attr & cmd_attr_desc[i].value)
694 continue;
695
696 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700697 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700698 xml_att_desc, newline);
699 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700700
701 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
702 if (flag != '.')
703 print_func(data, " flag='%c'", flag);
704 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700705 }
706
707 print_func(data, " </attributes>%s", newline);
708 }
709
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700710 /* Print application specific attributes and their description */
711 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700712 const char * const *desc;
713 const char *letters;
714
715 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
716 print_func(data, " <attributes scope='library'>%s", newline);
717 letters = &cmd_lib_attr_letters[0];
718 desc = &cmd_lib_attr_desc[0];
719 } else {
720 print_func(data, " <attributes scope='application'>%s", newline);
721 letters = &host.app_info->usr_attr_letters[0];
722 desc = &host.app_info->usr_attr_desc[0];
723 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700724
725 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
726 char *xml_att_desc;
727 char flag;
728
729 /* Skip attribute if *not* set */
Harald Weltec296e292020-12-21 15:44:52 +0100730 if (~cmd->usrattr & ((unsigned)1 << i))
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700731 continue;
732
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700733 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700734 print_func(data, " <attribute doc='%s'", xml_att_desc);
735 talloc_free(xml_att_desc);
736
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700737 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700738 print_func(data, " flag='%c'", flag);
739 print_func(data, " />%s", newline);
740 }
741
742 print_func(data, " </attributes>%s", newline);
743 }
744
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200745 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100746
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700747 for (i = 0; i < vector_count(cmd->strvec); ++i) {
748 vector descvec = vector_slot(cmd->strvec, i);
749 int j;
750 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100751 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700752 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100753 if (desc == NULL)
754 continue;
755
756 xml_param = xml_escape(desc->cmd);
757 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200758 print_func(data, " <param name='%s' doc='%s' />%s",
759 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100760 talloc_free(xml_param);
761 talloc_free(xml_doc);
762 }
763 }
764
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200765 print_func(data, " </params>%s", newline);
766 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100767
768 talloc_free(xml_string);
769 return 0;
770}
771
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700772static bool vty_command_is_common(const struct cmd_element *cmd);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200773
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100774/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200775 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700776 *
777 * (gflag_mask, match = false) - print only those commands with non-matching flags.
778 * (gflag_mask, match = true) - print only those commands with matching flags.
779 *
780 * Some examples:
781 *
782 * Print all commands except deprecated and hidden (default mode):
783 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
784 * Print only deprecated and hidden commands:
785 * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
786 * Print all commands except deprecated (expert mode):
787 * (CMD_ATTR_DEPRECATED, false)
788 * Print only hidden commands:
789 * (CMD_ATTR_HIDDEN, true)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100790 */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700791static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
792 unsigned char gflag_mask, bool match)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100793{
794 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200795 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100796
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200797 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100798
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200799 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200800 print_func(data, " <node id='_common_cmds_'>%s", newline);
801 print_func(data, " <name>Common Commands</name>%s", newline);
802 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
803 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200804 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700805 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200806 if (!cnode)
807 continue;
808 if (cnode->node != CONFIG_NODE)
809 continue;
810
811 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700812 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200813 if (!vty_command_is_common(elem))
814 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700815 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700816 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700817 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700818 continue;
819 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200820 }
821 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200822 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200823
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100824 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700825 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100826 if (!cnode)
827 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200828 if (vector_active(cnode->cmd_vector) < 1)
829 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100830
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200831 /* De-dup node IDs: how many times has this same name been used before? Count the first
832 * occurence as _1 and omit that first suffix, so that the first occurence is called
833 * 'name', the second becomes 'name_2', then 'name_3', ... */
834 same_name_count = 1;
835 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700836 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200837 if (!cnode2)
838 continue;
839 if (strcmp(cnode->name, cnode2->name) == 0)
840 same_name_count ++;
841 }
842
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200843 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200844 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200845 print_func(data, "_%d", same_name_count);
846 print_func(data, "'>%s", newline);
847 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100848
849 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700850 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200851 if (vty_command_is_common(elem))
852 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700853 if (!match && (elem->attr & gflag_mask) != 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700854 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700855 if (match && (elem->attr & gflag_mask) == 0x00)
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700856 continue;
857 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100858 }
859
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200860 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100861 }
862
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200863 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100864
865 return 0;
866}
867
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200868static int print_func_vty(void *data, const char *format, ...)
869{
870 struct vty *vty = data;
871 va_list args;
872 int rc;
873 va_start(args, format);
874 rc = vty_out_va(vty, format, args);
875 va_end(args);
876 return rc;
877}
878
879static int vty_dump_xml_ref_to_vty(struct vty *vty)
880{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700881 unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
882 if (!vty->expert_mode)
883 gflag_mask |= CMD_ATTR_HIDDEN;
884 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200885}
886
887static int print_func_stream(void *data, const char *format, ...)
888{
889 va_list args;
890 int rc;
891 va_start(args, format);
892 rc = vfprintf((FILE*)data, format, args);
893 va_end(args);
894 return rc;
895}
896
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700897const struct value_string vty_ref_gen_mode_names[] = {
898 { VTY_REF_GEN_MODE_DEFAULT, "default" },
899 { VTY_REF_GEN_MODE_EXPERT, "expert" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700900 { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700901 { 0, NULL }
902};
903
904const struct value_string vty_ref_gen_mode_desc[] = {
905 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
906 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700907 { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700908 { 0, NULL }
909};
910
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200911/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700912 * \param[out] stream Output stream to print the XML reference to.
913 * \param[in] mode The XML reference generation mode.
914 * \returns always 0 for now, no errors possible.
915 */
916int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
917{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700918 unsigned char gflag_mask;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700919 bool match = false;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700920
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700921 switch (mode) {
922 case VTY_REF_GEN_MODE_EXPERT:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700923 /* All commands except deprecated */
924 gflag_mask = CMD_ATTR_DEPRECATED;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700925 break;
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700926 case VTY_REF_GEN_MODE_HIDDEN:
927 /* Only hidden commands */
928 gflag_mask = CMD_ATTR_HIDDEN;
929 match = true;
930 break;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700931 case VTY_REF_GEN_MODE_DEFAULT:
932 default:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700933 /* All commands except deprecated and hidden */
934 gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700935 break;
936 }
937
Vadim Yanitskiy7031ac12020-11-17 16:22:44 +0700938 return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700939}
940
941/*! Print the XML reference of all VTY nodes to the given stream.
942 * \param[out] stream Output stream to print the XML reference to.
943 * \returns always 0 for now, no errors possible.
944 *
945 * NOTE: this function is deprecated because it does not allow to
946 * specify the XML reference generation mode (default mode
947 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200948 */
949int vty_dump_xml_ref(FILE *stream)
950{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700951 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200952}
953
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200954/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100955static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
956{
957 int i;
958
959 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
960 struct cmd_element *elem;
961 elem = vector_slot(cnode->cmd_vector, i);
962 if (!elem->string)
963 continue;
964 if (!strcmp(elem->string, cmdstring))
965 return 1;
966 }
967 return 0;
968}
969
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200970/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200971 * \param[in] ntype Node Type
972 * \param[cmd] element to be installed
973 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000974void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200975{
976 struct cmd_node *cnode;
977
978 cnode = vector_slot(cmdvec, ntype);
979
Harald Weltea99d45a2015-11-12 13:48:23 +0100980 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100981 /* ensure no _identical_ command has been registered at this
982 * node so far */
983 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200984
985 vector_set(cnode->cmd_vector, cmd);
986
987 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
988 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
989}
990
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700991/*! Install a library command into a node
992 * \param[in] ntype Node Type
993 * \param[in] cmd element to be installed
994 */
995void install_lib_element(int ntype, struct cmd_element *cmd)
996{
997 cmd->attr |= CMD_ATTR_LIB_COMMAND;
998 install_element(ntype, cmd);
999}
1000
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001001/* Install a command into VIEW and ENABLE node */
1002void install_element_ve(struct cmd_element *cmd)
1003{
1004 install_element(VIEW_NODE, cmd);
1005 install_element(ENABLE_NODE, cmd);
1006}
1007
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001008/* Install a library command into VIEW and ENABLE node */
1009void install_lib_element_ve(struct cmd_element *cmd)
1010{
1011 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1012 install_element_ve(cmd);
1013}
1014
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001015#ifdef VTY_CRYPT_PW
1016static unsigned char itoa64[] =
1017 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1018
1019static void to64(char *s, long v, int n)
1020{
1021 while (--n >= 0) {
1022 *s++ = itoa64[v & 0x3f];
1023 v >>= 6;
1024 }
1025}
1026
1027static char *zencrypt(const char *passwd)
1028{
1029 char salt[6];
1030 struct timeval tv;
1031 char *crypt(const char *, const char *);
1032
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001033 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001034
1035 to64(&salt[0], random(), 3);
1036 to64(&salt[3], tv.tv_usec, 3);
1037 salt[5] = '\0';
1038
1039 return crypt(passwd, salt);
1040}
1041#endif
1042
1043/* This function write configuration of this host. */
1044static int config_write_host(struct vty *vty)
1045{
1046 if (host.name)
1047 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1048
1049 if (host.encrypt) {
1050 if (host.password_encrypt)
1051 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1052 VTY_NEWLINE);
1053 if (host.enable_encrypt)
1054 vty_out(vty, "enable password 8 %s%s",
1055 host.enable_encrypt, VTY_NEWLINE);
1056 } else {
1057 if (host.password)
1058 vty_out(vty, "password %s%s", host.password,
1059 VTY_NEWLINE);
1060 if (host.enable)
1061 vty_out(vty, "enable password %s%s", host.enable,
1062 VTY_NEWLINE);
1063 }
1064
1065 if (host.advanced)
1066 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1067
1068 if (host.encrypt)
1069 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1070
1071 if (host.lines >= 0)
1072 vty_out(vty, "service terminal-length %d%s", host.lines,
1073 VTY_NEWLINE);
1074
1075 if (host.motdfile)
1076 vty_out(vty, "banner motd file %s%s", host.motdfile,
1077 VTY_NEWLINE);
1078 else if (!host.motd)
1079 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1080
1081 return 1;
1082}
1083
1084/* Utility function for getting command vector. */
1085static vector cmd_node_vector(vector v, enum node_type ntype)
1086{
1087 struct cmd_node *cnode = vector_slot(v, ntype);
1088 return cnode->cmd_vector;
1089}
1090
1091/* Completion match types. */
1092enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001093 NO_MATCH = 0,
1094 ANY_MATCH,
1095 EXTEND_MATCH,
1096 IPV4_PREFIX_MATCH,
1097 IPV4_MATCH,
1098 IPV6_PREFIX_MATCH,
1099 IPV6_MATCH,
1100 RANGE_MATCH,
1101 VARARG_MATCH,
1102 PARTLY_MATCH,
1103 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001104};
1105
1106static enum match_type cmd_ipv4_match(const char *str)
1107{
1108 const char *sp;
1109 int dots = 0, nums = 0;
1110 char buf[4];
1111
1112 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001113 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001114
1115 for (;;) {
1116 memset(buf, 0, sizeof(buf));
1117 sp = str;
1118 while (*str != '\0') {
1119 if (*str == '.') {
1120 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001121 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001122
1123 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001124 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125
1126 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001127 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001128
1129 dots++;
1130 break;
1131 }
1132 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001133 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001134
1135 str++;
1136 }
1137
1138 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001139 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001140
1141 strncpy(buf, sp, str - sp);
1142 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001143 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001144
1145 nums++;
1146
1147 if (*str == '\0')
1148 break;
1149
1150 str++;
1151 }
1152
1153 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001154 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001155
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001156 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001157}
1158
1159static enum match_type cmd_ipv4_prefix_match(const char *str)
1160{
1161 const char *sp;
1162 int dots = 0;
1163 char buf[4];
1164
1165 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001166 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001167
1168 for (;;) {
1169 memset(buf, 0, sizeof(buf));
1170 sp = str;
1171 while (*str != '\0' && *str != '/') {
1172 if (*str == '.') {
1173 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001174 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001175
1176 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001177 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001178
1179 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001180 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001181
1182 dots++;
1183 break;
1184 }
1185
1186 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001187 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001188
1189 str++;
1190 }
1191
1192 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001193 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001194
1195 strncpy(buf, sp, str - sp);
1196 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001197 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001198
1199 if (dots == 3) {
1200 if (*str == '/') {
1201 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001202 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001203
1204 str++;
1205 break;
1206 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001207 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001208 }
1209
1210 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001211 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001212
1213 str++;
1214 }
1215
1216 sp = str;
1217 while (*str != '\0') {
1218 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001219 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001220
1221 str++;
1222 }
1223
1224 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001227 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001228}
1229
1230#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1231#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1232#define STATE_START 1
1233#define STATE_COLON 2
1234#define STATE_DOUBLE 3
1235#define STATE_ADDR 4
1236#define STATE_DOT 5
1237#define STATE_SLASH 6
1238#define STATE_MASK 7
1239
1240#ifdef HAVE_IPV6
1241
1242static enum match_type cmd_ipv6_match(const char *str)
1243{
1244 int state = STATE_START;
1245 int colons = 0, nums = 0, double_colon = 0;
1246 const char *sp = NULL;
1247 struct sockaddr_in6 sin6_dummy;
1248 int ret;
1249
1250 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001251 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001252
1253 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001254 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001255
1256 /* use inet_pton that has a better support,
1257 * for example inet_pton can support the automatic addresses:
1258 * ::1.2.3.4
1259 */
1260 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1261
1262 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001263 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001264
1265 while (*str != '\0') {
1266 switch (state) {
1267 case STATE_START:
1268 if (*str == ':') {
1269 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001270 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001271 colons--;
1272 state = STATE_COLON;
1273 } else {
1274 sp = str;
1275 state = STATE_ADDR;
1276 }
1277
1278 continue;
1279 case STATE_COLON:
1280 colons++;
1281 if (*(str + 1) == ':')
1282 state = STATE_DOUBLE;
1283 else {
1284 sp = str + 1;
1285 state = STATE_ADDR;
1286 }
1287 break;
1288 case STATE_DOUBLE:
1289 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001290 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001291
1292 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001293 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001294 else {
1295 if (*(str + 1) != '\0')
1296 colons++;
1297 sp = str + 1;
1298 state = STATE_ADDR;
1299 }
1300
1301 double_colon++;
1302 nums++;
1303 break;
1304 case STATE_ADDR:
1305 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1306 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001307 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001308
1309 nums++;
1310 state = STATE_COLON;
1311 }
1312 if (*(str + 1) == '.')
1313 state = STATE_DOT;
1314 break;
1315 case STATE_DOT:
1316 state = STATE_ADDR;
1317 break;
1318 default:
1319 break;
1320 }
1321
1322 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001323 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001324
1325 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001326 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001327
1328 str++;
1329 }
1330
1331#if 0
1332 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001333 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001334#endif /* 0 */
1335
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001336 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001337}
1338
1339static enum match_type cmd_ipv6_prefix_match(const char *str)
1340{
1341 int state = STATE_START;
1342 int colons = 0, nums = 0, double_colon = 0;
1343 int mask;
1344 const char *sp = NULL;
1345 char *endptr = NULL;
1346
1347 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001348 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001349
1350 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001351 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001352
1353 while (*str != '\0' && state != STATE_MASK) {
1354 switch (state) {
1355 case STATE_START:
1356 if (*str == ':') {
1357 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001358 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001359 colons--;
1360 state = STATE_COLON;
1361 } else {
1362 sp = str;
1363 state = STATE_ADDR;
1364 }
1365
1366 continue;
1367 case STATE_COLON:
1368 colons++;
1369 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001370 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001371 else if (*(str + 1) == ':')
1372 state = STATE_DOUBLE;
1373 else {
1374 sp = str + 1;
1375 state = STATE_ADDR;
1376 }
1377 break;
1378 case STATE_DOUBLE:
1379 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001380 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001381
1382 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001383 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001384 else {
1385 if (*(str + 1) != '\0' && *(str + 1) != '/')
1386 colons++;
1387 sp = str + 1;
1388
1389 if (*(str + 1) == '/')
1390 state = STATE_SLASH;
1391 else
1392 state = STATE_ADDR;
1393 }
1394
1395 double_colon++;
1396 nums += 1;
1397 break;
1398 case STATE_ADDR:
1399 if (*(str + 1) == ':' || *(str + 1) == '.'
1400 || *(str + 1) == '\0' || *(str + 1) == '/') {
1401 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001402 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001403
1404 for (; sp <= str; sp++)
1405 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001406 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001407
1408 nums++;
1409
1410 if (*(str + 1) == ':')
1411 state = STATE_COLON;
1412 else if (*(str + 1) == '.')
1413 state = STATE_DOT;
1414 else if (*(str + 1) == '/')
1415 state = STATE_SLASH;
1416 }
1417 break;
1418 case STATE_DOT:
1419 state = STATE_ADDR;
1420 break;
1421 case STATE_SLASH:
1422 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001423 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001424
1425 state = STATE_MASK;
1426 break;
1427 default:
1428 break;
1429 }
1430
1431 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001432 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001433
1434 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001435 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001436
1437 str++;
1438 }
1439
1440 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001441 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001442
1443 mask = strtol(str, &endptr, 10);
1444 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001445 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001446
1447 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001448 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001449
1450/* I don't know why mask < 13 makes command match partly.
1451 Forgive me to make this comments. I Want to set static default route
1452 because of lack of function to originate default in ospf6d; sorry
1453 yasu
1454 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001455 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001456*/
1457
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001458 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459}
1460
1461#endif /* HAVE_IPV6 */
1462
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001463
1464#if ULONG_MAX == 18446744073709551615UL
1465#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1466#elif ULONG_MAX == 4294967295UL
1467#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1468#else
1469#error "ULONG_MAX not defined!"
1470#endif
1471
1472#if LONG_MAX == 9223372036854775807L
1473#define DECIMAL_STRLEN_MAX_SIGNED 19
1474#elif LONG_MAX == 2147483647L
1475#define DECIMAL_STRLEN_MAX_SIGNED 10
1476#else
1477#error "LONG_MAX not defined!"
1478#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001479
1480static int cmd_range_match(const char *range, const char *str)
1481{
1482 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001483 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001484 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001485
1486 if (str == NULL)
1487 return 1;
1488
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001489 if (range[1] == '-') {
1490 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001491
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001492 val = strtol(str, &endptr, 10);
1493 if (*endptr != '\0')
1494 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001495
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001496 range += 2;
1497 p = strchr(range, '-');
1498 if (p == NULL)
1499 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001500 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001501 return 0;
1502 strncpy(buf, range, p - range);
1503 buf[p - range] = '\0';
1504 min = -strtol(buf, &endptr, 10);
1505 if (*endptr != '\0')
1506 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001507
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001508 range = p + 1;
1509 p = strchr(range, '>');
1510 if (p == NULL)
1511 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001512 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001513 return 0;
1514 strncpy(buf, range, p - range);
1515 buf[p - range] = '\0';
1516 max = strtol(buf, &endptr, 10);
1517 if (*endptr != '\0')
1518 return 0;
1519
1520 if (val < min || val > max)
1521 return 0;
1522 } else {
1523 unsigned long min, max, val;
1524
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001525 if (str[0] == '-')
1526 return 0;
1527
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001528 val = strtoul(str, &endptr, 10);
1529 if (*endptr != '\0')
1530 return 0;
1531
1532 range++;
1533 p = strchr(range, '-');
1534 if (p == NULL)
1535 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001536 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001537 return 0;
1538 strncpy(buf, range, p - range);
1539 buf[p - range] = '\0';
1540 min = strtoul(buf, &endptr, 10);
1541 if (*endptr != '\0')
1542 return 0;
1543
1544 range = p + 1;
1545 p = strchr(range, '>');
1546 if (p == NULL)
1547 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001548 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001549 return 0;
1550 strncpy(buf, range, p - range);
1551 buf[p - range] = '\0';
1552 max = strtoul(buf, &endptr, 10);
1553 if (*endptr != '\0')
1554 return 0;
1555
1556 if (val < min || val > max)
1557 return 0;
1558 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001559
1560 return 1;
1561}
1562
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001563/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001564static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001565{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001566 /* we've got "[blah]". We want to strip off the []s and redo the
1567 * match check for "blah"
1568 */
1569 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001570
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001571 if (len < 3)
1572 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001573
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001574 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001575}
1576
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001578cmd_match(const char *str, const char *command,
1579 enum match_type min, bool recur)
1580{
1581
1582 if (recur && CMD_OPTION(str))
1583 {
1584 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001585 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001586
1587 /* this would be a bug in a command, however handle it gracefully
1588 * as it we only discover it if a user tries to run it
1589 */
1590 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001591 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001592
1593 ret = cmd_match(tmp, command, min, false);
1594
1595 talloc_free(tmp);
1596
1597 return ret;
1598 }
1599 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001600 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001601 else if (CMD_RANGE(str))
1602 {
1603 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001604 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001605 }
1606#ifdef HAVE_IPV6
1607 else if (CMD_IPV6(str))
1608 {
1609 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001610 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001611 }
1612 else if (CMD_IPV6_PREFIX(str))
1613 {
1614 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001615 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001616 }
1617#endif /* HAVE_IPV6 */
1618 else if (CMD_IPV4(str))
1619 {
1620 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001621 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001622 }
1623 else if (CMD_IPV4_PREFIX(str))
1624 {
1625 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001626 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001627 }
1628 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001629 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001630 else if (strncmp(command, str, strlen(command)) == 0)
1631 {
1632 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001633 return EXACT_MATCH;
1634 else if (PARTLY_MATCH >= min)
1635 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001636 }
1637
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001638 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001639}
1640
1641/* Filter vector at the specified index and by the given command string, to
1642 * the desired matching level (thus allowing part matches), and return match
1643 * type flag.
1644 */
1645static enum match_type
1646cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001647{
1648 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001649 struct cmd_element *cmd_element;
1650 enum match_type match_type;
1651 vector descvec;
1652 struct desc *desc;
1653
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001654 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001655
1656 /* If command and cmd_element string does not match set NULL to vector */
1657 for (i = 0; i < vector_active(v); i++)
1658 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001659 if (index >= vector_active(cmd_element->strvec))
1660 vector_slot(v, i) = NULL;
1661 else {
1662 unsigned int j;
1663 int matched = 0;
1664
1665 descvec =
1666 vector_slot(cmd_element->strvec, index);
1667
1668 for (j = 0; j < vector_active(descvec); j++)
1669 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001670 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001671
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001672 ret = cmd_match (desc->cmd, command, level, true);
1673
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001674 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001675 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001676
1677 if (match_type < ret)
1678 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001679 }
1680 if (!matched)
1681 vector_slot(v, i) = NULL;
1682 }
1683 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001684
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001685 if (match_type == NO_MATCH)
1686 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001687
1688 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1689 * go again and filter out commands whose argument (at this index) is
1690 * 'weaker'. E.g., if we have 2 commands:
1691 *
1692 * foo bar <1-255>
1693 * foo bar BLAH
1694 *
1695 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001696 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001697 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1698 *
1699 * If we don't do a 2nd pass and filter it out, the higher-layers will
1700 * consider this to be ambiguous.
1701 */
1702 for (i = 0; i < vector_active(v); i++)
1703 if ((cmd_element = vector_slot(v, i)) != NULL) {
1704 if (index >= vector_active(cmd_element->strvec))
1705 vector_slot(v, i) = NULL;
1706 else {
1707 unsigned int j;
1708 int matched = 0;
1709
1710 descvec =
1711 vector_slot(cmd_element->strvec, index);
1712
1713 for (j = 0; j < vector_active(descvec); j++)
1714 if ((desc = vector_slot(descvec, j))) {
1715 enum match_type ret;
1716
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001717 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001718
1719 if (ret >= match_type)
1720 matched++;
1721 }
1722 if (!matched)
1723 vector_slot(v, i) = NULL;
1724 }
1725 }
1726
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001727 return match_type;
1728}
1729
1730/* Check ambiguous match */
1731static int
1732is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1733{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001734 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001735 unsigned int i;
1736 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001737 struct cmd_element *cmd_element;
1738 const char *matched = NULL;
1739 vector descvec;
1740 struct desc *desc;
1741
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001742 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1743 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1744 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1745 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1746 * that case, the string must remain allocated until this function exits or another match comes
1747 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1748 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1749 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1750 void *cmd_deopt_ctx = NULL;
1751
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001752 for (i = 0; i < vector_active(v); i++) {
1753 cmd_element = vector_slot(v, i);
1754 if (!cmd_element)
1755 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001756
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001757 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001758
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001759 descvec = vector_slot(cmd_element->strvec, index);
1760
1761 for (j = 0; j < vector_active(descvec); j++) {
1762 desc = vector_slot(descvec, j);
1763 if (!desc)
1764 continue;
1765
1766 enum match_type mtype;
1767 const char *str = desc->cmd;
1768
1769 if (CMD_OPTION(str)) {
1770 if (!cmd_deopt_ctx)
1771 cmd_deopt_ctx =
1772 talloc_named_const(tall_vty_cmd_ctx, 0,
1773 __func__);
1774 str = cmd_deopt(cmd_deopt_ctx, str);
1775 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001776 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001777 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001778
1779 switch (type) {
1780 case EXACT_MATCH:
1781 if (!(CMD_VARIABLE (str))
1782 && strcmp(command, str) == 0)
1783 match++;
1784 break;
1785 case PARTLY_MATCH:
1786 if (!(CMD_VARIABLE (str))
1787 && strncmp(command, str, strlen (command)) == 0)
1788 {
1789 if (matched
1790 && strcmp(matched,
1791 str) != 0) {
1792 ret = 1; /* There is ambiguous match. */
1793 goto free_and_return;
1794 } else
1795 matched = str;
1796 match++;
1797 }
1798 break;
1799 case RANGE_MATCH:
1800 if (cmd_range_match
1801 (str, command)) {
1802 if (matched
1803 && strcmp(matched,
1804 str) != 0) {
1805 ret = 1;
1806 goto free_and_return;
1807 } else
1808 matched = str;
1809 match++;
1810 }
1811 break;
1812#ifdef HAVE_IPV6
1813 case IPV6_MATCH:
1814 if (CMD_IPV6(str))
1815 match++;
1816 break;
1817 case IPV6_PREFIX_MATCH:
1818 if ((mtype =
1819 cmd_ipv6_prefix_match
1820 (command)) != NO_MATCH) {
1821 if (mtype == PARTLY_MATCH) {
1822 ret = 2; /* There is incomplete match. */
1823 goto free_and_return;
1824 }
1825
1826 match++;
1827 }
1828 break;
1829#endif /* HAVE_IPV6 */
1830 case IPV4_MATCH:
1831 if (CMD_IPV4(str))
1832 match++;
1833 break;
1834 case IPV4_PREFIX_MATCH:
1835 if ((mtype =
1836 cmd_ipv4_prefix_match
1837 (command)) != NO_MATCH) {
1838 if (mtype == PARTLY_MATCH) {
1839 ret = 2; /* There is incomplete match. */
1840 goto free_and_return;
1841 }
1842
1843 match++;
1844 }
1845 break;
1846 case EXTEND_MATCH:
1847 if (CMD_VARIABLE (str))
1848 match++;
1849 break;
1850 case NO_MATCH:
1851 default:
1852 break;
1853 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001854 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001855 if (!match)
1856 vector_slot(v, i) = NULL;
1857 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001858
1859free_and_return:
1860 if (cmd_deopt_ctx)
1861 talloc_free(cmd_deopt_ctx);
1862 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001863}
1864
1865/* If src matches dst return dst string, otherwise return NULL */
1866static const char *cmd_entry_function(const char *src, const char *dst)
1867{
1868 /* Skip variable arguments. */
1869 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1870 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1871 return NULL;
1872
1873 /* In case of 'command \t', given src is NULL string. */
1874 if (src == NULL)
1875 return dst;
1876
1877 /* Matched with input string. */
1878 if (strncmp(src, dst, strlen(src)) == 0)
1879 return dst;
1880
1881 return NULL;
1882}
1883
1884/* If src matches dst return dst string, otherwise return NULL */
1885/* This version will return the dst string always if it is
1886 CMD_VARIABLE for '?' key processing */
1887static const char *cmd_entry_function_desc(const char *src, const char *dst)
1888{
1889 if (CMD_VARARG(dst))
1890 return dst;
1891
1892 if (CMD_RANGE(dst)) {
1893 if (cmd_range_match(dst, src))
1894 return dst;
1895 else
1896 return NULL;
1897 }
1898#ifdef HAVE_IPV6
1899 if (CMD_IPV6(dst)) {
1900 if (cmd_ipv6_match(src))
1901 return dst;
1902 else
1903 return NULL;
1904 }
1905
1906 if (CMD_IPV6_PREFIX(dst)) {
1907 if (cmd_ipv6_prefix_match(src))
1908 return dst;
1909 else
1910 return NULL;
1911 }
1912#endif /* HAVE_IPV6 */
1913
1914 if (CMD_IPV4(dst)) {
1915 if (cmd_ipv4_match(src))
1916 return dst;
1917 else
1918 return NULL;
1919 }
1920
1921 if (CMD_IPV4_PREFIX(dst)) {
1922 if (cmd_ipv4_prefix_match(src))
1923 return dst;
1924 else
1925 return NULL;
1926 }
1927
1928 /* Optional or variable commands always match on '?' */
1929 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1930 return dst;
1931
1932 /* In case of 'command \t', given src is NULL string. */
1933 if (src == NULL)
1934 return dst;
1935
1936 if (strncmp(src, dst, strlen(src)) == 0)
1937 return dst;
1938 else
1939 return NULL;
1940}
1941
1942/* Check same string element existence. If it isn't there return
1943 1. */
1944static int cmd_unique_string(vector v, const char *str)
1945{
1946 unsigned int i;
1947 char *match;
1948
1949 for (i = 0; i < vector_active(v); i++)
1950 if ((match = vector_slot(v, i)) != NULL)
1951 if (strcmp(match, str) == 0)
1952 return 0;
1953 return 1;
1954}
1955
1956/* Compare string to description vector. If there is same string
1957 return 1 else return 0. */
1958static int desc_unique_string(vector v, const char *str)
1959{
1960 unsigned int i;
1961 struct desc *desc;
1962
1963 for (i = 0; i < vector_active(v); i++)
1964 if ((desc = vector_slot(v, i)) != NULL)
1965 if (strcmp(desc->cmd, str) == 0)
1966 return 1;
1967 return 0;
1968}
1969
1970static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1971{
1972 if (first_word != NULL &&
1973 node != AUTH_NODE &&
1974 node != VIEW_NODE &&
1975 node != AUTH_ENABLE_NODE &&
1976 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1977 return 1;
1978 return 0;
1979}
1980
1981/* '?' describe command support. */
1982static vector
1983cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1984{
1985 unsigned int i;
1986 vector cmd_vector;
1987#define INIT_MATCHVEC_SIZE 10
1988 vector matchvec;
1989 struct cmd_element *cmd_element;
1990 unsigned int index;
1991 int ret;
1992 enum match_type match;
1993 char *command;
1994 static struct desc desc_cr = { "<cr>", "" };
1995
1996 /* Set index. */
1997 if (vector_active(vline) == 0) {
1998 *status = CMD_ERR_NO_MATCH;
1999 return NULL;
2000 } else
2001 index = vector_active(vline) - 1;
2002
2003 /* Make copy vector of current node's command vector. */
2004 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2005
2006 /* Prepare match vector */
2007 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2008
2009 /* Filter commands. */
2010 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002011 for (i = 0; i < index; i++) {
2012 command = vector_slot(vline, i);
2013 if (!command)
2014 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002015
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002016 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002017
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002018 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002019 struct cmd_element *cmd_element;
2020 vector descvec;
2021 unsigned int j, k;
2022
2023 for (j = 0; j < vector_active(cmd_vector); j++)
2024 if ((cmd_element =
2025 vector_slot(cmd_vector, j)) != NULL
2026 &&
2027 (vector_active(cmd_element->strvec))) {
2028 descvec =
2029 vector_slot(cmd_element->
2030 strvec,
2031 vector_active
2032 (cmd_element->
2033 strvec) - 1);
2034 for (k = 0;
2035 k < vector_active(descvec);
2036 k++) {
2037 struct desc *desc =
2038 vector_slot(descvec,
2039 k);
2040 vector_set(matchvec,
2041 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002042 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002043 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002044
Harald Welte80d30fe2013-02-12 11:08:57 +01002045 vector_set(matchvec, &desc_cr);
2046 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002047
Harald Welte80d30fe2013-02-12 11:08:57 +01002048 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002049 }
2050
Harald Welte80d30fe2013-02-12 11:08:57 +01002051 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2052 match)) == 1) {
2053 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002054 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002055 *status = CMD_ERR_AMBIGUOUS;
2056 return NULL;
2057 } else if (ret == 2) {
2058 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002059 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002060 *status = CMD_ERR_NO_MATCH;
2061 return NULL;
2062 }
2063 }
2064
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002065 /* Prepare match vector */
2066 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2067
2068 /* Make sure that cmd_vector is filtered based on current word */
2069 command = vector_slot(vline, index);
2070 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002071 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002072
2073 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002074 for (i = 0; i < vector_active(cmd_vector); i++) {
2075 const char *string = NULL;
2076 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002077
Harald Welte80d30fe2013-02-12 11:08:57 +01002078 cmd_element = vector_slot(cmd_vector, i);
2079 if (!cmd_element)
2080 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002081
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002082 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2083 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002084 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002085 continue;
2086
Harald Welte80d30fe2013-02-12 11:08:57 +01002087 strvec = cmd_element->strvec;
2088
2089 /* if command is NULL, index may be equal to vector_active */
2090 if (command && index >= vector_active(strvec))
2091 vector_slot(cmd_vector, i) = NULL;
2092 else {
2093 /* Check if command is completed. */
2094 if (command == NULL
2095 && index == vector_active(strvec)) {
2096 string = "<cr>";
2097 if (!desc_unique_string(matchvec, string))
2098 vector_set(matchvec, &desc_cr);
2099 } else {
2100 unsigned int j;
2101 vector descvec = vector_slot(strvec, index);
2102 struct desc *desc;
2103
2104 for (j = 0; j < vector_active(descvec); j++) {
2105 desc = vector_slot(descvec, j);
2106 if (!desc)
2107 continue;
2108 string = cmd_entry_function_desc
2109 (command, desc->cmd);
2110 if (!string)
2111 continue;
2112 /* Uniqueness check */
2113 if (!desc_unique_string(matchvec, string))
2114 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002115 }
2116 }
2117 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002118 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002119 vector_free(cmd_vector);
2120
2121 if (vector_slot(matchvec, 0) == NULL) {
2122 vector_free(matchvec);
2123 *status = CMD_ERR_NO_MATCH;
2124 } else
2125 *status = CMD_SUCCESS;
2126
2127 return matchvec;
2128}
2129
2130vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2131{
2132 vector ret;
2133
2134 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2135 enum node_type onode;
2136 vector shifted_vline;
2137 unsigned int index;
2138
2139 onode = vty->node;
2140 vty->node = ENABLE_NODE;
2141 /* We can try it on enable node, cos' the vty is authenticated */
2142
2143 shifted_vline = vector_init(vector_count(vline));
2144 /* use memcpy? */
2145 for (index = 1; index < vector_active(vline); index++) {
2146 vector_set_index(shifted_vline, index - 1,
2147 vector_lookup(vline, index));
2148 }
2149
2150 ret = cmd_describe_command_real(shifted_vline, vty, status);
2151
2152 vector_free(shifted_vline);
2153 vty->node = onode;
2154 return ret;
2155 }
2156
2157 return cmd_describe_command_real(vline, vty, status);
2158}
2159
2160/* Check LCD of matched command. */
2161static int cmd_lcd(char **matched)
2162{
2163 int i;
2164 int j;
2165 int lcd = -1;
2166 char *s1, *s2;
2167 char c1, c2;
2168
2169 if (matched[0] == NULL || matched[1] == NULL)
2170 return 0;
2171
2172 for (i = 1; matched[i] != NULL; i++) {
2173 s1 = matched[i - 1];
2174 s2 = matched[i];
2175
2176 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2177 if (c1 != c2)
2178 break;
2179
2180 if (lcd < 0)
2181 lcd = j;
2182 else {
2183 if (lcd > j)
2184 lcd = j;
2185 }
2186 }
2187 return lcd;
2188}
2189
2190/* Command line completion support. */
2191static char **cmd_complete_command_real(vector vline, struct vty *vty,
2192 int *status)
2193{
2194 unsigned int i;
2195 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2196#define INIT_MATCHVEC_SIZE 10
2197 vector matchvec;
2198 struct cmd_element *cmd_element;
2199 unsigned int index;
2200 char **match_str;
2201 struct desc *desc;
2202 vector descvec;
2203 char *command;
2204 int lcd;
2205
2206 if (vector_active(vline) == 0) {
2207 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002208 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002209 return NULL;
2210 } else
2211 index = vector_active(vline) - 1;
2212
2213 /* First, filter by preceeding command string */
2214 for (i = 0; i < index; i++)
2215 if ((command = vector_slot(vline, i))) {
2216 enum match_type match;
2217 int ret;
2218
2219 /* First try completion match, if there is exactly match return 1 */
2220 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002221 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002222
2223 /* If there is exact match then filter ambiguous match else check
2224 ambiguousness. */
2225 if ((ret =
2226 is_cmd_ambiguous(command, cmd_vector, i,
2227 match)) == 1) {
2228 vector_free(cmd_vector);
2229 *status = CMD_ERR_AMBIGUOUS;
2230 return NULL;
2231 }
2232 /*
2233 else if (ret == 2)
2234 {
2235 vector_free (cmd_vector);
2236 *status = CMD_ERR_NO_MATCH;
2237 return NULL;
2238 }
2239 */
2240 }
2241
2242 /* Prepare match vector. */
2243 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2244
2245 /* Now we got into completion */
2246 for (i = 0; i < vector_active(cmd_vector); i++)
2247 if ((cmd_element = vector_slot(cmd_vector, i))) {
2248 const char *string;
2249 vector strvec = cmd_element->strvec;
2250
2251 /* Check field length */
2252 if (index >= vector_active(strvec))
2253 vector_slot(cmd_vector, i) = NULL;
2254 else {
2255 unsigned int j;
2256
2257 descvec = vector_slot(strvec, index);
2258 for (j = 0; j < vector_active(descvec); j++)
2259 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002260 const char *cmd = desc->cmd;
2261 char *tmp = NULL;
2262
2263 if (CMD_OPTION(desc->cmd)) {
2264 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2265 cmd = tmp;
2266 }
2267 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002268 if (cmd_unique_string (matchvec, string))
2269 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002270 if (tmp)
2271 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002272 }
2273 }
2274 }
2275
2276 /* We don't need cmd_vector any more. */
2277 vector_free(cmd_vector);
2278
2279 /* No matched command */
2280 if (vector_slot(matchvec, 0) == NULL) {
2281 vector_free(matchvec);
2282
2283 /* In case of 'command \t' pattern. Do you need '?' command at
2284 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002285 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002286 *status = CMD_ERR_NOTHING_TODO;
2287 else
2288 *status = CMD_ERR_NO_MATCH;
2289 return NULL;
2290 }
2291
2292 /* Only one matched */
2293 if (vector_slot(matchvec, 1) == NULL) {
2294 match_str = (char **)matchvec->index;
2295 vector_only_wrapper_free(matchvec);
2296 *status = CMD_COMPLETE_FULL_MATCH;
2297 return match_str;
2298 }
2299 /* Make it sure last element is NULL. */
2300 vector_set(matchvec, NULL);
2301
2302 /* Check LCD of matched strings. */
2303 if (vector_slot(vline, index) != NULL) {
2304 lcd = cmd_lcd((char **)matchvec->index);
2305
2306 if (lcd) {
2307 int len = strlen(vector_slot(vline, index));
2308
2309 if (len < lcd) {
2310 char *lcdstr;
2311
2312 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2313 "complete-lcdstr");
2314 memcpy(lcdstr, matchvec->index[0], lcd);
2315 lcdstr[lcd] = '\0';
2316
2317 /* match_str = (char **) &lcdstr; */
2318
2319 /* Free matchvec. */
2320 for (i = 0; i < vector_active(matchvec); i++) {
2321 if (vector_slot(matchvec, i))
2322 talloc_free(vector_slot(matchvec, i));
2323 }
2324 vector_free(matchvec);
2325
2326 /* Make new matchvec. */
2327 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2328 vector_set(matchvec, lcdstr);
2329 match_str = (char **)matchvec->index;
2330 vector_only_wrapper_free(matchvec);
2331
2332 *status = CMD_COMPLETE_MATCH;
2333 return match_str;
2334 }
2335 }
2336 }
2337
2338 match_str = (char **)matchvec->index;
2339 vector_only_wrapper_free(matchvec);
2340 *status = CMD_COMPLETE_LIST_MATCH;
2341 return match_str;
2342}
2343
2344char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2345{
2346 char **ret;
2347
2348 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2349 enum node_type onode;
2350 vector shifted_vline;
2351 unsigned int index;
2352
2353 onode = vty->node;
2354 vty->node = ENABLE_NODE;
2355 /* We can try it on enable node, cos' the vty is authenticated */
2356
2357 shifted_vline = vector_init(vector_count(vline));
2358 /* use memcpy? */
2359 for (index = 1; index < vector_active(vline); index++) {
2360 vector_set_index(shifted_vline, index - 1,
2361 vector_lookup(vline, index));
2362 }
2363
2364 ret = cmd_complete_command_real(shifted_vline, vty, status);
2365
2366 vector_free(shifted_vline);
2367 vty->node = onode;
2368 return ret;
2369 }
2370
2371 return cmd_complete_command_real(vline, vty, status);
2372}
2373
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002374static struct vty_parent_node *vty_parent(struct vty *vty)
2375{
2376 return llist_first_entry_or_null(&vty->parent_nodes,
2377 struct vty_parent_node,
2378 entry);
2379}
2380
2381static bool vty_pop_parent(struct vty *vty)
2382{
2383 struct vty_parent_node *parent = vty_parent(vty);
2384 if (!parent)
2385 return false;
2386 llist_del(&parent->entry);
2387 vty->node = parent->node;
2388 vty->priv = parent->priv;
2389 if (vty->indent)
2390 talloc_free(vty->indent);
2391 vty->indent = parent->indent;
2392 talloc_free(parent);
2393 return true;
2394}
2395
2396static void vty_clear_parents(struct vty *vty)
2397{
2398 while (vty_pop_parent(vty));
2399}
2400
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002401/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002402/*
2403 * This function MUST eventually converge on a node when called repeatedly,
2404 * there must not be any cycles.
2405 * All 'config' nodes shall converge on CONFIG_NODE.
2406 * All other 'enable' nodes shall converge on ENABLE_NODE.
2407 * All 'view' only nodes shall converge on VIEW_NODE.
2408 * All other nodes shall converge on themselves or it must be ensured,
2409 * that the user's rights are not extended anyhow by calling this function.
2410 *
2411 * Note that these requirements also apply to all functions that are used
2412 * as go_parent_cb.
2413 * Note also that this function relies on the is_config_child callback to
2414 * recognize non-config nodes if go_parent_cb is not set.
2415 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002416int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002417{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002418 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002419 case AUTH_NODE:
2420 case VIEW_NODE:
2421 case ENABLE_NODE:
2422 case CONFIG_NODE:
2423 vty_clear_parents(vty);
2424 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002425
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002426 case AUTH_ENABLE_NODE:
2427 vty->node = VIEW_NODE;
2428 vty_clear_parents(vty);
2429 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002430
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002431 default:
2432 if (host.app_info->go_parent_cb)
2433 host.app_info->go_parent_cb(vty);
2434 vty_pop_parent(vty);
2435 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002436 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002437
2438 return vty->node;
2439}
2440
2441/* Execute command by argument vline vector. */
2442static int
2443cmd_execute_command_real(vector vline, struct vty *vty,
2444 struct cmd_element **cmd)
2445{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002446 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002447 unsigned int index;
2448 vector cmd_vector;
2449 struct cmd_element *cmd_element;
2450 struct cmd_element *matched_element;
2451 unsigned int matched_count, incomplete_count;
2452 int argc;
2453 const char *argv[CMD_ARGC_MAX];
2454 enum match_type match = 0;
2455 int varflag;
2456 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002457 int rc;
2458 /* Used for temporary storage of cmd_deopt() allocated arguments during
2459 argv[] generation */
2460 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002461
2462 /* Make copy of command elements. */
2463 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2464
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002465 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002466 if ((command = vector_slot(vline, index))) {
2467 int ret;
2468
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002469 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002470 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002471
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002472 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002473 break;
2474
2475 ret =
2476 is_cmd_ambiguous(command, cmd_vector, index, match);
2477
2478 if (ret == 1) {
2479 vector_free(cmd_vector);
2480 return CMD_ERR_AMBIGUOUS;
2481 } else if (ret == 2) {
2482 vector_free(cmd_vector);
2483 return CMD_ERR_NO_MATCH;
2484 }
2485 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002486 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002487
2488 /* Check matched count. */
2489 matched_element = NULL;
2490 matched_count = 0;
2491 incomplete_count = 0;
2492
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002493 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002494 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002495 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002496 || index >= cmd_element->cmdsize) {
2497 matched_element = cmd_element;
2498#if 0
2499 printf("DEBUG: %s\n", cmd_element->string);
2500#endif
2501 matched_count++;
2502 } else {
2503 incomplete_count++;
2504 }
2505 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002506 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002507
2508 /* Finish of using cmd_vector. */
2509 vector_free(cmd_vector);
2510
2511 /* To execute command, matched_count must be 1. */
2512 if (matched_count == 0) {
2513 if (incomplete_count)
2514 return CMD_ERR_INCOMPLETE;
2515 else
2516 return CMD_ERR_NO_MATCH;
2517 }
2518
2519 if (matched_count > 1)
2520 return CMD_ERR_AMBIGUOUS;
2521
2522 /* Argument treatment */
2523 varflag = 0;
2524 argc = 0;
2525
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002526 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2527
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002528 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002529 if (argc == CMD_ARGC_MAX) {
2530 rc = CMD_ERR_EXEED_ARGC_MAX;
2531 goto rc_free_deopt_ctx;
2532 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002533 if (varflag) {
2534 argv[argc++] = vector_slot(vline, i);
2535 continue;
2536 }
2537
2538 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002539 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002540
2541 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002542 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002543 struct desc *desc = vector_slot(descvec, 0);
2544
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002545 if (CMD_OPTION(desc->cmd)) {
2546 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2547 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2548 } else {
2549 tmp_cmd = desc->cmd;
2550 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002551
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002552 if (CMD_VARARG(tmp_cmd))
2553 varflag = 1;
2554 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002555 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002556 else if (CMD_OPTION(desc->cmd))
2557 argv[argc++] = tmp_cmd;
2558 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002559 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002560 /* multi choice argument. look up which choice
2561 the user meant (can only be one after
2562 filtering and checking for ambigous). For instance,
2563 if user typed "th" for "(two|three)" arg, we
2564 want to pass "three" in argv[]. */
2565 for (j = 0; j < vector_active(descvec); j++) {
2566 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002567 if (!desc)
2568 continue;
2569 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2570 continue;
2571 if (CMD_OPTION(desc->cmd)) {
2572 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2573 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2574 } else {
2575 tmp_cmd = desc->cmd;
2576 }
2577
2578 if(CMD_VARIABLE(tmp_cmd)) {
2579 argv[argc++] = vector_slot(vline, i);
2580 } else {
2581 argv[argc++] = tmp_cmd;
2582 }
2583 break;
2584 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002585 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002586 }
2587
2588 /* For vtysh execution. */
2589 if (cmd)
2590 *cmd = matched_element;
2591
2592 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002593 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002594 else {
2595 /* Execute matched command. */
2596 struct vty_parent_node this_node = {
2597 .node = vty->node,
2598 .priv = vty->priv,
2599 .indent = vty->indent,
2600 };
2601 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002602 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002603
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002604 /* If we have stepped down into a child node, push a parent frame.
2605 * The causality is such: we don't expect every single node entry implementation to push
2606 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2607 * a parent node. Hence if the node changed without the parent node changing, we must
2608 * have stepped into a child node. */
2609 if (vty->node != this_node.node && parent == vty_parent(vty)
2610 && vty->node > CONFIG_NODE) {
2611 /* Push the parent node. */
2612 parent = talloc_zero(vty, struct vty_parent_node);
2613 *parent = this_node;
2614 llist_add(&parent->entry, &vty->parent_nodes);
2615 }
2616 }
2617
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002618rc_free_deopt_ctx:
2619 /* Now after we called the command func, we can free temporary strings */
2620 talloc_free(cmd_deopt_ctx);
2621 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002622}
2623
2624int
2625cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2626 int vtysh)
2627{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002628 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002629 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002630
2631 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002632
2633 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2634 vector shifted_vline;
2635 unsigned int index;
2636
2637 vty->node = ENABLE_NODE;
2638 /* We can try it on enable node, cos' the vty is authenticated */
2639
2640 shifted_vline = vector_init(vector_count(vline));
2641 /* use memcpy? */
2642 for (index = 1; index < vector_active(vline); index++) {
2643 vector_set_index(shifted_vline, index - 1,
2644 vector_lookup(vline, index));
2645 }
2646
2647 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2648
2649 vector_free(shifted_vline);
2650 vty->node = onode;
2651 return ret;
2652 }
2653
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002654 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002655}
2656
2657/* Execute command by argument readline. */
2658int
2659cmd_execute_command_strict(vector vline, struct vty *vty,
2660 struct cmd_element **cmd)
2661{
2662 unsigned int i;
2663 unsigned int index;
2664 vector cmd_vector;
2665 struct cmd_element *cmd_element;
2666 struct cmd_element *matched_element;
2667 unsigned int matched_count, incomplete_count;
2668 int argc;
2669 const char *argv[CMD_ARGC_MAX];
2670 int varflag;
2671 enum match_type match = 0;
2672 char *command;
2673
2674 /* Make copy of command element */
2675 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2676
2677 for (index = 0; index < vector_active(vline); index++)
2678 if ((command = vector_slot(vline, index))) {
2679 int ret;
2680
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002681 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002682 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002683
2684 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002685 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002686 break;
2687
2688 ret =
2689 is_cmd_ambiguous(command, cmd_vector, index, match);
2690 if (ret == 1) {
2691 vector_free(cmd_vector);
2692 return CMD_ERR_AMBIGUOUS;
2693 }
2694 if (ret == 2) {
2695 vector_free(cmd_vector);
2696 return CMD_ERR_NO_MATCH;
2697 }
2698 }
2699
2700 /* Check matched count. */
2701 matched_element = NULL;
2702 matched_count = 0;
2703 incomplete_count = 0;
2704 for (i = 0; i < vector_active(cmd_vector); i++)
2705 if (vector_slot(cmd_vector, i) != NULL) {
2706 cmd_element = vector_slot(cmd_vector, i);
2707
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002708 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002709 || index >= cmd_element->cmdsize) {
2710 matched_element = cmd_element;
2711 matched_count++;
2712 } else
2713 incomplete_count++;
2714 }
2715
2716 /* Finish of using cmd_vector. */
2717 vector_free(cmd_vector);
2718
2719 /* To execute command, matched_count must be 1. */
2720 if (matched_count == 0) {
2721 if (incomplete_count)
2722 return CMD_ERR_INCOMPLETE;
2723 else
2724 return CMD_ERR_NO_MATCH;
2725 }
2726
2727 if (matched_count > 1)
2728 return CMD_ERR_AMBIGUOUS;
2729
2730 /* Argument treatment */
2731 varflag = 0;
2732 argc = 0;
2733
2734 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002735 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002736 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002737 if (varflag) {
2738 argv[argc++] = vector_slot(vline, i);
2739 continue;
2740 }
2741
2742 vector descvec = vector_slot(matched_element->strvec, i);
2743
2744 if (vector_active(descvec) == 1) {
2745 struct desc *desc = vector_slot(descvec, 0);
2746
2747 if (CMD_VARARG(desc->cmd))
2748 varflag = 1;
2749
2750 if (varflag || CMD_VARIABLE(desc->cmd)
2751 || CMD_OPTION(desc->cmd))
2752 argv[argc++] = vector_slot(vline, i);
2753 } else {
2754 argv[argc++] = vector_slot(vline, i);
2755 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002756 }
2757
2758 /* For vtysh execution. */
2759 if (cmd)
2760 *cmd = matched_element;
2761
2762 if (matched_element->daemon)
2763 return CMD_SUCCESS_DAEMON;
2764
2765 /* Now execute matched command */
2766 return (*matched_element->func) (matched_element, vty, argc, argv);
2767}
2768
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002769static inline size_t len(const char *str)
2770{
2771 return str? strlen(str) : 0;
2772}
2773
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002774/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2775 * is longer than b, a must start with exactly b, and vice versa.
2776 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2777 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002778static int indent_cmp(const char *a, const char *b)
2779{
2780 size_t al, bl;
2781 al = len(a);
2782 bl = len(b);
2783 if (al > bl) {
2784 if (bl && strncmp(a, b, bl) != 0)
2785 return EINVAL;
2786 return 1;
2787 }
2788 /* al <= bl */
2789 if (al && strncmp(a, b, al) != 0)
2790 return EINVAL;
2791 return (al < bl)? -1 : 0;
2792}
2793
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002794/* Configration make from file. */
2795int config_from_file(struct vty *vty, FILE * fp)
2796{
2797 int ret;
2798 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002799 char *indent;
2800 int cmp;
2801 struct vty_parent_node this_node;
2802 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002803
2804 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002805 indent = NULL;
2806 vline = NULL;
2807 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002808
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002809 if (ret != CMD_SUCCESS)
2810 goto return_invalid_indent;
2811
2812 /* In case of comment or empty line */
2813 if (vline == NULL) {
2814 if (indent) {
2815 talloc_free(indent);
2816 indent = NULL;
2817 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002818 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002819 }
2820
Neels Hofmeyr43063632017-09-19 23:54:01 +02002821 /* We have a nonempty line. */
2822 if (!vty->indent) {
2823 /* We have just entered a node and expecting the first child to come up; but we
2824 * may also skip right back to a parent or ancestor level. */
2825 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002826
Neels Hofmeyr43063632017-09-19 23:54:01 +02002827 /* If there is no parent, record any indentation we encounter. */
2828 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2829
2830 if (cmp == EINVAL)
2831 goto return_invalid_indent;
2832
2833 if (cmp <= 0) {
2834 /* We have gone right back to the parent level or higher, we are skipping
2835 * this child node level entirely. Pop the parent to go back to a node
2836 * that was actually there (to reinstate vty->indent) and re-use below
2837 * go-parent while-loop to find an accurate match of indent in the node
2838 * ancestry. */
2839 vty_go_parent(vty);
2840 } else {
2841 /* The indent is deeper than the just entered parent, record the new
2842 * indentation characters. */
2843 vty->indent = talloc_strdup(vty, indent);
2844 /* This *is* the new indentation. */
2845 cmp = 0;
2846 }
2847 } else {
2848 /* There is a known indentation for this node level, validate and detect node
2849 * exits. */
2850 cmp = indent_cmp(indent, vty->indent);
2851 if (cmp == EINVAL)
2852 goto return_invalid_indent;
2853 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002854
2855 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2856 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2857 while (cmp < 0) {
2858 vty_go_parent(vty);
2859 cmp = indent_cmp(indent, vty->indent);
2860 if (cmp == EINVAL)
2861 goto return_invalid_indent;
2862 }
2863
2864 /* More indent without having entered a child node level? Either the parent node's indent
2865 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2866 * or the indentation increased even though the vty command didn't enter a child. */
2867 if (cmp > 0)
2868 goto return_invalid_indent;
2869
2870 /* Remember the current node before the command possibly changes it. */
2871 this_node = (struct vty_parent_node){
2872 .node = vty->node,
2873 .priv = vty->priv,
2874 .indent = vty->indent,
2875 };
2876
2877 parent = vty_parent(vty);
2878 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002879 cmd_free_strvec(vline);
2880
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002881 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002882 if (indent) {
2883 talloc_free(indent);
2884 indent = NULL;
2885 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002886 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002887 }
2888
2889 /* If we have stepped down into a child node, push a parent frame.
2890 * The causality is such: we don't expect every single node entry implementation to push
2891 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2892 * a parent node. Hence if the node changed without the parent node changing, we must
2893 * have stepped into a child node (and now expect a deeper indent). */
2894 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2895 /* Push the parent node. */
2896 parent = talloc_zero(vty, struct vty_parent_node);
2897 *parent = this_node;
2898 llist_add(&parent->entry, &vty->parent_nodes);
2899
2900 /* The current talloc'ed vty->indent string will now be owned by this parent
2901 * struct. Indicate that we don't know what deeper indent characters the user
2902 * will choose. */
2903 vty->indent = NULL;
2904 }
2905
2906 if (indent) {
2907 talloc_free(indent);
2908 indent = NULL;
2909 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002910 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002911 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2912 while (vty_parent(vty))
2913 vty_go_parent(vty);
2914
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002915 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002916
2917return_invalid_indent:
2918 if (vline)
2919 cmd_free_strvec(vline);
2920 if (indent) {
2921 talloc_free(indent);
2922 indent = NULL;
2923 }
2924 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002925}
2926
2927/* Configration from terminal */
2928DEFUN(config_terminal,
2929 config_terminal_cmd,
2930 "configure terminal",
2931 "Configuration from vty interface\n" "Configuration terminal\n")
2932{
2933 if (vty_config_lock(vty))
2934 vty->node = CONFIG_NODE;
2935 else {
2936 vty_out(vty, "VTY configuration is locked by other VTY%s",
2937 VTY_NEWLINE);
2938 return CMD_WARNING;
2939 }
2940 return CMD_SUCCESS;
2941}
2942
2943/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002944DEFUN(enable, config_enable_cmd,
2945 "enable [expert-mode]",
2946 "Turn on privileged mode command\n"
2947 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002948{
2949 /* If enable password is NULL, change to ENABLE_NODE */
2950 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2951 vty->type == VTY_SHELL_SERV)
2952 vty->node = ENABLE_NODE;
2953 else
2954 vty->node = AUTH_ENABLE_NODE;
2955
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002956 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002957
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002958 return CMD_SUCCESS;
2959}
2960
2961/* Disable command */
2962DEFUN(disable,
2963 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2964{
2965 if (vty->node == ENABLE_NODE)
2966 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002967
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002968 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002969
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002970 return CMD_SUCCESS;
2971}
2972
2973/* Down vty node level. */
2974gDEFUN(config_exit,
2975 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2976{
2977 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002978 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002979 case VIEW_NODE:
2980 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002981 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002982 break;
2983 case CONFIG_NODE:
2984 vty->node = ENABLE_NODE;
2985 vty_config_unlock(vty);
2986 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002987 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002988 if (vty->node > CONFIG_NODE)
2989 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002990 break;
2991 }
2992 return CMD_SUCCESS;
2993}
2994
2995/* End of configuration. */
2996 gDEFUN(config_end,
2997 config_end_cmd, "end", "End current mode and change to enable mode.")
2998{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002999 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02003000 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003001
3002 /* Repeatedly call go_parent until a top node is reached. */
3003 while (vty->node > CONFIG_NODE) {
3004 if (vty->node == last_node) {
3005 /* Ensure termination, this shouldn't happen. */
3006 break;
3007 }
3008 last_node = vty->node;
3009 vty_go_parent(vty);
3010 }
3011
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003012 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003013 if (vty->node > ENABLE_NODE)
3014 vty->node = ENABLE_NODE;
3015 vty->index = NULL;
3016 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003017 }
3018 return CMD_SUCCESS;
3019}
3020
3021/* Show version. */
3022DEFUN(show_version,
3023 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3024{
Harald Welte237f6242010-05-25 23:00:45 +02003025 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3026 host.app_info->version,
3027 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3028 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003029
3030 return CMD_SUCCESS;
3031}
3032
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003033DEFUN(show_online_help,
3034 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3035{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003036 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003037 return CMD_SUCCESS;
3038}
3039
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003040/* Help display function for all node. */
3041gDEFUN(config_help,
3042 config_help_cmd, "help", "Description of the interactive help system\n")
3043{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003044 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3045 "anytime at the command line please press '?'.%s%s"
3046 "If nothing matches, the help list will be empty and you must backup%s"
3047 " until entering a '?' shows the available options.%s"
3048 "Two styles of help are provided:%s"
3049 "1. Full help is available when you are ready to enter a%s"
3050 "command argument (e.g. 'show ?') and describes each possible%s"
3051 "argument.%s"
3052 "2. Partial help is provided when an abbreviated argument is entered%s"
3053 " and you want to know what arguments match the input%s"
3054 " (e.g. 'show me?'.)%s%s",
3055 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3056 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3057 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3058 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003059 return CMD_SUCCESS;
3060}
3061
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003062enum {
3063 ATTR_TYPE_GLOBAL = (1 << 0),
3064 ATTR_TYPE_LIB = (1 << 1),
3065 ATTR_TYPE_APP = (1 << 2),
3066};
3067
3068static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3069{
3070 const char *desc;
3071 unsigned int i;
3072 bool found;
3073 char flag;
3074
3075 if (attr_mask & ATTR_TYPE_GLOBAL) {
3076 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3077
3078 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003079 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003080 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003081
3082 /* Skip attributes without flags */
3083 if (flag != '.')
3084 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003085 }
3086 }
3087
3088 if (attr_mask & ATTR_TYPE_LIB) {
3089 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3090
3091 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3092 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3093 continue;
3094 found = true;
3095
3096 flag = cmd_lib_attr_letters[i];
3097 if (flag == '\0')
3098 flag = '.';
3099
3100 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3101 }
3102
3103 if (!found)
3104 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3105 }
3106
3107 if (attr_mask & ATTR_TYPE_APP) {
3108 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3109
3110 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3111 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3112 continue;
3113 found = true;
3114
3115 flag = host.app_info->usr_attr_letters[i];
3116 if (flag == '\0')
3117 flag = '.';
3118
3119 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3120 }
3121
3122 if (!found)
3123 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3124 }
3125}
3126
3127gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3128 "show vty-attributes",
3129 SHOW_STR "List of VTY attributes\n")
3130{
3131 print_attr_list(vty, 0xff);
3132 return CMD_SUCCESS;
3133}
3134
3135gDEFUN(show_vty_attr, show_vty_attr_cmd,
3136 "show vty-attributes (application|library|global)",
3137 SHOW_STR "List of VTY attributes\n"
3138 "Application specific attributes only\n"
3139 "Library specific attributes only\n"
3140 "Global attributes only\n")
3141{
3142 unsigned int attr_mask = 0;
3143
3144 if (argv[0][0] == 'g') /* global */
3145 attr_mask |= ATTR_TYPE_GLOBAL;
3146 else if (argv[0][0] == 'l') /* library */
3147 attr_mask |= ATTR_TYPE_LIB;
3148 else if (argv[0][0] == 'a') /* application */
3149 attr_mask |= ATTR_TYPE_APP;
3150
3151 print_attr_list(vty, attr_mask);
3152 return CMD_SUCCESS;
3153}
3154
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003155/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003156static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003157{
3158 unsigned int flag_mask = 0x00;
3159 unsigned int f, i;
3160
3161 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3162 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3163 const struct cmd_element *cmd;
3164 char flag_letter;
3165
3166 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3167 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003168 if (cmd->attr & CMD_ATTR_DEPRECATED)
3169 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003170 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003171 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003172 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003173 continue;
3174
3175 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3176 flag_letter = cmd_lib_attr_letters[f];
3177 else
3178 flag_letter = host.app_info->usr_attr_letters[f];
3179
3180 if (flag_letter == '\0')
3181 continue;
3182
3183 flag_mask |= (1 << f);
3184 break;
3185 }
3186 }
3187
3188 return flag_mask;
3189}
3190
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003191/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3192static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3193{
3194 static char char_mask[8 + 1];
3195 char *ptr = &char_mask[0];
3196
3197 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003198 if (cmd->attr & CMD_ATTR_HIDDEN)
3199 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3200 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003201 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3202 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3203 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3204 else
3205 *(ptr++) = '.';
3206
3207 *ptr = '\0';
3208
3209 return char_mask;
3210}
3211
3212/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003213static const char *cmd_flag_mask(const struct cmd_element *cmd,
3214 unsigned int flag_mask)
3215{
3216 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3217 char *ptr = &char_mask[0];
3218 char flag_letter;
3219 unsigned int f;
3220
3221 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003222 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003223 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003224 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003225 *(ptr++) = '.';
3226 continue;
3227 }
3228
3229 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3230 flag_letter = cmd_lib_attr_letters[f];
3231 else
3232 flag_letter = host.app_info->usr_attr_letters[f];
3233
3234 *(ptr++) = flag_letter ? flag_letter : '.';
3235 }
3236
3237 *ptr = '\0';
3238
3239 return char_mask;
3240}
3241
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003242/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003243gDEFUN(config_list, config_list_cmd,
3244 "list [with-flags]",
3245 "Print command list\n"
3246 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003247{
3248 unsigned int i;
3249 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003250 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003251 struct cmd_element *cmd;
3252
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003253 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003254 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003255
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003256 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3257 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3258 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003259 if (cmd->attr & CMD_ATTR_DEPRECATED)
3260 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003261 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003262 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003263 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003264 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3265 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003266 vty_out(vty, " %s %s %s%s",
3267 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003268 cmd_flag_mask(cmd, flag_mask),
3269 cmd->string, VTY_NEWLINE);
3270 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003271 }
3272
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003273 return CMD_SUCCESS;
3274}
3275
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003276static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003277{
3278 unsigned int i;
3279 int fd;
3280 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003281 char *config_file_tmp = NULL;
3282 char *config_file_sav = NULL;
3283 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003284 struct stat st;
3285
3286 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003287
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003288 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3289 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3290 * manually instead. */
3291
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003292 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003293 config_file_sav =
3294 _talloc_zero(tall_vty_cmd_ctx,
3295 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3296 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003297 if (!config_file_sav)
3298 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003299 strcpy(config_file_sav, config_file);
3300 strcat(config_file_sav, CONF_BACKUP_EXT);
3301
3302 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003303 "config_file_tmp");
3304 if (!config_file_tmp) {
3305 talloc_free(config_file_sav);
3306 return -1;
3307 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003308 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3309
3310 /* Open file to configuration write. */
3311 fd = mkstemp(config_file_tmp);
3312 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003313 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003314 talloc_free(config_file_tmp);
3315 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003316 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003317 }
3318
3319 /* Make vty for configuration file. */
3320 file_vty = vty_new();
3321 file_vty->fd = fd;
3322 file_vty->type = VTY_FILE;
3323
3324 /* Config file header print. */
3325 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003326 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003327 //vty_time_print (file_vty, 1);
3328 vty_out(file_vty, "!\n");
3329
3330 for (i = 0; i < vector_active(cmdvec); i++)
3331 if ((node = vector_slot(cmdvec, i)) && node->func) {
3332 if ((*node->func) (file_vty))
3333 vty_out(file_vty, "!\n");
3334 }
3335 vty_close(file_vty);
3336
3337 if (unlink(config_file_sav) != 0)
3338 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003339 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003340 talloc_free(config_file_sav);
3341 talloc_free(config_file_tmp);
3342 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003343 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003344 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003345
3346 /* Only link the .sav file if the original file exists */
3347 if (stat(config_file, &st) == 0) {
3348 if (link(config_file, config_file_sav) != 0) {
3349 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3350 talloc_free(config_file_sav);
3351 talloc_free(config_file_tmp);
3352 unlink(config_file_tmp);
3353 return -3;
3354 }
3355 sync();
3356 if (unlink(config_file) != 0) {
3357 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3358 talloc_free(config_file_sav);
3359 talloc_free(config_file_tmp);
3360 unlink(config_file_tmp);
3361 return -4;
3362 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003363 }
3364 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003365 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003366 talloc_free(config_file_sav);
3367 talloc_free(config_file_tmp);
3368 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003369 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003370 }
3371 unlink(config_file_tmp);
3372 sync();
3373
3374 talloc_free(config_file_sav);
3375 talloc_free(config_file_tmp);
3376
3377 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003378 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3379 return -6;
3380 }
3381
3382 return 0;
3383}
3384
3385
3386/* Write current configuration into file. */
3387DEFUN(config_write_file,
3388 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003389 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003390 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003391 "Write to configuration file\n"
3392 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003393{
3394 char *failed_file;
3395 int rc;
3396
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003397 if (host.app_info->config_is_consistent) {
3398 rc = host.app_info->config_is_consistent(vty);
3399 if (!rc) {
3400 vty_out(vty, "Configuration is not consistent%s",
3401 VTY_NEWLINE);
3402 return CMD_WARNING;
3403 }
3404 }
3405
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003406 if (argc == 1)
3407 host_config_set(argv[0]);
3408
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003409 if (host.config == NULL) {
3410 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3411 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003412 return CMD_WARNING;
3413 }
3414
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003415 rc = write_config_file(host.config, &failed_file);
3416 switch (rc) {
3417 case -1:
3418 vty_out(vty, "Can't open configuration file %s.%s",
3419 failed_file, VTY_NEWLINE);
3420 rc = CMD_WARNING;
3421 break;
3422 case -2:
3423 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3424 failed_file, VTY_NEWLINE);
3425 rc = CMD_WARNING;
3426 break;
3427 case -3:
3428 vty_out(vty, "Can't backup old configuration file %s.%s",
3429 failed_file, VTY_NEWLINE);
3430 rc = CMD_WARNING;
3431 break;
3432 case -4:
3433 vty_out(vty, "Can't unlink configuration file %s.%s",
3434 failed_file, VTY_NEWLINE);
3435 rc = CMD_WARNING;
3436 break;
3437 case -5:
3438 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3439 VTY_NEWLINE);
3440 rc = CMD_WARNING;
3441 break;
3442 case -6:
3443 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3444 failed_file, strerror(errno), errno, VTY_NEWLINE);
3445 rc = CMD_WARNING;
3446 break;
3447 default:
3448 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3449 rc = CMD_SUCCESS;
3450 break;
3451 }
3452
3453 talloc_free(failed_file);
3454 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003455}
3456
3457ALIAS(config_write_file,
3458 config_write_cmd,
3459 "write", "Write running configuration to memory, network, or terminal\n")
3460
3461 ALIAS(config_write_file,
3462 config_write_memory_cmd,
3463 "write memory",
3464 "Write running configuration to memory, network, or terminal\n"
3465 "Write configuration to the file (same as write file)\n")
3466
3467 ALIAS(config_write_file,
3468 copy_runningconfig_startupconfig_cmd,
3469 "copy running-config startup-config",
3470 "Copy configuration\n"
3471 "Copy running config to... \n"
3472 "Copy running config to startup config (same as write file)\n")
3473
3474/* Write current configuration into the terminal. */
3475 DEFUN(config_write_terminal,
3476 config_write_terminal_cmd,
3477 "write terminal",
3478 "Write running configuration to memory, network, or terminal\n"
3479 "Write to terminal\n")
3480{
3481 unsigned int i;
3482 struct cmd_node *node;
3483
3484 if (vty->type == VTY_SHELL_SERV) {
3485 for (i = 0; i < vector_active(cmdvec); i++)
3486 if ((node = vector_slot(cmdvec, i)) && node->func
3487 && node->vtysh) {
3488 if ((*node->func) (vty))
3489 vty_out(vty, "!%s", VTY_NEWLINE);
3490 }
3491 } else {
3492 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3493 VTY_NEWLINE);
3494 vty_out(vty, "!%s", VTY_NEWLINE);
3495
3496 for (i = 0; i < vector_active(cmdvec); i++)
3497 if ((node = vector_slot(cmdvec, i)) && node->func) {
3498 if ((*node->func) (vty))
3499 vty_out(vty, "!%s", VTY_NEWLINE);
3500 }
3501 vty_out(vty, "end%s", VTY_NEWLINE);
3502 }
3503 return CMD_SUCCESS;
3504}
3505
3506/* Write current configuration into the terminal. */
3507ALIAS(config_write_terminal,
3508 show_running_config_cmd,
3509 "show running-config", SHOW_STR "running configuration\n")
3510
3511/* Write startup configuration into the terminal. */
3512 DEFUN(show_startup_config,
3513 show_startup_config_cmd,
3514 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3515{
3516 char buf[BUFSIZ];
3517 FILE *confp;
3518
3519 confp = fopen(host.config, "r");
3520 if (confp == NULL) {
3521 vty_out(vty, "Can't open configuration file [%s]%s",
3522 host.config, VTY_NEWLINE);
3523 return CMD_WARNING;
3524 }
3525
3526 while (fgets(buf, BUFSIZ, confp)) {
3527 char *cp = buf;
3528
3529 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3530 cp++;
3531 *cp = '\0';
3532
3533 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3534 }
3535
3536 fclose(confp);
3537
3538 return CMD_SUCCESS;
3539}
3540
3541/* Hostname configuration */
3542DEFUN(config_hostname,
3543 hostname_cmd,
3544 "hostname WORD",
3545 "Set system's network name\n" "This system's network name\n")
3546{
3547 if (!isalpha((int)*argv[0])) {
3548 vty_out(vty, "Please specify string starting with alphabet%s",
3549 VTY_NEWLINE);
3550 return CMD_WARNING;
3551 }
3552
3553 if (host.name)
3554 talloc_free(host.name);
3555
3556 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3557 return CMD_SUCCESS;
3558}
3559
3560DEFUN(config_no_hostname,
3561 no_hostname_cmd,
3562 "no hostname [HOSTNAME]",
3563 NO_STR "Reset system's network name\n" "Host name of this router\n")
3564{
3565 if (host.name)
3566 talloc_free(host.name);
3567 host.name = NULL;
3568 return CMD_SUCCESS;
3569}
3570
3571/* VTY interface password set. */
3572DEFUN(config_password, password_cmd,
3573 "password (8|) WORD",
3574 "Assign the terminal connection password\n"
3575 "Specifies a HIDDEN password will follow\n"
3576 "dummy string \n" "The HIDDEN line password string\n")
3577{
3578 /* Argument check. */
3579 if (argc == 0) {
3580 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3581 return CMD_WARNING;
3582 }
3583
3584 if (argc == 2) {
3585 if (*argv[0] == '8') {
3586 if (host.password)
3587 talloc_free(host.password);
3588 host.password = NULL;
3589 if (host.password_encrypt)
3590 talloc_free(host.password_encrypt);
3591 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3592 return CMD_SUCCESS;
3593 } else {
3594 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3595 return CMD_WARNING;
3596 }
3597 }
3598
3599 if (!isalnum((int)*argv[0])) {
3600 vty_out(vty,
3601 "Please specify string starting with alphanumeric%s",
3602 VTY_NEWLINE);
3603 return CMD_WARNING;
3604 }
3605
3606 if (host.password)
3607 talloc_free(host.password);
3608 host.password = NULL;
3609
3610#ifdef VTY_CRYPT_PW
3611 if (host.encrypt) {
3612 if (host.password_encrypt)
3613 talloc_free(host.password_encrypt);
3614 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3615 } else
3616#endif
3617 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3618
3619 return CMD_SUCCESS;
3620}
3621
3622ALIAS(config_password, password_text_cmd,
3623 "password LINE",
3624 "Assign the terminal connection password\n"
3625 "The UNENCRYPTED (cleartext) line password\n")
3626
3627/* VTY enable password set. */
3628 DEFUN(config_enable_password, enable_password_cmd,
3629 "enable password (8|) WORD",
3630 "Modify enable password parameters\n"
3631 "Assign the privileged level password\n"
3632 "Specifies a HIDDEN password will follow\n"
3633 "dummy string \n" "The HIDDEN 'enable' password string\n")
3634{
3635 /* Argument check. */
3636 if (argc == 0) {
3637 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3638 return CMD_WARNING;
3639 }
3640
3641 /* Crypt type is specified. */
3642 if (argc == 2) {
3643 if (*argv[0] == '8') {
3644 if (host.enable)
3645 talloc_free(host.enable);
3646 host.enable = NULL;
3647
3648 if (host.enable_encrypt)
3649 talloc_free(host.enable_encrypt);
3650 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3651
3652 return CMD_SUCCESS;
3653 } else {
3654 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3655 return CMD_WARNING;
3656 }
3657 }
3658
3659 if (!isalnum((int)*argv[0])) {
3660 vty_out(vty,
3661 "Please specify string starting with alphanumeric%s",
3662 VTY_NEWLINE);
3663 return CMD_WARNING;
3664 }
3665
3666 if (host.enable)
3667 talloc_free(host.enable);
3668 host.enable = NULL;
3669
3670 /* Plain password input. */
3671#ifdef VTY_CRYPT_PW
3672 if (host.encrypt) {
3673 if (host.enable_encrypt)
3674 talloc_free(host.enable_encrypt);
3675 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3676 } else
3677#endif
3678 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3679
3680 return CMD_SUCCESS;
3681}
3682
3683ALIAS(config_enable_password,
3684 enable_password_text_cmd,
3685 "enable password LINE",
3686 "Modify enable password parameters\n"
3687 "Assign the privileged level password\n"
3688 "The UNENCRYPTED (cleartext) 'enable' password\n")
3689
3690/* VTY enable password delete. */
3691 DEFUN(no_config_enable_password, no_enable_password_cmd,
3692 "no enable password",
3693 NO_STR
3694 "Modify enable password parameters\n"
3695 "Assign the privileged level password\n")
3696{
3697 if (host.enable)
3698 talloc_free(host.enable);
3699 host.enable = NULL;
3700
3701 if (host.enable_encrypt)
3702 talloc_free(host.enable_encrypt);
3703 host.enable_encrypt = NULL;
3704
3705 return CMD_SUCCESS;
3706}
3707
3708#ifdef VTY_CRYPT_PW
3709DEFUN(service_password_encrypt,
3710 service_password_encrypt_cmd,
3711 "service password-encryption",
3712 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3713{
3714 if (host.encrypt)
3715 return CMD_SUCCESS;
3716
3717 host.encrypt = 1;
3718
3719 if (host.password) {
3720 if (host.password_encrypt)
3721 talloc_free(host.password_encrypt);
3722 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3723 }
3724 if (host.enable) {
3725 if (host.enable_encrypt)
3726 talloc_free(host.enable_encrypt);
3727 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3728 }
3729
3730 return CMD_SUCCESS;
3731}
3732
3733DEFUN(no_service_password_encrypt,
3734 no_service_password_encrypt_cmd,
3735 "no service password-encryption",
3736 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3737{
3738 if (!host.encrypt)
3739 return CMD_SUCCESS;
3740
3741 host.encrypt = 0;
3742
3743 if (host.password_encrypt)
3744 talloc_free(host.password_encrypt);
3745 host.password_encrypt = NULL;
3746
3747 if (host.enable_encrypt)
3748 talloc_free(host.enable_encrypt);
3749 host.enable_encrypt = NULL;
3750
3751 return CMD_SUCCESS;
3752}
3753#endif
3754
3755DEFUN(config_terminal_length, config_terminal_length_cmd,
3756 "terminal length <0-512>",
3757 "Set terminal line parameters\n"
3758 "Set number of lines on a screen\n"
3759 "Number of lines on screen (0 for no pausing)\n")
3760{
3761 int lines;
3762 char *endptr = NULL;
3763
3764 lines = strtol(argv[0], &endptr, 10);
3765 if (lines < 0 || lines > 512 || *endptr != '\0') {
3766 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3767 return CMD_WARNING;
3768 }
3769 vty->lines = lines;
3770
3771 return CMD_SUCCESS;
3772}
3773
3774DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3775 "terminal no length",
3776 "Set terminal line parameters\n"
3777 NO_STR "Set number of lines on a screen\n")
3778{
3779 vty->lines = -1;
3780 return CMD_SUCCESS;
3781}
3782
3783DEFUN(service_terminal_length, service_terminal_length_cmd,
3784 "service terminal-length <0-512>",
3785 "Set up miscellaneous service\n"
3786 "System wide terminal length configuration\n"
3787 "Number of lines of VTY (0 means no line control)\n")
3788{
3789 int lines;
3790 char *endptr = NULL;
3791
3792 lines = strtol(argv[0], &endptr, 10);
3793 if (lines < 0 || lines > 512 || *endptr != '\0') {
3794 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3795 return CMD_WARNING;
3796 }
3797 host.lines = lines;
3798
3799 return CMD_SUCCESS;
3800}
3801
3802DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3803 "no service terminal-length [<0-512>]",
3804 NO_STR
3805 "Set up miscellaneous service\n"
3806 "System wide terminal length configuration\n"
3807 "Number of lines of VTY (0 means no line control)\n")
3808{
3809 host.lines = -1;
3810 return CMD_SUCCESS;
3811}
3812
3813DEFUN_HIDDEN(do_echo,
3814 echo_cmd,
3815 "echo .MESSAGE",
3816 "Echo a message back to the vty\n" "The message to echo\n")
3817{
3818 char *message;
3819
3820 vty_out(vty, "%s%s",
3821 ((message =
3822 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3823 if (message)
3824 talloc_free(message);
3825 return CMD_SUCCESS;
3826}
3827
3828#if 0
3829DEFUN(config_logmsg,
3830 config_logmsg_cmd,
3831 "logmsg " LOG_LEVELS " .MESSAGE",
3832 "Send a message to enabled logging destinations\n"
3833 LOG_LEVEL_DESC "The message to send\n")
3834{
3835 int level;
3836 char *message;
3837
3838 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3839 return CMD_ERR_NO_MATCH;
3840
3841 zlog(NULL, level,
3842 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3843 if (message)
3844 talloc_free(message);
3845 return CMD_SUCCESS;
3846}
3847
3848DEFUN(show_logging,
3849 show_logging_cmd,
3850 "show logging", SHOW_STR "Show current logging configuration\n")
3851{
3852 struct zlog *zl = zlog_default;
3853
3854 vty_out(vty, "Syslog logging: ");
3855 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3856 vty_out(vty, "disabled");
3857 else
3858 vty_out(vty, "level %s, facility %s, ident %s",
3859 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3860 facility_name(zl->facility), zl->ident);
3861 vty_out(vty, "%s", VTY_NEWLINE);
3862
3863 vty_out(vty, "Stdout logging: ");
3864 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3865 vty_out(vty, "disabled");
3866 else
3867 vty_out(vty, "level %s",
3868 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3869 vty_out(vty, "%s", VTY_NEWLINE);
3870
3871 vty_out(vty, "Monitor logging: ");
3872 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3873 vty_out(vty, "disabled");
3874 else
3875 vty_out(vty, "level %s",
3876 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3877 vty_out(vty, "%s", VTY_NEWLINE);
3878
3879 vty_out(vty, "File logging: ");
3880 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3881 vty_out(vty, "disabled");
3882 else
3883 vty_out(vty, "level %s, filename %s",
3884 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3885 zl->filename);
3886 vty_out(vty, "%s", VTY_NEWLINE);
3887
3888 vty_out(vty, "Protocol name: %s%s",
3889 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3890 vty_out(vty, "Record priority: %s%s",
3891 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3892
3893 return CMD_SUCCESS;
3894}
3895
3896DEFUN(config_log_stdout,
3897 config_log_stdout_cmd,
3898 "log stdout", "Logging control\n" "Set stdout logging level\n")
3899{
3900 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3901 return CMD_SUCCESS;
3902}
3903
3904DEFUN(config_log_stdout_level,
3905 config_log_stdout_level_cmd,
3906 "log stdout " LOG_LEVELS,
3907 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3908{
3909 int level;
3910
3911 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3912 return CMD_ERR_NO_MATCH;
3913 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3914 return CMD_SUCCESS;
3915}
3916
3917DEFUN(no_config_log_stdout,
3918 no_config_log_stdout_cmd,
3919 "no log stdout [LEVEL]",
3920 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3921{
3922 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3923 return CMD_SUCCESS;
3924}
3925
3926DEFUN(config_log_monitor,
3927 config_log_monitor_cmd,
3928 "log monitor",
3929 "Logging control\n" "Set terminal line (monitor) logging level\n")
3930{
3931 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3932 return CMD_SUCCESS;
3933}
3934
3935DEFUN(config_log_monitor_level,
3936 config_log_monitor_level_cmd,
3937 "log monitor " LOG_LEVELS,
3938 "Logging control\n"
3939 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3940{
3941 int level;
3942
3943 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3944 return CMD_ERR_NO_MATCH;
3945 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3946 return CMD_SUCCESS;
3947}
3948
3949DEFUN(no_config_log_monitor,
3950 no_config_log_monitor_cmd,
3951 "no log monitor [LEVEL]",
3952 NO_STR
3953 "Logging control\n"
3954 "Disable terminal line (monitor) logging\n" "Logging level\n")
3955{
3956 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3957 return CMD_SUCCESS;
3958}
3959
3960static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3961{
3962 int ret;
3963 char *p = NULL;
3964 const char *fullpath;
3965
3966 /* Path detection. */
3967 if (!IS_DIRECTORY_SEP(*fname)) {
3968 char cwd[MAXPATHLEN + 1];
3969 cwd[MAXPATHLEN] = '\0';
3970
3971 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3972 zlog_err("config_log_file: Unable to alloc mem!");
3973 return CMD_WARNING;
3974 }
3975
3976 if ((p = _talloc_zero(tall_vcmd_ctx,
3977 strlen(cwd) + strlen(fname) + 2),
3978 "set_log_file")
3979 == NULL) {
3980 zlog_err("config_log_file: Unable to alloc mem!");
3981 return CMD_WARNING;
3982 }
3983 sprintf(p, "%s/%s", cwd, fname);
3984 fullpath = p;
3985 } else
3986 fullpath = fname;
3987
3988 ret = zlog_set_file(NULL, fullpath, loglevel);
3989
3990 if (p)
3991 talloc_free(p);
3992
3993 if (!ret) {
3994 vty_out(vty, "can't open logfile %s\n", fname);
3995 return CMD_WARNING;
3996 }
3997
3998 if (host.logfile)
3999 talloc_free(host.logfile);
4000
4001 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
4002
4003 return CMD_SUCCESS;
4004}
4005
4006DEFUN(config_log_file,
4007 config_log_file_cmd,
4008 "log file FILENAME",
4009 "Logging control\n" "Logging to file\n" "Logging filename\n")
4010{
4011 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4012}
4013
4014DEFUN(config_log_file_level,
4015 config_log_file_level_cmd,
4016 "log file FILENAME " LOG_LEVELS,
4017 "Logging control\n"
4018 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4019{
4020 int level;
4021
4022 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4023 return CMD_ERR_NO_MATCH;
4024 return set_log_file(vty, argv[0], level);
4025}
4026
4027DEFUN(no_config_log_file,
4028 no_config_log_file_cmd,
4029 "no log file [FILENAME]",
4030 NO_STR
4031 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4032{
4033 zlog_reset_file(NULL);
4034
4035 if (host.logfile)
4036 talloc_free(host.logfile);
4037
4038 host.logfile = NULL;
4039
4040 return CMD_SUCCESS;
4041}
4042
4043ALIAS(no_config_log_file,
4044 no_config_log_file_level_cmd,
4045 "no log file FILENAME LEVEL",
4046 NO_STR
4047 "Logging control\n"
4048 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4049
4050 DEFUN(config_log_syslog,
4051 config_log_syslog_cmd,
4052 "log syslog", "Logging control\n" "Set syslog logging level\n")
4053{
4054 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4055 return CMD_SUCCESS;
4056}
4057
4058DEFUN(config_log_syslog_level,
4059 config_log_syslog_level_cmd,
4060 "log syslog " LOG_LEVELS,
4061 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4062{
4063 int level;
4064
4065 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4066 return CMD_ERR_NO_MATCH;
4067 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4068 return CMD_SUCCESS;
4069}
4070
4071DEFUN_DEPRECATED(config_log_syslog_facility,
4072 config_log_syslog_facility_cmd,
4073 "log syslog facility " LOG_FACILITIES,
4074 "Logging control\n"
4075 "Logging goes to syslog\n"
4076 "(Deprecated) Facility parameter for syslog messages\n"
4077 LOG_FACILITY_DESC)
4078{
4079 int facility;
4080
4081 if ((facility = facility_match(argv[0])) < 0)
4082 return CMD_ERR_NO_MATCH;
4083
4084 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4085 zlog_default->facility = facility;
4086 return CMD_SUCCESS;
4087}
4088
4089DEFUN(no_config_log_syslog,
4090 no_config_log_syslog_cmd,
4091 "no log syslog [LEVEL]",
4092 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4093{
4094 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4095 return CMD_SUCCESS;
4096}
4097
4098ALIAS(no_config_log_syslog,
4099 no_config_log_syslog_facility_cmd,
4100 "no log syslog facility " LOG_FACILITIES,
4101 NO_STR
4102 "Logging control\n"
4103 "Logging goes to syslog\n"
4104 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4105
4106 DEFUN(config_log_facility,
4107 config_log_facility_cmd,
4108 "log facility " LOG_FACILITIES,
4109 "Logging control\n"
4110 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4111{
4112 int facility;
4113
4114 if ((facility = facility_match(argv[0])) < 0)
4115 return CMD_ERR_NO_MATCH;
4116 zlog_default->facility = facility;
4117 return CMD_SUCCESS;
4118}
4119
4120DEFUN(no_config_log_facility,
4121 no_config_log_facility_cmd,
4122 "no log facility [FACILITY]",
4123 NO_STR
4124 "Logging control\n"
4125 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4126{
4127 zlog_default->facility = LOG_DAEMON;
4128 return CMD_SUCCESS;
4129}
4130
4131DEFUN_DEPRECATED(config_log_trap,
4132 config_log_trap_cmd,
4133 "log trap " LOG_LEVELS,
4134 "Logging control\n"
4135 "(Deprecated) Set logging level and default for all destinations\n"
4136 LOG_LEVEL_DESC)
4137{
4138 int new_level;
4139 int i;
4140
4141 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4142 return CMD_ERR_NO_MATCH;
4143
4144 zlog_default->default_lvl = new_level;
4145 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4146 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4147 zlog_default->maxlvl[i] = new_level;
4148 return CMD_SUCCESS;
4149}
4150
4151DEFUN_DEPRECATED(no_config_log_trap,
4152 no_config_log_trap_cmd,
4153 "no log trap [LEVEL]",
4154 NO_STR
4155 "Logging control\n"
4156 "Permit all logging information\n" "Logging level\n")
4157{
4158 zlog_default->default_lvl = LOG_DEBUG;
4159 return CMD_SUCCESS;
4160}
4161
4162DEFUN(config_log_record_priority,
4163 config_log_record_priority_cmd,
4164 "log record-priority",
4165 "Logging control\n"
4166 "Log the priority of the message within the message\n")
4167{
4168 zlog_default->record_priority = 1;
4169 return CMD_SUCCESS;
4170}
4171
4172DEFUN(no_config_log_record_priority,
4173 no_config_log_record_priority_cmd,
4174 "no log record-priority",
4175 NO_STR
4176 "Logging control\n"
4177 "Do not log the priority of the message within the message\n")
4178{
4179 zlog_default->record_priority = 0;
4180 return CMD_SUCCESS;
4181}
4182#endif
4183
4184DEFUN(banner_motd_file,
4185 banner_motd_file_cmd,
4186 "banner motd file [FILE]",
4187 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4188{
4189 if (host.motdfile)
4190 talloc_free(host.motdfile);
4191 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4192
4193 return CMD_SUCCESS;
4194}
4195
4196DEFUN(banner_motd_default,
4197 banner_motd_default_cmd,
4198 "banner motd default",
4199 "Set banner string\n" "Strings for motd\n" "Default string\n")
4200{
4201 host.motd = default_motd;
4202 return CMD_SUCCESS;
4203}
4204
4205DEFUN(no_banner_motd,
4206 no_banner_motd_cmd,
4207 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4208{
4209 host.motd = NULL;
4210 if (host.motdfile)
4211 talloc_free(host.motdfile);
4212 host.motdfile = NULL;
4213 return CMD_SUCCESS;
4214}
4215
4216/* Set config filename. Called from vty.c */
4217void host_config_set(const char *filename)
4218{
4219 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4220}
4221
Pau Espin Pedrolebb6c1f2021-05-17 18:54:21 +02004222const char *host_config_file(void)
4223{
4224 return host.config;
4225}
4226
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004227/*! Deprecated, now happens implicitly when calling install_node().
4228 * Users of the API may still attempt to call this function, hence
4229 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004230void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004231{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004232}
4233
4234/*! Deprecated, now happens implicitly when calling install_node().
4235 * Users of the API may still attempt to call this function, hence
4236 * leave it here as a no-op. */
4237void vty_install_default(int node)
4238{
4239}
4240
4241/*! Install common commands like 'exit' and 'list'. */
4242static void install_basic_node_commands(int node)
4243{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004244 install_lib_element(node, &config_help_cmd);
4245 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004246
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004247 install_lib_element(node, &show_vty_attr_all_cmd);
4248 install_lib_element(node, &show_vty_attr_cmd);
4249
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004250 install_lib_element(node, &config_write_terminal_cmd);
4251 install_lib_element(node, &config_write_file_cmd);
4252 install_lib_element(node, &config_write_memory_cmd);
4253 install_lib_element(node, &config_write_cmd);
4254 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004255
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004256 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004257
4258 if (node >= CONFIG_NODE) {
4259 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004260 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004261 }
4262}
4263
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004264/*! Return true if a node is installed by install_basic_node_commands(), so
4265 * that we can avoid repeating them for each and every node during 'show
4266 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004267static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004268{
4269 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004270 || cmd == &show_vty_attr_all_cmd
4271 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004272 || cmd == &config_list_cmd
4273 || cmd == &config_write_terminal_cmd
4274 || cmd == &config_write_file_cmd
4275 || cmd == &config_write_memory_cmd
4276 || cmd == &config_write_cmd
4277 || cmd == &show_running_config_cmd
4278 || cmd == &config_exit_cmd
4279 || cmd == &config_end_cmd)
4280 return true;
4281 return false;
4282}
4283
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004284/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004285 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004286 * \param[in] vty the vty of the code
4287 * \param[in] filename where to store the file
4288 * \return 0 in case of success.
4289 *
4290 * If the filename already exists create a filename.sav
4291 * version with the current code.
4292 *
4293 */
4294int osmo_vty_write_config_file(const char *filename)
4295{
4296 char *failed_file;
4297 int rc;
4298
4299 rc = write_config_file(filename, &failed_file);
4300 talloc_free(failed_file);
4301 return rc;
4302}
4303
4304/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004305 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004306 * \return 0 in case of success.
4307 *
4308 * If the filename already exists create a filename.sav
4309 * version with the current code.
4310 *
4311 */
4312int osmo_vty_save_config_file(void)
4313{
4314 char *failed_file;
4315 int rc;
4316
4317 if (host.config == NULL)
4318 return -7;
4319
4320 rc = write_config_file(host.config, &failed_file);
4321 talloc_free(failed_file);
4322 return rc;
4323}
4324
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004325/* Initialize command interface. Install basic nodes and commands. */
4326void cmd_init(int terminal)
4327{
4328 /* Allocate initial top vector of commands. */
4329 cmdvec = vector_init(VECTOR_MIN_SIZE);
4330
4331 /* Default host value settings. */
4332 host.name = NULL;
4333 host.password = NULL;
4334 host.enable = NULL;
4335 host.logfile = NULL;
4336 host.config = NULL;
4337 host.lines = -1;
4338 host.motd = default_motd;
4339 host.motdfile = NULL;
4340
4341 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004342 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004343 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004344 install_node_bare(&auth_node, NULL);
4345 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004346 install_node(&config_node, config_write_host);
4347
4348 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004349 install_lib_element(VIEW_NODE, &show_version_cmd);
4350 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004351 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004352 install_lib_element(VIEW_NODE, &config_list_cmd);
4353 install_lib_element(VIEW_NODE, &config_exit_cmd);
4354 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004355 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4356 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004357 install_lib_element(VIEW_NODE, &config_enable_cmd);
4358 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4359 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4360 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004361 }
4362
4363 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004364 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4365 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4366 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004367 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004368 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4369 install_lib_element(ENABLE_NODE, &show_version_cmd);
4370 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004371
4372 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004373 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4374 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4375 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004376 }
4377
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004378 install_lib_element(CONFIG_NODE, &hostname_cmd);
4379 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004380
4381 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004382 install_lib_element(CONFIG_NODE, &password_cmd);
4383 install_lib_element(CONFIG_NODE, &password_text_cmd);
4384 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4385 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4386 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004387
4388#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004389 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4390 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004391#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004392 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4393 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4394 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4395 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4396 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004397
4398 }
4399 srand(time(NULL));
4400}
Harald Welte7acb30c2011-08-17 17:13:48 +02004401
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004402/* FIXME: execute this section in the unit test instead */
4403static __attribute__((constructor)) void on_dso_load(void)
4404{
4405 unsigned int i, j;
4406
4407 /* Check total number of the library specific attributes */
4408 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4409
4410 /* Check for duplicates in the list of library specific flags */
4411 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4412 if (cmd_lib_attr_letters[i] == '\0')
4413 continue;
4414
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004415 /* Some flag characters are reserved for global attributes */
4416 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4417 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4418 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4419
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004420 /* Only upper case flag letters are allowed for libraries */
4421 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4422 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4423
4424 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4425 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4426 }
4427}
4428
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004429/*! @} */