blob: 7ea1971235f9a4d4c5018c12d8250cddc6c29652 [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.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100776 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200777static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100778{
779 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200780 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100781
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200782 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100783
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200784 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200785 print_func(data, " <node id='_common_cmds_'>%s", newline);
786 print_func(data, " <name>Common Commands</name>%s", newline);
787 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
788 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200789 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700790 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200791 if (!cnode)
792 continue;
793 if (cnode->node != CONFIG_NODE)
794 continue;
795
796 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700797 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200798 if (!vty_command_is_common(elem))
799 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700800 if (elem->attr & CMD_ATTR_DEPRECATED)
801 continue;
802 if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
803 continue;
804 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200805 }
806 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200807 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200808
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100809 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700810 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100811 if (!cnode)
812 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200813 if (vector_active(cnode->cmd_vector) < 1)
814 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100815
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200816 /* De-dup node IDs: how many times has this same name been used before? Count the first
817 * occurence as _1 and omit that first suffix, so that the first occurence is called
818 * 'name', the second becomes 'name_2', then 'name_3', ... */
819 same_name_count = 1;
820 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700821 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200822 if (!cnode2)
823 continue;
824 if (strcmp(cnode->name, cnode2->name) == 0)
825 same_name_count ++;
826 }
827
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200828 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200829 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200830 print_func(data, "_%d", same_name_count);
831 print_func(data, "'>%s", newline);
832 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100833
834 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700835 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200836 if (vty_command_is_common(elem))
837 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700838 if (elem->attr & CMD_ATTR_DEPRECATED)
839 continue;
840 if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
841 continue;
842 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100843 }
844
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200845 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100846 }
847
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200848 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100849
850 return 0;
851}
852
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200853static int print_func_vty(void *data, const char *format, ...)
854{
855 struct vty *vty = data;
856 va_list args;
857 int rc;
858 va_start(args, format);
859 rc = vty_out_va(vty, format, args);
860 va_end(args);
861 return rc;
862}
863
864static int vty_dump_xml_ref_to_vty(struct vty *vty)
865{
866 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
867}
868
869static int print_func_stream(void *data, const char *format, ...)
870{
871 va_list args;
872 int rc;
873 va_start(args, format);
874 rc = vfprintf((FILE*)data, format, args);
875 va_end(args);
876 return rc;
877}
878
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700879const struct value_string vty_ref_gen_mode_names[] = {
880 { VTY_REF_GEN_MODE_DEFAULT, "default" },
881 { VTY_REF_GEN_MODE_EXPERT, "expert" },
882 { 0, NULL }
883};
884
885const struct value_string vty_ref_gen_mode_desc[] = {
886 { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
887 { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
888 { 0, NULL }
889};
890
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200891/*! Print the XML reference of all VTY nodes to the given stream.
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700892 * \param[out] stream Output stream to print the XML reference to.
893 * \param[in] mode The XML reference generation mode.
894 * \returns always 0 for now, no errors possible.
895 */
896int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
897{
898 switch (mode) {
899 case VTY_REF_GEN_MODE_EXPERT:
900 host.expert_mode = true;
901 break;
902 case VTY_REF_GEN_MODE_DEFAULT:
903 default:
904 host.expert_mode = false;
905 break;
906 }
907
908 return vty_dump_nodes(print_func_stream, stream, "\n");
909}
910
911/*! Print the XML reference of all VTY nodes to the given stream.
912 * \param[out] stream Output stream to print the XML reference to.
913 * \returns always 0 for now, no errors possible.
914 *
915 * NOTE: this function is deprecated because it does not allow to
916 * specify the XML reference generation mode (default mode
917 * is hard-coded). Use vty_dump_xml_ref_mode() instead.
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200918 */
919int vty_dump_xml_ref(FILE *stream)
920{
Vadim Yanitskiy4165d042020-10-23 16:40:57 +0700921 return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200922}
923
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200924/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100925static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
926{
927 int i;
928
929 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
930 struct cmd_element *elem;
931 elem = vector_slot(cnode->cmd_vector, i);
932 if (!elem->string)
933 continue;
934 if (!strcmp(elem->string, cmdstring))
935 return 1;
936 }
937 return 0;
938}
939
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200940/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200941 * \param[in] ntype Node Type
942 * \param[cmd] element to be installed
943 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000944void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200945{
946 struct cmd_node *cnode;
947
948 cnode = vector_slot(cmdvec, ntype);
949
Harald Weltea99d45a2015-11-12 13:48:23 +0100950 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100951 /* ensure no _identical_ command has been registered at this
952 * node so far */
953 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200954
955 vector_set(cnode->cmd_vector, cmd);
956
957 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
958 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
959}
960
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700961/*! Install a library command into a node
962 * \param[in] ntype Node Type
963 * \param[in] cmd element to be installed
964 */
965void install_lib_element(int ntype, struct cmd_element *cmd)
966{
967 cmd->attr |= CMD_ATTR_LIB_COMMAND;
968 install_element(ntype, cmd);
969}
970
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200971/* Install a command into VIEW and ENABLE node */
972void install_element_ve(struct cmd_element *cmd)
973{
974 install_element(VIEW_NODE, cmd);
975 install_element(ENABLE_NODE, cmd);
976}
977
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700978/* Install a library command into VIEW and ENABLE node */
979void install_lib_element_ve(struct cmd_element *cmd)
980{
981 cmd->attr |= CMD_ATTR_LIB_COMMAND;
982 install_element_ve(cmd);
983}
984
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200985#ifdef VTY_CRYPT_PW
986static unsigned char itoa64[] =
987 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
988
989static void to64(char *s, long v, int n)
990{
991 while (--n >= 0) {
992 *s++ = itoa64[v & 0x3f];
993 v >>= 6;
994 }
995}
996
997static char *zencrypt(const char *passwd)
998{
999 char salt[6];
1000 struct timeval tv;
1001 char *crypt(const char *, const char *);
1002
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +02001003 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001004
1005 to64(&salt[0], random(), 3);
1006 to64(&salt[3], tv.tv_usec, 3);
1007 salt[5] = '\0';
1008
1009 return crypt(passwd, salt);
1010}
1011#endif
1012
1013/* This function write configuration of this host. */
1014static int config_write_host(struct vty *vty)
1015{
1016 if (host.name)
1017 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
1018
1019 if (host.encrypt) {
1020 if (host.password_encrypt)
1021 vty_out(vty, "password 8 %s%s", host.password_encrypt,
1022 VTY_NEWLINE);
1023 if (host.enable_encrypt)
1024 vty_out(vty, "enable password 8 %s%s",
1025 host.enable_encrypt, VTY_NEWLINE);
1026 } else {
1027 if (host.password)
1028 vty_out(vty, "password %s%s", host.password,
1029 VTY_NEWLINE);
1030 if (host.enable)
1031 vty_out(vty, "enable password %s%s", host.enable,
1032 VTY_NEWLINE);
1033 }
1034
1035 if (host.advanced)
1036 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1037
1038 if (host.encrypt)
1039 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1040
1041 if (host.lines >= 0)
1042 vty_out(vty, "service terminal-length %d%s", host.lines,
1043 VTY_NEWLINE);
1044
1045 if (host.motdfile)
1046 vty_out(vty, "banner motd file %s%s", host.motdfile,
1047 VTY_NEWLINE);
1048 else if (!host.motd)
1049 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1050
1051 return 1;
1052}
1053
1054/* Utility function for getting command vector. */
1055static vector cmd_node_vector(vector v, enum node_type ntype)
1056{
1057 struct cmd_node *cnode = vector_slot(v, ntype);
1058 return cnode->cmd_vector;
1059}
1060
1061/* Completion match types. */
1062enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001063 NO_MATCH = 0,
1064 ANY_MATCH,
1065 EXTEND_MATCH,
1066 IPV4_PREFIX_MATCH,
1067 IPV4_MATCH,
1068 IPV6_PREFIX_MATCH,
1069 IPV6_MATCH,
1070 RANGE_MATCH,
1071 VARARG_MATCH,
1072 PARTLY_MATCH,
1073 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001074};
1075
1076static enum match_type cmd_ipv4_match(const char *str)
1077{
1078 const char *sp;
1079 int dots = 0, nums = 0;
1080 char buf[4];
1081
1082 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001083 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001084
1085 for (;;) {
1086 memset(buf, 0, sizeof(buf));
1087 sp = str;
1088 while (*str != '\0') {
1089 if (*str == '.') {
1090 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001091 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001092
1093 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001094 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001095
1096 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001097 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001098
1099 dots++;
1100 break;
1101 }
1102 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001103 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001104
1105 str++;
1106 }
1107
1108 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001109 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001110
1111 strncpy(buf, sp, str - sp);
1112 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001113 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001114
1115 nums++;
1116
1117 if (*str == '\0')
1118 break;
1119
1120 str++;
1121 }
1122
1123 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001124 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001126 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001127}
1128
1129static enum match_type cmd_ipv4_prefix_match(const char *str)
1130{
1131 const char *sp;
1132 int dots = 0;
1133 char buf[4];
1134
1135 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001136 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001137
1138 for (;;) {
1139 memset(buf, 0, sizeof(buf));
1140 sp = str;
1141 while (*str != '\0' && *str != '/') {
1142 if (*str == '.') {
1143 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001144 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001145
1146 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 dots++;
1153 break;
1154 }
1155
1156 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001157 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001158
1159 str++;
1160 }
1161
1162 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001163 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001164
1165 strncpy(buf, sp, str - sp);
1166 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001167 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001168
1169 if (dots == 3) {
1170 if (*str == '/') {
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 str++;
1175 break;
1176 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001177 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001178 }
1179
1180 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001181 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001182
1183 str++;
1184 }
1185
1186 sp = str;
1187 while (*str != '\0') {
1188 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001189 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190
1191 str++;
1192 }
1193
1194 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001195 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001196
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001197 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001198}
1199
1200#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1201#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1202#define STATE_START 1
1203#define STATE_COLON 2
1204#define STATE_DOUBLE 3
1205#define STATE_ADDR 4
1206#define STATE_DOT 5
1207#define STATE_SLASH 6
1208#define STATE_MASK 7
1209
1210#ifdef HAVE_IPV6
1211
1212static enum match_type cmd_ipv6_match(const char *str)
1213{
1214 int state = STATE_START;
1215 int colons = 0, nums = 0, double_colon = 0;
1216 const char *sp = NULL;
1217 struct sockaddr_in6 sin6_dummy;
1218 int ret;
1219
1220 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001221 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001222
1223 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001224 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001225
1226 /* use inet_pton that has a better support,
1227 * for example inet_pton can support the automatic addresses:
1228 * ::1.2.3.4
1229 */
1230 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1231
1232 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001233 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001234
1235 while (*str != '\0') {
1236 switch (state) {
1237 case STATE_START:
1238 if (*str == ':') {
1239 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001240 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001241 colons--;
1242 state = STATE_COLON;
1243 } else {
1244 sp = str;
1245 state = STATE_ADDR;
1246 }
1247
1248 continue;
1249 case STATE_COLON:
1250 colons++;
1251 if (*(str + 1) == ':')
1252 state = STATE_DOUBLE;
1253 else {
1254 sp = str + 1;
1255 state = STATE_ADDR;
1256 }
1257 break;
1258 case STATE_DOUBLE:
1259 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001260 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001261
1262 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001263 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001264 else {
1265 if (*(str + 1) != '\0')
1266 colons++;
1267 sp = str + 1;
1268 state = STATE_ADDR;
1269 }
1270
1271 double_colon++;
1272 nums++;
1273 break;
1274 case STATE_ADDR:
1275 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1276 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001277 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278
1279 nums++;
1280 state = STATE_COLON;
1281 }
1282 if (*(str + 1) == '.')
1283 state = STATE_DOT;
1284 break;
1285 case STATE_DOT:
1286 state = STATE_ADDR;
1287 break;
1288 default:
1289 break;
1290 }
1291
1292 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001293 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001294
1295 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001296 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001297
1298 str++;
1299 }
1300
1301#if 0
1302 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001303 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001304#endif /* 0 */
1305
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001306 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001307}
1308
1309static enum match_type cmd_ipv6_prefix_match(const char *str)
1310{
1311 int state = STATE_START;
1312 int colons = 0, nums = 0, double_colon = 0;
1313 int mask;
1314 const char *sp = NULL;
1315 char *endptr = NULL;
1316
1317 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001318 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001319
1320 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001321 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001322
1323 while (*str != '\0' && state != STATE_MASK) {
1324 switch (state) {
1325 case STATE_START:
1326 if (*str == ':') {
1327 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001328 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001329 colons--;
1330 state = STATE_COLON;
1331 } else {
1332 sp = str;
1333 state = STATE_ADDR;
1334 }
1335
1336 continue;
1337 case STATE_COLON:
1338 colons++;
1339 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001341 else if (*(str + 1) == ':')
1342 state = STATE_DOUBLE;
1343 else {
1344 sp = str + 1;
1345 state = STATE_ADDR;
1346 }
1347 break;
1348 case STATE_DOUBLE:
1349 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001350 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001351
1352 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001353 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001354 else {
1355 if (*(str + 1) != '\0' && *(str + 1) != '/')
1356 colons++;
1357 sp = str + 1;
1358
1359 if (*(str + 1) == '/')
1360 state = STATE_SLASH;
1361 else
1362 state = STATE_ADDR;
1363 }
1364
1365 double_colon++;
1366 nums += 1;
1367 break;
1368 case STATE_ADDR:
1369 if (*(str + 1) == ':' || *(str + 1) == '.'
1370 || *(str + 1) == '\0' || *(str + 1) == '/') {
1371 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001372 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001373
1374 for (; sp <= str; sp++)
1375 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001376 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001377
1378 nums++;
1379
1380 if (*(str + 1) == ':')
1381 state = STATE_COLON;
1382 else if (*(str + 1) == '.')
1383 state = STATE_DOT;
1384 else if (*(str + 1) == '/')
1385 state = STATE_SLASH;
1386 }
1387 break;
1388 case STATE_DOT:
1389 state = STATE_ADDR;
1390 break;
1391 case STATE_SLASH:
1392 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001393 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001394
1395 state = STATE_MASK;
1396 break;
1397 default:
1398 break;
1399 }
1400
1401 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001402 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001403
1404 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001405 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001406
1407 str++;
1408 }
1409
1410 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001411 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001412
1413 mask = strtol(str, &endptr, 10);
1414 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001415 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001416
1417 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001418 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001419
1420/* I don't know why mask < 13 makes command match partly.
1421 Forgive me to make this comments. I Want to set static default route
1422 because of lack of function to originate default in ospf6d; sorry
1423 yasu
1424 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001425 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001426*/
1427
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001428 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001429}
1430
1431#endif /* HAVE_IPV6 */
1432
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001433
1434#if ULONG_MAX == 18446744073709551615UL
1435#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1436#elif ULONG_MAX == 4294967295UL
1437#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1438#else
1439#error "ULONG_MAX not defined!"
1440#endif
1441
1442#if LONG_MAX == 9223372036854775807L
1443#define DECIMAL_STRLEN_MAX_SIGNED 19
1444#elif LONG_MAX == 2147483647L
1445#define DECIMAL_STRLEN_MAX_SIGNED 10
1446#else
1447#error "LONG_MAX not defined!"
1448#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001449
1450static int cmd_range_match(const char *range, const char *str)
1451{
1452 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001453 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001454 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001455
1456 if (str == NULL)
1457 return 1;
1458
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001459 if (range[1] == '-') {
1460 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001461
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001462 val = strtol(str, &endptr, 10);
1463 if (*endptr != '\0')
1464 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001465
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001466 range += 2;
1467 p = strchr(range, '-');
1468 if (p == NULL)
1469 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001470 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001471 return 0;
1472 strncpy(buf, range, p - range);
1473 buf[p - range] = '\0';
1474 min = -strtol(buf, &endptr, 10);
1475 if (*endptr != '\0')
1476 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001477
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001478 range = p + 1;
1479 p = strchr(range, '>');
1480 if (p == NULL)
1481 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001482 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001483 return 0;
1484 strncpy(buf, range, p - range);
1485 buf[p - range] = '\0';
1486 max = strtol(buf, &endptr, 10);
1487 if (*endptr != '\0')
1488 return 0;
1489
1490 if (val < min || val > max)
1491 return 0;
1492 } else {
1493 unsigned long min, max, val;
1494
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001495 if (str[0] == '-')
1496 return 0;
1497
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001498 val = strtoul(str, &endptr, 10);
1499 if (*endptr != '\0')
1500 return 0;
1501
1502 range++;
1503 p = strchr(range, '-');
1504 if (p == NULL)
1505 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001506 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001507 return 0;
1508 strncpy(buf, range, p - range);
1509 buf[p - range] = '\0';
1510 min = strtoul(buf, &endptr, 10);
1511 if (*endptr != '\0')
1512 return 0;
1513
1514 range = p + 1;
1515 p = strchr(range, '>');
1516 if (p == NULL)
1517 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001518 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001519 return 0;
1520 strncpy(buf, range, p - range);
1521 buf[p - range] = '\0';
1522 max = strtoul(buf, &endptr, 10);
1523 if (*endptr != '\0')
1524 return 0;
1525
1526 if (val < min || val > max)
1527 return 0;
1528 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001529
1530 return 1;
1531}
1532
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001533/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001534static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001535{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001536 /* we've got "[blah]". We want to strip off the []s and redo the
1537 * match check for "blah"
1538 */
1539 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001540
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001541 if (len < 3)
1542 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001543
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001544 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001545}
1546
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001547static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001548cmd_match(const char *str, const char *command,
1549 enum match_type min, bool recur)
1550{
1551
1552 if (recur && CMD_OPTION(str))
1553 {
1554 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001555 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001556
1557 /* this would be a bug in a command, however handle it gracefully
1558 * as it we only discover it if a user tries to run it
1559 */
1560 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001561 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001562
1563 ret = cmd_match(tmp, command, min, false);
1564
1565 talloc_free(tmp);
1566
1567 return ret;
1568 }
1569 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001570 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001571 else if (CMD_RANGE(str))
1572 {
1573 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001574 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001575 }
1576#ifdef HAVE_IPV6
1577 else if (CMD_IPV6(str))
1578 {
1579 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001580 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001581 }
1582 else if (CMD_IPV6_PREFIX(str))
1583 {
1584 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001585 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001586 }
1587#endif /* HAVE_IPV6 */
1588 else if (CMD_IPV4(str))
1589 {
1590 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001591 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001592 }
1593 else if (CMD_IPV4_PREFIX(str))
1594 {
1595 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001596 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001597 }
1598 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001599 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001600 else if (strncmp(command, str, strlen(command)) == 0)
1601 {
1602 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001603 return EXACT_MATCH;
1604 else if (PARTLY_MATCH >= min)
1605 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001606 }
1607
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001608 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001609}
1610
1611/* Filter vector at the specified index and by the given command string, to
1612 * the desired matching level (thus allowing part matches), and return match
1613 * type flag.
1614 */
1615static enum match_type
1616cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001617{
1618 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001619 struct cmd_element *cmd_element;
1620 enum match_type match_type;
1621 vector descvec;
1622 struct desc *desc;
1623
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001624 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001625
1626 /* If command and cmd_element string does not match set NULL to vector */
1627 for (i = 0; i < vector_active(v); i++)
1628 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001629 if (index >= vector_active(cmd_element->strvec))
1630 vector_slot(v, i) = NULL;
1631 else {
1632 unsigned int j;
1633 int matched = 0;
1634
1635 descvec =
1636 vector_slot(cmd_element->strvec, index);
1637
1638 for (j = 0; j < vector_active(descvec); j++)
1639 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001640 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001641
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001642 ret = cmd_match (desc->cmd, command, level, true);
1643
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001644 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001645 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001646
1647 if (match_type < ret)
1648 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001649 }
1650 if (!matched)
1651 vector_slot(v, i) = NULL;
1652 }
1653 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001654
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001655 if (match_type == NO_MATCH)
1656 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001657
1658 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1659 * go again and filter out commands whose argument (at this index) is
1660 * 'weaker'. E.g., if we have 2 commands:
1661 *
1662 * foo bar <1-255>
1663 * foo bar BLAH
1664 *
1665 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001666 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001667 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1668 *
1669 * If we don't do a 2nd pass and filter it out, the higher-layers will
1670 * consider this to be ambiguous.
1671 */
1672 for (i = 0; i < vector_active(v); i++)
1673 if ((cmd_element = vector_slot(v, i)) != NULL) {
1674 if (index >= vector_active(cmd_element->strvec))
1675 vector_slot(v, i) = NULL;
1676 else {
1677 unsigned int j;
1678 int matched = 0;
1679
1680 descvec =
1681 vector_slot(cmd_element->strvec, index);
1682
1683 for (j = 0; j < vector_active(descvec); j++)
1684 if ((desc = vector_slot(descvec, j))) {
1685 enum match_type ret;
1686
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001687 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001688
1689 if (ret >= match_type)
1690 matched++;
1691 }
1692 if (!matched)
1693 vector_slot(v, i) = NULL;
1694 }
1695 }
1696
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001697 return match_type;
1698}
1699
1700/* Check ambiguous match */
1701static int
1702is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1703{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001704 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001705 unsigned int i;
1706 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001707 struct cmd_element *cmd_element;
1708 const char *matched = NULL;
1709 vector descvec;
1710 struct desc *desc;
1711
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001712 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1713 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1714 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1715 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1716 * that case, the string must remain allocated until this function exits or another match comes
1717 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1718 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1719 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1720 void *cmd_deopt_ctx = NULL;
1721
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001722 for (i = 0; i < vector_active(v); i++) {
1723 cmd_element = vector_slot(v, i);
1724 if (!cmd_element)
1725 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001726
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001727 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001728
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001729 descvec = vector_slot(cmd_element->strvec, index);
1730
1731 for (j = 0; j < vector_active(descvec); j++) {
1732 desc = vector_slot(descvec, j);
1733 if (!desc)
1734 continue;
1735
1736 enum match_type mtype;
1737 const char *str = desc->cmd;
1738
1739 if (CMD_OPTION(str)) {
1740 if (!cmd_deopt_ctx)
1741 cmd_deopt_ctx =
1742 talloc_named_const(tall_vty_cmd_ctx, 0,
1743 __func__);
1744 str = cmd_deopt(cmd_deopt_ctx, str);
1745 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001746 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001747 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001748
1749 switch (type) {
1750 case EXACT_MATCH:
1751 if (!(CMD_VARIABLE (str))
1752 && strcmp(command, str) == 0)
1753 match++;
1754 break;
1755 case PARTLY_MATCH:
1756 if (!(CMD_VARIABLE (str))
1757 && strncmp(command, str, strlen (command)) == 0)
1758 {
1759 if (matched
1760 && strcmp(matched,
1761 str) != 0) {
1762 ret = 1; /* There is ambiguous match. */
1763 goto free_and_return;
1764 } else
1765 matched = str;
1766 match++;
1767 }
1768 break;
1769 case RANGE_MATCH:
1770 if (cmd_range_match
1771 (str, command)) {
1772 if (matched
1773 && strcmp(matched,
1774 str) != 0) {
1775 ret = 1;
1776 goto free_and_return;
1777 } else
1778 matched = str;
1779 match++;
1780 }
1781 break;
1782#ifdef HAVE_IPV6
1783 case IPV6_MATCH:
1784 if (CMD_IPV6(str))
1785 match++;
1786 break;
1787 case IPV6_PREFIX_MATCH:
1788 if ((mtype =
1789 cmd_ipv6_prefix_match
1790 (command)) != NO_MATCH) {
1791 if (mtype == PARTLY_MATCH) {
1792 ret = 2; /* There is incomplete match. */
1793 goto free_and_return;
1794 }
1795
1796 match++;
1797 }
1798 break;
1799#endif /* HAVE_IPV6 */
1800 case IPV4_MATCH:
1801 if (CMD_IPV4(str))
1802 match++;
1803 break;
1804 case IPV4_PREFIX_MATCH:
1805 if ((mtype =
1806 cmd_ipv4_prefix_match
1807 (command)) != NO_MATCH) {
1808 if (mtype == PARTLY_MATCH) {
1809 ret = 2; /* There is incomplete match. */
1810 goto free_and_return;
1811 }
1812
1813 match++;
1814 }
1815 break;
1816 case EXTEND_MATCH:
1817 if (CMD_VARIABLE (str))
1818 match++;
1819 break;
1820 case NO_MATCH:
1821 default:
1822 break;
1823 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001824 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001825 if (!match)
1826 vector_slot(v, i) = NULL;
1827 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001828
1829free_and_return:
1830 if (cmd_deopt_ctx)
1831 talloc_free(cmd_deopt_ctx);
1832 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001833}
1834
1835/* If src matches dst return dst string, otherwise return NULL */
1836static const char *cmd_entry_function(const char *src, const char *dst)
1837{
1838 /* Skip variable arguments. */
1839 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1840 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1841 return NULL;
1842
1843 /* In case of 'command \t', given src is NULL string. */
1844 if (src == NULL)
1845 return dst;
1846
1847 /* Matched with input string. */
1848 if (strncmp(src, dst, strlen(src)) == 0)
1849 return dst;
1850
1851 return NULL;
1852}
1853
1854/* If src matches dst return dst string, otherwise return NULL */
1855/* This version will return the dst string always if it is
1856 CMD_VARIABLE for '?' key processing */
1857static const char *cmd_entry_function_desc(const char *src, const char *dst)
1858{
1859 if (CMD_VARARG(dst))
1860 return dst;
1861
1862 if (CMD_RANGE(dst)) {
1863 if (cmd_range_match(dst, src))
1864 return dst;
1865 else
1866 return NULL;
1867 }
1868#ifdef HAVE_IPV6
1869 if (CMD_IPV6(dst)) {
1870 if (cmd_ipv6_match(src))
1871 return dst;
1872 else
1873 return NULL;
1874 }
1875
1876 if (CMD_IPV6_PREFIX(dst)) {
1877 if (cmd_ipv6_prefix_match(src))
1878 return dst;
1879 else
1880 return NULL;
1881 }
1882#endif /* HAVE_IPV6 */
1883
1884 if (CMD_IPV4(dst)) {
1885 if (cmd_ipv4_match(src))
1886 return dst;
1887 else
1888 return NULL;
1889 }
1890
1891 if (CMD_IPV4_PREFIX(dst)) {
1892 if (cmd_ipv4_prefix_match(src))
1893 return dst;
1894 else
1895 return NULL;
1896 }
1897
1898 /* Optional or variable commands always match on '?' */
1899 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1900 return dst;
1901
1902 /* In case of 'command \t', given src is NULL string. */
1903 if (src == NULL)
1904 return dst;
1905
1906 if (strncmp(src, dst, strlen(src)) == 0)
1907 return dst;
1908 else
1909 return NULL;
1910}
1911
1912/* Check same string element existence. If it isn't there return
1913 1. */
1914static int cmd_unique_string(vector v, const char *str)
1915{
1916 unsigned int i;
1917 char *match;
1918
1919 for (i = 0; i < vector_active(v); i++)
1920 if ((match = vector_slot(v, i)) != NULL)
1921 if (strcmp(match, str) == 0)
1922 return 0;
1923 return 1;
1924}
1925
1926/* Compare string to description vector. If there is same string
1927 return 1 else return 0. */
1928static int desc_unique_string(vector v, const char *str)
1929{
1930 unsigned int i;
1931 struct desc *desc;
1932
1933 for (i = 0; i < vector_active(v); i++)
1934 if ((desc = vector_slot(v, i)) != NULL)
1935 if (strcmp(desc->cmd, str) == 0)
1936 return 1;
1937 return 0;
1938}
1939
1940static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1941{
1942 if (first_word != NULL &&
1943 node != AUTH_NODE &&
1944 node != VIEW_NODE &&
1945 node != AUTH_ENABLE_NODE &&
1946 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1947 return 1;
1948 return 0;
1949}
1950
1951/* '?' describe command support. */
1952static vector
1953cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1954{
1955 unsigned int i;
1956 vector cmd_vector;
1957#define INIT_MATCHVEC_SIZE 10
1958 vector matchvec;
1959 struct cmd_element *cmd_element;
1960 unsigned int index;
1961 int ret;
1962 enum match_type match;
1963 char *command;
1964 static struct desc desc_cr = { "<cr>", "" };
1965
1966 /* Set index. */
1967 if (vector_active(vline) == 0) {
1968 *status = CMD_ERR_NO_MATCH;
1969 return NULL;
1970 } else
1971 index = vector_active(vline) - 1;
1972
1973 /* Make copy vector of current node's command vector. */
1974 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1975
1976 /* Prepare match vector */
1977 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1978
1979 /* Filter commands. */
1980 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001981 for (i = 0; i < index; i++) {
1982 command = vector_slot(vline, i);
1983 if (!command)
1984 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001985
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001986 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001987
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001988 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001989 struct cmd_element *cmd_element;
1990 vector descvec;
1991 unsigned int j, k;
1992
1993 for (j = 0; j < vector_active(cmd_vector); j++)
1994 if ((cmd_element =
1995 vector_slot(cmd_vector, j)) != NULL
1996 &&
1997 (vector_active(cmd_element->strvec))) {
1998 descvec =
1999 vector_slot(cmd_element->
2000 strvec,
2001 vector_active
2002 (cmd_element->
2003 strvec) - 1);
2004 for (k = 0;
2005 k < vector_active(descvec);
2006 k++) {
2007 struct desc *desc =
2008 vector_slot(descvec,
2009 k);
2010 vector_set(matchvec,
2011 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002012 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002013 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002014
Harald Welte80d30fe2013-02-12 11:08:57 +01002015 vector_set(matchvec, &desc_cr);
2016 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002017
Harald Welte80d30fe2013-02-12 11:08:57 +01002018 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002019 }
2020
Harald Welte80d30fe2013-02-12 11:08:57 +01002021 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
2022 match)) == 1) {
2023 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002024 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002025 *status = CMD_ERR_AMBIGUOUS;
2026 return NULL;
2027 } else if (ret == 2) {
2028 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002029 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01002030 *status = CMD_ERR_NO_MATCH;
2031 return NULL;
2032 }
2033 }
2034
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002035 /* Prepare match vector */
2036 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2037
2038 /* Make sure that cmd_vector is filtered based on current word */
2039 command = vector_slot(vline, index);
2040 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002041 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002042
2043 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002044 for (i = 0; i < vector_active(cmd_vector); i++) {
2045 const char *string = NULL;
2046 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002047
Harald Welte80d30fe2013-02-12 11:08:57 +01002048 cmd_element = vector_slot(cmd_vector, i);
2049 if (!cmd_element)
2050 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002051
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002052 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2053 continue;
2054 if (!host.expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002055 continue;
2056
Harald Welte80d30fe2013-02-12 11:08:57 +01002057 strvec = cmd_element->strvec;
2058
2059 /* if command is NULL, index may be equal to vector_active */
2060 if (command && index >= vector_active(strvec))
2061 vector_slot(cmd_vector, i) = NULL;
2062 else {
2063 /* Check if command is completed. */
2064 if (command == NULL
2065 && index == vector_active(strvec)) {
2066 string = "<cr>";
2067 if (!desc_unique_string(matchvec, string))
2068 vector_set(matchvec, &desc_cr);
2069 } else {
2070 unsigned int j;
2071 vector descvec = vector_slot(strvec, index);
2072 struct desc *desc;
2073
2074 for (j = 0; j < vector_active(descvec); j++) {
2075 desc = vector_slot(descvec, j);
2076 if (!desc)
2077 continue;
2078 string = cmd_entry_function_desc
2079 (command, desc->cmd);
2080 if (!string)
2081 continue;
2082 /* Uniqueness check */
2083 if (!desc_unique_string(matchvec, string))
2084 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002085 }
2086 }
2087 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002088 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002089 vector_free(cmd_vector);
2090
2091 if (vector_slot(matchvec, 0) == NULL) {
2092 vector_free(matchvec);
2093 *status = CMD_ERR_NO_MATCH;
2094 } else
2095 *status = CMD_SUCCESS;
2096
2097 return matchvec;
2098}
2099
2100vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2101{
2102 vector ret;
2103
2104 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2105 enum node_type onode;
2106 vector shifted_vline;
2107 unsigned int index;
2108
2109 onode = vty->node;
2110 vty->node = ENABLE_NODE;
2111 /* We can try it on enable node, cos' the vty is authenticated */
2112
2113 shifted_vline = vector_init(vector_count(vline));
2114 /* use memcpy? */
2115 for (index = 1; index < vector_active(vline); index++) {
2116 vector_set_index(shifted_vline, index - 1,
2117 vector_lookup(vline, index));
2118 }
2119
2120 ret = cmd_describe_command_real(shifted_vline, vty, status);
2121
2122 vector_free(shifted_vline);
2123 vty->node = onode;
2124 return ret;
2125 }
2126
2127 return cmd_describe_command_real(vline, vty, status);
2128}
2129
2130/* Check LCD of matched command. */
2131static int cmd_lcd(char **matched)
2132{
2133 int i;
2134 int j;
2135 int lcd = -1;
2136 char *s1, *s2;
2137 char c1, c2;
2138
2139 if (matched[0] == NULL || matched[1] == NULL)
2140 return 0;
2141
2142 for (i = 1; matched[i] != NULL; i++) {
2143 s1 = matched[i - 1];
2144 s2 = matched[i];
2145
2146 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2147 if (c1 != c2)
2148 break;
2149
2150 if (lcd < 0)
2151 lcd = j;
2152 else {
2153 if (lcd > j)
2154 lcd = j;
2155 }
2156 }
2157 return lcd;
2158}
2159
2160/* Command line completion support. */
2161static char **cmd_complete_command_real(vector vline, struct vty *vty,
2162 int *status)
2163{
2164 unsigned int i;
2165 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2166#define INIT_MATCHVEC_SIZE 10
2167 vector matchvec;
2168 struct cmd_element *cmd_element;
2169 unsigned int index;
2170 char **match_str;
2171 struct desc *desc;
2172 vector descvec;
2173 char *command;
2174 int lcd;
2175
2176 if (vector_active(vline) == 0) {
2177 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002178 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002179 return NULL;
2180 } else
2181 index = vector_active(vline) - 1;
2182
2183 /* First, filter by preceeding command string */
2184 for (i = 0; i < index; i++)
2185 if ((command = vector_slot(vline, i))) {
2186 enum match_type match;
2187 int ret;
2188
2189 /* First try completion match, if there is exactly match return 1 */
2190 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002191 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002192
2193 /* If there is exact match then filter ambiguous match else check
2194 ambiguousness. */
2195 if ((ret =
2196 is_cmd_ambiguous(command, cmd_vector, i,
2197 match)) == 1) {
2198 vector_free(cmd_vector);
2199 *status = CMD_ERR_AMBIGUOUS;
2200 return NULL;
2201 }
2202 /*
2203 else if (ret == 2)
2204 {
2205 vector_free (cmd_vector);
2206 *status = CMD_ERR_NO_MATCH;
2207 return NULL;
2208 }
2209 */
2210 }
2211
2212 /* Prepare match vector. */
2213 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2214
2215 /* Now we got into completion */
2216 for (i = 0; i < vector_active(cmd_vector); i++)
2217 if ((cmd_element = vector_slot(cmd_vector, i))) {
2218 const char *string;
2219 vector strvec = cmd_element->strvec;
2220
2221 /* Check field length */
2222 if (index >= vector_active(strvec))
2223 vector_slot(cmd_vector, i) = NULL;
2224 else {
2225 unsigned int j;
2226
2227 descvec = vector_slot(strvec, index);
2228 for (j = 0; j < vector_active(descvec); j++)
2229 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002230 const char *cmd = desc->cmd;
2231 char *tmp = NULL;
2232
2233 if (CMD_OPTION(desc->cmd)) {
2234 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2235 cmd = tmp;
2236 }
2237 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002238 if (cmd_unique_string (matchvec, string))
2239 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002240 if (tmp)
2241 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002242 }
2243 }
2244 }
2245
2246 /* We don't need cmd_vector any more. */
2247 vector_free(cmd_vector);
2248
2249 /* No matched command */
2250 if (vector_slot(matchvec, 0) == NULL) {
2251 vector_free(matchvec);
2252
2253 /* In case of 'command \t' pattern. Do you need '?' command at
2254 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002255 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002256 *status = CMD_ERR_NOTHING_TODO;
2257 else
2258 *status = CMD_ERR_NO_MATCH;
2259 return NULL;
2260 }
2261
2262 /* Only one matched */
2263 if (vector_slot(matchvec, 1) == NULL) {
2264 match_str = (char **)matchvec->index;
2265 vector_only_wrapper_free(matchvec);
2266 *status = CMD_COMPLETE_FULL_MATCH;
2267 return match_str;
2268 }
2269 /* Make it sure last element is NULL. */
2270 vector_set(matchvec, NULL);
2271
2272 /* Check LCD of matched strings. */
2273 if (vector_slot(vline, index) != NULL) {
2274 lcd = cmd_lcd((char **)matchvec->index);
2275
2276 if (lcd) {
2277 int len = strlen(vector_slot(vline, index));
2278
2279 if (len < lcd) {
2280 char *lcdstr;
2281
2282 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2283 "complete-lcdstr");
2284 memcpy(lcdstr, matchvec->index[0], lcd);
2285 lcdstr[lcd] = '\0';
2286
2287 /* match_str = (char **) &lcdstr; */
2288
2289 /* Free matchvec. */
2290 for (i = 0; i < vector_active(matchvec); i++) {
2291 if (vector_slot(matchvec, i))
2292 talloc_free(vector_slot(matchvec, i));
2293 }
2294 vector_free(matchvec);
2295
2296 /* Make new matchvec. */
2297 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2298 vector_set(matchvec, lcdstr);
2299 match_str = (char **)matchvec->index;
2300 vector_only_wrapper_free(matchvec);
2301
2302 *status = CMD_COMPLETE_MATCH;
2303 return match_str;
2304 }
2305 }
2306 }
2307
2308 match_str = (char **)matchvec->index;
2309 vector_only_wrapper_free(matchvec);
2310 *status = CMD_COMPLETE_LIST_MATCH;
2311 return match_str;
2312}
2313
2314char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2315{
2316 char **ret;
2317
2318 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2319 enum node_type onode;
2320 vector shifted_vline;
2321 unsigned int index;
2322
2323 onode = vty->node;
2324 vty->node = ENABLE_NODE;
2325 /* We can try it on enable node, cos' the vty is authenticated */
2326
2327 shifted_vline = vector_init(vector_count(vline));
2328 /* use memcpy? */
2329 for (index = 1; index < vector_active(vline); index++) {
2330 vector_set_index(shifted_vline, index - 1,
2331 vector_lookup(vline, index));
2332 }
2333
2334 ret = cmd_complete_command_real(shifted_vline, vty, status);
2335
2336 vector_free(shifted_vline);
2337 vty->node = onode;
2338 return ret;
2339 }
2340
2341 return cmd_complete_command_real(vline, vty, status);
2342}
2343
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002344static struct vty_parent_node *vty_parent(struct vty *vty)
2345{
2346 return llist_first_entry_or_null(&vty->parent_nodes,
2347 struct vty_parent_node,
2348 entry);
2349}
2350
2351static bool vty_pop_parent(struct vty *vty)
2352{
2353 struct vty_parent_node *parent = vty_parent(vty);
2354 if (!parent)
2355 return false;
2356 llist_del(&parent->entry);
2357 vty->node = parent->node;
2358 vty->priv = parent->priv;
2359 if (vty->indent)
2360 talloc_free(vty->indent);
2361 vty->indent = parent->indent;
2362 talloc_free(parent);
2363 return true;
2364}
2365
2366static void vty_clear_parents(struct vty *vty)
2367{
2368 while (vty_pop_parent(vty));
2369}
2370
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002371/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002372/*
2373 * This function MUST eventually converge on a node when called repeatedly,
2374 * there must not be any cycles.
2375 * All 'config' nodes shall converge on CONFIG_NODE.
2376 * All other 'enable' nodes shall converge on ENABLE_NODE.
2377 * All 'view' only nodes shall converge on VIEW_NODE.
2378 * All other nodes shall converge on themselves or it must be ensured,
2379 * that the user's rights are not extended anyhow by calling this function.
2380 *
2381 * Note that these requirements also apply to all functions that are used
2382 * as go_parent_cb.
2383 * Note also that this function relies on the is_config_child callback to
2384 * recognize non-config nodes if go_parent_cb is not set.
2385 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002386int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002387{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002388 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002389 case AUTH_NODE:
2390 case VIEW_NODE:
2391 case ENABLE_NODE:
2392 case CONFIG_NODE:
2393 vty_clear_parents(vty);
2394 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002395
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002396 case AUTH_ENABLE_NODE:
2397 vty->node = VIEW_NODE;
2398 vty_clear_parents(vty);
2399 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002400
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002401 default:
2402 if (host.app_info->go_parent_cb)
2403 host.app_info->go_parent_cb(vty);
2404 vty_pop_parent(vty);
2405 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002406 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002407
2408 return vty->node;
2409}
2410
2411/* Execute command by argument vline vector. */
2412static int
2413cmd_execute_command_real(vector vline, struct vty *vty,
2414 struct cmd_element **cmd)
2415{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002416 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002417 unsigned int index;
2418 vector cmd_vector;
2419 struct cmd_element *cmd_element;
2420 struct cmd_element *matched_element;
2421 unsigned int matched_count, incomplete_count;
2422 int argc;
2423 const char *argv[CMD_ARGC_MAX];
2424 enum match_type match = 0;
2425 int varflag;
2426 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002427 int rc;
2428 /* Used for temporary storage of cmd_deopt() allocated arguments during
2429 argv[] generation */
2430 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002431
2432 /* Make copy of command elements. */
2433 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2434
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002435 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002436 if ((command = vector_slot(vline, index))) {
2437 int ret;
2438
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002439 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002440 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002441
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002442 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002443 break;
2444
2445 ret =
2446 is_cmd_ambiguous(command, cmd_vector, index, match);
2447
2448 if (ret == 1) {
2449 vector_free(cmd_vector);
2450 return CMD_ERR_AMBIGUOUS;
2451 } else if (ret == 2) {
2452 vector_free(cmd_vector);
2453 return CMD_ERR_NO_MATCH;
2454 }
2455 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002456 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002457
2458 /* Check matched count. */
2459 matched_element = NULL;
2460 matched_count = 0;
2461 incomplete_count = 0;
2462
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002463 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002464 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002465 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002466 || index >= cmd_element->cmdsize) {
2467 matched_element = cmd_element;
2468#if 0
2469 printf("DEBUG: %s\n", cmd_element->string);
2470#endif
2471 matched_count++;
2472 } else {
2473 incomplete_count++;
2474 }
2475 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002476 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002477
2478 /* Finish of using cmd_vector. */
2479 vector_free(cmd_vector);
2480
2481 /* To execute command, matched_count must be 1. */
2482 if (matched_count == 0) {
2483 if (incomplete_count)
2484 return CMD_ERR_INCOMPLETE;
2485 else
2486 return CMD_ERR_NO_MATCH;
2487 }
2488
2489 if (matched_count > 1)
2490 return CMD_ERR_AMBIGUOUS;
2491
2492 /* Argument treatment */
2493 varflag = 0;
2494 argc = 0;
2495
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002496 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2497
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002498 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002499 if (argc == CMD_ARGC_MAX) {
2500 rc = CMD_ERR_EXEED_ARGC_MAX;
2501 goto rc_free_deopt_ctx;
2502 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002503 if (varflag) {
2504 argv[argc++] = vector_slot(vline, i);
2505 continue;
2506 }
2507
2508 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002509 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002510
2511 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002512 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002513 struct desc *desc = vector_slot(descvec, 0);
2514
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002515 if (CMD_OPTION(desc->cmd)) {
2516 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2517 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2518 } else {
2519 tmp_cmd = desc->cmd;
2520 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002521
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002522 if (CMD_VARARG(tmp_cmd))
2523 varflag = 1;
2524 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002525 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002526 else if (CMD_OPTION(desc->cmd))
2527 argv[argc++] = tmp_cmd;
2528 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002529 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002530 /* multi choice argument. look up which choice
2531 the user meant (can only be one after
2532 filtering and checking for ambigous). For instance,
2533 if user typed "th" for "(two|three)" arg, we
2534 want to pass "three" in argv[]. */
2535 for (j = 0; j < vector_active(descvec); j++) {
2536 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002537 if (!desc)
2538 continue;
2539 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2540 continue;
2541 if (CMD_OPTION(desc->cmd)) {
2542 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2543 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2544 } else {
2545 tmp_cmd = desc->cmd;
2546 }
2547
2548 if(CMD_VARIABLE(tmp_cmd)) {
2549 argv[argc++] = vector_slot(vline, i);
2550 } else {
2551 argv[argc++] = tmp_cmd;
2552 }
2553 break;
2554 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002555 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002556 }
2557
2558 /* For vtysh execution. */
2559 if (cmd)
2560 *cmd = matched_element;
2561
2562 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002563 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002564 else {
2565 /* Execute matched command. */
2566 struct vty_parent_node this_node = {
2567 .node = vty->node,
2568 .priv = vty->priv,
2569 .indent = vty->indent,
2570 };
2571 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002572 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002573
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002574 /* If we have stepped down into a child node, push a parent frame.
2575 * The causality is such: we don't expect every single node entry implementation to push
2576 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2577 * a parent node. Hence if the node changed without the parent node changing, we must
2578 * have stepped into a child node. */
2579 if (vty->node != this_node.node && parent == vty_parent(vty)
2580 && vty->node > CONFIG_NODE) {
2581 /* Push the parent node. */
2582 parent = talloc_zero(vty, struct vty_parent_node);
2583 *parent = this_node;
2584 llist_add(&parent->entry, &vty->parent_nodes);
2585 }
2586 }
2587
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002588rc_free_deopt_ctx:
2589 /* Now after we called the command func, we can free temporary strings */
2590 talloc_free(cmd_deopt_ctx);
2591 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002592}
2593
2594int
2595cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2596 int vtysh)
2597{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002598 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002599 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002600
2601 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002602
2603 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2604 vector shifted_vline;
2605 unsigned int index;
2606
2607 vty->node = ENABLE_NODE;
2608 /* We can try it on enable node, cos' the vty is authenticated */
2609
2610 shifted_vline = vector_init(vector_count(vline));
2611 /* use memcpy? */
2612 for (index = 1; index < vector_active(vline); index++) {
2613 vector_set_index(shifted_vline, index - 1,
2614 vector_lookup(vline, index));
2615 }
2616
2617 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2618
2619 vector_free(shifted_vline);
2620 vty->node = onode;
2621 return ret;
2622 }
2623
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002624 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002625}
2626
2627/* Execute command by argument readline. */
2628int
2629cmd_execute_command_strict(vector vline, struct vty *vty,
2630 struct cmd_element **cmd)
2631{
2632 unsigned int i;
2633 unsigned int index;
2634 vector cmd_vector;
2635 struct cmd_element *cmd_element;
2636 struct cmd_element *matched_element;
2637 unsigned int matched_count, incomplete_count;
2638 int argc;
2639 const char *argv[CMD_ARGC_MAX];
2640 int varflag;
2641 enum match_type match = 0;
2642 char *command;
2643
2644 /* Make copy of command element */
2645 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2646
2647 for (index = 0; index < vector_active(vline); index++)
2648 if ((command = vector_slot(vline, index))) {
2649 int ret;
2650
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002651 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002652 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002653
2654 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002655 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002656 break;
2657
2658 ret =
2659 is_cmd_ambiguous(command, cmd_vector, index, match);
2660 if (ret == 1) {
2661 vector_free(cmd_vector);
2662 return CMD_ERR_AMBIGUOUS;
2663 }
2664 if (ret == 2) {
2665 vector_free(cmd_vector);
2666 return CMD_ERR_NO_MATCH;
2667 }
2668 }
2669
2670 /* Check matched count. */
2671 matched_element = NULL;
2672 matched_count = 0;
2673 incomplete_count = 0;
2674 for (i = 0; i < vector_active(cmd_vector); i++)
2675 if (vector_slot(cmd_vector, i) != NULL) {
2676 cmd_element = vector_slot(cmd_vector, i);
2677
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002678 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002679 || index >= cmd_element->cmdsize) {
2680 matched_element = cmd_element;
2681 matched_count++;
2682 } else
2683 incomplete_count++;
2684 }
2685
2686 /* Finish of using cmd_vector. */
2687 vector_free(cmd_vector);
2688
2689 /* To execute command, matched_count must be 1. */
2690 if (matched_count == 0) {
2691 if (incomplete_count)
2692 return CMD_ERR_INCOMPLETE;
2693 else
2694 return CMD_ERR_NO_MATCH;
2695 }
2696
2697 if (matched_count > 1)
2698 return CMD_ERR_AMBIGUOUS;
2699
2700 /* Argument treatment */
2701 varflag = 0;
2702 argc = 0;
2703
2704 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002705 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002706 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002707 if (varflag) {
2708 argv[argc++] = vector_slot(vline, i);
2709 continue;
2710 }
2711
2712 vector descvec = vector_slot(matched_element->strvec, i);
2713
2714 if (vector_active(descvec) == 1) {
2715 struct desc *desc = vector_slot(descvec, 0);
2716
2717 if (CMD_VARARG(desc->cmd))
2718 varflag = 1;
2719
2720 if (varflag || CMD_VARIABLE(desc->cmd)
2721 || CMD_OPTION(desc->cmd))
2722 argv[argc++] = vector_slot(vline, i);
2723 } else {
2724 argv[argc++] = vector_slot(vline, i);
2725 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002726 }
2727
2728 /* For vtysh execution. */
2729 if (cmd)
2730 *cmd = matched_element;
2731
2732 if (matched_element->daemon)
2733 return CMD_SUCCESS_DAEMON;
2734
2735 /* Now execute matched command */
2736 return (*matched_element->func) (matched_element, vty, argc, argv);
2737}
2738
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002739static inline size_t len(const char *str)
2740{
2741 return str? strlen(str) : 0;
2742}
2743
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002744/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2745 * is longer than b, a must start with exactly b, and vice versa.
2746 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2747 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002748static int indent_cmp(const char *a, const char *b)
2749{
2750 size_t al, bl;
2751 al = len(a);
2752 bl = len(b);
2753 if (al > bl) {
2754 if (bl && strncmp(a, b, bl) != 0)
2755 return EINVAL;
2756 return 1;
2757 }
2758 /* al <= bl */
2759 if (al && strncmp(a, b, al) != 0)
2760 return EINVAL;
2761 return (al < bl)? -1 : 0;
2762}
2763
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002764/* Configration make from file. */
2765int config_from_file(struct vty *vty, FILE * fp)
2766{
2767 int ret;
2768 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002769 char *indent;
2770 int cmp;
2771 struct vty_parent_node this_node;
2772 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002773
2774 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002775 indent = NULL;
2776 vline = NULL;
2777 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002778
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002779 if (ret != CMD_SUCCESS)
2780 goto return_invalid_indent;
2781
2782 /* In case of comment or empty line */
2783 if (vline == NULL) {
2784 if (indent) {
2785 talloc_free(indent);
2786 indent = NULL;
2787 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002788 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002789 }
2790
Neels Hofmeyr43063632017-09-19 23:54:01 +02002791 /* We have a nonempty line. */
2792 if (!vty->indent) {
2793 /* We have just entered a node and expecting the first child to come up; but we
2794 * may also skip right back to a parent or ancestor level. */
2795 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002796
Neels Hofmeyr43063632017-09-19 23:54:01 +02002797 /* If there is no parent, record any indentation we encounter. */
2798 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2799
2800 if (cmp == EINVAL)
2801 goto return_invalid_indent;
2802
2803 if (cmp <= 0) {
2804 /* We have gone right back to the parent level or higher, we are skipping
2805 * this child node level entirely. Pop the parent to go back to a node
2806 * that was actually there (to reinstate vty->indent) and re-use below
2807 * go-parent while-loop to find an accurate match of indent in the node
2808 * ancestry. */
2809 vty_go_parent(vty);
2810 } else {
2811 /* The indent is deeper than the just entered parent, record the new
2812 * indentation characters. */
2813 vty->indent = talloc_strdup(vty, indent);
2814 /* This *is* the new indentation. */
2815 cmp = 0;
2816 }
2817 } else {
2818 /* There is a known indentation for this node level, validate and detect node
2819 * exits. */
2820 cmp = indent_cmp(indent, vty->indent);
2821 if (cmp == EINVAL)
2822 goto return_invalid_indent;
2823 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002824
2825 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2826 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2827 while (cmp < 0) {
2828 vty_go_parent(vty);
2829 cmp = indent_cmp(indent, vty->indent);
2830 if (cmp == EINVAL)
2831 goto return_invalid_indent;
2832 }
2833
2834 /* More indent without having entered a child node level? Either the parent node's indent
2835 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2836 * or the indentation increased even though the vty command didn't enter a child. */
2837 if (cmp > 0)
2838 goto return_invalid_indent;
2839
2840 /* Remember the current node before the command possibly changes it. */
2841 this_node = (struct vty_parent_node){
2842 .node = vty->node,
2843 .priv = vty->priv,
2844 .indent = vty->indent,
2845 };
2846
2847 parent = vty_parent(vty);
2848 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002849 cmd_free_strvec(vline);
2850
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002851 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002852 if (indent) {
2853 talloc_free(indent);
2854 indent = NULL;
2855 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002856 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002857 }
2858
2859 /* If we have stepped down into a child node, push a parent frame.
2860 * The causality is such: we don't expect every single node entry implementation to push
2861 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2862 * a parent node. Hence if the node changed without the parent node changing, we must
2863 * have stepped into a child node (and now expect a deeper indent). */
2864 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2865 /* Push the parent node. */
2866 parent = talloc_zero(vty, struct vty_parent_node);
2867 *parent = this_node;
2868 llist_add(&parent->entry, &vty->parent_nodes);
2869
2870 /* The current talloc'ed vty->indent string will now be owned by this parent
2871 * struct. Indicate that we don't know what deeper indent characters the user
2872 * will choose. */
2873 vty->indent = NULL;
2874 }
2875
2876 if (indent) {
2877 talloc_free(indent);
2878 indent = NULL;
2879 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002880 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002881 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2882 while (vty_parent(vty))
2883 vty_go_parent(vty);
2884
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002885 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002886
2887return_invalid_indent:
2888 if (vline)
2889 cmd_free_strvec(vline);
2890 if (indent) {
2891 talloc_free(indent);
2892 indent = NULL;
2893 }
2894 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002895}
2896
2897/* Configration from terminal */
2898DEFUN(config_terminal,
2899 config_terminal_cmd,
2900 "configure terminal",
2901 "Configuration from vty interface\n" "Configuration terminal\n")
2902{
2903 if (vty_config_lock(vty))
2904 vty->node = CONFIG_NODE;
2905 else {
2906 vty_out(vty, "VTY configuration is locked by other VTY%s",
2907 VTY_NEWLINE);
2908 return CMD_WARNING;
2909 }
2910 return CMD_SUCCESS;
2911}
2912
2913/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002914DEFUN(enable, config_enable_cmd,
2915 "enable [expert-mode]",
2916 "Turn on privileged mode command\n"
2917 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002918{
2919 /* If enable password is NULL, change to ENABLE_NODE */
2920 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2921 vty->type == VTY_SHELL_SERV)
2922 vty->node = ENABLE_NODE;
2923 else
2924 vty->node = AUTH_ENABLE_NODE;
2925
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002926 host.expert_mode = argc > 0;
2927
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002928 return CMD_SUCCESS;
2929}
2930
2931/* Disable command */
2932DEFUN(disable,
2933 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2934{
2935 if (vty->node == ENABLE_NODE)
2936 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002937
2938 host.expert_mode = false;
2939
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002940 return CMD_SUCCESS;
2941}
2942
2943/* Down vty node level. */
2944gDEFUN(config_exit,
2945 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2946{
2947 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002948 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002949 case VIEW_NODE:
2950 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002951 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002952 break;
2953 case CONFIG_NODE:
2954 vty->node = ENABLE_NODE;
2955 vty_config_unlock(vty);
2956 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002957 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002958 if (vty->node > CONFIG_NODE)
2959 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002960 break;
2961 }
2962 return CMD_SUCCESS;
2963}
2964
2965/* End of configuration. */
2966 gDEFUN(config_end,
2967 config_end_cmd, "end", "End current mode and change to enable mode.")
2968{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002969 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002970 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002971
2972 /* Repeatedly call go_parent until a top node is reached. */
2973 while (vty->node > CONFIG_NODE) {
2974 if (vty->node == last_node) {
2975 /* Ensure termination, this shouldn't happen. */
2976 break;
2977 }
2978 last_node = vty->node;
2979 vty_go_parent(vty);
2980 }
2981
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002982 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002983 if (vty->node > ENABLE_NODE)
2984 vty->node = ENABLE_NODE;
2985 vty->index = NULL;
2986 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002987 }
2988 return CMD_SUCCESS;
2989}
2990
2991/* Show version. */
2992DEFUN(show_version,
2993 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2994{
Harald Welte237f6242010-05-25 23:00:45 +02002995 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2996 host.app_info->version,
2997 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2998 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002999
3000 return CMD_SUCCESS;
3001}
3002
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003003DEFUN(show_online_help,
3004 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
3005{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02003006 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003007 return CMD_SUCCESS;
3008}
3009
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003010/* Help display function for all node. */
3011gDEFUN(config_help,
3012 config_help_cmd, "help", "Description of the interactive help system\n")
3013{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07003014 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
3015 "anytime at the command line please press '?'.%s%s"
3016 "If nothing matches, the help list will be empty and you must backup%s"
3017 " until entering a '?' shows the available options.%s"
3018 "Two styles of help are provided:%s"
3019 "1. Full help is available when you are ready to enter a%s"
3020 "command argument (e.g. 'show ?') and describes each possible%s"
3021 "argument.%s"
3022 "2. Partial help is provided when an abbreviated argument is entered%s"
3023 " and you want to know what arguments match the input%s"
3024 " (e.g. 'show me?'.)%s%s",
3025 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3026 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3027 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3028 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003029 return CMD_SUCCESS;
3030}
3031
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003032enum {
3033 ATTR_TYPE_GLOBAL = (1 << 0),
3034 ATTR_TYPE_LIB = (1 << 1),
3035 ATTR_TYPE_APP = (1 << 2),
3036};
3037
3038static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3039{
3040 const char *desc;
3041 unsigned int i;
3042 bool found;
3043 char flag;
3044
3045 if (attr_mask & ATTR_TYPE_GLOBAL) {
3046 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3047
3048 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003049 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003050 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003051
3052 /* Skip attributes without flags */
3053 if (flag != '.')
3054 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003055 }
3056 }
3057
3058 if (attr_mask & ATTR_TYPE_LIB) {
3059 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3060
3061 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3062 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3063 continue;
3064 found = true;
3065
3066 flag = cmd_lib_attr_letters[i];
3067 if (flag == '\0')
3068 flag = '.';
3069
3070 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3071 }
3072
3073 if (!found)
3074 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3075 }
3076
3077 if (attr_mask & ATTR_TYPE_APP) {
3078 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3079
3080 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3081 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3082 continue;
3083 found = true;
3084
3085 flag = host.app_info->usr_attr_letters[i];
3086 if (flag == '\0')
3087 flag = '.';
3088
3089 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3090 }
3091
3092 if (!found)
3093 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3094 }
3095}
3096
3097gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3098 "show vty-attributes",
3099 SHOW_STR "List of VTY attributes\n")
3100{
3101 print_attr_list(vty, 0xff);
3102 return CMD_SUCCESS;
3103}
3104
3105gDEFUN(show_vty_attr, show_vty_attr_cmd,
3106 "show vty-attributes (application|library|global)",
3107 SHOW_STR "List of VTY attributes\n"
3108 "Application specific attributes only\n"
3109 "Library specific attributes only\n"
3110 "Global attributes only\n")
3111{
3112 unsigned int attr_mask = 0;
3113
3114 if (argv[0][0] == 'g') /* global */
3115 attr_mask |= ATTR_TYPE_GLOBAL;
3116 else if (argv[0][0] == 'l') /* library */
3117 attr_mask |= ATTR_TYPE_LIB;
3118 else if (argv[0][0] == 'a') /* application */
3119 attr_mask |= ATTR_TYPE_APP;
3120
3121 print_attr_list(vty, attr_mask);
3122 return CMD_SUCCESS;
3123}
3124
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003125/* Compose flag bit-mask for all commands within the given node */
3126static unsigned int node_flag_mask(const struct cmd_node *cnode)
3127{
3128 unsigned int flag_mask = 0x00;
3129 unsigned int f, i;
3130
3131 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3132 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3133 const struct cmd_element *cmd;
3134 char flag_letter;
3135
3136 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3137 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003138 if (cmd->attr & CMD_ATTR_DEPRECATED)
3139 continue;
3140 if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003141 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003142 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003143 continue;
3144
3145 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3146 flag_letter = cmd_lib_attr_letters[f];
3147 else
3148 flag_letter = host.app_info->usr_attr_letters[f];
3149
3150 if (flag_letter == '\0')
3151 continue;
3152
3153 flag_mask |= (1 << f);
3154 break;
3155 }
3156 }
3157
3158 return flag_mask;
3159}
3160
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003161/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3162static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3163{
3164 static char char_mask[8 + 1];
3165 char *ptr = &char_mask[0];
3166
3167 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003168 if (cmd->attr & CMD_ATTR_HIDDEN)
3169 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3170 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003171 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3172 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3173 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3174 else
3175 *(ptr++) = '.';
3176
3177 *ptr = '\0';
3178
3179 return char_mask;
3180}
3181
3182/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003183static const char *cmd_flag_mask(const struct cmd_element *cmd,
3184 unsigned int flag_mask)
3185{
3186 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3187 char *ptr = &char_mask[0];
3188 char flag_letter;
3189 unsigned int f;
3190
3191 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003192 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003193 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003194 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003195 *(ptr++) = '.';
3196 continue;
3197 }
3198
3199 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3200 flag_letter = cmd_lib_attr_letters[f];
3201 else
3202 flag_letter = host.app_info->usr_attr_letters[f];
3203
3204 *(ptr++) = flag_letter ? flag_letter : '.';
3205 }
3206
3207 *ptr = '\0';
3208
3209 return char_mask;
3210}
3211
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003212/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003213gDEFUN(config_list, config_list_cmd,
3214 "list [with-flags]",
3215 "Print command list\n"
3216 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003217{
3218 unsigned int i;
3219 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003220 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003221 struct cmd_element *cmd;
3222
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003223 if (argc > 0)
3224 flag_mask = node_flag_mask(cnode);
3225
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003226 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3227 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3228 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003229 if (cmd->attr & CMD_ATTR_DEPRECATED)
3230 continue;
3231 if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003232 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003233 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003234 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3235 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003236 vty_out(vty, " %s %s %s%s",
3237 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003238 cmd_flag_mask(cmd, flag_mask),
3239 cmd->string, VTY_NEWLINE);
3240 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003241 }
3242
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003243 return CMD_SUCCESS;
3244}
3245
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003246static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003247{
3248 unsigned int i;
3249 int fd;
3250 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003251 char *config_file_tmp = NULL;
3252 char *config_file_sav = NULL;
3253 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003254 struct stat st;
3255
3256 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003257
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003258 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3259 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3260 * manually instead. */
3261
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003262 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003263 config_file_sav =
3264 _talloc_zero(tall_vty_cmd_ctx,
3265 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3266 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003267 if (!config_file_sav)
3268 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003269 strcpy(config_file_sav, config_file);
3270 strcat(config_file_sav, CONF_BACKUP_EXT);
3271
3272 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003273 "config_file_tmp");
3274 if (!config_file_tmp) {
3275 talloc_free(config_file_sav);
3276 return -1;
3277 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003278 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3279
3280 /* Open file to configuration write. */
3281 fd = mkstemp(config_file_tmp);
3282 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003283 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003284 talloc_free(config_file_tmp);
3285 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003286 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003287 }
3288
3289 /* Make vty for configuration file. */
3290 file_vty = vty_new();
3291 file_vty->fd = fd;
3292 file_vty->type = VTY_FILE;
3293
3294 /* Config file header print. */
3295 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003296 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003297 //vty_time_print (file_vty, 1);
3298 vty_out(file_vty, "!\n");
3299
3300 for (i = 0; i < vector_active(cmdvec); i++)
3301 if ((node = vector_slot(cmdvec, i)) && node->func) {
3302 if ((*node->func) (file_vty))
3303 vty_out(file_vty, "!\n");
3304 }
3305 vty_close(file_vty);
3306
3307 if (unlink(config_file_sav) != 0)
3308 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003309 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003310 talloc_free(config_file_sav);
3311 talloc_free(config_file_tmp);
3312 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003313 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003314 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003315
3316 /* Only link the .sav file if the original file exists */
3317 if (stat(config_file, &st) == 0) {
3318 if (link(config_file, config_file_sav) != 0) {
3319 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3320 talloc_free(config_file_sav);
3321 talloc_free(config_file_tmp);
3322 unlink(config_file_tmp);
3323 return -3;
3324 }
3325 sync();
3326 if (unlink(config_file) != 0) {
3327 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3328 talloc_free(config_file_sav);
3329 talloc_free(config_file_tmp);
3330 unlink(config_file_tmp);
3331 return -4;
3332 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003333 }
3334 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003335 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003336 talloc_free(config_file_sav);
3337 talloc_free(config_file_tmp);
3338 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003339 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003340 }
3341 unlink(config_file_tmp);
3342 sync();
3343
3344 talloc_free(config_file_sav);
3345 talloc_free(config_file_tmp);
3346
3347 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003348 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3349 return -6;
3350 }
3351
3352 return 0;
3353}
3354
3355
3356/* Write current configuration into file. */
3357DEFUN(config_write_file,
3358 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003359 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003360 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003361 "Write to configuration file\n"
3362 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003363{
3364 char *failed_file;
3365 int rc;
3366
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003367 if (host.app_info->config_is_consistent) {
3368 rc = host.app_info->config_is_consistent(vty);
3369 if (!rc) {
3370 vty_out(vty, "Configuration is not consistent%s",
3371 VTY_NEWLINE);
3372 return CMD_WARNING;
3373 }
3374 }
3375
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003376 if (argc == 1)
3377 host_config_set(argv[0]);
3378
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003379 if (host.config == NULL) {
3380 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3381 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003382 return CMD_WARNING;
3383 }
3384
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003385 rc = write_config_file(host.config, &failed_file);
3386 switch (rc) {
3387 case -1:
3388 vty_out(vty, "Can't open configuration file %s.%s",
3389 failed_file, VTY_NEWLINE);
3390 rc = CMD_WARNING;
3391 break;
3392 case -2:
3393 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3394 failed_file, VTY_NEWLINE);
3395 rc = CMD_WARNING;
3396 break;
3397 case -3:
3398 vty_out(vty, "Can't backup old configuration file %s.%s",
3399 failed_file, VTY_NEWLINE);
3400 rc = CMD_WARNING;
3401 break;
3402 case -4:
3403 vty_out(vty, "Can't unlink configuration file %s.%s",
3404 failed_file, VTY_NEWLINE);
3405 rc = CMD_WARNING;
3406 break;
3407 case -5:
3408 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3409 VTY_NEWLINE);
3410 rc = CMD_WARNING;
3411 break;
3412 case -6:
3413 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3414 failed_file, strerror(errno), errno, VTY_NEWLINE);
3415 rc = CMD_WARNING;
3416 break;
3417 default:
3418 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3419 rc = CMD_SUCCESS;
3420 break;
3421 }
3422
3423 talloc_free(failed_file);
3424 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003425}
3426
3427ALIAS(config_write_file,
3428 config_write_cmd,
3429 "write", "Write running configuration to memory, network, or terminal\n")
3430
3431 ALIAS(config_write_file,
3432 config_write_memory_cmd,
3433 "write memory",
3434 "Write running configuration to memory, network, or terminal\n"
3435 "Write configuration to the file (same as write file)\n")
3436
3437 ALIAS(config_write_file,
3438 copy_runningconfig_startupconfig_cmd,
3439 "copy running-config startup-config",
3440 "Copy configuration\n"
3441 "Copy running config to... \n"
3442 "Copy running config to startup config (same as write file)\n")
3443
3444/* Write current configuration into the terminal. */
3445 DEFUN(config_write_terminal,
3446 config_write_terminal_cmd,
3447 "write terminal",
3448 "Write running configuration to memory, network, or terminal\n"
3449 "Write to terminal\n")
3450{
3451 unsigned int i;
3452 struct cmd_node *node;
3453
3454 if (vty->type == VTY_SHELL_SERV) {
3455 for (i = 0; i < vector_active(cmdvec); i++)
3456 if ((node = vector_slot(cmdvec, i)) && node->func
3457 && node->vtysh) {
3458 if ((*node->func) (vty))
3459 vty_out(vty, "!%s", VTY_NEWLINE);
3460 }
3461 } else {
3462 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3463 VTY_NEWLINE);
3464 vty_out(vty, "!%s", VTY_NEWLINE);
3465
3466 for (i = 0; i < vector_active(cmdvec); i++)
3467 if ((node = vector_slot(cmdvec, i)) && node->func) {
3468 if ((*node->func) (vty))
3469 vty_out(vty, "!%s", VTY_NEWLINE);
3470 }
3471 vty_out(vty, "end%s", VTY_NEWLINE);
3472 }
3473 return CMD_SUCCESS;
3474}
3475
3476/* Write current configuration into the terminal. */
3477ALIAS(config_write_terminal,
3478 show_running_config_cmd,
3479 "show running-config", SHOW_STR "running configuration\n")
3480
3481/* Write startup configuration into the terminal. */
3482 DEFUN(show_startup_config,
3483 show_startup_config_cmd,
3484 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3485{
3486 char buf[BUFSIZ];
3487 FILE *confp;
3488
3489 confp = fopen(host.config, "r");
3490 if (confp == NULL) {
3491 vty_out(vty, "Can't open configuration file [%s]%s",
3492 host.config, VTY_NEWLINE);
3493 return CMD_WARNING;
3494 }
3495
3496 while (fgets(buf, BUFSIZ, confp)) {
3497 char *cp = buf;
3498
3499 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3500 cp++;
3501 *cp = '\0';
3502
3503 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3504 }
3505
3506 fclose(confp);
3507
3508 return CMD_SUCCESS;
3509}
3510
3511/* Hostname configuration */
3512DEFUN(config_hostname,
3513 hostname_cmd,
3514 "hostname WORD",
3515 "Set system's network name\n" "This system's network name\n")
3516{
3517 if (!isalpha((int)*argv[0])) {
3518 vty_out(vty, "Please specify string starting with alphabet%s",
3519 VTY_NEWLINE);
3520 return CMD_WARNING;
3521 }
3522
3523 if (host.name)
3524 talloc_free(host.name);
3525
3526 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3527 return CMD_SUCCESS;
3528}
3529
3530DEFUN(config_no_hostname,
3531 no_hostname_cmd,
3532 "no hostname [HOSTNAME]",
3533 NO_STR "Reset system's network name\n" "Host name of this router\n")
3534{
3535 if (host.name)
3536 talloc_free(host.name);
3537 host.name = NULL;
3538 return CMD_SUCCESS;
3539}
3540
3541/* VTY interface password set. */
3542DEFUN(config_password, password_cmd,
3543 "password (8|) WORD",
3544 "Assign the terminal connection password\n"
3545 "Specifies a HIDDEN password will follow\n"
3546 "dummy string \n" "The HIDDEN line password string\n")
3547{
3548 /* Argument check. */
3549 if (argc == 0) {
3550 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3551 return CMD_WARNING;
3552 }
3553
3554 if (argc == 2) {
3555 if (*argv[0] == '8') {
3556 if (host.password)
3557 talloc_free(host.password);
3558 host.password = NULL;
3559 if (host.password_encrypt)
3560 talloc_free(host.password_encrypt);
3561 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3562 return CMD_SUCCESS;
3563 } else {
3564 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3565 return CMD_WARNING;
3566 }
3567 }
3568
3569 if (!isalnum((int)*argv[0])) {
3570 vty_out(vty,
3571 "Please specify string starting with alphanumeric%s",
3572 VTY_NEWLINE);
3573 return CMD_WARNING;
3574 }
3575
3576 if (host.password)
3577 talloc_free(host.password);
3578 host.password = NULL;
3579
3580#ifdef VTY_CRYPT_PW
3581 if (host.encrypt) {
3582 if (host.password_encrypt)
3583 talloc_free(host.password_encrypt);
3584 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3585 } else
3586#endif
3587 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3588
3589 return CMD_SUCCESS;
3590}
3591
3592ALIAS(config_password, password_text_cmd,
3593 "password LINE",
3594 "Assign the terminal connection password\n"
3595 "The UNENCRYPTED (cleartext) line password\n")
3596
3597/* VTY enable password set. */
3598 DEFUN(config_enable_password, enable_password_cmd,
3599 "enable password (8|) WORD",
3600 "Modify enable password parameters\n"
3601 "Assign the privileged level password\n"
3602 "Specifies a HIDDEN password will follow\n"
3603 "dummy string \n" "The HIDDEN 'enable' password string\n")
3604{
3605 /* Argument check. */
3606 if (argc == 0) {
3607 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3608 return CMD_WARNING;
3609 }
3610
3611 /* Crypt type is specified. */
3612 if (argc == 2) {
3613 if (*argv[0] == '8') {
3614 if (host.enable)
3615 talloc_free(host.enable);
3616 host.enable = NULL;
3617
3618 if (host.enable_encrypt)
3619 talloc_free(host.enable_encrypt);
3620 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3621
3622 return CMD_SUCCESS;
3623 } else {
3624 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3625 return CMD_WARNING;
3626 }
3627 }
3628
3629 if (!isalnum((int)*argv[0])) {
3630 vty_out(vty,
3631 "Please specify string starting with alphanumeric%s",
3632 VTY_NEWLINE);
3633 return CMD_WARNING;
3634 }
3635
3636 if (host.enable)
3637 talloc_free(host.enable);
3638 host.enable = NULL;
3639
3640 /* Plain password input. */
3641#ifdef VTY_CRYPT_PW
3642 if (host.encrypt) {
3643 if (host.enable_encrypt)
3644 talloc_free(host.enable_encrypt);
3645 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3646 } else
3647#endif
3648 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3649
3650 return CMD_SUCCESS;
3651}
3652
3653ALIAS(config_enable_password,
3654 enable_password_text_cmd,
3655 "enable password LINE",
3656 "Modify enable password parameters\n"
3657 "Assign the privileged level password\n"
3658 "The UNENCRYPTED (cleartext) 'enable' password\n")
3659
3660/* VTY enable password delete. */
3661 DEFUN(no_config_enable_password, no_enable_password_cmd,
3662 "no enable password",
3663 NO_STR
3664 "Modify enable password parameters\n"
3665 "Assign the privileged level password\n")
3666{
3667 if (host.enable)
3668 talloc_free(host.enable);
3669 host.enable = NULL;
3670
3671 if (host.enable_encrypt)
3672 talloc_free(host.enable_encrypt);
3673 host.enable_encrypt = NULL;
3674
3675 return CMD_SUCCESS;
3676}
3677
3678#ifdef VTY_CRYPT_PW
3679DEFUN(service_password_encrypt,
3680 service_password_encrypt_cmd,
3681 "service password-encryption",
3682 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3683{
3684 if (host.encrypt)
3685 return CMD_SUCCESS;
3686
3687 host.encrypt = 1;
3688
3689 if (host.password) {
3690 if (host.password_encrypt)
3691 talloc_free(host.password_encrypt);
3692 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3693 }
3694 if (host.enable) {
3695 if (host.enable_encrypt)
3696 talloc_free(host.enable_encrypt);
3697 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3698 }
3699
3700 return CMD_SUCCESS;
3701}
3702
3703DEFUN(no_service_password_encrypt,
3704 no_service_password_encrypt_cmd,
3705 "no service password-encryption",
3706 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3707{
3708 if (!host.encrypt)
3709 return CMD_SUCCESS;
3710
3711 host.encrypt = 0;
3712
3713 if (host.password_encrypt)
3714 talloc_free(host.password_encrypt);
3715 host.password_encrypt = NULL;
3716
3717 if (host.enable_encrypt)
3718 talloc_free(host.enable_encrypt);
3719 host.enable_encrypt = NULL;
3720
3721 return CMD_SUCCESS;
3722}
3723#endif
3724
3725DEFUN(config_terminal_length, config_terminal_length_cmd,
3726 "terminal length <0-512>",
3727 "Set terminal line parameters\n"
3728 "Set number of lines on a screen\n"
3729 "Number of lines on screen (0 for no pausing)\n")
3730{
3731 int lines;
3732 char *endptr = NULL;
3733
3734 lines = strtol(argv[0], &endptr, 10);
3735 if (lines < 0 || lines > 512 || *endptr != '\0') {
3736 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3737 return CMD_WARNING;
3738 }
3739 vty->lines = lines;
3740
3741 return CMD_SUCCESS;
3742}
3743
3744DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3745 "terminal no length",
3746 "Set terminal line parameters\n"
3747 NO_STR "Set number of lines on a screen\n")
3748{
3749 vty->lines = -1;
3750 return CMD_SUCCESS;
3751}
3752
3753DEFUN(service_terminal_length, service_terminal_length_cmd,
3754 "service terminal-length <0-512>",
3755 "Set up miscellaneous service\n"
3756 "System wide terminal length configuration\n"
3757 "Number of lines of VTY (0 means no line control)\n")
3758{
3759 int lines;
3760 char *endptr = NULL;
3761
3762 lines = strtol(argv[0], &endptr, 10);
3763 if (lines < 0 || lines > 512 || *endptr != '\0') {
3764 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3765 return CMD_WARNING;
3766 }
3767 host.lines = lines;
3768
3769 return CMD_SUCCESS;
3770}
3771
3772DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3773 "no service terminal-length [<0-512>]",
3774 NO_STR
3775 "Set up miscellaneous service\n"
3776 "System wide terminal length configuration\n"
3777 "Number of lines of VTY (0 means no line control)\n")
3778{
3779 host.lines = -1;
3780 return CMD_SUCCESS;
3781}
3782
3783DEFUN_HIDDEN(do_echo,
3784 echo_cmd,
3785 "echo .MESSAGE",
3786 "Echo a message back to the vty\n" "The message to echo\n")
3787{
3788 char *message;
3789
3790 vty_out(vty, "%s%s",
3791 ((message =
3792 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3793 if (message)
3794 talloc_free(message);
3795 return CMD_SUCCESS;
3796}
3797
3798#if 0
3799DEFUN(config_logmsg,
3800 config_logmsg_cmd,
3801 "logmsg " LOG_LEVELS " .MESSAGE",
3802 "Send a message to enabled logging destinations\n"
3803 LOG_LEVEL_DESC "The message to send\n")
3804{
3805 int level;
3806 char *message;
3807
3808 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3809 return CMD_ERR_NO_MATCH;
3810
3811 zlog(NULL, level,
3812 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3813 if (message)
3814 talloc_free(message);
3815 return CMD_SUCCESS;
3816}
3817
3818DEFUN(show_logging,
3819 show_logging_cmd,
3820 "show logging", SHOW_STR "Show current logging configuration\n")
3821{
3822 struct zlog *zl = zlog_default;
3823
3824 vty_out(vty, "Syslog logging: ");
3825 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3826 vty_out(vty, "disabled");
3827 else
3828 vty_out(vty, "level %s, facility %s, ident %s",
3829 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3830 facility_name(zl->facility), zl->ident);
3831 vty_out(vty, "%s", VTY_NEWLINE);
3832
3833 vty_out(vty, "Stdout logging: ");
3834 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3835 vty_out(vty, "disabled");
3836 else
3837 vty_out(vty, "level %s",
3838 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3839 vty_out(vty, "%s", VTY_NEWLINE);
3840
3841 vty_out(vty, "Monitor logging: ");
3842 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3843 vty_out(vty, "disabled");
3844 else
3845 vty_out(vty, "level %s",
3846 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3847 vty_out(vty, "%s", VTY_NEWLINE);
3848
3849 vty_out(vty, "File logging: ");
3850 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3851 vty_out(vty, "disabled");
3852 else
3853 vty_out(vty, "level %s, filename %s",
3854 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3855 zl->filename);
3856 vty_out(vty, "%s", VTY_NEWLINE);
3857
3858 vty_out(vty, "Protocol name: %s%s",
3859 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3860 vty_out(vty, "Record priority: %s%s",
3861 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3862
3863 return CMD_SUCCESS;
3864}
3865
3866DEFUN(config_log_stdout,
3867 config_log_stdout_cmd,
3868 "log stdout", "Logging control\n" "Set stdout logging level\n")
3869{
3870 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3871 return CMD_SUCCESS;
3872}
3873
3874DEFUN(config_log_stdout_level,
3875 config_log_stdout_level_cmd,
3876 "log stdout " LOG_LEVELS,
3877 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3878{
3879 int level;
3880
3881 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3882 return CMD_ERR_NO_MATCH;
3883 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3884 return CMD_SUCCESS;
3885}
3886
3887DEFUN(no_config_log_stdout,
3888 no_config_log_stdout_cmd,
3889 "no log stdout [LEVEL]",
3890 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3891{
3892 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3893 return CMD_SUCCESS;
3894}
3895
3896DEFUN(config_log_monitor,
3897 config_log_monitor_cmd,
3898 "log monitor",
3899 "Logging control\n" "Set terminal line (monitor) logging level\n")
3900{
3901 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3902 return CMD_SUCCESS;
3903}
3904
3905DEFUN(config_log_monitor_level,
3906 config_log_monitor_level_cmd,
3907 "log monitor " LOG_LEVELS,
3908 "Logging control\n"
3909 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3910{
3911 int level;
3912
3913 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3914 return CMD_ERR_NO_MATCH;
3915 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3916 return CMD_SUCCESS;
3917}
3918
3919DEFUN(no_config_log_monitor,
3920 no_config_log_monitor_cmd,
3921 "no log monitor [LEVEL]",
3922 NO_STR
3923 "Logging control\n"
3924 "Disable terminal line (monitor) logging\n" "Logging level\n")
3925{
3926 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3927 return CMD_SUCCESS;
3928}
3929
3930static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3931{
3932 int ret;
3933 char *p = NULL;
3934 const char *fullpath;
3935
3936 /* Path detection. */
3937 if (!IS_DIRECTORY_SEP(*fname)) {
3938 char cwd[MAXPATHLEN + 1];
3939 cwd[MAXPATHLEN] = '\0';
3940
3941 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3942 zlog_err("config_log_file: Unable to alloc mem!");
3943 return CMD_WARNING;
3944 }
3945
3946 if ((p = _talloc_zero(tall_vcmd_ctx,
3947 strlen(cwd) + strlen(fname) + 2),
3948 "set_log_file")
3949 == NULL) {
3950 zlog_err("config_log_file: Unable to alloc mem!");
3951 return CMD_WARNING;
3952 }
3953 sprintf(p, "%s/%s", cwd, fname);
3954 fullpath = p;
3955 } else
3956 fullpath = fname;
3957
3958 ret = zlog_set_file(NULL, fullpath, loglevel);
3959
3960 if (p)
3961 talloc_free(p);
3962
3963 if (!ret) {
3964 vty_out(vty, "can't open logfile %s\n", fname);
3965 return CMD_WARNING;
3966 }
3967
3968 if (host.logfile)
3969 talloc_free(host.logfile);
3970
3971 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3972
3973 return CMD_SUCCESS;
3974}
3975
3976DEFUN(config_log_file,
3977 config_log_file_cmd,
3978 "log file FILENAME",
3979 "Logging control\n" "Logging to file\n" "Logging filename\n")
3980{
3981 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3982}
3983
3984DEFUN(config_log_file_level,
3985 config_log_file_level_cmd,
3986 "log file FILENAME " LOG_LEVELS,
3987 "Logging control\n"
3988 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3989{
3990 int level;
3991
3992 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3993 return CMD_ERR_NO_MATCH;
3994 return set_log_file(vty, argv[0], level);
3995}
3996
3997DEFUN(no_config_log_file,
3998 no_config_log_file_cmd,
3999 "no log file [FILENAME]",
4000 NO_STR
4001 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
4002{
4003 zlog_reset_file(NULL);
4004
4005 if (host.logfile)
4006 talloc_free(host.logfile);
4007
4008 host.logfile = NULL;
4009
4010 return CMD_SUCCESS;
4011}
4012
4013ALIAS(no_config_log_file,
4014 no_config_log_file_level_cmd,
4015 "no log file FILENAME LEVEL",
4016 NO_STR
4017 "Logging control\n"
4018 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
4019
4020 DEFUN(config_log_syslog,
4021 config_log_syslog_cmd,
4022 "log syslog", "Logging control\n" "Set syslog logging level\n")
4023{
4024 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4025 return CMD_SUCCESS;
4026}
4027
4028DEFUN(config_log_syslog_level,
4029 config_log_syslog_level_cmd,
4030 "log syslog " LOG_LEVELS,
4031 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
4032{
4033 int level;
4034
4035 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4036 return CMD_ERR_NO_MATCH;
4037 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4038 return CMD_SUCCESS;
4039}
4040
4041DEFUN_DEPRECATED(config_log_syslog_facility,
4042 config_log_syslog_facility_cmd,
4043 "log syslog facility " LOG_FACILITIES,
4044 "Logging control\n"
4045 "Logging goes to syslog\n"
4046 "(Deprecated) Facility parameter for syslog messages\n"
4047 LOG_FACILITY_DESC)
4048{
4049 int facility;
4050
4051 if ((facility = facility_match(argv[0])) < 0)
4052 return CMD_ERR_NO_MATCH;
4053
4054 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4055 zlog_default->facility = facility;
4056 return CMD_SUCCESS;
4057}
4058
4059DEFUN(no_config_log_syslog,
4060 no_config_log_syslog_cmd,
4061 "no log syslog [LEVEL]",
4062 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4063{
4064 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4065 return CMD_SUCCESS;
4066}
4067
4068ALIAS(no_config_log_syslog,
4069 no_config_log_syslog_facility_cmd,
4070 "no log syslog facility " LOG_FACILITIES,
4071 NO_STR
4072 "Logging control\n"
4073 "Logging goes to syslog\n"
4074 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4075
4076 DEFUN(config_log_facility,
4077 config_log_facility_cmd,
4078 "log facility " LOG_FACILITIES,
4079 "Logging control\n"
4080 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4081{
4082 int facility;
4083
4084 if ((facility = facility_match(argv[0])) < 0)
4085 return CMD_ERR_NO_MATCH;
4086 zlog_default->facility = facility;
4087 return CMD_SUCCESS;
4088}
4089
4090DEFUN(no_config_log_facility,
4091 no_config_log_facility_cmd,
4092 "no log facility [FACILITY]",
4093 NO_STR
4094 "Logging control\n"
4095 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4096{
4097 zlog_default->facility = LOG_DAEMON;
4098 return CMD_SUCCESS;
4099}
4100
4101DEFUN_DEPRECATED(config_log_trap,
4102 config_log_trap_cmd,
4103 "log trap " LOG_LEVELS,
4104 "Logging control\n"
4105 "(Deprecated) Set logging level and default for all destinations\n"
4106 LOG_LEVEL_DESC)
4107{
4108 int new_level;
4109 int i;
4110
4111 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4112 return CMD_ERR_NO_MATCH;
4113
4114 zlog_default->default_lvl = new_level;
4115 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4116 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4117 zlog_default->maxlvl[i] = new_level;
4118 return CMD_SUCCESS;
4119}
4120
4121DEFUN_DEPRECATED(no_config_log_trap,
4122 no_config_log_trap_cmd,
4123 "no log trap [LEVEL]",
4124 NO_STR
4125 "Logging control\n"
4126 "Permit all logging information\n" "Logging level\n")
4127{
4128 zlog_default->default_lvl = LOG_DEBUG;
4129 return CMD_SUCCESS;
4130}
4131
4132DEFUN(config_log_record_priority,
4133 config_log_record_priority_cmd,
4134 "log record-priority",
4135 "Logging control\n"
4136 "Log the priority of the message within the message\n")
4137{
4138 zlog_default->record_priority = 1;
4139 return CMD_SUCCESS;
4140}
4141
4142DEFUN(no_config_log_record_priority,
4143 no_config_log_record_priority_cmd,
4144 "no log record-priority",
4145 NO_STR
4146 "Logging control\n"
4147 "Do not log the priority of the message within the message\n")
4148{
4149 zlog_default->record_priority = 0;
4150 return CMD_SUCCESS;
4151}
4152#endif
4153
4154DEFUN(banner_motd_file,
4155 banner_motd_file_cmd,
4156 "banner motd file [FILE]",
4157 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4158{
4159 if (host.motdfile)
4160 talloc_free(host.motdfile);
4161 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4162
4163 return CMD_SUCCESS;
4164}
4165
4166DEFUN(banner_motd_default,
4167 banner_motd_default_cmd,
4168 "banner motd default",
4169 "Set banner string\n" "Strings for motd\n" "Default string\n")
4170{
4171 host.motd = default_motd;
4172 return CMD_SUCCESS;
4173}
4174
4175DEFUN(no_banner_motd,
4176 no_banner_motd_cmd,
4177 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4178{
4179 host.motd = NULL;
4180 if (host.motdfile)
4181 talloc_free(host.motdfile);
4182 host.motdfile = NULL;
4183 return CMD_SUCCESS;
4184}
4185
4186/* Set config filename. Called from vty.c */
4187void host_config_set(const char *filename)
4188{
4189 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4190}
4191
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004192/*! Deprecated, now happens implicitly when calling install_node().
4193 * Users of the API may still attempt to call this function, hence
4194 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004195void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004196{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004197}
4198
4199/*! Deprecated, now happens implicitly when calling install_node().
4200 * Users of the API may still attempt to call this function, hence
4201 * leave it here as a no-op. */
4202void vty_install_default(int node)
4203{
4204}
4205
4206/*! Install common commands like 'exit' and 'list'. */
4207static void install_basic_node_commands(int node)
4208{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004209 install_lib_element(node, &config_help_cmd);
4210 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004211
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004212 install_lib_element(node, &show_vty_attr_all_cmd);
4213 install_lib_element(node, &show_vty_attr_cmd);
4214
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004215 install_lib_element(node, &config_write_terminal_cmd);
4216 install_lib_element(node, &config_write_file_cmd);
4217 install_lib_element(node, &config_write_memory_cmd);
4218 install_lib_element(node, &config_write_cmd);
4219 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004220
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004221 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004222
4223 if (node >= CONFIG_NODE) {
4224 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004225 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004226 }
4227}
4228
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004229/*! Return true if a node is installed by install_basic_node_commands(), so
4230 * that we can avoid repeating them for each and every node during 'show
4231 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004232static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004233{
4234 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004235 || cmd == &show_vty_attr_all_cmd
4236 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004237 || cmd == &config_list_cmd
4238 || cmd == &config_write_terminal_cmd
4239 || cmd == &config_write_file_cmd
4240 || cmd == &config_write_memory_cmd
4241 || cmd == &config_write_cmd
4242 || cmd == &show_running_config_cmd
4243 || cmd == &config_exit_cmd
4244 || cmd == &config_end_cmd)
4245 return true;
4246 return false;
4247}
4248
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004249/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004250 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004251 * \param[in] vty the vty of the code
4252 * \param[in] filename where to store the file
4253 * \return 0 in case of success.
4254 *
4255 * If the filename already exists create a filename.sav
4256 * version with the current code.
4257 *
4258 */
4259int osmo_vty_write_config_file(const char *filename)
4260{
4261 char *failed_file;
4262 int rc;
4263
4264 rc = write_config_file(filename, &failed_file);
4265 talloc_free(failed_file);
4266 return rc;
4267}
4268
4269/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004270 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004271 * \return 0 in case of success.
4272 *
4273 * If the filename already exists create a filename.sav
4274 * version with the current code.
4275 *
4276 */
4277int osmo_vty_save_config_file(void)
4278{
4279 char *failed_file;
4280 int rc;
4281
4282 if (host.config == NULL)
4283 return -7;
4284
4285 rc = write_config_file(host.config, &failed_file);
4286 talloc_free(failed_file);
4287 return rc;
4288}
4289
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004290/* Initialize command interface. Install basic nodes and commands. */
4291void cmd_init(int terminal)
4292{
4293 /* Allocate initial top vector of commands. */
4294 cmdvec = vector_init(VECTOR_MIN_SIZE);
4295
4296 /* Default host value settings. */
4297 host.name = NULL;
4298 host.password = NULL;
4299 host.enable = NULL;
4300 host.logfile = NULL;
4301 host.config = NULL;
4302 host.lines = -1;
4303 host.motd = default_motd;
4304 host.motdfile = NULL;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07004305 host.expert_mode = false;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004306
4307 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004308 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004309 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004310 install_node_bare(&auth_node, NULL);
4311 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004312 install_node(&config_node, config_write_host);
4313
4314 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004315 install_lib_element(VIEW_NODE, &show_version_cmd);
4316 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004317 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004318 install_lib_element(VIEW_NODE, &config_list_cmd);
4319 install_lib_element(VIEW_NODE, &config_exit_cmd);
4320 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004321 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4322 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004323 install_lib_element(VIEW_NODE, &config_enable_cmd);
4324 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4325 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4326 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004327 }
4328
4329 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004330 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4331 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4332 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004333 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004334 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4335 install_lib_element(ENABLE_NODE, &show_version_cmd);
4336 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004337
4338 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004339 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4340 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4341 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004342 }
4343
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004344 install_lib_element(CONFIG_NODE, &hostname_cmd);
4345 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004346
4347 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004348 install_lib_element(CONFIG_NODE, &password_cmd);
4349 install_lib_element(CONFIG_NODE, &password_text_cmd);
4350 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4351 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4352 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004353
4354#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004355 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4356 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004357#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004358 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4359 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4360 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4361 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4362 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004363
4364 }
4365 srand(time(NULL));
4366}
Harald Welte7acb30c2011-08-17 17:13:48 +02004367
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004368/* FIXME: execute this section in the unit test instead */
4369static __attribute__((constructor)) void on_dso_load(void)
4370{
4371 unsigned int i, j;
4372
4373 /* Check total number of the library specific attributes */
4374 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4375
4376 /* Check for duplicates in the list of library specific flags */
4377 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4378 if (cmd_lib_attr_letters[i] == '\0')
4379 continue;
4380
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004381 /* Some flag characters are reserved for global attributes */
4382 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4383 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4384 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4385
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004386 /* Only upper case flag letters are allowed for libraries */
4387 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4388 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4389
4390 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4391 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4392 }
4393}
4394
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004395/*! @} */