blob: bad268880f3e385123771a045d7f0b6a97cf0fbf [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
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100625/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200626 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100627 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200628static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100629{
630 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700631 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100632
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200633 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700634
635 /* Print application specific attributes and their description */
636 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
637 print_func(data, " <attributes scope='application'>%s", newline);
638
639 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
640 char *xml_att_desc;
641 char flag;
642
643 /* Skip attribute if *not* set */
644 if (~cmd->usrattr & (1 << i))
645 continue;
646
647 xml_att_desc = xml_escape(host.app_info->usr_attr_desc[i]);
648 print_func(data, " <attribute doc='%s'", xml_att_desc);
649 talloc_free(xml_att_desc);
650
651 if ((flag = host.app_info->usr_attr_letters[i]) != '\0')
652 print_func(data, " flag='%c'", flag);
653 print_func(data, " />%s", newline);
654 }
655
656 print_func(data, " </attributes>%s", newline);
657 }
658
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200659 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100660
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700661 for (i = 0; i < vector_count(cmd->strvec); ++i) {
662 vector descvec = vector_slot(cmd->strvec, i);
663 int j;
664 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100665 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700666 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100667 if (desc == NULL)
668 continue;
669
670 xml_param = xml_escape(desc->cmd);
671 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200672 print_func(data, " <param name='%s' doc='%s' />%s",
673 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100674 talloc_free(xml_param);
675 talloc_free(xml_doc);
676 }
677 }
678
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200679 print_func(data, " </params>%s", newline);
680 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100681
682 talloc_free(xml_string);
683 return 0;
684}
685
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200686static bool vty_command_is_common(struct cmd_element *cmd);
687
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100688/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200689 * 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 +0100690 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200691static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100692{
693 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200694 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100695
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200696 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100697
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200698 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200699 print_func(data, " <node id='_common_cmds_'>%s", newline);
700 print_func(data, " <name>Common Commands</name>%s", newline);
701 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
702 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200703 for (i = 0; i < vector_active(cmdvec); ++i) {
704 struct cmd_node *cnode;
705 cnode = vector_slot(cmdvec, i);
706 if (!cnode)
707 continue;
708 if (cnode->node != CONFIG_NODE)
709 continue;
710
711 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
712 struct cmd_element *elem;
713 elem = vector_slot(cnode->cmd_vector, j);
714 if (!vty_command_is_common(elem))
715 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200716 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200717 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200718 }
719 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200720 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200721
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100722 for (i = 0; i < vector_active(cmdvec); ++i) {
723 struct cmd_node *cnode;
724 cnode = vector_slot(cmdvec, i);
725 if (!cnode)
726 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200727 if (vector_active(cnode->cmd_vector) < 1)
728 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100729
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200730 /* De-dup node IDs: how many times has this same name been used before? Count the first
731 * occurence as _1 and omit that first suffix, so that the first occurence is called
732 * 'name', the second becomes 'name_2', then 'name_3', ... */
733 same_name_count = 1;
734 for (j = 0; j < i; ++j) {
735 struct cmd_node *cnode2;
736 cnode2 = vector_slot(cmdvec, j);
737 if (!cnode2)
738 continue;
739 if (strcmp(cnode->name, cnode2->name) == 0)
740 same_name_count ++;
741 }
742
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200743 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200744 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200745 print_func(data, "_%d", same_name_count);
746 print_func(data, "'>%s", newline);
747 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100748
749 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
750 struct cmd_element *elem;
751 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200752 if (vty_command_is_common(elem))
753 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200754 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200755 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100756 }
757
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200758 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100759 }
760
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200761 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100762
763 return 0;
764}
765
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200766static int print_func_vty(void *data, const char *format, ...)
767{
768 struct vty *vty = data;
769 va_list args;
770 int rc;
771 va_start(args, format);
772 rc = vty_out_va(vty, format, args);
773 va_end(args);
774 return rc;
775}
776
777static int vty_dump_xml_ref_to_vty(struct vty *vty)
778{
779 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
780}
781
782static int print_func_stream(void *data, const char *format, ...)
783{
784 va_list args;
785 int rc;
786 va_start(args, format);
787 rc = vfprintf((FILE*)data, format, args);
788 va_end(args);
789 return rc;
790}
791
792/*! Print the XML reference of all VTY nodes to the given stream.
793 */
794int vty_dump_xml_ref(FILE *stream)
795{
796 return vty_dump_nodes(print_func_stream, stream, "\n");
797}
798
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200799/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100800static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
801{
802 int i;
803
804 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
805 struct cmd_element *elem;
806 elem = vector_slot(cnode->cmd_vector, i);
807 if (!elem->string)
808 continue;
809 if (!strcmp(elem->string, cmdstring))
810 return 1;
811 }
812 return 0;
813}
814
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200815/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200816 * \param[in] ntype Node Type
817 * \param[cmd] element to be installed
818 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000819void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200820{
821 struct cmd_node *cnode;
822
823 cnode = vector_slot(cmdvec, ntype);
824
Harald Weltea99d45a2015-11-12 13:48:23 +0100825 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100826 /* ensure no _identical_ command has been registered at this
827 * node so far */
828 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200829
830 vector_set(cnode->cmd_vector, cmd);
831
832 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
833 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
834}
835
836/* Install a command into VIEW and ENABLE node */
837void install_element_ve(struct cmd_element *cmd)
838{
839 install_element(VIEW_NODE, cmd);
840 install_element(ENABLE_NODE, cmd);
841}
842
843#ifdef VTY_CRYPT_PW
844static unsigned char itoa64[] =
845 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
846
847static void to64(char *s, long v, int n)
848{
849 while (--n >= 0) {
850 *s++ = itoa64[v & 0x3f];
851 v >>= 6;
852 }
853}
854
855static char *zencrypt(const char *passwd)
856{
857 char salt[6];
858 struct timeval tv;
859 char *crypt(const char *, const char *);
860
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200861 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200862
863 to64(&salt[0], random(), 3);
864 to64(&salt[3], tv.tv_usec, 3);
865 salt[5] = '\0';
866
867 return crypt(passwd, salt);
868}
869#endif
870
871/* This function write configuration of this host. */
872static int config_write_host(struct vty *vty)
873{
874 if (host.name)
875 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
876
877 if (host.encrypt) {
878 if (host.password_encrypt)
879 vty_out(vty, "password 8 %s%s", host.password_encrypt,
880 VTY_NEWLINE);
881 if (host.enable_encrypt)
882 vty_out(vty, "enable password 8 %s%s",
883 host.enable_encrypt, VTY_NEWLINE);
884 } else {
885 if (host.password)
886 vty_out(vty, "password %s%s", host.password,
887 VTY_NEWLINE);
888 if (host.enable)
889 vty_out(vty, "enable password %s%s", host.enable,
890 VTY_NEWLINE);
891 }
892
893 if (host.advanced)
894 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
895
896 if (host.encrypt)
897 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
898
899 if (host.lines >= 0)
900 vty_out(vty, "service terminal-length %d%s", host.lines,
901 VTY_NEWLINE);
902
903 if (host.motdfile)
904 vty_out(vty, "banner motd file %s%s", host.motdfile,
905 VTY_NEWLINE);
906 else if (!host.motd)
907 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
908
909 return 1;
910}
911
912/* Utility function for getting command vector. */
913static vector cmd_node_vector(vector v, enum node_type ntype)
914{
915 struct cmd_node *cnode = vector_slot(v, ntype);
916 return cnode->cmd_vector;
917}
918
919/* Completion match types. */
920enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200921 NO_MATCH = 0,
922 ANY_MATCH,
923 EXTEND_MATCH,
924 IPV4_PREFIX_MATCH,
925 IPV4_MATCH,
926 IPV6_PREFIX_MATCH,
927 IPV6_MATCH,
928 RANGE_MATCH,
929 VARARG_MATCH,
930 PARTLY_MATCH,
931 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200932};
933
934static enum match_type cmd_ipv4_match(const char *str)
935{
936 const char *sp;
937 int dots = 0, nums = 0;
938 char buf[4];
939
940 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200941 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200942
943 for (;;) {
944 memset(buf, 0, sizeof(buf));
945 sp = str;
946 while (*str != '\0') {
947 if (*str == '.') {
948 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200949 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200950
951 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200952 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200953
954 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200955 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200956
957 dots++;
958 break;
959 }
960 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200961 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200962
963 str++;
964 }
965
966 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200967 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200968
969 strncpy(buf, sp, str - sp);
970 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200971 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200972
973 nums++;
974
975 if (*str == '\0')
976 break;
977
978 str++;
979 }
980
981 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200982 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200983
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200984 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200985}
986
987static enum match_type cmd_ipv4_prefix_match(const char *str)
988{
989 const char *sp;
990 int dots = 0;
991 char buf[4];
992
993 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200994 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200995
996 for (;;) {
997 memset(buf, 0, sizeof(buf));
998 sp = str;
999 while (*str != '\0' && *str != '/') {
1000 if (*str == '.') {
1001 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001002 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001003
1004 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001005 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001006
1007 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001008 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001009
1010 dots++;
1011 break;
1012 }
1013
1014 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001015 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001016
1017 str++;
1018 }
1019
1020 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001021 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022
1023 strncpy(buf, sp, str - sp);
1024 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001025 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001026
1027 if (dots == 3) {
1028 if (*str == '/') {
1029 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001030 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001031
1032 str++;
1033 break;
1034 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001035 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001036 }
1037
1038 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001039 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001040
1041 str++;
1042 }
1043
1044 sp = str;
1045 while (*str != '\0') {
1046 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001047 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001048
1049 str++;
1050 }
1051
1052 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001053 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001054
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001055 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001056}
1057
1058#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1059#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1060#define STATE_START 1
1061#define STATE_COLON 2
1062#define STATE_DOUBLE 3
1063#define STATE_ADDR 4
1064#define STATE_DOT 5
1065#define STATE_SLASH 6
1066#define STATE_MASK 7
1067
1068#ifdef HAVE_IPV6
1069
1070static enum match_type cmd_ipv6_match(const char *str)
1071{
1072 int state = STATE_START;
1073 int colons = 0, nums = 0, double_colon = 0;
1074 const char *sp = NULL;
1075 struct sockaddr_in6 sin6_dummy;
1076 int ret;
1077
1078 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001079 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001080
1081 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001082 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001083
1084 /* use inet_pton that has a better support,
1085 * for example inet_pton can support the automatic addresses:
1086 * ::1.2.3.4
1087 */
1088 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1089
1090 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001091 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001092
1093 while (*str != '\0') {
1094 switch (state) {
1095 case STATE_START:
1096 if (*str == ':') {
1097 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001098 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001099 colons--;
1100 state = STATE_COLON;
1101 } else {
1102 sp = str;
1103 state = STATE_ADDR;
1104 }
1105
1106 continue;
1107 case STATE_COLON:
1108 colons++;
1109 if (*(str + 1) == ':')
1110 state = STATE_DOUBLE;
1111 else {
1112 sp = str + 1;
1113 state = STATE_ADDR;
1114 }
1115 break;
1116 case STATE_DOUBLE:
1117 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001118 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001119
1120 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001121 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001122 else {
1123 if (*(str + 1) != '\0')
1124 colons++;
1125 sp = str + 1;
1126 state = STATE_ADDR;
1127 }
1128
1129 double_colon++;
1130 nums++;
1131 break;
1132 case STATE_ADDR:
1133 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1134 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001135 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001136
1137 nums++;
1138 state = STATE_COLON;
1139 }
1140 if (*(str + 1) == '.')
1141 state = STATE_DOT;
1142 break;
1143 case STATE_DOT:
1144 state = STATE_ADDR;
1145 break;
1146 default:
1147 break;
1148 }
1149
1150 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001151 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001152
1153 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001154 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001155
1156 str++;
1157 }
1158
1159#if 0
1160 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001161 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001162#endif /* 0 */
1163
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001164 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001165}
1166
1167static enum match_type cmd_ipv6_prefix_match(const char *str)
1168{
1169 int state = STATE_START;
1170 int colons = 0, nums = 0, double_colon = 0;
1171 int mask;
1172 const char *sp = NULL;
1173 char *endptr = NULL;
1174
1175 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001176 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001177
1178 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001179 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001180
1181 while (*str != '\0' && state != STATE_MASK) {
1182 switch (state) {
1183 case STATE_START:
1184 if (*str == ':') {
1185 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001186 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001187 colons--;
1188 state = STATE_COLON;
1189 } else {
1190 sp = str;
1191 state = STATE_ADDR;
1192 }
1193
1194 continue;
1195 case STATE_COLON:
1196 colons++;
1197 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001198 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001199 else if (*(str + 1) == ':')
1200 state = STATE_DOUBLE;
1201 else {
1202 sp = str + 1;
1203 state = STATE_ADDR;
1204 }
1205 break;
1206 case STATE_DOUBLE:
1207 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001208 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001209
1210 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001211 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001212 else {
1213 if (*(str + 1) != '\0' && *(str + 1) != '/')
1214 colons++;
1215 sp = str + 1;
1216
1217 if (*(str + 1) == '/')
1218 state = STATE_SLASH;
1219 else
1220 state = STATE_ADDR;
1221 }
1222
1223 double_colon++;
1224 nums += 1;
1225 break;
1226 case STATE_ADDR:
1227 if (*(str + 1) == ':' || *(str + 1) == '.'
1228 || *(str + 1) == '\0' || *(str + 1) == '/') {
1229 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001230 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001231
1232 for (; sp <= str; sp++)
1233 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001234 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235
1236 nums++;
1237
1238 if (*(str + 1) == ':')
1239 state = STATE_COLON;
1240 else if (*(str + 1) == '.')
1241 state = STATE_DOT;
1242 else if (*(str + 1) == '/')
1243 state = STATE_SLASH;
1244 }
1245 break;
1246 case STATE_DOT:
1247 state = STATE_ADDR;
1248 break;
1249 case STATE_SLASH:
1250 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001251 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001252
1253 state = STATE_MASK;
1254 break;
1255 default:
1256 break;
1257 }
1258
1259 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001260 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001261
1262 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001263 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001264
1265 str++;
1266 }
1267
1268 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001269 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001270
1271 mask = strtol(str, &endptr, 10);
1272 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001273 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001274
1275 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001276 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001277
1278/* I don't know why mask < 13 makes command match partly.
1279 Forgive me to make this comments. I Want to set static default route
1280 because of lack of function to originate default in ospf6d; sorry
1281 yasu
1282 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001283 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001284*/
1285
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001286 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001287}
1288
1289#endif /* HAVE_IPV6 */
1290
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001291
1292#if ULONG_MAX == 18446744073709551615UL
1293#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1294#elif ULONG_MAX == 4294967295UL
1295#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1296#else
1297#error "ULONG_MAX not defined!"
1298#endif
1299
1300#if LONG_MAX == 9223372036854775807L
1301#define DECIMAL_STRLEN_MAX_SIGNED 19
1302#elif LONG_MAX == 2147483647L
1303#define DECIMAL_STRLEN_MAX_SIGNED 10
1304#else
1305#error "LONG_MAX not defined!"
1306#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001307
1308static int cmd_range_match(const char *range, const char *str)
1309{
1310 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001311 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001312 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001313
1314 if (str == NULL)
1315 return 1;
1316
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001317 if (range[1] == '-') {
1318 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001319
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001320 val = strtol(str, &endptr, 10);
1321 if (*endptr != '\0')
1322 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001323
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001324 range += 2;
1325 p = strchr(range, '-');
1326 if (p == NULL)
1327 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001328 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001329 return 0;
1330 strncpy(buf, range, p - range);
1331 buf[p - range] = '\0';
1332 min = -strtol(buf, &endptr, 10);
1333 if (*endptr != '\0')
1334 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001335
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001336 range = p + 1;
1337 p = strchr(range, '>');
1338 if (p == NULL)
1339 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001340 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001341 return 0;
1342 strncpy(buf, range, p - range);
1343 buf[p - range] = '\0';
1344 max = strtol(buf, &endptr, 10);
1345 if (*endptr != '\0')
1346 return 0;
1347
1348 if (val < min || val > max)
1349 return 0;
1350 } else {
1351 unsigned long min, max, val;
1352
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001353 if (str[0] == '-')
1354 return 0;
1355
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001356 val = strtoul(str, &endptr, 10);
1357 if (*endptr != '\0')
1358 return 0;
1359
1360 range++;
1361 p = strchr(range, '-');
1362 if (p == NULL)
1363 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001364 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001365 return 0;
1366 strncpy(buf, range, p - range);
1367 buf[p - range] = '\0';
1368 min = strtoul(buf, &endptr, 10);
1369 if (*endptr != '\0')
1370 return 0;
1371
1372 range = p + 1;
1373 p = strchr(range, '>');
1374 if (p == NULL)
1375 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001376 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001377 return 0;
1378 strncpy(buf, range, p - range);
1379 buf[p - range] = '\0';
1380 max = strtoul(buf, &endptr, 10);
1381 if (*endptr != '\0')
1382 return 0;
1383
1384 if (val < min || val > max)
1385 return 0;
1386 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001387
1388 return 1;
1389}
1390
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001391/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001392static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001393{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001394 /* we've got "[blah]". We want to strip off the []s and redo the
1395 * match check for "blah"
1396 */
1397 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001398
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001399 if (len < 3)
1400 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001401
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001402 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001403}
1404
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001405static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001406cmd_match(const char *str, const char *command,
1407 enum match_type min, bool recur)
1408{
1409
1410 if (recur && CMD_OPTION(str))
1411 {
1412 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001413 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001414
1415 /* this would be a bug in a command, however handle it gracefully
1416 * as it we only discover it if a user tries to run it
1417 */
1418 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001419 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001420
1421 ret = cmd_match(tmp, command, min, false);
1422
1423 talloc_free(tmp);
1424
1425 return ret;
1426 }
1427 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001428 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001429 else if (CMD_RANGE(str))
1430 {
1431 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001432 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001433 }
1434#ifdef HAVE_IPV6
1435 else if (CMD_IPV6(str))
1436 {
1437 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001438 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001439 }
1440 else if (CMD_IPV6_PREFIX(str))
1441 {
1442 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001443 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001444 }
1445#endif /* HAVE_IPV6 */
1446 else if (CMD_IPV4(str))
1447 {
1448 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001449 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001450 }
1451 else if (CMD_IPV4_PREFIX(str))
1452 {
1453 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001454 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001455 }
1456 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001457 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001458 else if (strncmp(command, str, strlen(command)) == 0)
1459 {
1460 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001461 return EXACT_MATCH;
1462 else if (PARTLY_MATCH >= min)
1463 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001464 }
1465
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001466 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001467}
1468
1469/* Filter vector at the specified index and by the given command string, to
1470 * the desired matching level (thus allowing part matches), and return match
1471 * type flag.
1472 */
1473static enum match_type
1474cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001475{
1476 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001477 struct cmd_element *cmd_element;
1478 enum match_type match_type;
1479 vector descvec;
1480 struct desc *desc;
1481
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001482 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001483
1484 /* If command and cmd_element string does not match set NULL to vector */
1485 for (i = 0; i < vector_active(v); i++)
1486 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487 if (index >= vector_active(cmd_element->strvec))
1488 vector_slot(v, i) = NULL;
1489 else {
1490 unsigned int j;
1491 int matched = 0;
1492
1493 descvec =
1494 vector_slot(cmd_element->strvec, index);
1495
1496 for (j = 0; j < vector_active(descvec); j++)
1497 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001498 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001499
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001500 ret = cmd_match (desc->cmd, command, level, true);
1501
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001502 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001503 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001504
1505 if (match_type < ret)
1506 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001507 }
1508 if (!matched)
1509 vector_slot(v, i) = NULL;
1510 }
1511 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001512
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001513 if (match_type == NO_MATCH)
1514 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001515
1516 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1517 * go again and filter out commands whose argument (at this index) is
1518 * 'weaker'. E.g., if we have 2 commands:
1519 *
1520 * foo bar <1-255>
1521 * foo bar BLAH
1522 *
1523 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001524 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001525 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1526 *
1527 * If we don't do a 2nd pass and filter it out, the higher-layers will
1528 * consider this to be ambiguous.
1529 */
1530 for (i = 0; i < vector_active(v); i++)
1531 if ((cmd_element = vector_slot(v, i)) != NULL) {
1532 if (index >= vector_active(cmd_element->strvec))
1533 vector_slot(v, i) = NULL;
1534 else {
1535 unsigned int j;
1536 int matched = 0;
1537
1538 descvec =
1539 vector_slot(cmd_element->strvec, index);
1540
1541 for (j = 0; j < vector_active(descvec); j++)
1542 if ((desc = vector_slot(descvec, j))) {
1543 enum match_type ret;
1544
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001545 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001546
1547 if (ret >= match_type)
1548 matched++;
1549 }
1550 if (!matched)
1551 vector_slot(v, i) = NULL;
1552 }
1553 }
1554
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001555 return match_type;
1556}
1557
1558/* Check ambiguous match */
1559static int
1560is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1561{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001562 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001563 unsigned int i;
1564 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001565 struct cmd_element *cmd_element;
1566 const char *matched = NULL;
1567 vector descvec;
1568 struct desc *desc;
1569
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001570 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1571 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1572 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1573 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1574 * that case, the string must remain allocated until this function exits or another match comes
1575 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1576 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1577 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1578 void *cmd_deopt_ctx = NULL;
1579
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001580 for (i = 0; i < vector_active(v); i++) {
1581 cmd_element = vector_slot(v, i);
1582 if (!cmd_element)
1583 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001584
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001585 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001586
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001587 descvec = vector_slot(cmd_element->strvec, index);
1588
1589 for (j = 0; j < vector_active(descvec); j++) {
1590 desc = vector_slot(descvec, j);
1591 if (!desc)
1592 continue;
1593
1594 enum match_type mtype;
1595 const char *str = desc->cmd;
1596
1597 if (CMD_OPTION(str)) {
1598 if (!cmd_deopt_ctx)
1599 cmd_deopt_ctx =
1600 talloc_named_const(tall_vty_cmd_ctx, 0,
1601 __func__);
1602 str = cmd_deopt(cmd_deopt_ctx, str);
1603 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001604 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001605 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001606
1607 switch (type) {
1608 case EXACT_MATCH:
1609 if (!(CMD_VARIABLE (str))
1610 && strcmp(command, str) == 0)
1611 match++;
1612 break;
1613 case PARTLY_MATCH:
1614 if (!(CMD_VARIABLE (str))
1615 && strncmp(command, str, strlen (command)) == 0)
1616 {
1617 if (matched
1618 && strcmp(matched,
1619 str) != 0) {
1620 ret = 1; /* There is ambiguous match. */
1621 goto free_and_return;
1622 } else
1623 matched = str;
1624 match++;
1625 }
1626 break;
1627 case RANGE_MATCH:
1628 if (cmd_range_match
1629 (str, command)) {
1630 if (matched
1631 && strcmp(matched,
1632 str) != 0) {
1633 ret = 1;
1634 goto free_and_return;
1635 } else
1636 matched = str;
1637 match++;
1638 }
1639 break;
1640#ifdef HAVE_IPV6
1641 case IPV6_MATCH:
1642 if (CMD_IPV6(str))
1643 match++;
1644 break;
1645 case IPV6_PREFIX_MATCH:
1646 if ((mtype =
1647 cmd_ipv6_prefix_match
1648 (command)) != NO_MATCH) {
1649 if (mtype == PARTLY_MATCH) {
1650 ret = 2; /* There is incomplete match. */
1651 goto free_and_return;
1652 }
1653
1654 match++;
1655 }
1656 break;
1657#endif /* HAVE_IPV6 */
1658 case IPV4_MATCH:
1659 if (CMD_IPV4(str))
1660 match++;
1661 break;
1662 case IPV4_PREFIX_MATCH:
1663 if ((mtype =
1664 cmd_ipv4_prefix_match
1665 (command)) != NO_MATCH) {
1666 if (mtype == PARTLY_MATCH) {
1667 ret = 2; /* There is incomplete match. */
1668 goto free_and_return;
1669 }
1670
1671 match++;
1672 }
1673 break;
1674 case EXTEND_MATCH:
1675 if (CMD_VARIABLE (str))
1676 match++;
1677 break;
1678 case NO_MATCH:
1679 default:
1680 break;
1681 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001682 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001683 if (!match)
1684 vector_slot(v, i) = NULL;
1685 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001686
1687free_and_return:
1688 if (cmd_deopt_ctx)
1689 talloc_free(cmd_deopt_ctx);
1690 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001691}
1692
1693/* If src matches dst return dst string, otherwise return NULL */
1694static const char *cmd_entry_function(const char *src, const char *dst)
1695{
1696 /* Skip variable arguments. */
1697 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1698 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1699 return NULL;
1700
1701 /* In case of 'command \t', given src is NULL string. */
1702 if (src == NULL)
1703 return dst;
1704
1705 /* Matched with input string. */
1706 if (strncmp(src, dst, strlen(src)) == 0)
1707 return dst;
1708
1709 return NULL;
1710}
1711
1712/* If src matches dst return dst string, otherwise return NULL */
1713/* This version will return the dst string always if it is
1714 CMD_VARIABLE for '?' key processing */
1715static const char *cmd_entry_function_desc(const char *src, const char *dst)
1716{
1717 if (CMD_VARARG(dst))
1718 return dst;
1719
1720 if (CMD_RANGE(dst)) {
1721 if (cmd_range_match(dst, src))
1722 return dst;
1723 else
1724 return NULL;
1725 }
1726#ifdef HAVE_IPV6
1727 if (CMD_IPV6(dst)) {
1728 if (cmd_ipv6_match(src))
1729 return dst;
1730 else
1731 return NULL;
1732 }
1733
1734 if (CMD_IPV6_PREFIX(dst)) {
1735 if (cmd_ipv6_prefix_match(src))
1736 return dst;
1737 else
1738 return NULL;
1739 }
1740#endif /* HAVE_IPV6 */
1741
1742 if (CMD_IPV4(dst)) {
1743 if (cmd_ipv4_match(src))
1744 return dst;
1745 else
1746 return NULL;
1747 }
1748
1749 if (CMD_IPV4_PREFIX(dst)) {
1750 if (cmd_ipv4_prefix_match(src))
1751 return dst;
1752 else
1753 return NULL;
1754 }
1755
1756 /* Optional or variable commands always match on '?' */
1757 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1758 return dst;
1759
1760 /* In case of 'command \t', given src is NULL string. */
1761 if (src == NULL)
1762 return dst;
1763
1764 if (strncmp(src, dst, strlen(src)) == 0)
1765 return dst;
1766 else
1767 return NULL;
1768}
1769
1770/* Check same string element existence. If it isn't there return
1771 1. */
1772static int cmd_unique_string(vector v, const char *str)
1773{
1774 unsigned int i;
1775 char *match;
1776
1777 for (i = 0; i < vector_active(v); i++)
1778 if ((match = vector_slot(v, i)) != NULL)
1779 if (strcmp(match, str) == 0)
1780 return 0;
1781 return 1;
1782}
1783
1784/* Compare string to description vector. If there is same string
1785 return 1 else return 0. */
1786static int desc_unique_string(vector v, const char *str)
1787{
1788 unsigned int i;
1789 struct desc *desc;
1790
1791 for (i = 0; i < vector_active(v); i++)
1792 if ((desc = vector_slot(v, i)) != NULL)
1793 if (strcmp(desc->cmd, str) == 0)
1794 return 1;
1795 return 0;
1796}
1797
1798static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1799{
1800 if (first_word != NULL &&
1801 node != AUTH_NODE &&
1802 node != VIEW_NODE &&
1803 node != AUTH_ENABLE_NODE &&
1804 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1805 return 1;
1806 return 0;
1807}
1808
1809/* '?' describe command support. */
1810static vector
1811cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1812{
1813 unsigned int i;
1814 vector cmd_vector;
1815#define INIT_MATCHVEC_SIZE 10
1816 vector matchvec;
1817 struct cmd_element *cmd_element;
1818 unsigned int index;
1819 int ret;
1820 enum match_type match;
1821 char *command;
1822 static struct desc desc_cr = { "<cr>", "" };
1823
1824 /* Set index. */
1825 if (vector_active(vline) == 0) {
1826 *status = CMD_ERR_NO_MATCH;
1827 return NULL;
1828 } else
1829 index = vector_active(vline) - 1;
1830
1831 /* Make copy vector of current node's command vector. */
1832 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1833
1834 /* Prepare match vector */
1835 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1836
1837 /* Filter commands. */
1838 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001839 for (i = 0; i < index; i++) {
1840 command = vector_slot(vline, i);
1841 if (!command)
1842 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001843
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001844 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001845
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001846 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001847 struct cmd_element *cmd_element;
1848 vector descvec;
1849 unsigned int j, k;
1850
1851 for (j = 0; j < vector_active(cmd_vector); j++)
1852 if ((cmd_element =
1853 vector_slot(cmd_vector, j)) != NULL
1854 &&
1855 (vector_active(cmd_element->strvec))) {
1856 descvec =
1857 vector_slot(cmd_element->
1858 strvec,
1859 vector_active
1860 (cmd_element->
1861 strvec) - 1);
1862 for (k = 0;
1863 k < vector_active(descvec);
1864 k++) {
1865 struct desc *desc =
1866 vector_slot(descvec,
1867 k);
1868 vector_set(matchvec,
1869 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001870 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001871 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001872
Harald Welte80d30fe2013-02-12 11:08:57 +01001873 vector_set(matchvec, &desc_cr);
1874 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001875
Harald Welte80d30fe2013-02-12 11:08:57 +01001876 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001877 }
1878
Harald Welte80d30fe2013-02-12 11:08:57 +01001879 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1880 match)) == 1) {
1881 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001882 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001883 *status = CMD_ERR_AMBIGUOUS;
1884 return NULL;
1885 } else if (ret == 2) {
1886 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001887 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001888 *status = CMD_ERR_NO_MATCH;
1889 return NULL;
1890 }
1891 }
1892
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001893 /* Prepare match vector */
1894 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1895
1896 /* Make sure that cmd_vector is filtered based on current word */
1897 command = vector_slot(vline, index);
1898 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001899 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001900
1901 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001902 for (i = 0; i < vector_active(cmd_vector); i++) {
1903 const char *string = NULL;
1904 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001905
Harald Welte80d30fe2013-02-12 11:08:57 +01001906 cmd_element = vector_slot(cmd_vector, i);
1907 if (!cmd_element)
1908 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001909
Harald Welted17aa592013-02-12 11:11:34 +01001910 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1911 continue;
1912
Harald Welte80d30fe2013-02-12 11:08:57 +01001913 strvec = cmd_element->strvec;
1914
1915 /* if command is NULL, index may be equal to vector_active */
1916 if (command && index >= vector_active(strvec))
1917 vector_slot(cmd_vector, i) = NULL;
1918 else {
1919 /* Check if command is completed. */
1920 if (command == NULL
1921 && index == vector_active(strvec)) {
1922 string = "<cr>";
1923 if (!desc_unique_string(matchvec, string))
1924 vector_set(matchvec, &desc_cr);
1925 } else {
1926 unsigned int j;
1927 vector descvec = vector_slot(strvec, index);
1928 struct desc *desc;
1929
1930 for (j = 0; j < vector_active(descvec); j++) {
1931 desc = vector_slot(descvec, j);
1932 if (!desc)
1933 continue;
1934 string = cmd_entry_function_desc
1935 (command, desc->cmd);
1936 if (!string)
1937 continue;
1938 /* Uniqueness check */
1939 if (!desc_unique_string(matchvec, string))
1940 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001941 }
1942 }
1943 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001944 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001945 vector_free(cmd_vector);
1946
1947 if (vector_slot(matchvec, 0) == NULL) {
1948 vector_free(matchvec);
1949 *status = CMD_ERR_NO_MATCH;
1950 } else
1951 *status = CMD_SUCCESS;
1952
1953 return matchvec;
1954}
1955
1956vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1957{
1958 vector ret;
1959
1960 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1961 enum node_type onode;
1962 vector shifted_vline;
1963 unsigned int index;
1964
1965 onode = vty->node;
1966 vty->node = ENABLE_NODE;
1967 /* We can try it on enable node, cos' the vty is authenticated */
1968
1969 shifted_vline = vector_init(vector_count(vline));
1970 /* use memcpy? */
1971 for (index = 1; index < vector_active(vline); index++) {
1972 vector_set_index(shifted_vline, index - 1,
1973 vector_lookup(vline, index));
1974 }
1975
1976 ret = cmd_describe_command_real(shifted_vline, vty, status);
1977
1978 vector_free(shifted_vline);
1979 vty->node = onode;
1980 return ret;
1981 }
1982
1983 return cmd_describe_command_real(vline, vty, status);
1984}
1985
1986/* Check LCD of matched command. */
1987static int cmd_lcd(char **matched)
1988{
1989 int i;
1990 int j;
1991 int lcd = -1;
1992 char *s1, *s2;
1993 char c1, c2;
1994
1995 if (matched[0] == NULL || matched[1] == NULL)
1996 return 0;
1997
1998 for (i = 1; matched[i] != NULL; i++) {
1999 s1 = matched[i - 1];
2000 s2 = matched[i];
2001
2002 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2003 if (c1 != c2)
2004 break;
2005
2006 if (lcd < 0)
2007 lcd = j;
2008 else {
2009 if (lcd > j)
2010 lcd = j;
2011 }
2012 }
2013 return lcd;
2014}
2015
2016/* Command line completion support. */
2017static char **cmd_complete_command_real(vector vline, struct vty *vty,
2018 int *status)
2019{
2020 unsigned int i;
2021 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2022#define INIT_MATCHVEC_SIZE 10
2023 vector matchvec;
2024 struct cmd_element *cmd_element;
2025 unsigned int index;
2026 char **match_str;
2027 struct desc *desc;
2028 vector descvec;
2029 char *command;
2030 int lcd;
2031
2032 if (vector_active(vline) == 0) {
2033 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002034 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002035 return NULL;
2036 } else
2037 index = vector_active(vline) - 1;
2038
2039 /* First, filter by preceeding command string */
2040 for (i = 0; i < index; i++)
2041 if ((command = vector_slot(vline, i))) {
2042 enum match_type match;
2043 int ret;
2044
2045 /* First try completion match, if there is exactly match return 1 */
2046 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002047 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002048
2049 /* If there is exact match then filter ambiguous match else check
2050 ambiguousness. */
2051 if ((ret =
2052 is_cmd_ambiguous(command, cmd_vector, i,
2053 match)) == 1) {
2054 vector_free(cmd_vector);
2055 *status = CMD_ERR_AMBIGUOUS;
2056 return NULL;
2057 }
2058 /*
2059 else if (ret == 2)
2060 {
2061 vector_free (cmd_vector);
2062 *status = CMD_ERR_NO_MATCH;
2063 return NULL;
2064 }
2065 */
2066 }
2067
2068 /* Prepare match vector. */
2069 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2070
2071 /* Now we got into completion */
2072 for (i = 0; i < vector_active(cmd_vector); i++)
2073 if ((cmd_element = vector_slot(cmd_vector, i))) {
2074 const char *string;
2075 vector strvec = cmd_element->strvec;
2076
2077 /* Check field length */
2078 if (index >= vector_active(strvec))
2079 vector_slot(cmd_vector, i) = NULL;
2080 else {
2081 unsigned int j;
2082
2083 descvec = vector_slot(strvec, index);
2084 for (j = 0; j < vector_active(descvec); j++)
2085 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002086 const char *cmd = desc->cmd;
2087 char *tmp = NULL;
2088
2089 if (CMD_OPTION(desc->cmd)) {
2090 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2091 cmd = tmp;
2092 }
2093 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002094 if (cmd_unique_string (matchvec, string))
2095 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002096 if (tmp)
2097 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002098 }
2099 }
2100 }
2101
2102 /* We don't need cmd_vector any more. */
2103 vector_free(cmd_vector);
2104
2105 /* No matched command */
2106 if (vector_slot(matchvec, 0) == NULL) {
2107 vector_free(matchvec);
2108
2109 /* In case of 'command \t' pattern. Do you need '?' command at
2110 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002111 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002112 *status = CMD_ERR_NOTHING_TODO;
2113 else
2114 *status = CMD_ERR_NO_MATCH;
2115 return NULL;
2116 }
2117
2118 /* Only one matched */
2119 if (vector_slot(matchvec, 1) == NULL) {
2120 match_str = (char **)matchvec->index;
2121 vector_only_wrapper_free(matchvec);
2122 *status = CMD_COMPLETE_FULL_MATCH;
2123 return match_str;
2124 }
2125 /* Make it sure last element is NULL. */
2126 vector_set(matchvec, NULL);
2127
2128 /* Check LCD of matched strings. */
2129 if (vector_slot(vline, index) != NULL) {
2130 lcd = cmd_lcd((char **)matchvec->index);
2131
2132 if (lcd) {
2133 int len = strlen(vector_slot(vline, index));
2134
2135 if (len < lcd) {
2136 char *lcdstr;
2137
2138 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2139 "complete-lcdstr");
2140 memcpy(lcdstr, matchvec->index[0], lcd);
2141 lcdstr[lcd] = '\0';
2142
2143 /* match_str = (char **) &lcdstr; */
2144
2145 /* Free matchvec. */
2146 for (i = 0; i < vector_active(matchvec); i++) {
2147 if (vector_slot(matchvec, i))
2148 talloc_free(vector_slot(matchvec, i));
2149 }
2150 vector_free(matchvec);
2151
2152 /* Make new matchvec. */
2153 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2154 vector_set(matchvec, lcdstr);
2155 match_str = (char **)matchvec->index;
2156 vector_only_wrapper_free(matchvec);
2157
2158 *status = CMD_COMPLETE_MATCH;
2159 return match_str;
2160 }
2161 }
2162 }
2163
2164 match_str = (char **)matchvec->index;
2165 vector_only_wrapper_free(matchvec);
2166 *status = CMD_COMPLETE_LIST_MATCH;
2167 return match_str;
2168}
2169
2170char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2171{
2172 char **ret;
2173
2174 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2175 enum node_type onode;
2176 vector shifted_vline;
2177 unsigned int index;
2178
2179 onode = vty->node;
2180 vty->node = ENABLE_NODE;
2181 /* We can try it on enable node, cos' the vty is authenticated */
2182
2183 shifted_vline = vector_init(vector_count(vline));
2184 /* use memcpy? */
2185 for (index = 1; index < vector_active(vline); index++) {
2186 vector_set_index(shifted_vline, index - 1,
2187 vector_lookup(vline, index));
2188 }
2189
2190 ret = cmd_complete_command_real(shifted_vline, vty, status);
2191
2192 vector_free(shifted_vline);
2193 vty->node = onode;
2194 return ret;
2195 }
2196
2197 return cmd_complete_command_real(vline, vty, status);
2198}
2199
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002200static struct vty_parent_node *vty_parent(struct vty *vty)
2201{
2202 return llist_first_entry_or_null(&vty->parent_nodes,
2203 struct vty_parent_node,
2204 entry);
2205}
2206
2207static bool vty_pop_parent(struct vty *vty)
2208{
2209 struct vty_parent_node *parent = vty_parent(vty);
2210 if (!parent)
2211 return false;
2212 llist_del(&parent->entry);
2213 vty->node = parent->node;
2214 vty->priv = parent->priv;
2215 if (vty->indent)
2216 talloc_free(vty->indent);
2217 vty->indent = parent->indent;
2218 talloc_free(parent);
2219 return true;
2220}
2221
2222static void vty_clear_parents(struct vty *vty)
2223{
2224 while (vty_pop_parent(vty));
2225}
2226
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002227/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002228/*
2229 * This function MUST eventually converge on a node when called repeatedly,
2230 * there must not be any cycles.
2231 * All 'config' nodes shall converge on CONFIG_NODE.
2232 * All other 'enable' nodes shall converge on ENABLE_NODE.
2233 * All 'view' only nodes shall converge on VIEW_NODE.
2234 * All other nodes shall converge on themselves or it must be ensured,
2235 * that the user's rights are not extended anyhow by calling this function.
2236 *
2237 * Note that these requirements also apply to all functions that are used
2238 * as go_parent_cb.
2239 * Note also that this function relies on the is_config_child callback to
2240 * recognize non-config nodes if go_parent_cb is not set.
2241 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002242int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002243{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002244 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002245 case AUTH_NODE:
2246 case VIEW_NODE:
2247 case ENABLE_NODE:
2248 case CONFIG_NODE:
2249 vty_clear_parents(vty);
2250 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002251
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002252 case AUTH_ENABLE_NODE:
2253 vty->node = VIEW_NODE;
2254 vty_clear_parents(vty);
2255 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002256
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002257 default:
2258 if (host.app_info->go_parent_cb)
2259 host.app_info->go_parent_cb(vty);
2260 vty_pop_parent(vty);
2261 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002262 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002263
2264 return vty->node;
2265}
2266
2267/* Execute command by argument vline vector. */
2268static int
2269cmd_execute_command_real(vector vline, struct vty *vty,
2270 struct cmd_element **cmd)
2271{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002272 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002273 unsigned int index;
2274 vector cmd_vector;
2275 struct cmd_element *cmd_element;
2276 struct cmd_element *matched_element;
2277 unsigned int matched_count, incomplete_count;
2278 int argc;
2279 const char *argv[CMD_ARGC_MAX];
2280 enum match_type match = 0;
2281 int varflag;
2282 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002283 int rc;
2284 /* Used for temporary storage of cmd_deopt() allocated arguments during
2285 argv[] generation */
2286 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002287
2288 /* Make copy of command elements. */
2289 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2290
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002291 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002292 if ((command = vector_slot(vline, index))) {
2293 int ret;
2294
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002295 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002296 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002297
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002298 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002299 break;
2300
2301 ret =
2302 is_cmd_ambiguous(command, cmd_vector, index, match);
2303
2304 if (ret == 1) {
2305 vector_free(cmd_vector);
2306 return CMD_ERR_AMBIGUOUS;
2307 } else if (ret == 2) {
2308 vector_free(cmd_vector);
2309 return CMD_ERR_NO_MATCH;
2310 }
2311 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002312 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002313
2314 /* Check matched count. */
2315 matched_element = NULL;
2316 matched_count = 0;
2317 incomplete_count = 0;
2318
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002319 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002320 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002321 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002322 || index >= cmd_element->cmdsize) {
2323 matched_element = cmd_element;
2324#if 0
2325 printf("DEBUG: %s\n", cmd_element->string);
2326#endif
2327 matched_count++;
2328 } else {
2329 incomplete_count++;
2330 }
2331 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002332 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002333
2334 /* Finish of using cmd_vector. */
2335 vector_free(cmd_vector);
2336
2337 /* To execute command, matched_count must be 1. */
2338 if (matched_count == 0) {
2339 if (incomplete_count)
2340 return CMD_ERR_INCOMPLETE;
2341 else
2342 return CMD_ERR_NO_MATCH;
2343 }
2344
2345 if (matched_count > 1)
2346 return CMD_ERR_AMBIGUOUS;
2347
2348 /* Argument treatment */
2349 varflag = 0;
2350 argc = 0;
2351
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002352 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2353
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002354 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002355 if (argc == CMD_ARGC_MAX) {
2356 rc = CMD_ERR_EXEED_ARGC_MAX;
2357 goto rc_free_deopt_ctx;
2358 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002359 if (varflag) {
2360 argv[argc++] = vector_slot(vline, i);
2361 continue;
2362 }
2363
2364 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002365 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002366
2367 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002368 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002369 struct desc *desc = vector_slot(descvec, 0);
2370
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002371 if (CMD_OPTION(desc->cmd)) {
2372 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2373 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2374 } else {
2375 tmp_cmd = desc->cmd;
2376 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002377
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002378 if (CMD_VARARG(tmp_cmd))
2379 varflag = 1;
2380 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002381 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002382 else if (CMD_OPTION(desc->cmd))
2383 argv[argc++] = tmp_cmd;
2384 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002385 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002386 /* multi choice argument. look up which choice
2387 the user meant (can only be one after
2388 filtering and checking for ambigous). For instance,
2389 if user typed "th" for "(two|three)" arg, we
2390 want to pass "three" in argv[]. */
2391 for (j = 0; j < vector_active(descvec); j++) {
2392 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002393 if (!desc)
2394 continue;
2395 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2396 continue;
2397 if (CMD_OPTION(desc->cmd)) {
2398 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2399 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2400 } else {
2401 tmp_cmd = desc->cmd;
2402 }
2403
2404 if(CMD_VARIABLE(tmp_cmd)) {
2405 argv[argc++] = vector_slot(vline, i);
2406 } else {
2407 argv[argc++] = tmp_cmd;
2408 }
2409 break;
2410 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002411 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002412 }
2413
2414 /* For vtysh execution. */
2415 if (cmd)
2416 *cmd = matched_element;
2417
2418 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002419 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002420 else {
2421 /* Execute matched command. */
2422 struct vty_parent_node this_node = {
2423 .node = vty->node,
2424 .priv = vty->priv,
2425 .indent = vty->indent,
2426 };
2427 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002428 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002429
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002430 /* If we have stepped down into a child node, push a parent frame.
2431 * The causality is such: we don't expect every single node entry implementation to push
2432 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2433 * a parent node. Hence if the node changed without the parent node changing, we must
2434 * have stepped into a child node. */
2435 if (vty->node != this_node.node && parent == vty_parent(vty)
2436 && vty->node > CONFIG_NODE) {
2437 /* Push the parent node. */
2438 parent = talloc_zero(vty, struct vty_parent_node);
2439 *parent = this_node;
2440 llist_add(&parent->entry, &vty->parent_nodes);
2441 }
2442 }
2443
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002444rc_free_deopt_ctx:
2445 /* Now after we called the command func, we can free temporary strings */
2446 talloc_free(cmd_deopt_ctx);
2447 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002448}
2449
2450int
2451cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2452 int vtysh)
2453{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002454 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002455 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002456
2457 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002458
2459 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2460 vector shifted_vline;
2461 unsigned int index;
2462
2463 vty->node = ENABLE_NODE;
2464 /* We can try it on enable node, cos' the vty is authenticated */
2465
2466 shifted_vline = vector_init(vector_count(vline));
2467 /* use memcpy? */
2468 for (index = 1; index < vector_active(vline); index++) {
2469 vector_set_index(shifted_vline, index - 1,
2470 vector_lookup(vline, index));
2471 }
2472
2473 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2474
2475 vector_free(shifted_vline);
2476 vty->node = onode;
2477 return ret;
2478 }
2479
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002480 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002481}
2482
2483/* Execute command by argument readline. */
2484int
2485cmd_execute_command_strict(vector vline, struct vty *vty,
2486 struct cmd_element **cmd)
2487{
2488 unsigned int i;
2489 unsigned int index;
2490 vector cmd_vector;
2491 struct cmd_element *cmd_element;
2492 struct cmd_element *matched_element;
2493 unsigned int matched_count, incomplete_count;
2494 int argc;
2495 const char *argv[CMD_ARGC_MAX];
2496 int varflag;
2497 enum match_type match = 0;
2498 char *command;
2499
2500 /* Make copy of command element */
2501 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2502
2503 for (index = 0; index < vector_active(vline); index++)
2504 if ((command = vector_slot(vline, index))) {
2505 int ret;
2506
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002507 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002508 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002509
2510 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002511 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002512 break;
2513
2514 ret =
2515 is_cmd_ambiguous(command, cmd_vector, index, match);
2516 if (ret == 1) {
2517 vector_free(cmd_vector);
2518 return CMD_ERR_AMBIGUOUS;
2519 }
2520 if (ret == 2) {
2521 vector_free(cmd_vector);
2522 return CMD_ERR_NO_MATCH;
2523 }
2524 }
2525
2526 /* Check matched count. */
2527 matched_element = NULL;
2528 matched_count = 0;
2529 incomplete_count = 0;
2530 for (i = 0; i < vector_active(cmd_vector); i++)
2531 if (vector_slot(cmd_vector, i) != NULL) {
2532 cmd_element = vector_slot(cmd_vector, i);
2533
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002534 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002535 || index >= cmd_element->cmdsize) {
2536 matched_element = cmd_element;
2537 matched_count++;
2538 } else
2539 incomplete_count++;
2540 }
2541
2542 /* Finish of using cmd_vector. */
2543 vector_free(cmd_vector);
2544
2545 /* To execute command, matched_count must be 1. */
2546 if (matched_count == 0) {
2547 if (incomplete_count)
2548 return CMD_ERR_INCOMPLETE;
2549 else
2550 return CMD_ERR_NO_MATCH;
2551 }
2552
2553 if (matched_count > 1)
2554 return CMD_ERR_AMBIGUOUS;
2555
2556 /* Argument treatment */
2557 varflag = 0;
2558 argc = 0;
2559
2560 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002561 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002562 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002563 if (varflag) {
2564 argv[argc++] = vector_slot(vline, i);
2565 continue;
2566 }
2567
2568 vector descvec = vector_slot(matched_element->strvec, i);
2569
2570 if (vector_active(descvec) == 1) {
2571 struct desc *desc = vector_slot(descvec, 0);
2572
2573 if (CMD_VARARG(desc->cmd))
2574 varflag = 1;
2575
2576 if (varflag || CMD_VARIABLE(desc->cmd)
2577 || CMD_OPTION(desc->cmd))
2578 argv[argc++] = vector_slot(vline, i);
2579 } else {
2580 argv[argc++] = vector_slot(vline, i);
2581 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002582 }
2583
2584 /* For vtysh execution. */
2585 if (cmd)
2586 *cmd = matched_element;
2587
2588 if (matched_element->daemon)
2589 return CMD_SUCCESS_DAEMON;
2590
2591 /* Now execute matched command */
2592 return (*matched_element->func) (matched_element, vty, argc, argv);
2593}
2594
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002595static inline size_t len(const char *str)
2596{
2597 return str? strlen(str) : 0;
2598}
2599
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002600/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2601 * is longer than b, a must start with exactly b, and vice versa.
2602 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2603 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002604static int indent_cmp(const char *a, const char *b)
2605{
2606 size_t al, bl;
2607 al = len(a);
2608 bl = len(b);
2609 if (al > bl) {
2610 if (bl && strncmp(a, b, bl) != 0)
2611 return EINVAL;
2612 return 1;
2613 }
2614 /* al <= bl */
2615 if (al && strncmp(a, b, al) != 0)
2616 return EINVAL;
2617 return (al < bl)? -1 : 0;
2618}
2619
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002620/* Configration make from file. */
2621int config_from_file(struct vty *vty, FILE * fp)
2622{
2623 int ret;
2624 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002625 char *indent;
2626 int cmp;
2627 struct vty_parent_node this_node;
2628 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002629
2630 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002631 indent = NULL;
2632 vline = NULL;
2633 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002634
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002635 if (ret != CMD_SUCCESS)
2636 goto return_invalid_indent;
2637
2638 /* In case of comment or empty line */
2639 if (vline == NULL) {
2640 if (indent) {
2641 talloc_free(indent);
2642 indent = NULL;
2643 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002644 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002645 }
2646
Neels Hofmeyr43063632017-09-19 23:54:01 +02002647 /* We have a nonempty line. */
2648 if (!vty->indent) {
2649 /* We have just entered a node and expecting the first child to come up; but we
2650 * may also skip right back to a parent or ancestor level. */
2651 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002652
Neels Hofmeyr43063632017-09-19 23:54:01 +02002653 /* If there is no parent, record any indentation we encounter. */
2654 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2655
2656 if (cmp == EINVAL)
2657 goto return_invalid_indent;
2658
2659 if (cmp <= 0) {
2660 /* We have gone right back to the parent level or higher, we are skipping
2661 * this child node level entirely. Pop the parent to go back to a node
2662 * that was actually there (to reinstate vty->indent) and re-use below
2663 * go-parent while-loop to find an accurate match of indent in the node
2664 * ancestry. */
2665 vty_go_parent(vty);
2666 } else {
2667 /* The indent is deeper than the just entered parent, record the new
2668 * indentation characters. */
2669 vty->indent = talloc_strdup(vty, indent);
2670 /* This *is* the new indentation. */
2671 cmp = 0;
2672 }
2673 } else {
2674 /* There is a known indentation for this node level, validate and detect node
2675 * exits. */
2676 cmp = indent_cmp(indent, vty->indent);
2677 if (cmp == EINVAL)
2678 goto return_invalid_indent;
2679 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002680
2681 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2682 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2683 while (cmp < 0) {
2684 vty_go_parent(vty);
2685 cmp = indent_cmp(indent, vty->indent);
2686 if (cmp == EINVAL)
2687 goto return_invalid_indent;
2688 }
2689
2690 /* More indent without having entered a child node level? Either the parent node's indent
2691 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2692 * or the indentation increased even though the vty command didn't enter a child. */
2693 if (cmp > 0)
2694 goto return_invalid_indent;
2695
2696 /* Remember the current node before the command possibly changes it. */
2697 this_node = (struct vty_parent_node){
2698 .node = vty->node,
2699 .priv = vty->priv,
2700 .indent = vty->indent,
2701 };
2702
2703 parent = vty_parent(vty);
2704 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002705 cmd_free_strvec(vline);
2706
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002707 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002708 if (indent) {
2709 talloc_free(indent);
2710 indent = NULL;
2711 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002712 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002713 }
2714
2715 /* If we have stepped down into a child node, push a parent frame.
2716 * The causality is such: we don't expect every single node entry implementation to push
2717 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2718 * a parent node. Hence if the node changed without the parent node changing, we must
2719 * have stepped into a child node (and now expect a deeper indent). */
2720 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2721 /* Push the parent node. */
2722 parent = talloc_zero(vty, struct vty_parent_node);
2723 *parent = this_node;
2724 llist_add(&parent->entry, &vty->parent_nodes);
2725
2726 /* The current talloc'ed vty->indent string will now be owned by this parent
2727 * struct. Indicate that we don't know what deeper indent characters the user
2728 * will choose. */
2729 vty->indent = NULL;
2730 }
2731
2732 if (indent) {
2733 talloc_free(indent);
2734 indent = NULL;
2735 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002736 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002737 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2738 while (vty_parent(vty))
2739 vty_go_parent(vty);
2740
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002741 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002742
2743return_invalid_indent:
2744 if (vline)
2745 cmd_free_strvec(vline);
2746 if (indent) {
2747 talloc_free(indent);
2748 indent = NULL;
2749 }
2750 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002751}
2752
2753/* Configration from terminal */
2754DEFUN(config_terminal,
2755 config_terminal_cmd,
2756 "configure terminal",
2757 "Configuration from vty interface\n" "Configuration terminal\n")
2758{
2759 if (vty_config_lock(vty))
2760 vty->node = CONFIG_NODE;
2761 else {
2762 vty_out(vty, "VTY configuration is locked by other VTY%s",
2763 VTY_NEWLINE);
2764 return CMD_WARNING;
2765 }
2766 return CMD_SUCCESS;
2767}
2768
2769/* Enable command */
2770DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2771{
2772 /* If enable password is NULL, change to ENABLE_NODE */
2773 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2774 vty->type == VTY_SHELL_SERV)
2775 vty->node = ENABLE_NODE;
2776 else
2777 vty->node = AUTH_ENABLE_NODE;
2778
2779 return CMD_SUCCESS;
2780}
2781
2782/* Disable command */
2783DEFUN(disable,
2784 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2785{
2786 if (vty->node == ENABLE_NODE)
2787 vty->node = VIEW_NODE;
2788 return CMD_SUCCESS;
2789}
2790
2791/* Down vty node level. */
2792gDEFUN(config_exit,
2793 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2794{
2795 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002796 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002797 case VIEW_NODE:
2798 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002799 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002800 break;
2801 case CONFIG_NODE:
2802 vty->node = ENABLE_NODE;
2803 vty_config_unlock(vty);
2804 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002805 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002806 if (vty->node > CONFIG_NODE)
2807 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002808 break;
2809 }
2810 return CMD_SUCCESS;
2811}
2812
2813/* End of configuration. */
2814 gDEFUN(config_end,
2815 config_end_cmd, "end", "End current mode and change to enable mode.")
2816{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002817 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002818 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002819
2820 /* Repeatedly call go_parent until a top node is reached. */
2821 while (vty->node > CONFIG_NODE) {
2822 if (vty->node == last_node) {
2823 /* Ensure termination, this shouldn't happen. */
2824 break;
2825 }
2826 last_node = vty->node;
2827 vty_go_parent(vty);
2828 }
2829
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002830 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002831 if (vty->node > ENABLE_NODE)
2832 vty->node = ENABLE_NODE;
2833 vty->index = NULL;
2834 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002835 }
2836 return CMD_SUCCESS;
2837}
2838
2839/* Show version. */
2840DEFUN(show_version,
2841 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2842{
Harald Welte237f6242010-05-25 23:00:45 +02002843 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2844 host.app_info->version,
2845 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2846 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002847
2848 return CMD_SUCCESS;
2849}
2850
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002851DEFUN(show_online_help,
2852 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2853{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002854 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002855 return CMD_SUCCESS;
2856}
2857
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002858/* Help display function for all node. */
2859gDEFUN(config_help,
2860 config_help_cmd, "help", "Description of the interactive help system\n")
2861{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002862 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2863 "anytime at the command line please press '?'.%s%s"
2864 "If nothing matches, the help list will be empty and you must backup%s"
2865 " until entering a '?' shows the available options.%s"
2866 "Two styles of help are provided:%s"
2867 "1. Full help is available when you are ready to enter a%s"
2868 "command argument (e.g. 'show ?') and describes each possible%s"
2869 "argument.%s"
2870 "2. Partial help is provided when an abbreviated argument is entered%s"
2871 " and you want to know what arguments match the input%s"
2872 " (e.g. 'show me?'.)%s%s",
2873 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2874 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2875 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2876 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002877 return CMD_SUCCESS;
2878}
2879
2880/* Help display function for all node. */
2881gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2882{
2883 unsigned int i;
2884 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2885 struct cmd_element *cmd;
2886
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002887 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2888 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2889 continue;
2890 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2891 continue;
2892 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2893 }
2894
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002895 return CMD_SUCCESS;
2896}
2897
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002898static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002899{
2900 unsigned int i;
2901 int fd;
2902 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002903 char *config_file_tmp = NULL;
2904 char *config_file_sav = NULL;
2905 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002906 struct stat st;
2907
2908 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002909
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002910 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2911 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2912 * manually instead. */
2913
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002914 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002915 config_file_sav =
2916 _talloc_zero(tall_vty_cmd_ctx,
2917 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2918 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002919 if (!config_file_sav)
2920 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002921 strcpy(config_file_sav, config_file);
2922 strcat(config_file_sav, CONF_BACKUP_EXT);
2923
2924 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002925 "config_file_tmp");
2926 if (!config_file_tmp) {
2927 talloc_free(config_file_sav);
2928 return -1;
2929 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002930 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2931
2932 /* Open file to configuration write. */
2933 fd = mkstemp(config_file_tmp);
2934 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002935 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002936 talloc_free(config_file_tmp);
2937 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002938 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002939 }
2940
2941 /* Make vty for configuration file. */
2942 file_vty = vty_new();
2943 file_vty->fd = fd;
2944 file_vty->type = VTY_FILE;
2945
2946 /* Config file header print. */
2947 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002948 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002949 //vty_time_print (file_vty, 1);
2950 vty_out(file_vty, "!\n");
2951
2952 for (i = 0; i < vector_active(cmdvec); i++)
2953 if ((node = vector_slot(cmdvec, i)) && node->func) {
2954 if ((*node->func) (file_vty))
2955 vty_out(file_vty, "!\n");
2956 }
2957 vty_close(file_vty);
2958
2959 if (unlink(config_file_sav) != 0)
2960 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002961 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002962 talloc_free(config_file_sav);
2963 talloc_free(config_file_tmp);
2964 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002965 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002966 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002967
2968 /* Only link the .sav file if the original file exists */
2969 if (stat(config_file, &st) == 0) {
2970 if (link(config_file, config_file_sav) != 0) {
2971 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2972 talloc_free(config_file_sav);
2973 talloc_free(config_file_tmp);
2974 unlink(config_file_tmp);
2975 return -3;
2976 }
2977 sync();
2978 if (unlink(config_file) != 0) {
2979 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2980 talloc_free(config_file_sav);
2981 talloc_free(config_file_tmp);
2982 unlink(config_file_tmp);
2983 return -4;
2984 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002985 }
2986 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002987 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002988 talloc_free(config_file_sav);
2989 talloc_free(config_file_tmp);
2990 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002991 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002992 }
2993 unlink(config_file_tmp);
2994 sync();
2995
2996 talloc_free(config_file_sav);
2997 talloc_free(config_file_tmp);
2998
2999 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003000 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3001 return -6;
3002 }
3003
3004 return 0;
3005}
3006
3007
3008/* Write current configuration into file. */
3009DEFUN(config_write_file,
3010 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003011 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003012 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003013 "Write to configuration file\n"
3014 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003015{
3016 char *failed_file;
3017 int rc;
3018
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003019 if (host.app_info->config_is_consistent) {
3020 rc = host.app_info->config_is_consistent(vty);
3021 if (!rc) {
3022 vty_out(vty, "Configuration is not consistent%s",
3023 VTY_NEWLINE);
3024 return CMD_WARNING;
3025 }
3026 }
3027
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003028 if (argc == 1)
3029 host_config_set(argv[0]);
3030
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003031 if (host.config == NULL) {
3032 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3033 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003034 return CMD_WARNING;
3035 }
3036
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003037 rc = write_config_file(host.config, &failed_file);
3038 switch (rc) {
3039 case -1:
3040 vty_out(vty, "Can't open configuration file %s.%s",
3041 failed_file, VTY_NEWLINE);
3042 rc = CMD_WARNING;
3043 break;
3044 case -2:
3045 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3046 failed_file, VTY_NEWLINE);
3047 rc = CMD_WARNING;
3048 break;
3049 case -3:
3050 vty_out(vty, "Can't backup old configuration file %s.%s",
3051 failed_file, VTY_NEWLINE);
3052 rc = CMD_WARNING;
3053 break;
3054 case -4:
3055 vty_out(vty, "Can't unlink configuration file %s.%s",
3056 failed_file, VTY_NEWLINE);
3057 rc = CMD_WARNING;
3058 break;
3059 case -5:
3060 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3061 VTY_NEWLINE);
3062 rc = CMD_WARNING;
3063 break;
3064 case -6:
3065 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3066 failed_file, strerror(errno), errno, VTY_NEWLINE);
3067 rc = CMD_WARNING;
3068 break;
3069 default:
3070 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3071 rc = CMD_SUCCESS;
3072 break;
3073 }
3074
3075 talloc_free(failed_file);
3076 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003077}
3078
3079ALIAS(config_write_file,
3080 config_write_cmd,
3081 "write", "Write running configuration to memory, network, or terminal\n")
3082
3083 ALIAS(config_write_file,
3084 config_write_memory_cmd,
3085 "write memory",
3086 "Write running configuration to memory, network, or terminal\n"
3087 "Write configuration to the file (same as write file)\n")
3088
3089 ALIAS(config_write_file,
3090 copy_runningconfig_startupconfig_cmd,
3091 "copy running-config startup-config",
3092 "Copy configuration\n"
3093 "Copy running config to... \n"
3094 "Copy running config to startup config (same as write file)\n")
3095
3096/* Write current configuration into the terminal. */
3097 DEFUN(config_write_terminal,
3098 config_write_terminal_cmd,
3099 "write terminal",
3100 "Write running configuration to memory, network, or terminal\n"
3101 "Write to terminal\n")
3102{
3103 unsigned int i;
3104 struct cmd_node *node;
3105
3106 if (vty->type == VTY_SHELL_SERV) {
3107 for (i = 0; i < vector_active(cmdvec); i++)
3108 if ((node = vector_slot(cmdvec, i)) && node->func
3109 && node->vtysh) {
3110 if ((*node->func) (vty))
3111 vty_out(vty, "!%s", VTY_NEWLINE);
3112 }
3113 } else {
3114 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3115 VTY_NEWLINE);
3116 vty_out(vty, "!%s", VTY_NEWLINE);
3117
3118 for (i = 0; i < vector_active(cmdvec); i++)
3119 if ((node = vector_slot(cmdvec, i)) && node->func) {
3120 if ((*node->func) (vty))
3121 vty_out(vty, "!%s", VTY_NEWLINE);
3122 }
3123 vty_out(vty, "end%s", VTY_NEWLINE);
3124 }
3125 return CMD_SUCCESS;
3126}
3127
3128/* Write current configuration into the terminal. */
3129ALIAS(config_write_terminal,
3130 show_running_config_cmd,
3131 "show running-config", SHOW_STR "running configuration\n")
3132
3133/* Write startup configuration into the terminal. */
3134 DEFUN(show_startup_config,
3135 show_startup_config_cmd,
3136 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3137{
3138 char buf[BUFSIZ];
3139 FILE *confp;
3140
3141 confp = fopen(host.config, "r");
3142 if (confp == NULL) {
3143 vty_out(vty, "Can't open configuration file [%s]%s",
3144 host.config, VTY_NEWLINE);
3145 return CMD_WARNING;
3146 }
3147
3148 while (fgets(buf, BUFSIZ, confp)) {
3149 char *cp = buf;
3150
3151 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3152 cp++;
3153 *cp = '\0';
3154
3155 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3156 }
3157
3158 fclose(confp);
3159
3160 return CMD_SUCCESS;
3161}
3162
3163/* Hostname configuration */
3164DEFUN(config_hostname,
3165 hostname_cmd,
3166 "hostname WORD",
3167 "Set system's network name\n" "This system's network name\n")
3168{
3169 if (!isalpha((int)*argv[0])) {
3170 vty_out(vty, "Please specify string starting with alphabet%s",
3171 VTY_NEWLINE);
3172 return CMD_WARNING;
3173 }
3174
3175 if (host.name)
3176 talloc_free(host.name);
3177
3178 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3179 return CMD_SUCCESS;
3180}
3181
3182DEFUN(config_no_hostname,
3183 no_hostname_cmd,
3184 "no hostname [HOSTNAME]",
3185 NO_STR "Reset system's network name\n" "Host name of this router\n")
3186{
3187 if (host.name)
3188 talloc_free(host.name);
3189 host.name = NULL;
3190 return CMD_SUCCESS;
3191}
3192
3193/* VTY interface password set. */
3194DEFUN(config_password, password_cmd,
3195 "password (8|) WORD",
3196 "Assign the terminal connection password\n"
3197 "Specifies a HIDDEN password will follow\n"
3198 "dummy string \n" "The HIDDEN line password string\n")
3199{
3200 /* Argument check. */
3201 if (argc == 0) {
3202 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3203 return CMD_WARNING;
3204 }
3205
3206 if (argc == 2) {
3207 if (*argv[0] == '8') {
3208 if (host.password)
3209 talloc_free(host.password);
3210 host.password = NULL;
3211 if (host.password_encrypt)
3212 talloc_free(host.password_encrypt);
3213 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3214 return CMD_SUCCESS;
3215 } else {
3216 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3217 return CMD_WARNING;
3218 }
3219 }
3220
3221 if (!isalnum((int)*argv[0])) {
3222 vty_out(vty,
3223 "Please specify string starting with alphanumeric%s",
3224 VTY_NEWLINE);
3225 return CMD_WARNING;
3226 }
3227
3228 if (host.password)
3229 talloc_free(host.password);
3230 host.password = NULL;
3231
3232#ifdef VTY_CRYPT_PW
3233 if (host.encrypt) {
3234 if (host.password_encrypt)
3235 talloc_free(host.password_encrypt);
3236 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3237 } else
3238#endif
3239 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3240
3241 return CMD_SUCCESS;
3242}
3243
3244ALIAS(config_password, password_text_cmd,
3245 "password LINE",
3246 "Assign the terminal connection password\n"
3247 "The UNENCRYPTED (cleartext) line password\n")
3248
3249/* VTY enable password set. */
3250 DEFUN(config_enable_password, enable_password_cmd,
3251 "enable password (8|) WORD",
3252 "Modify enable password parameters\n"
3253 "Assign the privileged level password\n"
3254 "Specifies a HIDDEN password will follow\n"
3255 "dummy string \n" "The HIDDEN 'enable' password string\n")
3256{
3257 /* Argument check. */
3258 if (argc == 0) {
3259 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3260 return CMD_WARNING;
3261 }
3262
3263 /* Crypt type is specified. */
3264 if (argc == 2) {
3265 if (*argv[0] == '8') {
3266 if (host.enable)
3267 talloc_free(host.enable);
3268 host.enable = NULL;
3269
3270 if (host.enable_encrypt)
3271 talloc_free(host.enable_encrypt);
3272 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3273
3274 return CMD_SUCCESS;
3275 } else {
3276 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3277 return CMD_WARNING;
3278 }
3279 }
3280
3281 if (!isalnum((int)*argv[0])) {
3282 vty_out(vty,
3283 "Please specify string starting with alphanumeric%s",
3284 VTY_NEWLINE);
3285 return CMD_WARNING;
3286 }
3287
3288 if (host.enable)
3289 talloc_free(host.enable);
3290 host.enable = NULL;
3291
3292 /* Plain password input. */
3293#ifdef VTY_CRYPT_PW
3294 if (host.encrypt) {
3295 if (host.enable_encrypt)
3296 talloc_free(host.enable_encrypt);
3297 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3298 } else
3299#endif
3300 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3301
3302 return CMD_SUCCESS;
3303}
3304
3305ALIAS(config_enable_password,
3306 enable_password_text_cmd,
3307 "enable password LINE",
3308 "Modify enable password parameters\n"
3309 "Assign the privileged level password\n"
3310 "The UNENCRYPTED (cleartext) 'enable' password\n")
3311
3312/* VTY enable password delete. */
3313 DEFUN(no_config_enable_password, no_enable_password_cmd,
3314 "no enable password",
3315 NO_STR
3316 "Modify enable password parameters\n"
3317 "Assign the privileged level password\n")
3318{
3319 if (host.enable)
3320 talloc_free(host.enable);
3321 host.enable = NULL;
3322
3323 if (host.enable_encrypt)
3324 talloc_free(host.enable_encrypt);
3325 host.enable_encrypt = NULL;
3326
3327 return CMD_SUCCESS;
3328}
3329
3330#ifdef VTY_CRYPT_PW
3331DEFUN(service_password_encrypt,
3332 service_password_encrypt_cmd,
3333 "service password-encryption",
3334 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3335{
3336 if (host.encrypt)
3337 return CMD_SUCCESS;
3338
3339 host.encrypt = 1;
3340
3341 if (host.password) {
3342 if (host.password_encrypt)
3343 talloc_free(host.password_encrypt);
3344 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3345 }
3346 if (host.enable) {
3347 if (host.enable_encrypt)
3348 talloc_free(host.enable_encrypt);
3349 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3350 }
3351
3352 return CMD_SUCCESS;
3353}
3354
3355DEFUN(no_service_password_encrypt,
3356 no_service_password_encrypt_cmd,
3357 "no service password-encryption",
3358 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3359{
3360 if (!host.encrypt)
3361 return CMD_SUCCESS;
3362
3363 host.encrypt = 0;
3364
3365 if (host.password_encrypt)
3366 talloc_free(host.password_encrypt);
3367 host.password_encrypt = NULL;
3368
3369 if (host.enable_encrypt)
3370 talloc_free(host.enable_encrypt);
3371 host.enable_encrypt = NULL;
3372
3373 return CMD_SUCCESS;
3374}
3375#endif
3376
3377DEFUN(config_terminal_length, config_terminal_length_cmd,
3378 "terminal length <0-512>",
3379 "Set terminal line parameters\n"
3380 "Set number of lines on a screen\n"
3381 "Number of lines on screen (0 for no pausing)\n")
3382{
3383 int lines;
3384 char *endptr = NULL;
3385
3386 lines = strtol(argv[0], &endptr, 10);
3387 if (lines < 0 || lines > 512 || *endptr != '\0') {
3388 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3389 return CMD_WARNING;
3390 }
3391 vty->lines = lines;
3392
3393 return CMD_SUCCESS;
3394}
3395
3396DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3397 "terminal no length",
3398 "Set terminal line parameters\n"
3399 NO_STR "Set number of lines on a screen\n")
3400{
3401 vty->lines = -1;
3402 return CMD_SUCCESS;
3403}
3404
3405DEFUN(service_terminal_length, service_terminal_length_cmd,
3406 "service terminal-length <0-512>",
3407 "Set up miscellaneous service\n"
3408 "System wide terminal length configuration\n"
3409 "Number of lines of VTY (0 means no line control)\n")
3410{
3411 int lines;
3412 char *endptr = NULL;
3413
3414 lines = strtol(argv[0], &endptr, 10);
3415 if (lines < 0 || lines > 512 || *endptr != '\0') {
3416 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3417 return CMD_WARNING;
3418 }
3419 host.lines = lines;
3420
3421 return CMD_SUCCESS;
3422}
3423
3424DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3425 "no service terminal-length [<0-512>]",
3426 NO_STR
3427 "Set up miscellaneous service\n"
3428 "System wide terminal length configuration\n"
3429 "Number of lines of VTY (0 means no line control)\n")
3430{
3431 host.lines = -1;
3432 return CMD_SUCCESS;
3433}
3434
3435DEFUN_HIDDEN(do_echo,
3436 echo_cmd,
3437 "echo .MESSAGE",
3438 "Echo a message back to the vty\n" "The message to echo\n")
3439{
3440 char *message;
3441
3442 vty_out(vty, "%s%s",
3443 ((message =
3444 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3445 if (message)
3446 talloc_free(message);
3447 return CMD_SUCCESS;
3448}
3449
3450#if 0
3451DEFUN(config_logmsg,
3452 config_logmsg_cmd,
3453 "logmsg " LOG_LEVELS " .MESSAGE",
3454 "Send a message to enabled logging destinations\n"
3455 LOG_LEVEL_DESC "The message to send\n")
3456{
3457 int level;
3458 char *message;
3459
3460 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3461 return CMD_ERR_NO_MATCH;
3462
3463 zlog(NULL, level,
3464 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3465 if (message)
3466 talloc_free(message);
3467 return CMD_SUCCESS;
3468}
3469
3470DEFUN(show_logging,
3471 show_logging_cmd,
3472 "show logging", SHOW_STR "Show current logging configuration\n")
3473{
3474 struct zlog *zl = zlog_default;
3475
3476 vty_out(vty, "Syslog logging: ");
3477 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3478 vty_out(vty, "disabled");
3479 else
3480 vty_out(vty, "level %s, facility %s, ident %s",
3481 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3482 facility_name(zl->facility), zl->ident);
3483 vty_out(vty, "%s", VTY_NEWLINE);
3484
3485 vty_out(vty, "Stdout logging: ");
3486 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3487 vty_out(vty, "disabled");
3488 else
3489 vty_out(vty, "level %s",
3490 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3491 vty_out(vty, "%s", VTY_NEWLINE);
3492
3493 vty_out(vty, "Monitor logging: ");
3494 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3495 vty_out(vty, "disabled");
3496 else
3497 vty_out(vty, "level %s",
3498 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3499 vty_out(vty, "%s", VTY_NEWLINE);
3500
3501 vty_out(vty, "File logging: ");
3502 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3503 vty_out(vty, "disabled");
3504 else
3505 vty_out(vty, "level %s, filename %s",
3506 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3507 zl->filename);
3508 vty_out(vty, "%s", VTY_NEWLINE);
3509
3510 vty_out(vty, "Protocol name: %s%s",
3511 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3512 vty_out(vty, "Record priority: %s%s",
3513 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3514
3515 return CMD_SUCCESS;
3516}
3517
3518DEFUN(config_log_stdout,
3519 config_log_stdout_cmd,
3520 "log stdout", "Logging control\n" "Set stdout logging level\n")
3521{
3522 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3523 return CMD_SUCCESS;
3524}
3525
3526DEFUN(config_log_stdout_level,
3527 config_log_stdout_level_cmd,
3528 "log stdout " LOG_LEVELS,
3529 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3530{
3531 int level;
3532
3533 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3534 return CMD_ERR_NO_MATCH;
3535 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3536 return CMD_SUCCESS;
3537}
3538
3539DEFUN(no_config_log_stdout,
3540 no_config_log_stdout_cmd,
3541 "no log stdout [LEVEL]",
3542 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3543{
3544 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3545 return CMD_SUCCESS;
3546}
3547
3548DEFUN(config_log_monitor,
3549 config_log_monitor_cmd,
3550 "log monitor",
3551 "Logging control\n" "Set terminal line (monitor) logging level\n")
3552{
3553 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3554 return CMD_SUCCESS;
3555}
3556
3557DEFUN(config_log_monitor_level,
3558 config_log_monitor_level_cmd,
3559 "log monitor " LOG_LEVELS,
3560 "Logging control\n"
3561 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3562{
3563 int level;
3564
3565 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3566 return CMD_ERR_NO_MATCH;
3567 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3568 return CMD_SUCCESS;
3569}
3570
3571DEFUN(no_config_log_monitor,
3572 no_config_log_monitor_cmd,
3573 "no log monitor [LEVEL]",
3574 NO_STR
3575 "Logging control\n"
3576 "Disable terminal line (monitor) logging\n" "Logging level\n")
3577{
3578 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3579 return CMD_SUCCESS;
3580}
3581
3582static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3583{
3584 int ret;
3585 char *p = NULL;
3586 const char *fullpath;
3587
3588 /* Path detection. */
3589 if (!IS_DIRECTORY_SEP(*fname)) {
3590 char cwd[MAXPATHLEN + 1];
3591 cwd[MAXPATHLEN] = '\0';
3592
3593 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3594 zlog_err("config_log_file: Unable to alloc mem!");
3595 return CMD_WARNING;
3596 }
3597
3598 if ((p = _talloc_zero(tall_vcmd_ctx,
3599 strlen(cwd) + strlen(fname) + 2),
3600 "set_log_file")
3601 == NULL) {
3602 zlog_err("config_log_file: Unable to alloc mem!");
3603 return CMD_WARNING;
3604 }
3605 sprintf(p, "%s/%s", cwd, fname);
3606 fullpath = p;
3607 } else
3608 fullpath = fname;
3609
3610 ret = zlog_set_file(NULL, fullpath, loglevel);
3611
3612 if (p)
3613 talloc_free(p);
3614
3615 if (!ret) {
3616 vty_out(vty, "can't open logfile %s\n", fname);
3617 return CMD_WARNING;
3618 }
3619
3620 if (host.logfile)
3621 talloc_free(host.logfile);
3622
3623 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3624
3625 return CMD_SUCCESS;
3626}
3627
3628DEFUN(config_log_file,
3629 config_log_file_cmd,
3630 "log file FILENAME",
3631 "Logging control\n" "Logging to file\n" "Logging filename\n")
3632{
3633 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3634}
3635
3636DEFUN(config_log_file_level,
3637 config_log_file_level_cmd,
3638 "log file FILENAME " LOG_LEVELS,
3639 "Logging control\n"
3640 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3641{
3642 int level;
3643
3644 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3645 return CMD_ERR_NO_MATCH;
3646 return set_log_file(vty, argv[0], level);
3647}
3648
3649DEFUN(no_config_log_file,
3650 no_config_log_file_cmd,
3651 "no log file [FILENAME]",
3652 NO_STR
3653 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3654{
3655 zlog_reset_file(NULL);
3656
3657 if (host.logfile)
3658 talloc_free(host.logfile);
3659
3660 host.logfile = NULL;
3661
3662 return CMD_SUCCESS;
3663}
3664
3665ALIAS(no_config_log_file,
3666 no_config_log_file_level_cmd,
3667 "no log file FILENAME LEVEL",
3668 NO_STR
3669 "Logging control\n"
3670 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3671
3672 DEFUN(config_log_syslog,
3673 config_log_syslog_cmd,
3674 "log syslog", "Logging control\n" "Set syslog logging level\n")
3675{
3676 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3677 return CMD_SUCCESS;
3678}
3679
3680DEFUN(config_log_syslog_level,
3681 config_log_syslog_level_cmd,
3682 "log syslog " LOG_LEVELS,
3683 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3684{
3685 int level;
3686
3687 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3688 return CMD_ERR_NO_MATCH;
3689 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3690 return CMD_SUCCESS;
3691}
3692
3693DEFUN_DEPRECATED(config_log_syslog_facility,
3694 config_log_syslog_facility_cmd,
3695 "log syslog facility " LOG_FACILITIES,
3696 "Logging control\n"
3697 "Logging goes to syslog\n"
3698 "(Deprecated) Facility parameter for syslog messages\n"
3699 LOG_FACILITY_DESC)
3700{
3701 int facility;
3702
3703 if ((facility = facility_match(argv[0])) < 0)
3704 return CMD_ERR_NO_MATCH;
3705
3706 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3707 zlog_default->facility = facility;
3708 return CMD_SUCCESS;
3709}
3710
3711DEFUN(no_config_log_syslog,
3712 no_config_log_syslog_cmd,
3713 "no log syslog [LEVEL]",
3714 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3715{
3716 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3717 return CMD_SUCCESS;
3718}
3719
3720ALIAS(no_config_log_syslog,
3721 no_config_log_syslog_facility_cmd,
3722 "no log syslog facility " LOG_FACILITIES,
3723 NO_STR
3724 "Logging control\n"
3725 "Logging goes to syslog\n"
3726 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3727
3728 DEFUN(config_log_facility,
3729 config_log_facility_cmd,
3730 "log facility " LOG_FACILITIES,
3731 "Logging control\n"
3732 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3733{
3734 int facility;
3735
3736 if ((facility = facility_match(argv[0])) < 0)
3737 return CMD_ERR_NO_MATCH;
3738 zlog_default->facility = facility;
3739 return CMD_SUCCESS;
3740}
3741
3742DEFUN(no_config_log_facility,
3743 no_config_log_facility_cmd,
3744 "no log facility [FACILITY]",
3745 NO_STR
3746 "Logging control\n"
3747 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3748{
3749 zlog_default->facility = LOG_DAEMON;
3750 return CMD_SUCCESS;
3751}
3752
3753DEFUN_DEPRECATED(config_log_trap,
3754 config_log_trap_cmd,
3755 "log trap " LOG_LEVELS,
3756 "Logging control\n"
3757 "(Deprecated) Set logging level and default for all destinations\n"
3758 LOG_LEVEL_DESC)
3759{
3760 int new_level;
3761 int i;
3762
3763 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3764 return CMD_ERR_NO_MATCH;
3765
3766 zlog_default->default_lvl = new_level;
3767 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3768 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3769 zlog_default->maxlvl[i] = new_level;
3770 return CMD_SUCCESS;
3771}
3772
3773DEFUN_DEPRECATED(no_config_log_trap,
3774 no_config_log_trap_cmd,
3775 "no log trap [LEVEL]",
3776 NO_STR
3777 "Logging control\n"
3778 "Permit all logging information\n" "Logging level\n")
3779{
3780 zlog_default->default_lvl = LOG_DEBUG;
3781 return CMD_SUCCESS;
3782}
3783
3784DEFUN(config_log_record_priority,
3785 config_log_record_priority_cmd,
3786 "log record-priority",
3787 "Logging control\n"
3788 "Log the priority of the message within the message\n")
3789{
3790 zlog_default->record_priority = 1;
3791 return CMD_SUCCESS;
3792}
3793
3794DEFUN(no_config_log_record_priority,
3795 no_config_log_record_priority_cmd,
3796 "no log record-priority",
3797 NO_STR
3798 "Logging control\n"
3799 "Do not log the priority of the message within the message\n")
3800{
3801 zlog_default->record_priority = 0;
3802 return CMD_SUCCESS;
3803}
3804#endif
3805
3806DEFUN(banner_motd_file,
3807 banner_motd_file_cmd,
3808 "banner motd file [FILE]",
3809 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3810{
3811 if (host.motdfile)
3812 talloc_free(host.motdfile);
3813 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3814
3815 return CMD_SUCCESS;
3816}
3817
3818DEFUN(banner_motd_default,
3819 banner_motd_default_cmd,
3820 "banner motd default",
3821 "Set banner string\n" "Strings for motd\n" "Default string\n")
3822{
3823 host.motd = default_motd;
3824 return CMD_SUCCESS;
3825}
3826
3827DEFUN(no_banner_motd,
3828 no_banner_motd_cmd,
3829 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3830{
3831 host.motd = NULL;
3832 if (host.motdfile)
3833 talloc_free(host.motdfile);
3834 host.motdfile = NULL;
3835 return CMD_SUCCESS;
3836}
3837
3838/* Set config filename. Called from vty.c */
3839void host_config_set(const char *filename)
3840{
3841 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3842}
3843
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003844/*! Deprecated, now happens implicitly when calling install_node().
3845 * Users of the API may still attempt to call this function, hence
3846 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003847void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003848{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003849}
3850
3851/*! Deprecated, now happens implicitly when calling install_node().
3852 * Users of the API may still attempt to call this function, hence
3853 * leave it here as a no-op. */
3854void vty_install_default(int node)
3855{
3856}
3857
3858/*! Install common commands like 'exit' and 'list'. */
3859static void install_basic_node_commands(int node)
3860{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003861 install_element(node, &config_help_cmd);
3862 install_element(node, &config_list_cmd);
3863
3864 install_element(node, &config_write_terminal_cmd);
3865 install_element(node, &config_write_file_cmd);
3866 install_element(node, &config_write_memory_cmd);
3867 install_element(node, &config_write_cmd);
3868 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003869
3870 install_element(node, &config_exit_cmd);
3871
3872 if (node >= CONFIG_NODE) {
3873 /* It's not a top node. */
3874 install_element(node, &config_end_cmd);
3875 }
3876}
3877
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003878/*! Return true if a node is installed by install_basic_node_commands(), so
3879 * that we can avoid repeating them for each and every node during 'show
3880 * running-config' */
3881static bool vty_command_is_common(struct cmd_element *cmd)
3882{
3883 if (cmd == &config_help_cmd
3884 || cmd == &config_list_cmd
3885 || cmd == &config_write_terminal_cmd
3886 || cmd == &config_write_file_cmd
3887 || cmd == &config_write_memory_cmd
3888 || cmd == &config_write_cmd
3889 || cmd == &show_running_config_cmd
3890 || cmd == &config_exit_cmd
3891 || cmd == &config_end_cmd)
3892 return true;
3893 return false;
3894}
3895
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003896/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003897 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003898 * \param[in] vty the vty of the code
3899 * \param[in] filename where to store the file
3900 * \return 0 in case of success.
3901 *
3902 * If the filename already exists create a filename.sav
3903 * version with the current code.
3904 *
3905 */
3906int osmo_vty_write_config_file(const char *filename)
3907{
3908 char *failed_file;
3909 int rc;
3910
3911 rc = write_config_file(filename, &failed_file);
3912 talloc_free(failed_file);
3913 return rc;
3914}
3915
3916/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003917 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003918 * \return 0 in case of success.
3919 *
3920 * If the filename already exists create a filename.sav
3921 * version with the current code.
3922 *
3923 */
3924int osmo_vty_save_config_file(void)
3925{
3926 char *failed_file;
3927 int rc;
3928
3929 if (host.config == NULL)
3930 return -7;
3931
3932 rc = write_config_file(host.config, &failed_file);
3933 talloc_free(failed_file);
3934 return rc;
3935}
3936
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003937/* Initialize command interface. Install basic nodes and commands. */
3938void cmd_init(int terminal)
3939{
3940 /* Allocate initial top vector of commands. */
3941 cmdvec = vector_init(VECTOR_MIN_SIZE);
3942
3943 /* Default host value settings. */
3944 host.name = NULL;
3945 host.password = NULL;
3946 host.enable = NULL;
3947 host.logfile = NULL;
3948 host.config = NULL;
3949 host.lines = -1;
3950 host.motd = default_motd;
3951 host.motdfile = NULL;
3952
3953 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003954 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003955 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003956 install_node_bare(&auth_node, NULL);
3957 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003958 install_node(&config_node, config_write_host);
3959
3960 /* Each node's basic commands. */
3961 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003962 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003963 if (terminal) {
3964 install_element(VIEW_NODE, &config_list_cmd);
3965 install_element(VIEW_NODE, &config_exit_cmd);
3966 install_element(VIEW_NODE, &config_help_cmd);
3967 install_element(VIEW_NODE, &config_enable_cmd);
3968 install_element(VIEW_NODE, &config_terminal_length_cmd);
3969 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3970 install_element(VIEW_NODE, &echo_cmd);
3971 }
3972
3973 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003974 install_element(ENABLE_NODE, &config_disable_cmd);
3975 install_element(ENABLE_NODE, &config_terminal_cmd);
3976 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3977 }
3978 install_element (ENABLE_NODE, &show_startup_config_cmd);
3979 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003980 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003981
3982 if (terminal) {
3983 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3984 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3985 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003986 }
3987
3988 install_element(CONFIG_NODE, &hostname_cmd);
3989 install_element(CONFIG_NODE, &no_hostname_cmd);
3990
3991 if (terminal) {
3992 install_element(CONFIG_NODE, &password_cmd);
3993 install_element(CONFIG_NODE, &password_text_cmd);
3994 install_element(CONFIG_NODE, &enable_password_cmd);
3995 install_element(CONFIG_NODE, &enable_password_text_cmd);
3996 install_element(CONFIG_NODE, &no_enable_password_cmd);
3997
3998#ifdef VTY_CRYPT_PW
3999 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
4000 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
4001#endif
4002 install_element(CONFIG_NODE, &banner_motd_default_cmd);
4003 install_element(CONFIG_NODE, &banner_motd_file_cmd);
4004 install_element(CONFIG_NODE, &no_banner_motd_cmd);
4005 install_element(CONFIG_NODE, &service_terminal_length_cmd);
4006 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
4007
4008 }
4009 srand(time(NULL));
4010}
Harald Welte7acb30c2011-08-17 17:13:48 +02004011
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004012/*! @} */