blob: c233aa6a7a08a3259043d1181932e5fc78f3fcb3 [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 */
730 if (~cmd->usrattr & (1 << i))
731 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" },
900 { 0, NULL }
901};
902
903const struct value_string vty_ref_gen_mode_desc[] = {
904 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
905 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
906 { 0, NULL }
907};
908
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200909/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700910 * \param[out] stream Output stream to print the XML reference to.
911 * \param[in] mode The XML reference generation mode.
912 * \returns always 0 for now, no errors possible.
913 */
914int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
915{
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700916 unsigned char gflag_mask;
917
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700918 switch (mode) {
919 case VTY_REF_GEN_MODE_EXPERT:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700920 /* All commands except deprecated */
921 gflag_mask = CMD_ATTR_DEPRECATED;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700922 break;
923 case VTY_REF_GEN_MODE_DEFAULT:
924 default:
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700925 /* All commands except deprecated and hidden */
926 gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700927 break;
928 }
929
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +0700930 return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, false);
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700931}
932
933/*! Print the XML reference of all VTY nodes to the given stream.
934 * \param[out] stream Output stream to print the XML reference to.
935 * \returns always 0 for now, no errors possible.
936 *
937 * NOTE: this function is deprecated because it does not allow to
938 * specify the XML reference generation mode (default mode
939 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200940 */
941int vty_dump_xml_ref(FILE *stream)
942{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700943 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200944}
945
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200946/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100947static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
948{
949 int i;
950
951 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
952 struct cmd_element *elem;
953 elem = vector_slot(cnode->cmd_vector, i);
954 if (!elem->string)
955 continue;
956 if (!strcmp(elem->string, cmdstring))
957 return 1;
958 }
959 return 0;
960}
961
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200962/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200963 * \param[in] ntype Node Type
964 * \param[cmd] element to be installed
965 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000966void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200967{
968 struct cmd_node *cnode;
969
970 cnode = vector_slot(cmdvec, ntype);
971
Harald Weltea99d45a2015-11-12 13:48:23 +0100972 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100973 /* ensure no _identical_ command has been registered at this
974 * node so far */
975 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200976
977 vector_set(cnode->cmd_vector, cmd);
978
979 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
980 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
981}
982
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700983/*! Install a library command into a node
984 * \param[in] ntype Node Type
985 * \param[in] cmd element to be installed
986 */
987void install_lib_element(int ntype, struct cmd_element *cmd)
988{
989 cmd->attr |= CMD_ATTR_LIB_COMMAND;
990 install_element(ntype, cmd);
991}
992
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200993/* Install a command into VIEW and ENABLE node */
994void install_element_ve(struct cmd_element *cmd)
995{
996 install_element(VIEW_NODE, cmd);
997 install_element(ENABLE_NODE, cmd);
998}
999
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +07001000/* Install a library command into VIEW and ENABLE node */
1001void install_lib_element_ve(struct cmd_element *cmd)
1002{
1003 cmd->attr |= CMD_ATTR_LIB_COMMAND;
1004 install_element_ve(cmd);
1005}
1006
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001007#ifdef VTY_CRYPT_PW
1008static unsigned char itoa64[] =
1009 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1010
1011static void to64(char *s, long v, int n)
1012{
1013 while (--n >= 0) {
1014 *s++ = itoa64[v & 0x3f];
1015 v >>= 6;
1016 }
1017}
1018
1019static char *zencrypt(const char *passwd)
1020{
1021 char salt[6];
1022 struct timeval tv;
1023 char *crypt(const char *, const char *);
1024
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001025 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001026
1027 to64(&salt[0], random(), 3);
1028 to64(&salt[3], tv.tv_usec, 3);
1029 salt[5] = '\0';
1030
1031 return crypt(passwd, salt);
1032}
1033#endif
1034
1035/* This function write configuration of this host. */
1036static int config_write_host(struct vty *vty)
1037{
1038 if (host.name)
1039 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1040
1041 if (host.encrypt) {
1042 if (host.password_encrypt)
1043 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1044 VTY_NEWLINE);
1045 if (host.enable_encrypt)
1046 vty_out(vty, "enable password 8 %s%s",
1047 host.enable_encrypt, VTY_NEWLINE);
1048 } else {
1049 if (host.password)
1050 vty_out(vty, "password %s%s", host.password,
1051 VTY_NEWLINE);
1052 if (host.enable)
1053 vty_out(vty, "enable password %s%s", host.enable,
1054 VTY_NEWLINE);
1055 }
1056
1057 if (host.advanced)
1058 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1059
1060 if (host.encrypt)
1061 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1062
1063 if (host.lines >= 0)
1064 vty_out(vty, "service terminal-length %d%s", host.lines,
1065 VTY_NEWLINE);
1066
1067 if (host.motdfile)
1068 vty_out(vty, "banner motd file %s%s", host.motdfile,
1069 VTY_NEWLINE);
1070 else if (!host.motd)
1071 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1072
1073 return 1;
1074}
1075
1076/* Utility function for getting command vector. */
1077static vector cmd_node_vector(vector v, enum node_type ntype)
1078{
1079 struct cmd_node *cnode = vector_slot(v, ntype);
1080 return cnode->cmd_vector;
1081}
1082
1083/* Completion match types. */
1084enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001085 NO_MATCH = 0,
1086 ANY_MATCH,
1087 EXTEND_MATCH,
1088 IPV4_PREFIX_MATCH,
1089 IPV4_MATCH,
1090 IPV6_PREFIX_MATCH,
1091 IPV6_MATCH,
1092 RANGE_MATCH,
1093 VARARG_MATCH,
1094 PARTLY_MATCH,
1095 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001096};
1097
1098static enum match_type cmd_ipv4_match(const char *str)
1099{
1100 const char *sp;
1101 int dots = 0, nums = 0;
1102 char buf[4];
1103
1104 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001105 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001106
1107 for (;;) {
1108 memset(buf, 0, sizeof(buf));
1109 sp = str;
1110 while (*str != '\0') {
1111 if (*str == '.') {
1112 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001113 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001114
1115 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001116 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001117
1118 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001119 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001120
1121 dots++;
1122 break;
1123 }
1124 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126
1127 str++;
1128 }
1129
1130 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001131 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001132
1133 strncpy(buf, sp, str - sp);
1134 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001135 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001136
1137 nums++;
1138
1139 if (*str == '\0')
1140 break;
1141
1142 str++;
1143 }
1144
1145 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001146 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001147
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001148 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001149}
1150
1151static enum match_type cmd_ipv4_prefix_match(const char *str)
1152{
1153 const char *sp;
1154 int dots = 0;
1155 char buf[4];
1156
1157 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001158 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001159
1160 for (;;) {
1161 memset(buf, 0, sizeof(buf));
1162 sp = str;
1163 while (*str != '\0' && *str != '/') {
1164 if (*str == '.') {
1165 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001166 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001167
1168 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001169 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001170
1171 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001172 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001173
1174 dots++;
1175 break;
1176 }
1177
1178 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001179 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001180
1181 str++;
1182 }
1183
1184 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001185 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186
1187 strncpy(buf, sp, str - sp);
1188 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001189 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190
1191 if (dots == 3) {
1192 if (*str == '/') {
1193 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001194 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001195
1196 str++;
1197 break;
1198 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001199 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001200 }
1201
1202 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001203 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001204
1205 str++;
1206 }
1207
1208 sp = str;
1209 while (*str != '\0') {
1210 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001211 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001212
1213 str++;
1214 }
1215
1216 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001217 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001218
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001219 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001220}
1221
1222#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1223#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1224#define STATE_START 1
1225#define STATE_COLON 2
1226#define STATE_DOUBLE 3
1227#define STATE_ADDR 4
1228#define STATE_DOT 5
1229#define STATE_SLASH 6
1230#define STATE_MASK 7
1231
1232#ifdef HAVE_IPV6
1233
1234static enum match_type cmd_ipv6_match(const char *str)
1235{
1236 int state = STATE_START;
1237 int colons = 0, nums = 0, double_colon = 0;
1238 const char *sp = NULL;
1239 struct sockaddr_in6 sin6_dummy;
1240 int ret;
1241
1242 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001243 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001244
1245 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001246 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001247
1248 /* use inet_pton that has a better support,
1249 * for example inet_pton can support the automatic addresses:
1250 * ::1.2.3.4
1251 */
1252 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1253
1254 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001255 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001256
1257 while (*str != '\0') {
1258 switch (state) {
1259 case STATE_START:
1260 if (*str == ':') {
1261 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001262 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001263 colons--;
1264 state = STATE_COLON;
1265 } else {
1266 sp = str;
1267 state = STATE_ADDR;
1268 }
1269
1270 continue;
1271 case STATE_COLON:
1272 colons++;
1273 if (*(str + 1) == ':')
1274 state = STATE_DOUBLE;
1275 else {
1276 sp = str + 1;
1277 state = STATE_ADDR;
1278 }
1279 break;
1280 case STATE_DOUBLE:
1281 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001282 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001283
1284 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001285 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001286 else {
1287 if (*(str + 1) != '\0')
1288 colons++;
1289 sp = str + 1;
1290 state = STATE_ADDR;
1291 }
1292
1293 double_colon++;
1294 nums++;
1295 break;
1296 case STATE_ADDR:
1297 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1298 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001299 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001300
1301 nums++;
1302 state = STATE_COLON;
1303 }
1304 if (*(str + 1) == '.')
1305 state = STATE_DOT;
1306 break;
1307 case STATE_DOT:
1308 state = STATE_ADDR;
1309 break;
1310 default:
1311 break;
1312 }
1313
1314 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001315 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001316
1317 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001318 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001319
1320 str++;
1321 }
1322
1323#if 0
1324 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001325 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001326#endif /* 0 */
1327
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001328 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001329}
1330
1331static enum match_type cmd_ipv6_prefix_match(const char *str)
1332{
1333 int state = STATE_START;
1334 int colons = 0, nums = 0, double_colon = 0;
1335 int mask;
1336 const char *sp = NULL;
1337 char *endptr = NULL;
1338
1339 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001341
1342 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001343 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001344
1345 while (*str != '\0' && state != STATE_MASK) {
1346 switch (state) {
1347 case STATE_START:
1348 if (*str == ':') {
1349 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001350 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001351 colons--;
1352 state = STATE_COLON;
1353 } else {
1354 sp = str;
1355 state = STATE_ADDR;
1356 }
1357
1358 continue;
1359 case STATE_COLON:
1360 colons++;
1361 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001362 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001363 else if (*(str + 1) == ':')
1364 state = STATE_DOUBLE;
1365 else {
1366 sp = str + 1;
1367 state = STATE_ADDR;
1368 }
1369 break;
1370 case STATE_DOUBLE:
1371 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001372 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001373
1374 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001375 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001376 else {
1377 if (*(str + 1) != '\0' && *(str + 1) != '/')
1378 colons++;
1379 sp = str + 1;
1380
1381 if (*(str + 1) == '/')
1382 state = STATE_SLASH;
1383 else
1384 state = STATE_ADDR;
1385 }
1386
1387 double_colon++;
1388 nums += 1;
1389 break;
1390 case STATE_ADDR:
1391 if (*(str + 1) == ':' || *(str + 1) == '.'
1392 || *(str + 1) == '\0' || *(str + 1) == '/') {
1393 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001394 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001395
1396 for (; sp <= str; sp++)
1397 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001398 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001399
1400 nums++;
1401
1402 if (*(str + 1) == ':')
1403 state = STATE_COLON;
1404 else if (*(str + 1) == '.')
1405 state = STATE_DOT;
1406 else if (*(str + 1) == '/')
1407 state = STATE_SLASH;
1408 }
1409 break;
1410 case STATE_DOT:
1411 state = STATE_ADDR;
1412 break;
1413 case STATE_SLASH:
1414 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001415 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001416
1417 state = STATE_MASK;
1418 break;
1419 default:
1420 break;
1421 }
1422
1423 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001424 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001425
1426 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001427 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428
1429 str++;
1430 }
1431
1432 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001433 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001434
1435 mask = strtol(str, &endptr, 10);
1436 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001437 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001438
1439 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001440 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001441
1442/* I don't know why mask < 13 makes command match partly.
1443 Forgive me to make this comments. I Want to set static default route
1444 because of lack of function to originate default in ospf6d; sorry
1445 yasu
1446 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001447 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001448*/
1449
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001450 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001451}
1452
1453#endif /* HAVE_IPV6 */
1454
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001455
1456#if ULONG_MAX == 18446744073709551615UL
1457#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1458#elif ULONG_MAX == 4294967295UL
1459#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1460#else
1461#error "ULONG_MAX not defined!"
1462#endif
1463
1464#if LONG_MAX == 9223372036854775807L
1465#define DECIMAL_STRLEN_MAX_SIGNED 19
1466#elif LONG_MAX == 2147483647L
1467#define DECIMAL_STRLEN_MAX_SIGNED 10
1468#else
1469#error "LONG_MAX not defined!"
1470#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001471
1472static int cmd_range_match(const char *range, const char *str)
1473{
1474 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001475 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001476 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001477
1478 if (str == NULL)
1479 return 1;
1480
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001481 if (range[1] == '-') {
1482 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001483
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001484 val = strtol(str, &endptr, 10);
1485 if (*endptr != '\0')
1486 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001488 range += 2;
1489 p = strchr(range, '-');
1490 if (p == NULL)
1491 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001492 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001493 return 0;
1494 strncpy(buf, range, p - range);
1495 buf[p - range] = '\0';
1496 min = -strtol(buf, &endptr, 10);
1497 if (*endptr != '\0')
1498 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001499
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001500 range = p + 1;
1501 p = strchr(range, '>');
1502 if (p == NULL)
1503 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001504 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001505 return 0;
1506 strncpy(buf, range, p - range);
1507 buf[p - range] = '\0';
1508 max = strtol(buf, &endptr, 10);
1509 if (*endptr != '\0')
1510 return 0;
1511
1512 if (val < min || val > max)
1513 return 0;
1514 } else {
1515 unsigned long min, max, val;
1516
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001517 if (str[0] == '-')
1518 return 0;
1519
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001520 val = strtoul(str, &endptr, 10);
1521 if (*endptr != '\0')
1522 return 0;
1523
1524 range++;
1525 p = strchr(range, '-');
1526 if (p == NULL)
1527 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001528 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001529 return 0;
1530 strncpy(buf, range, p - range);
1531 buf[p - range] = '\0';
1532 min = strtoul(buf, &endptr, 10);
1533 if (*endptr != '\0')
1534 return 0;
1535
1536 range = p + 1;
1537 p = strchr(range, '>');
1538 if (p == NULL)
1539 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001540 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001541 return 0;
1542 strncpy(buf, range, p - range);
1543 buf[p - range] = '\0';
1544 max = strtoul(buf, &endptr, 10);
1545 if (*endptr != '\0')
1546 return 0;
1547
1548 if (val < min || val > max)
1549 return 0;
1550 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001551
1552 return 1;
1553}
1554
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001555/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001556static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001557{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001558 /* we've got "[blah]". We want to strip off the []s and redo the
1559 * match check for "blah"
1560 */
1561 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001562
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001563 if (len < 3)
1564 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001565
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001566 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001567}
1568
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001569static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001570cmd_match(const char *str, const char *command,
1571 enum match_type min, bool recur)
1572{
1573
1574 if (recur && CMD_OPTION(str))
1575 {
1576 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001577 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001578
1579 /* this would be a bug in a command, however handle it gracefully
1580 * as it we only discover it if a user tries to run it
1581 */
1582 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001583 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001584
1585 ret = cmd_match(tmp, command, min, false);
1586
1587 talloc_free(tmp);
1588
1589 return ret;
1590 }
1591 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001592 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001593 else if (CMD_RANGE(str))
1594 {
1595 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001596 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001597 }
1598#ifdef HAVE_IPV6
1599 else if (CMD_IPV6(str))
1600 {
1601 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001602 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001603 }
1604 else if (CMD_IPV6_PREFIX(str))
1605 {
1606 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001607 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001608 }
1609#endif /* HAVE_IPV6 */
1610 else if (CMD_IPV4(str))
1611 {
1612 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001613 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001614 }
1615 else if (CMD_IPV4_PREFIX(str))
1616 {
1617 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001618 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001619 }
1620 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001621 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001622 else if (strncmp(command, str, strlen(command)) == 0)
1623 {
1624 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001625 return EXACT_MATCH;
1626 else if (PARTLY_MATCH >= min)
1627 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001628 }
1629
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001630 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001631}
1632
1633/* Filter vector at the specified index and by the given command string, to
1634 * the desired matching level (thus allowing part matches), and return match
1635 * type flag.
1636 */
1637static enum match_type
1638cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001639{
1640 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001641 struct cmd_element *cmd_element;
1642 enum match_type match_type;
1643 vector descvec;
1644 struct desc *desc;
1645
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001646 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001647
1648 /* If command and cmd_element string does not match set NULL to vector */
1649 for (i = 0; i < vector_active(v); i++)
1650 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001651 if (index >= vector_active(cmd_element->strvec))
1652 vector_slot(v, i) = NULL;
1653 else {
1654 unsigned int j;
1655 int matched = 0;
1656
1657 descvec =
1658 vector_slot(cmd_element->strvec, index);
1659
1660 for (j = 0; j < vector_active(descvec); j++)
1661 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001662 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001663
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001664 ret = cmd_match (desc->cmd, command, level, true);
1665
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001666 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001667 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001668
1669 if (match_type < ret)
1670 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001671 }
1672 if (!matched)
1673 vector_slot(v, i) = NULL;
1674 }
1675 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001676
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001677 if (match_type == NO_MATCH)
1678 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001679
1680 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1681 * go again and filter out commands whose argument (at this index) is
1682 * 'weaker'. E.g., if we have 2 commands:
1683 *
1684 * foo bar <1-255>
1685 * foo bar BLAH
1686 *
1687 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001688 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001689 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1690 *
1691 * If we don't do a 2nd pass and filter it out, the higher-layers will
1692 * consider this to be ambiguous.
1693 */
1694 for (i = 0; i < vector_active(v); i++)
1695 if ((cmd_element = vector_slot(v, i)) != NULL) {
1696 if (index >= vector_active(cmd_element->strvec))
1697 vector_slot(v, i) = NULL;
1698 else {
1699 unsigned int j;
1700 int matched = 0;
1701
1702 descvec =
1703 vector_slot(cmd_element->strvec, index);
1704
1705 for (j = 0; j < vector_active(descvec); j++)
1706 if ((desc = vector_slot(descvec, j))) {
1707 enum match_type ret;
1708
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001709 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001710
1711 if (ret >= match_type)
1712 matched++;
1713 }
1714 if (!matched)
1715 vector_slot(v, i) = NULL;
1716 }
1717 }
1718
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001719 return match_type;
1720}
1721
1722/* Check ambiguous match */
1723static int
1724is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1725{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001726 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001727 unsigned int i;
1728 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001729 struct cmd_element *cmd_element;
1730 const char *matched = NULL;
1731 vector descvec;
1732 struct desc *desc;
1733
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001734 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1735 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1736 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1737 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1738 * that case, the string must remain allocated until this function exits or another match comes
1739 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1740 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1741 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1742 void *cmd_deopt_ctx = NULL;
1743
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001744 for (i = 0; i < vector_active(v); i++) {
1745 cmd_element = vector_slot(v, i);
1746 if (!cmd_element)
1747 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001748
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001749 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001750
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001751 descvec = vector_slot(cmd_element->strvec, index);
1752
1753 for (j = 0; j < vector_active(descvec); j++) {
1754 desc = vector_slot(descvec, j);
1755 if (!desc)
1756 continue;
1757
1758 enum match_type mtype;
1759 const char *str = desc->cmd;
1760
1761 if (CMD_OPTION(str)) {
1762 if (!cmd_deopt_ctx)
1763 cmd_deopt_ctx =
1764 talloc_named_const(tall_vty_cmd_ctx, 0,
1765 __func__);
1766 str = cmd_deopt(cmd_deopt_ctx, str);
1767 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001768 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001769 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001770
1771 switch (type) {
1772 case EXACT_MATCH:
1773 if (!(CMD_VARIABLE (str))
1774 && strcmp(command, str) == 0)
1775 match++;
1776 break;
1777 case PARTLY_MATCH:
1778 if (!(CMD_VARIABLE (str))
1779 && strncmp(command, str, strlen (command)) == 0)
1780 {
1781 if (matched
1782 && strcmp(matched,
1783 str) != 0) {
1784 ret = 1; /* There is ambiguous match. */
1785 goto free_and_return;
1786 } else
1787 matched = str;
1788 match++;
1789 }
1790 break;
1791 case RANGE_MATCH:
1792 if (cmd_range_match
1793 (str, command)) {
1794 if (matched
1795 && strcmp(matched,
1796 str) != 0) {
1797 ret = 1;
1798 goto free_and_return;
1799 } else
1800 matched = str;
1801 match++;
1802 }
1803 break;
1804#ifdef HAVE_IPV6
1805 case IPV6_MATCH:
1806 if (CMD_IPV6(str))
1807 match++;
1808 break;
1809 case IPV6_PREFIX_MATCH:
1810 if ((mtype =
1811 cmd_ipv6_prefix_match
1812 (command)) != NO_MATCH) {
1813 if (mtype == PARTLY_MATCH) {
1814 ret = 2; /* There is incomplete match. */
1815 goto free_and_return;
1816 }
1817
1818 match++;
1819 }
1820 break;
1821#endif /* HAVE_IPV6 */
1822 case IPV4_MATCH:
1823 if (CMD_IPV4(str))
1824 match++;
1825 break;
1826 case IPV4_PREFIX_MATCH:
1827 if ((mtype =
1828 cmd_ipv4_prefix_match
1829 (command)) != NO_MATCH) {
1830 if (mtype == PARTLY_MATCH) {
1831 ret = 2; /* There is incomplete match. */
1832 goto free_and_return;
1833 }
1834
1835 match++;
1836 }
1837 break;
1838 case EXTEND_MATCH:
1839 if (CMD_VARIABLE (str))
1840 match++;
1841 break;
1842 case NO_MATCH:
1843 default:
1844 break;
1845 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001846 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001847 if (!match)
1848 vector_slot(v, i) = NULL;
1849 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001850
1851free_and_return:
1852 if (cmd_deopt_ctx)
1853 talloc_free(cmd_deopt_ctx);
1854 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001855}
1856
1857/* If src matches dst return dst string, otherwise return NULL */
1858static const char *cmd_entry_function(const char *src, const char *dst)
1859{
1860 /* Skip variable arguments. */
1861 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1862 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1863 return NULL;
1864
1865 /* In case of 'command \t', given src is NULL string. */
1866 if (src == NULL)
1867 return dst;
1868
1869 /* Matched with input string. */
1870 if (strncmp(src, dst, strlen(src)) == 0)
1871 return dst;
1872
1873 return NULL;
1874}
1875
1876/* If src matches dst return dst string, otherwise return NULL */
1877/* This version will return the dst string always if it is
1878 CMD_VARIABLE for '?' key processing */
1879static const char *cmd_entry_function_desc(const char *src, const char *dst)
1880{
1881 if (CMD_VARARG(dst))
1882 return dst;
1883
1884 if (CMD_RANGE(dst)) {
1885 if (cmd_range_match(dst, src))
1886 return dst;
1887 else
1888 return NULL;
1889 }
1890#ifdef HAVE_IPV6
1891 if (CMD_IPV6(dst)) {
1892 if (cmd_ipv6_match(src))
1893 return dst;
1894 else
1895 return NULL;
1896 }
1897
1898 if (CMD_IPV6_PREFIX(dst)) {
1899 if (cmd_ipv6_prefix_match(src))
1900 return dst;
1901 else
1902 return NULL;
1903 }
1904#endif /* HAVE_IPV6 */
1905
1906 if (CMD_IPV4(dst)) {
1907 if (cmd_ipv4_match(src))
1908 return dst;
1909 else
1910 return NULL;
1911 }
1912
1913 if (CMD_IPV4_PREFIX(dst)) {
1914 if (cmd_ipv4_prefix_match(src))
1915 return dst;
1916 else
1917 return NULL;
1918 }
1919
1920 /* Optional or variable commands always match on '?' */
1921 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1922 return dst;
1923
1924 /* In case of 'command \t', given src is NULL string. */
1925 if (src == NULL)
1926 return dst;
1927
1928 if (strncmp(src, dst, strlen(src)) == 0)
1929 return dst;
1930 else
1931 return NULL;
1932}
1933
1934/* Check same string element existence. If it isn't there return
1935 1. */
1936static int cmd_unique_string(vector v, const char *str)
1937{
1938 unsigned int i;
1939 char *match;
1940
1941 for (i = 0; i < vector_active(v); i++)
1942 if ((match = vector_slot(v, i)) != NULL)
1943 if (strcmp(match, str) == 0)
1944 return 0;
1945 return 1;
1946}
1947
1948/* Compare string to description vector. If there is same string
1949 return 1 else return 0. */
1950static int desc_unique_string(vector v, const char *str)
1951{
1952 unsigned int i;
1953 struct desc *desc;
1954
1955 for (i = 0; i < vector_active(v); i++)
1956 if ((desc = vector_slot(v, i)) != NULL)
1957 if (strcmp(desc->cmd, str) == 0)
1958 return 1;
1959 return 0;
1960}
1961
1962static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1963{
1964 if (first_word != NULL &&
1965 node != AUTH_NODE &&
1966 node != VIEW_NODE &&
1967 node != AUTH_ENABLE_NODE &&
1968 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1969 return 1;
1970 return 0;
1971}
1972
1973/* '?' describe command support. */
1974static vector
1975cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1976{
1977 unsigned int i;
1978 vector cmd_vector;
1979#define INIT_MATCHVEC_SIZE 10
1980 vector matchvec;
1981 struct cmd_element *cmd_element;
1982 unsigned int index;
1983 int ret;
1984 enum match_type match;
1985 char *command;
1986 static struct desc desc_cr = { "<cr>", "" };
1987
1988 /* Set index. */
1989 if (vector_active(vline) == 0) {
1990 *status = CMD_ERR_NO_MATCH;
1991 return NULL;
1992 } else
1993 index = vector_active(vline) - 1;
1994
1995 /* Make copy vector of current node's command vector. */
1996 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1997
1998 /* Prepare match vector */
1999 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2000
2001 /* Filter commands. */
2002 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002003 for (i = 0; i < index; i++) {
2004 command = vector_slot(vline, i);
2005 if (!command)
2006 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002007
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002008 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002009
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002010 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01002011 struct cmd_element *cmd_element;
2012 vector descvec;
2013 unsigned int j, k;
2014
2015 for (j = 0; j < vector_active(cmd_vector); j++)
2016 if ((cmd_element =
2017 vector_slot(cmd_vector, j)) != NULL
2018 &&
2019 (vector_active(cmd_element->strvec))) {
2020 descvec =
2021 vector_slot(cmd_element->
2022 strvec,
2023 vector_active
2024 (cmd_element->
2025 strvec) - 1);
2026 for (k = 0;
2027 k < vector_active(descvec);
2028 k++) {
2029 struct desc *desc =
2030 vector_slot(descvec,
2031 k);
2032 vector_set(matchvec,
2033 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002034 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002035 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002036
Harald Welte80d30fe2013-02-12 11:08:57 +01002037 vector_set(matchvec, &desc_cr);
2038 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002039
Harald Welte80d30fe2013-02-12 11:08:57 +01002040 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002041 }
2042
Harald Welte80d30fe2013-02-12 11:08:57 +01002043 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2044 match)) == 1) {
2045 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002046 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002047 *status = CMD_ERR_AMBIGUOUS;
2048 return NULL;
2049 } else if (ret == 2) {
2050 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002051 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002052 *status = CMD_ERR_NO_MATCH;
2053 return NULL;
2054 }
2055 }
2056
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002057 /* Prepare match vector */
2058 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2059
2060 /* Make sure that cmd_vector is filtered based on current word */
2061 command = vector_slot(vline, index);
2062 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002063 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002064
2065 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002066 for (i = 0; i < vector_active(cmd_vector); i++) {
2067 const char *string = NULL;
2068 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002069
Harald Welte80d30fe2013-02-12 11:08:57 +01002070 cmd_element = vector_slot(cmd_vector, i);
2071 if (!cmd_element)
2072 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002073
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002074 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2075 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002076 if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002077 continue;
2078
Harald Welte80d30fe2013-02-12 11:08:57 +01002079 strvec = cmd_element->strvec;
2080
2081 /* if command is NULL, index may be equal to vector_active */
2082 if (command && index >= vector_active(strvec))
2083 vector_slot(cmd_vector, i) = NULL;
2084 else {
2085 /* Check if command is completed. */
2086 if (command == NULL
2087 && index == vector_active(strvec)) {
2088 string = "<cr>";
2089 if (!desc_unique_string(matchvec, string))
2090 vector_set(matchvec, &desc_cr);
2091 } else {
2092 unsigned int j;
2093 vector descvec = vector_slot(strvec, index);
2094 struct desc *desc;
2095
2096 for (j = 0; j < vector_active(descvec); j++) {
2097 desc = vector_slot(descvec, j);
2098 if (!desc)
2099 continue;
2100 string = cmd_entry_function_desc
2101 (command, desc->cmd);
2102 if (!string)
2103 continue;
2104 /* Uniqueness check */
2105 if (!desc_unique_string(matchvec, string))
2106 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002107 }
2108 }
2109 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002110 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002111 vector_free(cmd_vector);
2112
2113 if (vector_slot(matchvec, 0) == NULL) {
2114 vector_free(matchvec);
2115 *status = CMD_ERR_NO_MATCH;
2116 } else
2117 *status = CMD_SUCCESS;
2118
2119 return matchvec;
2120}
2121
2122vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2123{
2124 vector ret;
2125
2126 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2127 enum node_type onode;
2128 vector shifted_vline;
2129 unsigned int index;
2130
2131 onode = vty->node;
2132 vty->node = ENABLE_NODE;
2133 /* We can try it on enable node, cos' the vty is authenticated */
2134
2135 shifted_vline = vector_init(vector_count(vline));
2136 /* use memcpy? */
2137 for (index = 1; index < vector_active(vline); index++) {
2138 vector_set_index(shifted_vline, index - 1,
2139 vector_lookup(vline, index));
2140 }
2141
2142 ret = cmd_describe_command_real(shifted_vline, vty, status);
2143
2144 vector_free(shifted_vline);
2145 vty->node = onode;
2146 return ret;
2147 }
2148
2149 return cmd_describe_command_real(vline, vty, status);
2150}
2151
2152/* Check LCD of matched command. */
2153static int cmd_lcd(char **matched)
2154{
2155 int i;
2156 int j;
2157 int lcd = -1;
2158 char *s1, *s2;
2159 char c1, c2;
2160
2161 if (matched[0] == NULL || matched[1] == NULL)
2162 return 0;
2163
2164 for (i = 1; matched[i] != NULL; i++) {
2165 s1 = matched[i - 1];
2166 s2 = matched[i];
2167
2168 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2169 if (c1 != c2)
2170 break;
2171
2172 if (lcd < 0)
2173 lcd = j;
2174 else {
2175 if (lcd > j)
2176 lcd = j;
2177 }
2178 }
2179 return lcd;
2180}
2181
2182/* Command line completion support. */
2183static char **cmd_complete_command_real(vector vline, struct vty *vty,
2184 int *status)
2185{
2186 unsigned int i;
2187 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2188#define INIT_MATCHVEC_SIZE 10
2189 vector matchvec;
2190 struct cmd_element *cmd_element;
2191 unsigned int index;
2192 char **match_str;
2193 struct desc *desc;
2194 vector descvec;
2195 char *command;
2196 int lcd;
2197
2198 if (vector_active(vline) == 0) {
2199 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002200 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002201 return NULL;
2202 } else
2203 index = vector_active(vline) - 1;
2204
2205 /* First, filter by preceeding command string */
2206 for (i = 0; i < index; i++)
2207 if ((command = vector_slot(vline, i))) {
2208 enum match_type match;
2209 int ret;
2210
2211 /* First try completion match, if there is exactly match return 1 */
2212 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002213 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002214
2215 /* If there is exact match then filter ambiguous match else check
2216 ambiguousness. */
2217 if ((ret =
2218 is_cmd_ambiguous(command, cmd_vector, i,
2219 match)) == 1) {
2220 vector_free(cmd_vector);
2221 *status = CMD_ERR_AMBIGUOUS;
2222 return NULL;
2223 }
2224 /*
2225 else if (ret == 2)
2226 {
2227 vector_free (cmd_vector);
2228 *status = CMD_ERR_NO_MATCH;
2229 return NULL;
2230 }
2231 */
2232 }
2233
2234 /* Prepare match vector. */
2235 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2236
2237 /* Now we got into completion */
2238 for (i = 0; i < vector_active(cmd_vector); i++)
2239 if ((cmd_element = vector_slot(cmd_vector, i))) {
2240 const char *string;
2241 vector strvec = cmd_element->strvec;
2242
2243 /* Check field length */
2244 if (index >= vector_active(strvec))
2245 vector_slot(cmd_vector, i) = NULL;
2246 else {
2247 unsigned int j;
2248
2249 descvec = vector_slot(strvec, index);
2250 for (j = 0; j < vector_active(descvec); j++)
2251 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002252 const char *cmd = desc->cmd;
2253 char *tmp = NULL;
2254
2255 if (CMD_OPTION(desc->cmd)) {
2256 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2257 cmd = tmp;
2258 }
2259 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002260 if (cmd_unique_string (matchvec, string))
2261 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002262 if (tmp)
2263 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002264 }
2265 }
2266 }
2267
2268 /* We don't need cmd_vector any more. */
2269 vector_free(cmd_vector);
2270
2271 /* No matched command */
2272 if (vector_slot(matchvec, 0) == NULL) {
2273 vector_free(matchvec);
2274
2275 /* In case of 'command \t' pattern. Do you need '?' command at
2276 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002277 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002278 *status = CMD_ERR_NOTHING_TODO;
2279 else
2280 *status = CMD_ERR_NO_MATCH;
2281 return NULL;
2282 }
2283
2284 /* Only one matched */
2285 if (vector_slot(matchvec, 1) == NULL) {
2286 match_str = (char **)matchvec->index;
2287 vector_only_wrapper_free(matchvec);
2288 *status = CMD_COMPLETE_FULL_MATCH;
2289 return match_str;
2290 }
2291 /* Make it sure last element is NULL. */
2292 vector_set(matchvec, NULL);
2293
2294 /* Check LCD of matched strings. */
2295 if (vector_slot(vline, index) != NULL) {
2296 lcd = cmd_lcd((char **)matchvec->index);
2297
2298 if (lcd) {
2299 int len = strlen(vector_slot(vline, index));
2300
2301 if (len < lcd) {
2302 char *lcdstr;
2303
2304 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2305 "complete-lcdstr");
2306 memcpy(lcdstr, matchvec->index[0], lcd);
2307 lcdstr[lcd] = '\0';
2308
2309 /* match_str = (char **) &lcdstr; */
2310
2311 /* Free matchvec. */
2312 for (i = 0; i < vector_active(matchvec); i++) {
2313 if (vector_slot(matchvec, i))
2314 talloc_free(vector_slot(matchvec, i));
2315 }
2316 vector_free(matchvec);
2317
2318 /* Make new matchvec. */
2319 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2320 vector_set(matchvec, lcdstr);
2321 match_str = (char **)matchvec->index;
2322 vector_only_wrapper_free(matchvec);
2323
2324 *status = CMD_COMPLETE_MATCH;
2325 return match_str;
2326 }
2327 }
2328 }
2329
2330 match_str = (char **)matchvec->index;
2331 vector_only_wrapper_free(matchvec);
2332 *status = CMD_COMPLETE_LIST_MATCH;
2333 return match_str;
2334}
2335
2336char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2337{
2338 char **ret;
2339
2340 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2341 enum node_type onode;
2342 vector shifted_vline;
2343 unsigned int index;
2344
2345 onode = vty->node;
2346 vty->node = ENABLE_NODE;
2347 /* We can try it on enable node, cos' the vty is authenticated */
2348
2349 shifted_vline = vector_init(vector_count(vline));
2350 /* use memcpy? */
2351 for (index = 1; index < vector_active(vline); index++) {
2352 vector_set_index(shifted_vline, index - 1,
2353 vector_lookup(vline, index));
2354 }
2355
2356 ret = cmd_complete_command_real(shifted_vline, vty, status);
2357
2358 vector_free(shifted_vline);
2359 vty->node = onode;
2360 return ret;
2361 }
2362
2363 return cmd_complete_command_real(vline, vty, status);
2364}
2365
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002366static struct vty_parent_node *vty_parent(struct vty *vty)
2367{
2368 return llist_first_entry_or_null(&vty->parent_nodes,
2369 struct vty_parent_node,
2370 entry);
2371}
2372
2373static bool vty_pop_parent(struct vty *vty)
2374{
2375 struct vty_parent_node *parent = vty_parent(vty);
2376 if (!parent)
2377 return false;
2378 llist_del(&parent->entry);
2379 vty->node = parent->node;
2380 vty->priv = parent->priv;
2381 if (vty->indent)
2382 talloc_free(vty->indent);
2383 vty->indent = parent->indent;
2384 talloc_free(parent);
2385 return true;
2386}
2387
2388static void vty_clear_parents(struct vty *vty)
2389{
2390 while (vty_pop_parent(vty));
2391}
2392
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002393/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002394/*
2395 * This function MUST eventually converge on a node when called repeatedly,
2396 * there must not be any cycles.
2397 * All 'config' nodes shall converge on CONFIG_NODE.
2398 * All other 'enable' nodes shall converge on ENABLE_NODE.
2399 * All 'view' only nodes shall converge on VIEW_NODE.
2400 * All other nodes shall converge on themselves or it must be ensured,
2401 * that the user's rights are not extended anyhow by calling this function.
2402 *
2403 * Note that these requirements also apply to all functions that are used
2404 * as go_parent_cb.
2405 * Note also that this function relies on the is_config_child callback to
2406 * recognize non-config nodes if go_parent_cb is not set.
2407 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002408int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002409{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002410 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002411 case AUTH_NODE:
2412 case VIEW_NODE:
2413 case ENABLE_NODE:
2414 case CONFIG_NODE:
2415 vty_clear_parents(vty);
2416 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002417
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002418 case AUTH_ENABLE_NODE:
2419 vty->node = VIEW_NODE;
2420 vty_clear_parents(vty);
2421 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002422
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002423 default:
2424 if (host.app_info->go_parent_cb)
2425 host.app_info->go_parent_cb(vty);
2426 vty_pop_parent(vty);
2427 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002428 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002429
2430 return vty->node;
2431}
2432
2433/* Execute command by argument vline vector. */
2434static int
2435cmd_execute_command_real(vector vline, struct vty *vty,
2436 struct cmd_element **cmd)
2437{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002438 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002439 unsigned int index;
2440 vector cmd_vector;
2441 struct cmd_element *cmd_element;
2442 struct cmd_element *matched_element;
2443 unsigned int matched_count, incomplete_count;
2444 int argc;
2445 const char *argv[CMD_ARGC_MAX];
2446 enum match_type match = 0;
2447 int varflag;
2448 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002449 int rc;
2450 /* Used for temporary storage of cmd_deopt() allocated arguments during
2451 argv[] generation */
2452 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002453
2454 /* Make copy of command elements. */
2455 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2456
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002457 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002458 if ((command = vector_slot(vline, index))) {
2459 int ret;
2460
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002461 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002462 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002463
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002464 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002465 break;
2466
2467 ret =
2468 is_cmd_ambiguous(command, cmd_vector, index, match);
2469
2470 if (ret == 1) {
2471 vector_free(cmd_vector);
2472 return CMD_ERR_AMBIGUOUS;
2473 } else if (ret == 2) {
2474 vector_free(cmd_vector);
2475 return CMD_ERR_NO_MATCH;
2476 }
2477 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002478 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002479
2480 /* Check matched count. */
2481 matched_element = NULL;
2482 matched_count = 0;
2483 incomplete_count = 0;
2484
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002485 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002486 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002487 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002488 || index >= cmd_element->cmdsize) {
2489 matched_element = cmd_element;
2490#if 0
2491 printf("DEBUG: %s\n", cmd_element->string);
2492#endif
2493 matched_count++;
2494 } else {
2495 incomplete_count++;
2496 }
2497 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002498 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002499
2500 /* Finish of using cmd_vector. */
2501 vector_free(cmd_vector);
2502
2503 /* To execute command, matched_count must be 1. */
2504 if (matched_count == 0) {
2505 if (incomplete_count)
2506 return CMD_ERR_INCOMPLETE;
2507 else
2508 return CMD_ERR_NO_MATCH;
2509 }
2510
2511 if (matched_count > 1)
2512 return CMD_ERR_AMBIGUOUS;
2513
2514 /* Argument treatment */
2515 varflag = 0;
2516 argc = 0;
2517
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002518 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2519
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002520 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002521 if (argc == CMD_ARGC_MAX) {
2522 rc = CMD_ERR_EXEED_ARGC_MAX;
2523 goto rc_free_deopt_ctx;
2524 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002525 if (varflag) {
2526 argv[argc++] = vector_slot(vline, i);
2527 continue;
2528 }
2529
2530 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002531 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002532
2533 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002534 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002535 struct desc *desc = vector_slot(descvec, 0);
2536
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002537 if (CMD_OPTION(desc->cmd)) {
2538 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2539 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2540 } else {
2541 tmp_cmd = desc->cmd;
2542 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002543
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002544 if (CMD_VARARG(tmp_cmd))
2545 varflag = 1;
2546 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002547 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002548 else if (CMD_OPTION(desc->cmd))
2549 argv[argc++] = tmp_cmd;
2550 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002551 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002552 /* multi choice argument. look up which choice
2553 the user meant (can only be one after
2554 filtering and checking for ambigous). For instance,
2555 if user typed "th" for "(two|three)" arg, we
2556 want to pass "three" in argv[]. */
2557 for (j = 0; j < vector_active(descvec); j++) {
2558 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002559 if (!desc)
2560 continue;
2561 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2562 continue;
2563 if (CMD_OPTION(desc->cmd)) {
2564 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2565 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2566 } else {
2567 tmp_cmd = desc->cmd;
2568 }
2569
2570 if(CMD_VARIABLE(tmp_cmd)) {
2571 argv[argc++] = vector_slot(vline, i);
2572 } else {
2573 argv[argc++] = tmp_cmd;
2574 }
2575 break;
2576 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002577 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002578 }
2579
2580 /* For vtysh execution. */
2581 if (cmd)
2582 *cmd = matched_element;
2583
2584 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002585 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002586 else {
2587 /* Execute matched command. */
2588 struct vty_parent_node this_node = {
2589 .node = vty->node,
2590 .priv = vty->priv,
2591 .indent = vty->indent,
2592 };
2593 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002594 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002595
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002596 /* If we have stepped down into a child node, push a parent frame.
2597 * The causality is such: we don't expect every single node entry implementation to push
2598 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2599 * a parent node. Hence if the node changed without the parent node changing, we must
2600 * have stepped into a child node. */
2601 if (vty->node != this_node.node && parent == vty_parent(vty)
2602 && vty->node > CONFIG_NODE) {
2603 /* Push the parent node. */
2604 parent = talloc_zero(vty, struct vty_parent_node);
2605 *parent = this_node;
2606 llist_add(&parent->entry, &vty->parent_nodes);
2607 }
2608 }
2609
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002610rc_free_deopt_ctx:
2611 /* Now after we called the command func, we can free temporary strings */
2612 talloc_free(cmd_deopt_ctx);
2613 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002614}
2615
2616int
2617cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2618 int vtysh)
2619{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002620 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002621 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002622
2623 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002624
2625 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2626 vector shifted_vline;
2627 unsigned int index;
2628
2629 vty->node = ENABLE_NODE;
2630 /* We can try it on enable node, cos' the vty is authenticated */
2631
2632 shifted_vline = vector_init(vector_count(vline));
2633 /* use memcpy? */
2634 for (index = 1; index < vector_active(vline); index++) {
2635 vector_set_index(shifted_vline, index - 1,
2636 vector_lookup(vline, index));
2637 }
2638
2639 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2640
2641 vector_free(shifted_vline);
2642 vty->node = onode;
2643 return ret;
2644 }
2645
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002646 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002647}
2648
2649/* Execute command by argument readline. */
2650int
2651cmd_execute_command_strict(vector vline, struct vty *vty,
2652 struct cmd_element **cmd)
2653{
2654 unsigned int i;
2655 unsigned int index;
2656 vector cmd_vector;
2657 struct cmd_element *cmd_element;
2658 struct cmd_element *matched_element;
2659 unsigned int matched_count, incomplete_count;
2660 int argc;
2661 const char *argv[CMD_ARGC_MAX];
2662 int varflag;
2663 enum match_type match = 0;
2664 char *command;
2665
2666 /* Make copy of command element */
2667 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2668
2669 for (index = 0; index < vector_active(vline); index++)
2670 if ((command = vector_slot(vline, index))) {
2671 int ret;
2672
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002673 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002674 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002675
2676 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002677 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002678 break;
2679
2680 ret =
2681 is_cmd_ambiguous(command, cmd_vector, index, match);
2682 if (ret == 1) {
2683 vector_free(cmd_vector);
2684 return CMD_ERR_AMBIGUOUS;
2685 }
2686 if (ret == 2) {
2687 vector_free(cmd_vector);
2688 return CMD_ERR_NO_MATCH;
2689 }
2690 }
2691
2692 /* Check matched count. */
2693 matched_element = NULL;
2694 matched_count = 0;
2695 incomplete_count = 0;
2696 for (i = 0; i < vector_active(cmd_vector); i++)
2697 if (vector_slot(cmd_vector, i) != NULL) {
2698 cmd_element = vector_slot(cmd_vector, i);
2699
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002700 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002701 || index >= cmd_element->cmdsize) {
2702 matched_element = cmd_element;
2703 matched_count++;
2704 } else
2705 incomplete_count++;
2706 }
2707
2708 /* Finish of using cmd_vector. */
2709 vector_free(cmd_vector);
2710
2711 /* To execute command, matched_count must be 1. */
2712 if (matched_count == 0) {
2713 if (incomplete_count)
2714 return CMD_ERR_INCOMPLETE;
2715 else
2716 return CMD_ERR_NO_MATCH;
2717 }
2718
2719 if (matched_count > 1)
2720 return CMD_ERR_AMBIGUOUS;
2721
2722 /* Argument treatment */
2723 varflag = 0;
2724 argc = 0;
2725
2726 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002727 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002728 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002729 if (varflag) {
2730 argv[argc++] = vector_slot(vline, i);
2731 continue;
2732 }
2733
2734 vector descvec = vector_slot(matched_element->strvec, i);
2735
2736 if (vector_active(descvec) == 1) {
2737 struct desc *desc = vector_slot(descvec, 0);
2738
2739 if (CMD_VARARG(desc->cmd))
2740 varflag = 1;
2741
2742 if (varflag || CMD_VARIABLE(desc->cmd)
2743 || CMD_OPTION(desc->cmd))
2744 argv[argc++] = vector_slot(vline, i);
2745 } else {
2746 argv[argc++] = vector_slot(vline, i);
2747 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002748 }
2749
2750 /* For vtysh execution. */
2751 if (cmd)
2752 *cmd = matched_element;
2753
2754 if (matched_element->daemon)
2755 return CMD_SUCCESS_DAEMON;
2756
2757 /* Now execute matched command */
2758 return (*matched_element->func) (matched_element, vty, argc, argv);
2759}
2760
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002761static inline size_t len(const char *str)
2762{
2763 return str? strlen(str) : 0;
2764}
2765
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002766/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2767 * is longer than b, a must start with exactly b, and vice versa.
2768 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2769 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002770static int indent_cmp(const char *a, const char *b)
2771{
2772 size_t al, bl;
2773 al = len(a);
2774 bl = len(b);
2775 if (al > bl) {
2776 if (bl && strncmp(a, b, bl) != 0)
2777 return EINVAL;
2778 return 1;
2779 }
2780 /* al <= bl */
2781 if (al && strncmp(a, b, al) != 0)
2782 return EINVAL;
2783 return (al < bl)? -1 : 0;
2784}
2785
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002786/* Configration make from file. */
2787int config_from_file(struct vty *vty, FILE * fp)
2788{
2789 int ret;
2790 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002791 char *indent;
2792 int cmp;
2793 struct vty_parent_node this_node;
2794 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002795
2796 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002797 indent = NULL;
2798 vline = NULL;
2799 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002800
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002801 if (ret != CMD_SUCCESS)
2802 goto return_invalid_indent;
2803
2804 /* In case of comment or empty line */
2805 if (vline == NULL) {
2806 if (indent) {
2807 talloc_free(indent);
2808 indent = NULL;
2809 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002810 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002811 }
2812
Neels Hofmeyr43063632017-09-19 23:54:01 +02002813 /* We have a nonempty line. */
2814 if (!vty->indent) {
2815 /* We have just entered a node and expecting the first child to come up; but we
2816 * may also skip right back to a parent or ancestor level. */
2817 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002818
Neels Hofmeyr43063632017-09-19 23:54:01 +02002819 /* If there is no parent, record any indentation we encounter. */
2820 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2821
2822 if (cmp == EINVAL)
2823 goto return_invalid_indent;
2824
2825 if (cmp <= 0) {
2826 /* We have gone right back to the parent level or higher, we are skipping
2827 * this child node level entirely. Pop the parent to go back to a node
2828 * that was actually there (to reinstate vty->indent) and re-use below
2829 * go-parent while-loop to find an accurate match of indent in the node
2830 * ancestry. */
2831 vty_go_parent(vty);
2832 } else {
2833 /* The indent is deeper than the just entered parent, record the new
2834 * indentation characters. */
2835 vty->indent = talloc_strdup(vty, indent);
2836 /* This *is* the new indentation. */
2837 cmp = 0;
2838 }
2839 } else {
2840 /* There is a known indentation for this node level, validate and detect node
2841 * exits. */
2842 cmp = indent_cmp(indent, vty->indent);
2843 if (cmp == EINVAL)
2844 goto return_invalid_indent;
2845 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002846
2847 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2848 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2849 while (cmp < 0) {
2850 vty_go_parent(vty);
2851 cmp = indent_cmp(indent, vty->indent);
2852 if (cmp == EINVAL)
2853 goto return_invalid_indent;
2854 }
2855
2856 /* More indent without having entered a child node level? Either the parent node's indent
2857 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2858 * or the indentation increased even though the vty command didn't enter a child. */
2859 if (cmp > 0)
2860 goto return_invalid_indent;
2861
2862 /* Remember the current node before the command possibly changes it. */
2863 this_node = (struct vty_parent_node){
2864 .node = vty->node,
2865 .priv = vty->priv,
2866 .indent = vty->indent,
2867 };
2868
2869 parent = vty_parent(vty);
2870 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002871 cmd_free_strvec(vline);
2872
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002873 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002874 if (indent) {
2875 talloc_free(indent);
2876 indent = NULL;
2877 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002878 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002879 }
2880
2881 /* If we have stepped down into a child node, push a parent frame.
2882 * The causality is such: we don't expect every single node entry implementation to push
2883 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2884 * a parent node. Hence if the node changed without the parent node changing, we must
2885 * have stepped into a child node (and now expect a deeper indent). */
2886 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2887 /* Push the parent node. */
2888 parent = talloc_zero(vty, struct vty_parent_node);
2889 *parent = this_node;
2890 llist_add(&parent->entry, &vty->parent_nodes);
2891
2892 /* The current talloc'ed vty->indent string will now be owned by this parent
2893 * struct. Indicate that we don't know what deeper indent characters the user
2894 * will choose. */
2895 vty->indent = NULL;
2896 }
2897
2898 if (indent) {
2899 talloc_free(indent);
2900 indent = NULL;
2901 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002902 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002903 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2904 while (vty_parent(vty))
2905 vty_go_parent(vty);
2906
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002907 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002908
2909return_invalid_indent:
2910 if (vline)
2911 cmd_free_strvec(vline);
2912 if (indent) {
2913 talloc_free(indent);
2914 indent = NULL;
2915 }
2916 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002917}
2918
2919/* Configration from terminal */
2920DEFUN(config_terminal,
2921 config_terminal_cmd,
2922 "configure terminal",
2923 "Configuration from vty interface\n" "Configuration terminal\n")
2924{
2925 if (vty_config_lock(vty))
2926 vty->node = CONFIG_NODE;
2927 else {
2928 vty_out(vty, "VTY configuration is locked by other VTY%s",
2929 VTY_NEWLINE);
2930 return CMD_WARNING;
2931 }
2932 return CMD_SUCCESS;
2933}
2934
2935/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002936DEFUN(enable, config_enable_cmd,
2937 "enable [expert-mode]",
2938 "Turn on privileged mode command\n"
2939 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002940{
2941 /* If enable password is NULL, change to ENABLE_NODE */
2942 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2943 vty->type == VTY_SHELL_SERV)
2944 vty->node = ENABLE_NODE;
2945 else
2946 vty->node = AUTH_ENABLE_NODE;
2947
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002948 vty->expert_mode = argc > 0;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002949
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002950 return CMD_SUCCESS;
2951}
2952
2953/* Disable command */
2954DEFUN(disable,
2955 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2956{
2957 if (vty->node == ENABLE_NODE)
2958 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002959
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07002960 vty->expert_mode = false;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002961
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002962 return CMD_SUCCESS;
2963}
2964
2965/* Down vty node level. */
2966gDEFUN(config_exit,
2967 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2968{
2969 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002970 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002971 case VIEW_NODE:
2972 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002973 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002974 break;
2975 case CONFIG_NODE:
2976 vty->node = ENABLE_NODE;
2977 vty_config_unlock(vty);
2978 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002979 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002980 if (vty->node > CONFIG_NODE)
2981 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002982 break;
2983 }
2984 return CMD_SUCCESS;
2985}
2986
2987/* End of configuration. */
2988 gDEFUN(config_end,
2989 config_end_cmd, "end", "End current mode and change to enable mode.")
2990{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002991 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002992 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002993
2994 /* Repeatedly call go_parent until a top node is reached. */
2995 while (vty->node > CONFIG_NODE) {
2996 if (vty->node == last_node) {
2997 /* Ensure termination, this shouldn't happen. */
2998 break;
2999 }
3000 last_node = vty->node;
3001 vty_go_parent(vty);
3002 }
3003
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003004 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02003005 if (vty->node > ENABLE_NODE)
3006 vty->node = ENABLE_NODE;
3007 vty->index = NULL;
3008 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003009 }
3010 return CMD_SUCCESS;
3011}
3012
3013/* Show version. */
3014DEFUN(show_version,
3015 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
3016{
Harald Welte237f6242010-05-25 23:00:45 +02003017 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
3018 host.app_info->version,
3019 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
3020 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003021
3022 return CMD_SUCCESS;
3023}
3024
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003025DEFUN(show_online_help,
3026 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3027{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003028 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003029 return CMD_SUCCESS;
3030}
3031
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003032/* Help display function for all node. */
3033gDEFUN(config_help,
3034 config_help_cmd, "help", "Description of the interactive help system\n")
3035{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003036 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3037 "anytime at the command line please press '?'.%s%s"
3038 "If nothing matches, the help list will be empty and you must backup%s"
3039 " until entering a '?' shows the available options.%s"
3040 "Two styles of help are provided:%s"
3041 "1. Full help is available when you are ready to enter a%s"
3042 "command argument (e.g. 'show ?') and describes each possible%s"
3043 "argument.%s"
3044 "2. Partial help is provided when an abbreviated argument is entered%s"
3045 " and you want to know what arguments match the input%s"
3046 " (e.g. 'show me?'.)%s%s",
3047 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3048 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3049 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3050 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003051 return CMD_SUCCESS;
3052}
3053
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003054enum {
3055 ATTR_TYPE_GLOBAL = (1 << 0),
3056 ATTR_TYPE_LIB = (1 << 1),
3057 ATTR_TYPE_APP = (1 << 2),
3058};
3059
3060static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3061{
3062 const char *desc;
3063 unsigned int i;
3064 bool found;
3065 char flag;
3066
3067 if (attr_mask & ATTR_TYPE_GLOBAL) {
3068 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3069
3070 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003071 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003072 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003073
3074 /* Skip attributes without flags */
3075 if (flag != '.')
3076 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003077 }
3078 }
3079
3080 if (attr_mask & ATTR_TYPE_LIB) {
3081 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3082
3083 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3084 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3085 continue;
3086 found = true;
3087
3088 flag = cmd_lib_attr_letters[i];
3089 if (flag == '\0')
3090 flag = '.';
3091
3092 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3093 }
3094
3095 if (!found)
3096 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3097 }
3098
3099 if (attr_mask & ATTR_TYPE_APP) {
3100 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3101
3102 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3103 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3104 continue;
3105 found = true;
3106
3107 flag = host.app_info->usr_attr_letters[i];
3108 if (flag == '\0')
3109 flag = '.';
3110
3111 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3112 }
3113
3114 if (!found)
3115 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3116 }
3117}
3118
3119gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3120 "show vty-attributes",
3121 SHOW_STR "List of VTY attributes\n")
3122{
3123 print_attr_list(vty, 0xff);
3124 return CMD_SUCCESS;
3125}
3126
3127gDEFUN(show_vty_attr, show_vty_attr_cmd,
3128 "show vty-attributes (application|library|global)",
3129 SHOW_STR "List of VTY attributes\n"
3130 "Application specific attributes only\n"
3131 "Library specific attributes only\n"
3132 "Global attributes only\n")
3133{
3134 unsigned int attr_mask = 0;
3135
3136 if (argv[0][0] == 'g') /* global */
3137 attr_mask |= ATTR_TYPE_GLOBAL;
3138 else if (argv[0][0] == 'l') /* library */
3139 attr_mask |= ATTR_TYPE_LIB;
3140 else if (argv[0][0] == 'a') /* application */
3141 attr_mask |= ATTR_TYPE_APP;
3142
3143 print_attr_list(vty, attr_mask);
3144 return CMD_SUCCESS;
3145}
3146
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003147/* Compose flag bit-mask for all commands within the given node */
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003148static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003149{
3150 unsigned int flag_mask = 0x00;
3151 unsigned int f, i;
3152
3153 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3154 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3155 const struct cmd_element *cmd;
3156 char flag_letter;
3157
3158 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3159 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003160 if (cmd->attr & CMD_ATTR_DEPRECATED)
3161 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003162 if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003163 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003164 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003165 continue;
3166
3167 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3168 flag_letter = cmd_lib_attr_letters[f];
3169 else
3170 flag_letter = host.app_info->usr_attr_letters[f];
3171
3172 if (flag_letter == '\0')
3173 continue;
3174
3175 flag_mask |= (1 << f);
3176 break;
3177 }
3178 }
3179
3180 return flag_mask;
3181}
3182
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003183/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3184static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3185{
3186 static char char_mask[8 + 1];
3187 char *ptr = &char_mask[0];
3188
3189 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003190 if (cmd->attr & CMD_ATTR_HIDDEN)
3191 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3192 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003193 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3194 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3195 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3196 else
3197 *(ptr++) = '.';
3198
3199 *ptr = '\0';
3200
3201 return char_mask;
3202}
3203
3204/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003205static const char *cmd_flag_mask(const struct cmd_element *cmd,
3206 unsigned int flag_mask)
3207{
3208 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3209 char *ptr = &char_mask[0];
3210 char flag_letter;
3211 unsigned int f;
3212
3213 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003214 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003215 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003216 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003217 *(ptr++) = '.';
3218 continue;
3219 }
3220
3221 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3222 flag_letter = cmd_lib_attr_letters[f];
3223 else
3224 flag_letter = host.app_info->usr_attr_letters[f];
3225
3226 *(ptr++) = flag_letter ? flag_letter : '.';
3227 }
3228
3229 *ptr = '\0';
3230
3231 return char_mask;
3232}
3233
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003234/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003235gDEFUN(config_list, config_list_cmd,
3236 "list [with-flags]",
3237 "Print command list\n"
3238 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003239{
3240 unsigned int i;
3241 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003242 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003243 struct cmd_element *cmd;
3244
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003245 if (argc > 0)
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003246 flag_mask = node_flag_mask(cnode, vty->expert_mode);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003247
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003248 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3249 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3250 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003251 if (cmd->attr & CMD_ATTR_DEPRECATED)
3252 continue;
Vadim Yanitskiy0a2d9bd2020-10-25 16:34:57 +07003253 if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003254 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003255 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003256 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3257 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003258 vty_out(vty, " %s %s %s%s",
3259 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003260 cmd_flag_mask(cmd, flag_mask),
3261 cmd->string, VTY_NEWLINE);
3262 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003263 }
3264
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003265 return CMD_SUCCESS;
3266}
3267
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003268static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003269{
3270 unsigned int i;
3271 int fd;
3272 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003273 char *config_file_tmp = NULL;
3274 char *config_file_sav = NULL;
3275 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003276 struct stat st;
3277
3278 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003279
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003280 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3281 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3282 * manually instead. */
3283
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003284 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003285 config_file_sav =
3286 _talloc_zero(tall_vty_cmd_ctx,
3287 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3288 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003289 if (!config_file_sav)
3290 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003291 strcpy(config_file_sav, config_file);
3292 strcat(config_file_sav, CONF_BACKUP_EXT);
3293
3294 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003295 "config_file_tmp");
3296 if (!config_file_tmp) {
3297 talloc_free(config_file_sav);
3298 return -1;
3299 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003300 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3301
3302 /* Open file to configuration write. */
3303 fd = mkstemp(config_file_tmp);
3304 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003305 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003306 talloc_free(config_file_tmp);
3307 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003308 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003309 }
3310
3311 /* Make vty for configuration file. */
3312 file_vty = vty_new();
3313 file_vty->fd = fd;
3314 file_vty->type = VTY_FILE;
3315
3316 /* Config file header print. */
3317 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003318 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003319 //vty_time_print (file_vty, 1);
3320 vty_out(file_vty, "!\n");
3321
3322 for (i = 0; i < vector_active(cmdvec); i++)
3323 if ((node = vector_slot(cmdvec, i)) && node->func) {
3324 if ((*node->func) (file_vty))
3325 vty_out(file_vty, "!\n");
3326 }
3327 vty_close(file_vty);
3328
3329 if (unlink(config_file_sav) != 0)
3330 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003331 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003332 talloc_free(config_file_sav);
3333 talloc_free(config_file_tmp);
3334 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003335 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003336 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003337
3338 /* Only link the .sav file if the original file exists */
3339 if (stat(config_file, &st) == 0) {
3340 if (link(config_file, config_file_sav) != 0) {
3341 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3342 talloc_free(config_file_sav);
3343 talloc_free(config_file_tmp);
3344 unlink(config_file_tmp);
3345 return -3;
3346 }
3347 sync();
3348 if (unlink(config_file) != 0) {
3349 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3350 talloc_free(config_file_sav);
3351 talloc_free(config_file_tmp);
3352 unlink(config_file_tmp);
3353 return -4;
3354 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003355 }
3356 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003357 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003358 talloc_free(config_file_sav);
3359 talloc_free(config_file_tmp);
3360 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003361 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003362 }
3363 unlink(config_file_tmp);
3364 sync();
3365
3366 talloc_free(config_file_sav);
3367 talloc_free(config_file_tmp);
3368
3369 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003370 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3371 return -6;
3372 }
3373
3374 return 0;
3375}
3376
3377
3378/* Write current configuration into file. */
3379DEFUN(config_write_file,
3380 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003381 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003382 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003383 "Write to configuration file\n"
3384 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003385{
3386 char *failed_file;
3387 int rc;
3388
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003389 if (host.app_info->config_is_consistent) {
3390 rc = host.app_info->config_is_consistent(vty);
3391 if (!rc) {
3392 vty_out(vty, "Configuration is not consistent%s",
3393 VTY_NEWLINE);
3394 return CMD_WARNING;
3395 }
3396 }
3397
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003398 if (argc == 1)
3399 host_config_set(argv[0]);
3400
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003401 if (host.config == NULL) {
3402 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3403 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003404 return CMD_WARNING;
3405 }
3406
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003407 rc = write_config_file(host.config, &failed_file);
3408 switch (rc) {
3409 case -1:
3410 vty_out(vty, "Can't open configuration file %s.%s",
3411 failed_file, VTY_NEWLINE);
3412 rc = CMD_WARNING;
3413 break;
3414 case -2:
3415 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3416 failed_file, VTY_NEWLINE);
3417 rc = CMD_WARNING;
3418 break;
3419 case -3:
3420 vty_out(vty, "Can't backup old configuration file %s.%s",
3421 failed_file, VTY_NEWLINE);
3422 rc = CMD_WARNING;
3423 break;
3424 case -4:
3425 vty_out(vty, "Can't unlink configuration file %s.%s",
3426 failed_file, VTY_NEWLINE);
3427 rc = CMD_WARNING;
3428 break;
3429 case -5:
3430 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3431 VTY_NEWLINE);
3432 rc = CMD_WARNING;
3433 break;
3434 case -6:
3435 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3436 failed_file, strerror(errno), errno, VTY_NEWLINE);
3437 rc = CMD_WARNING;
3438 break;
3439 default:
3440 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3441 rc = CMD_SUCCESS;
3442 break;
3443 }
3444
3445 talloc_free(failed_file);
3446 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003447}
3448
3449ALIAS(config_write_file,
3450 config_write_cmd,
3451 "write", "Write running configuration to memory, network, or terminal\n")
3452
3453 ALIAS(config_write_file,
3454 config_write_memory_cmd,
3455 "write memory",
3456 "Write running configuration to memory, network, or terminal\n"
3457 "Write configuration to the file (same as write file)\n")
3458
3459 ALIAS(config_write_file,
3460 copy_runningconfig_startupconfig_cmd,
3461 "copy running-config startup-config",
3462 "Copy configuration\n"
3463 "Copy running config to... \n"
3464 "Copy running config to startup config (same as write file)\n")
3465
3466/* Write current configuration into the terminal. */
3467 DEFUN(config_write_terminal,
3468 config_write_terminal_cmd,
3469 "write terminal",
3470 "Write running configuration to memory, network, or terminal\n"
3471 "Write to terminal\n")
3472{
3473 unsigned int i;
3474 struct cmd_node *node;
3475
3476 if (vty->type == VTY_SHELL_SERV) {
3477 for (i = 0; i < vector_active(cmdvec); i++)
3478 if ((node = vector_slot(cmdvec, i)) && node->func
3479 && node->vtysh) {
3480 if ((*node->func) (vty))
3481 vty_out(vty, "!%s", VTY_NEWLINE);
3482 }
3483 } else {
3484 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3485 VTY_NEWLINE);
3486 vty_out(vty, "!%s", VTY_NEWLINE);
3487
3488 for (i = 0; i < vector_active(cmdvec); i++)
3489 if ((node = vector_slot(cmdvec, i)) && node->func) {
3490 if ((*node->func) (vty))
3491 vty_out(vty, "!%s", VTY_NEWLINE);
3492 }
3493 vty_out(vty, "end%s", VTY_NEWLINE);
3494 }
3495 return CMD_SUCCESS;
3496}
3497
3498/* Write current configuration into the terminal. */
3499ALIAS(config_write_terminal,
3500 show_running_config_cmd,
3501 "show running-config", SHOW_STR "running configuration\n")
3502
3503/* Write startup configuration into the terminal. */
3504 DEFUN(show_startup_config,
3505 show_startup_config_cmd,
3506 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3507{
3508 char buf[BUFSIZ];
3509 FILE *confp;
3510
3511 confp = fopen(host.config, "r");
3512 if (confp == NULL) {
3513 vty_out(vty, "Can't open configuration file [%s]%s",
3514 host.config, VTY_NEWLINE);
3515 return CMD_WARNING;
3516 }
3517
3518 while (fgets(buf, BUFSIZ, confp)) {
3519 char *cp = buf;
3520
3521 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3522 cp++;
3523 *cp = '\0';
3524
3525 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3526 }
3527
3528 fclose(confp);
3529
3530 return CMD_SUCCESS;
3531}
3532
3533/* Hostname configuration */
3534DEFUN(config_hostname,
3535 hostname_cmd,
3536 "hostname WORD",
3537 "Set system's network name\n" "This system's network name\n")
3538{
3539 if (!isalpha((int)*argv[0])) {
3540 vty_out(vty, "Please specify string starting with alphabet%s",
3541 VTY_NEWLINE);
3542 return CMD_WARNING;
3543 }
3544
3545 if (host.name)
3546 talloc_free(host.name);
3547
3548 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3549 return CMD_SUCCESS;
3550}
3551
3552DEFUN(config_no_hostname,
3553 no_hostname_cmd,
3554 "no hostname [HOSTNAME]",
3555 NO_STR "Reset system's network name\n" "Host name of this router\n")
3556{
3557 if (host.name)
3558 talloc_free(host.name);
3559 host.name = NULL;
3560 return CMD_SUCCESS;
3561}
3562
3563/* VTY interface password set. */
3564DEFUN(config_password, password_cmd,
3565 "password (8|) WORD",
3566 "Assign the terminal connection password\n"
3567 "Specifies a HIDDEN password will follow\n"
3568 "dummy string \n" "The HIDDEN line password string\n")
3569{
3570 /* Argument check. */
3571 if (argc == 0) {
3572 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3573 return CMD_WARNING;
3574 }
3575
3576 if (argc == 2) {
3577 if (*argv[0] == '8') {
3578 if (host.password)
3579 talloc_free(host.password);
3580 host.password = NULL;
3581 if (host.password_encrypt)
3582 talloc_free(host.password_encrypt);
3583 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3584 return CMD_SUCCESS;
3585 } else {
3586 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3587 return CMD_WARNING;
3588 }
3589 }
3590
3591 if (!isalnum((int)*argv[0])) {
3592 vty_out(vty,
3593 "Please specify string starting with alphanumeric%s",
3594 VTY_NEWLINE);
3595 return CMD_WARNING;
3596 }
3597
3598 if (host.password)
3599 talloc_free(host.password);
3600 host.password = NULL;
3601
3602#ifdef VTY_CRYPT_PW
3603 if (host.encrypt) {
3604 if (host.password_encrypt)
3605 talloc_free(host.password_encrypt);
3606 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3607 } else
3608#endif
3609 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3610
3611 return CMD_SUCCESS;
3612}
3613
3614ALIAS(config_password, password_text_cmd,
3615 "password LINE",
3616 "Assign the terminal connection password\n"
3617 "The UNENCRYPTED (cleartext) line password\n")
3618
3619/* VTY enable password set. */
3620 DEFUN(config_enable_password, enable_password_cmd,
3621 "enable password (8|) WORD",
3622 "Modify enable password parameters\n"
3623 "Assign the privileged level password\n"
3624 "Specifies a HIDDEN password will follow\n"
3625 "dummy string \n" "The HIDDEN 'enable' password string\n")
3626{
3627 /* Argument check. */
3628 if (argc == 0) {
3629 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3630 return CMD_WARNING;
3631 }
3632
3633 /* Crypt type is specified. */
3634 if (argc == 2) {
3635 if (*argv[0] == '8') {
3636 if (host.enable)
3637 talloc_free(host.enable);
3638 host.enable = NULL;
3639
3640 if (host.enable_encrypt)
3641 talloc_free(host.enable_encrypt);
3642 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3643
3644 return CMD_SUCCESS;
3645 } else {
3646 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3647 return CMD_WARNING;
3648 }
3649 }
3650
3651 if (!isalnum((int)*argv[0])) {
3652 vty_out(vty,
3653 "Please specify string starting with alphanumeric%s",
3654 VTY_NEWLINE);
3655 return CMD_WARNING;
3656 }
3657
3658 if (host.enable)
3659 talloc_free(host.enable);
3660 host.enable = NULL;
3661
3662 /* Plain password input. */
3663#ifdef VTY_CRYPT_PW
3664 if (host.encrypt) {
3665 if (host.enable_encrypt)
3666 talloc_free(host.enable_encrypt);
3667 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3668 } else
3669#endif
3670 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3671
3672 return CMD_SUCCESS;
3673}
3674
3675ALIAS(config_enable_password,
3676 enable_password_text_cmd,
3677 "enable password LINE",
3678 "Modify enable password parameters\n"
3679 "Assign the privileged level password\n"
3680 "The UNENCRYPTED (cleartext) 'enable' password\n")
3681
3682/* VTY enable password delete. */
3683 DEFUN(no_config_enable_password, no_enable_password_cmd,
3684 "no enable password",
3685 NO_STR
3686 "Modify enable password parameters\n"
3687 "Assign the privileged level password\n")
3688{
3689 if (host.enable)
3690 talloc_free(host.enable);
3691 host.enable = NULL;
3692
3693 if (host.enable_encrypt)
3694 talloc_free(host.enable_encrypt);
3695 host.enable_encrypt = NULL;
3696
3697 return CMD_SUCCESS;
3698}
3699
3700#ifdef VTY_CRYPT_PW
3701DEFUN(service_password_encrypt,
3702 service_password_encrypt_cmd,
3703 "service password-encryption",
3704 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3705{
3706 if (host.encrypt)
3707 return CMD_SUCCESS;
3708
3709 host.encrypt = 1;
3710
3711 if (host.password) {
3712 if (host.password_encrypt)
3713 talloc_free(host.password_encrypt);
3714 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3715 }
3716 if (host.enable) {
3717 if (host.enable_encrypt)
3718 talloc_free(host.enable_encrypt);
3719 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3720 }
3721
3722 return CMD_SUCCESS;
3723}
3724
3725DEFUN(no_service_password_encrypt,
3726 no_service_password_encrypt_cmd,
3727 "no service password-encryption",
3728 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3729{
3730 if (!host.encrypt)
3731 return CMD_SUCCESS;
3732
3733 host.encrypt = 0;
3734
3735 if (host.password_encrypt)
3736 talloc_free(host.password_encrypt);
3737 host.password_encrypt = NULL;
3738
3739 if (host.enable_encrypt)
3740 talloc_free(host.enable_encrypt);
3741 host.enable_encrypt = NULL;
3742
3743 return CMD_SUCCESS;
3744}
3745#endif
3746
3747DEFUN(config_terminal_length, config_terminal_length_cmd,
3748 "terminal length <0-512>",
3749 "Set terminal line parameters\n"
3750 "Set number of lines on a screen\n"
3751 "Number of lines on screen (0 for no pausing)\n")
3752{
3753 int lines;
3754 char *endptr = NULL;
3755
3756 lines = strtol(argv[0], &endptr, 10);
3757 if (lines < 0 || lines > 512 || *endptr != '\0') {
3758 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3759 return CMD_WARNING;
3760 }
3761 vty->lines = lines;
3762
3763 return CMD_SUCCESS;
3764}
3765
3766DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3767 "terminal no length",
3768 "Set terminal line parameters\n"
3769 NO_STR "Set number of lines on a screen\n")
3770{
3771 vty->lines = -1;
3772 return CMD_SUCCESS;
3773}
3774
3775DEFUN(service_terminal_length, service_terminal_length_cmd,
3776 "service terminal-length <0-512>",
3777 "Set up miscellaneous service\n"
3778 "System wide terminal length configuration\n"
3779 "Number of lines of VTY (0 means no line control)\n")
3780{
3781 int lines;
3782 char *endptr = NULL;
3783
3784 lines = strtol(argv[0], &endptr, 10);
3785 if (lines < 0 || lines > 512 || *endptr != '\0') {
3786 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3787 return CMD_WARNING;
3788 }
3789 host.lines = lines;
3790
3791 return CMD_SUCCESS;
3792}
3793
3794DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3795 "no service terminal-length [<0-512>]",
3796 NO_STR
3797 "Set up miscellaneous service\n"
3798 "System wide terminal length configuration\n"
3799 "Number of lines of VTY (0 means no line control)\n")
3800{
3801 host.lines = -1;
3802 return CMD_SUCCESS;
3803}
3804
3805DEFUN_HIDDEN(do_echo,
3806 echo_cmd,
3807 "echo .MESSAGE",
3808 "Echo a message back to the vty\n" "The message to echo\n")
3809{
3810 char *message;
3811
3812 vty_out(vty, "%s%s",
3813 ((message =
3814 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3815 if (message)
3816 talloc_free(message);
3817 return CMD_SUCCESS;
3818}
3819
3820#if 0
3821DEFUN(config_logmsg,
3822 config_logmsg_cmd,
3823 "logmsg " LOG_LEVELS " .MESSAGE",
3824 "Send a message to enabled logging destinations\n"
3825 LOG_LEVEL_DESC "The message to send\n")
3826{
3827 int level;
3828 char *message;
3829
3830 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3831 return CMD_ERR_NO_MATCH;
3832
3833 zlog(NULL, level,
3834 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3835 if (message)
3836 talloc_free(message);
3837 return CMD_SUCCESS;
3838}
3839
3840DEFUN(show_logging,
3841 show_logging_cmd,
3842 "show logging", SHOW_STR "Show current logging configuration\n")
3843{
3844 struct zlog *zl = zlog_default;
3845
3846 vty_out(vty, "Syslog logging: ");
3847 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3848 vty_out(vty, "disabled");
3849 else
3850 vty_out(vty, "level %s, facility %s, ident %s",
3851 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3852 facility_name(zl->facility), zl->ident);
3853 vty_out(vty, "%s", VTY_NEWLINE);
3854
3855 vty_out(vty, "Stdout logging: ");
3856 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3857 vty_out(vty, "disabled");
3858 else
3859 vty_out(vty, "level %s",
3860 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3861 vty_out(vty, "%s", VTY_NEWLINE);
3862
3863 vty_out(vty, "Monitor logging: ");
3864 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3865 vty_out(vty, "disabled");
3866 else
3867 vty_out(vty, "level %s",
3868 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3869 vty_out(vty, "%s", VTY_NEWLINE);
3870
3871 vty_out(vty, "File logging: ");
3872 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3873 vty_out(vty, "disabled");
3874 else
3875 vty_out(vty, "level %s, filename %s",
3876 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3877 zl->filename);
3878 vty_out(vty, "%s", VTY_NEWLINE);
3879
3880 vty_out(vty, "Protocol name: %s%s",
3881 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3882 vty_out(vty, "Record priority: %s%s",
3883 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3884
3885 return CMD_SUCCESS;
3886}
3887
3888DEFUN(config_log_stdout,
3889 config_log_stdout_cmd,
3890 "log stdout", "Logging control\n" "Set stdout logging level\n")
3891{
3892 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3893 return CMD_SUCCESS;
3894}
3895
3896DEFUN(config_log_stdout_level,
3897 config_log_stdout_level_cmd,
3898 "log stdout " LOG_LEVELS,
3899 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3900{
3901 int level;
3902
3903 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3904 return CMD_ERR_NO_MATCH;
3905 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3906 return CMD_SUCCESS;
3907}
3908
3909DEFUN(no_config_log_stdout,
3910 no_config_log_stdout_cmd,
3911 "no log stdout [LEVEL]",
3912 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3913{
3914 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3915 return CMD_SUCCESS;
3916}
3917
3918DEFUN(config_log_monitor,
3919 config_log_monitor_cmd,
3920 "log monitor",
3921 "Logging control\n" "Set terminal line (monitor) logging level\n")
3922{
3923 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3924 return CMD_SUCCESS;
3925}
3926
3927DEFUN(config_log_monitor_level,
3928 config_log_monitor_level_cmd,
3929 "log monitor " LOG_LEVELS,
3930 "Logging control\n"
3931 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3932{
3933 int level;
3934
3935 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3936 return CMD_ERR_NO_MATCH;
3937 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3938 return CMD_SUCCESS;
3939}
3940
3941DEFUN(no_config_log_monitor,
3942 no_config_log_monitor_cmd,
3943 "no log monitor [LEVEL]",
3944 NO_STR
3945 "Logging control\n"
3946 "Disable terminal line (monitor) logging\n" "Logging level\n")
3947{
3948 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3949 return CMD_SUCCESS;
3950}
3951
3952static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3953{
3954 int ret;
3955 char *p = NULL;
3956 const char *fullpath;
3957
3958 /* Path detection. */
3959 if (!IS_DIRECTORY_SEP(*fname)) {
3960 char cwd[MAXPATHLEN + 1];
3961 cwd[MAXPATHLEN] = '\0';
3962
3963 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3964 zlog_err("config_log_file: Unable to alloc mem!");
3965 return CMD_WARNING;
3966 }
3967
3968 if ((p = _talloc_zero(tall_vcmd_ctx,
3969 strlen(cwd) + strlen(fname) + 2),
3970 "set_log_file")
3971 == NULL) {
3972 zlog_err("config_log_file: Unable to alloc mem!");
3973 return CMD_WARNING;
3974 }
3975 sprintf(p, "%s/%s", cwd, fname);
3976 fullpath = p;
3977 } else
3978 fullpath = fname;
3979
3980 ret = zlog_set_file(NULL, fullpath, loglevel);
3981
3982 if (p)
3983 talloc_free(p);
3984
3985 if (!ret) {
3986 vty_out(vty, "can't open logfile %s\n", fname);
3987 return CMD_WARNING;
3988 }
3989
3990 if (host.logfile)
3991 talloc_free(host.logfile);
3992
3993 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3994
3995 return CMD_SUCCESS;
3996}
3997
3998DEFUN(config_log_file,
3999 config_log_file_cmd,
4000 "log file FILENAME",
4001 "Logging control\n" "Logging to file\n" "Logging filename\n")
4002{
4003 return set_log_file(vty, argv[0], zlog_default->default_lvl);
4004}
4005
4006DEFUN(config_log_file_level,
4007 config_log_file_level_cmd,
4008 "log file FILENAME " LOG_LEVELS,
4009 "Logging control\n"
4010 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
4011{
4012 int level;
4013
4014 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
4015 return CMD_ERR_NO_MATCH;
4016 return set_log_file(vty, argv[0], level);
4017}
4018
4019DEFUN(no_config_log_file,
4020 no_config_log_file_cmd,
4021 "no log file [FILENAME]",
4022 NO_STR
4023 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4024{
4025 zlog_reset_file(NULL);
4026
4027 if (host.logfile)
4028 talloc_free(host.logfile);
4029
4030 host.logfile = NULL;
4031
4032 return CMD_SUCCESS;
4033}
4034
4035ALIAS(no_config_log_file,
4036 no_config_log_file_level_cmd,
4037 "no log file FILENAME LEVEL",
4038 NO_STR
4039 "Logging control\n"
4040 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4041
4042 DEFUN(config_log_syslog,
4043 config_log_syslog_cmd,
4044 "log syslog", "Logging control\n" "Set syslog logging level\n")
4045{
4046 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4047 return CMD_SUCCESS;
4048}
4049
4050DEFUN(config_log_syslog_level,
4051 config_log_syslog_level_cmd,
4052 "log syslog " LOG_LEVELS,
4053 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4054{
4055 int level;
4056
4057 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4058 return CMD_ERR_NO_MATCH;
4059 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4060 return CMD_SUCCESS;
4061}
4062
4063DEFUN_DEPRECATED(config_log_syslog_facility,
4064 config_log_syslog_facility_cmd,
4065 "log syslog facility " LOG_FACILITIES,
4066 "Logging control\n"
4067 "Logging goes to syslog\n"
4068 "(Deprecated) Facility parameter for syslog messages\n"
4069 LOG_FACILITY_DESC)
4070{
4071 int facility;
4072
4073 if ((facility = facility_match(argv[0])) < 0)
4074 return CMD_ERR_NO_MATCH;
4075
4076 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4077 zlog_default->facility = facility;
4078 return CMD_SUCCESS;
4079}
4080
4081DEFUN(no_config_log_syslog,
4082 no_config_log_syslog_cmd,
4083 "no log syslog [LEVEL]",
4084 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4085{
4086 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4087 return CMD_SUCCESS;
4088}
4089
4090ALIAS(no_config_log_syslog,
4091 no_config_log_syslog_facility_cmd,
4092 "no log syslog facility " LOG_FACILITIES,
4093 NO_STR
4094 "Logging control\n"
4095 "Logging goes to syslog\n"
4096 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4097
4098 DEFUN(config_log_facility,
4099 config_log_facility_cmd,
4100 "log facility " LOG_FACILITIES,
4101 "Logging control\n"
4102 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4103{
4104 int facility;
4105
4106 if ((facility = facility_match(argv[0])) < 0)
4107 return CMD_ERR_NO_MATCH;
4108 zlog_default->facility = facility;
4109 return CMD_SUCCESS;
4110}
4111
4112DEFUN(no_config_log_facility,
4113 no_config_log_facility_cmd,
4114 "no log facility [FACILITY]",
4115 NO_STR
4116 "Logging control\n"
4117 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4118{
4119 zlog_default->facility = LOG_DAEMON;
4120 return CMD_SUCCESS;
4121}
4122
4123DEFUN_DEPRECATED(config_log_trap,
4124 config_log_trap_cmd,
4125 "log trap " LOG_LEVELS,
4126 "Logging control\n"
4127 "(Deprecated) Set logging level and default for all destinations\n"
4128 LOG_LEVEL_DESC)
4129{
4130 int new_level;
4131 int i;
4132
4133 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4134 return CMD_ERR_NO_MATCH;
4135
4136 zlog_default->default_lvl = new_level;
4137 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4138 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4139 zlog_default->maxlvl[i] = new_level;
4140 return CMD_SUCCESS;
4141}
4142
4143DEFUN_DEPRECATED(no_config_log_trap,
4144 no_config_log_trap_cmd,
4145 "no log trap [LEVEL]",
4146 NO_STR
4147 "Logging control\n"
4148 "Permit all logging information\n" "Logging level\n")
4149{
4150 zlog_default->default_lvl = LOG_DEBUG;
4151 return CMD_SUCCESS;
4152}
4153
4154DEFUN(config_log_record_priority,
4155 config_log_record_priority_cmd,
4156 "log record-priority",
4157 "Logging control\n"
4158 "Log the priority of the message within the message\n")
4159{
4160 zlog_default->record_priority = 1;
4161 return CMD_SUCCESS;
4162}
4163
4164DEFUN(no_config_log_record_priority,
4165 no_config_log_record_priority_cmd,
4166 "no log record-priority",
4167 NO_STR
4168 "Logging control\n"
4169 "Do not log the priority of the message within the message\n")
4170{
4171 zlog_default->record_priority = 0;
4172 return CMD_SUCCESS;
4173}
4174#endif
4175
4176DEFUN(banner_motd_file,
4177 banner_motd_file_cmd,
4178 "banner motd file [FILE]",
4179 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4180{
4181 if (host.motdfile)
4182 talloc_free(host.motdfile);
4183 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4184
4185 return CMD_SUCCESS;
4186}
4187
4188DEFUN(banner_motd_default,
4189 banner_motd_default_cmd,
4190 "banner motd default",
4191 "Set banner string\n" "Strings for motd\n" "Default string\n")
4192{
4193 host.motd = default_motd;
4194 return CMD_SUCCESS;
4195}
4196
4197DEFUN(no_banner_motd,
4198 no_banner_motd_cmd,
4199 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4200{
4201 host.motd = NULL;
4202 if (host.motdfile)
4203 talloc_free(host.motdfile);
4204 host.motdfile = NULL;
4205 return CMD_SUCCESS;
4206}
4207
4208/* Set config filename. Called from vty.c */
4209void host_config_set(const char *filename)
4210{
4211 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4212}
4213
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004214/*! Deprecated, now happens implicitly when calling install_node().
4215 * Users of the API may still attempt to call this function, hence
4216 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004217void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004218{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004219}
4220
4221/*! Deprecated, now happens implicitly when calling install_node().
4222 * Users of the API may still attempt to call this function, hence
4223 * leave it here as a no-op. */
4224void vty_install_default(int node)
4225{
4226}
4227
4228/*! Install common commands like 'exit' and 'list'. */
4229static void install_basic_node_commands(int node)
4230{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004231 install_lib_element(node, &config_help_cmd);
4232 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004233
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004234 install_lib_element(node, &show_vty_attr_all_cmd);
4235 install_lib_element(node, &show_vty_attr_cmd);
4236
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004237 install_lib_element(node, &config_write_terminal_cmd);
4238 install_lib_element(node, &config_write_file_cmd);
4239 install_lib_element(node, &config_write_memory_cmd);
4240 install_lib_element(node, &config_write_cmd);
4241 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004242
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004243 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004244
4245 if (node >= CONFIG_NODE) {
4246 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004247 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004248 }
4249}
4250
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004251/*! Return true if a node is installed by install_basic_node_commands(), so
4252 * that we can avoid repeating them for each and every node during 'show
4253 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004254static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004255{
4256 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004257 || cmd == &show_vty_attr_all_cmd
4258 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004259 || cmd == &config_list_cmd
4260 || cmd == &config_write_terminal_cmd
4261 || cmd == &config_write_file_cmd
4262 || cmd == &config_write_memory_cmd
4263 || cmd == &config_write_cmd
4264 || cmd == &show_running_config_cmd
4265 || cmd == &config_exit_cmd
4266 || cmd == &config_end_cmd)
4267 return true;
4268 return false;
4269}
4270
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004271/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004272 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004273 * \param[in] vty the vty of the code
4274 * \param[in] filename where to store the file
4275 * \return 0 in case of success.
4276 *
4277 * If the filename already exists create a filename.sav
4278 * version with the current code.
4279 *
4280 */
4281int osmo_vty_write_config_file(const char *filename)
4282{
4283 char *failed_file;
4284 int rc;
4285
4286 rc = write_config_file(filename, &failed_file);
4287 talloc_free(failed_file);
4288 return rc;
4289}
4290
4291/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004292 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004293 * \return 0 in case of success.
4294 *
4295 * If the filename already exists create a filename.sav
4296 * version with the current code.
4297 *
4298 */
4299int osmo_vty_save_config_file(void)
4300{
4301 char *failed_file;
4302 int rc;
4303
4304 if (host.config == NULL)
4305 return -7;
4306
4307 rc = write_config_file(host.config, &failed_file);
4308 talloc_free(failed_file);
4309 return rc;
4310}
4311
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004312/* Initialize command interface. Install basic nodes and commands. */
4313void cmd_init(int terminal)
4314{
4315 /* Allocate initial top vector of commands. */
4316 cmdvec = vector_init(VECTOR_MIN_SIZE);
4317
4318 /* Default host value settings. */
4319 host.name = NULL;
4320 host.password = NULL;
4321 host.enable = NULL;
4322 host.logfile = NULL;
4323 host.config = NULL;
4324 host.lines = -1;
4325 host.motd = default_motd;
4326 host.motdfile = NULL;
4327
4328 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004329 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004330 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004331 install_node_bare(&auth_node, NULL);
4332 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004333 install_node(&config_node, config_write_host);
4334
4335 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004336 install_lib_element(VIEW_NODE, &show_version_cmd);
4337 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004338 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004339 install_lib_element(VIEW_NODE, &config_list_cmd);
4340 install_lib_element(VIEW_NODE, &config_exit_cmd);
4341 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004342 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4343 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004344 install_lib_element(VIEW_NODE, &config_enable_cmd);
4345 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4346 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4347 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004348 }
4349
4350 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004351 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4352 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4353 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004354 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004355 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4356 install_lib_element(ENABLE_NODE, &show_version_cmd);
4357 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004358
4359 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004360 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4361 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4362 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004363 }
4364
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004365 install_lib_element(CONFIG_NODE, &hostname_cmd);
4366 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004367
4368 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004369 install_lib_element(CONFIG_NODE, &password_cmd);
4370 install_lib_element(CONFIG_NODE, &password_text_cmd);
4371 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4372 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4373 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004374
4375#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004376 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4377 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004378#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004379 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4380 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4381 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4382 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4383 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004384
4385 }
4386 srand(time(NULL));
4387}
Harald Welte7acb30c2011-08-17 17:13:48 +02004388
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004389/* FIXME: execute this section in the unit test instead */
4390static __attribute__((constructor)) void on_dso_load(void)
4391{
4392 unsigned int i, j;
4393
4394 /* Check total number of the library specific attributes */
4395 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4396
4397 /* Check for duplicates in the list of library specific flags */
4398 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4399 if (cmd_lib_attr_letters[i] == '\0')
4400 continue;
4401
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004402 /* Some flag characters are reserved for global attributes */
4403 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4404 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4405 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4406
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004407 /* Only upper case flag letters are allowed for libraries */
4408 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4409 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4410
4411 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4412 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4413 }
4414}
4415
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004416/*! @} */