blob: d8fe4637840a9d617e6251a75b3e1ef4c5f0a311 [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);
631
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200632 print_func(data, " <command id='%s'>%s", xml_string, newline);
633 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100634
635 int j;
636 for (j = 0; j < vector_count(cmd->strvec); ++j) {
637 vector descvec = vector_slot(cmd->strvec, j);
638 int i;
639 for (i = 0; i < vector_active(descvec); ++i) {
640 char *xml_param, *xml_doc;
641 struct desc *desc = vector_slot(descvec, i);
642 if (desc == NULL)
643 continue;
644
645 xml_param = xml_escape(desc->cmd);
646 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200647 print_func(data, " <param name='%s' doc='%s' />%s",
648 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100649 talloc_free(xml_param);
650 talloc_free(xml_doc);
651 }
652 }
653
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200654 print_func(data, " </params>%s", newline);
655 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100656
657 talloc_free(xml_string);
658 return 0;
659}
660
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200661static bool vty_command_is_common(struct cmd_element *cmd);
662
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100663/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200664 * 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 +0100665 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200666static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100667{
668 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200669 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100670
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200671 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100672
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200673 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200674 print_func(data, " <node id='_common_cmds_'>%s", newline);
675 print_func(data, " <name>Common Commands</name>%s", newline);
676 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
677 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200678 for (i = 0; i < vector_active(cmdvec); ++i) {
679 struct cmd_node *cnode;
680 cnode = vector_slot(cmdvec, i);
681 if (!cnode)
682 continue;
683 if (cnode->node != CONFIG_NODE)
684 continue;
685
686 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
687 struct cmd_element *elem;
688 elem = vector_slot(cnode->cmd_vector, j);
689 if (!vty_command_is_common(elem))
690 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200691 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200692 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200693 }
694 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200695 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200696
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100697 for (i = 0; i < vector_active(cmdvec); ++i) {
698 struct cmd_node *cnode;
699 cnode = vector_slot(cmdvec, i);
700 if (!cnode)
701 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200702 if (vector_active(cnode->cmd_vector) < 1)
703 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100704
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200705 /* De-dup node IDs: how many times has this same name been used before? Count the first
706 * occurence as _1 and omit that first suffix, so that the first occurence is called
707 * 'name', the second becomes 'name_2', then 'name_3', ... */
708 same_name_count = 1;
709 for (j = 0; j < i; ++j) {
710 struct cmd_node *cnode2;
711 cnode2 = vector_slot(cmdvec, j);
712 if (!cnode2)
713 continue;
714 if (strcmp(cnode->name, cnode2->name) == 0)
715 same_name_count ++;
716 }
717
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200718 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200719 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200720 print_func(data, "_%d", same_name_count);
721 print_func(data, "'>%s", newline);
722 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100723
724 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
725 struct cmd_element *elem;
726 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200727 if (vty_command_is_common(elem))
728 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200729 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200730 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100731 }
732
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200733 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100734 }
735
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200736 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100737
738 return 0;
739}
740
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200741static int print_func_vty(void *data, const char *format, ...)
742{
743 struct vty *vty = data;
744 va_list args;
745 int rc;
746 va_start(args, format);
747 rc = vty_out_va(vty, format, args);
748 va_end(args);
749 return rc;
750}
751
752static int vty_dump_xml_ref_to_vty(struct vty *vty)
753{
754 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
755}
756
757static int print_func_stream(void *data, const char *format, ...)
758{
759 va_list args;
760 int rc;
761 va_start(args, format);
762 rc = vfprintf((FILE*)data, format, args);
763 va_end(args);
764 return rc;
765}
766
767/*! Print the XML reference of all VTY nodes to the given stream.
768 */
769int vty_dump_xml_ref(FILE *stream)
770{
771 return vty_dump_nodes(print_func_stream, stream, "\n");
772}
773
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200774/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100775static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
776{
777 int i;
778
779 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
780 struct cmd_element *elem;
781 elem = vector_slot(cnode->cmd_vector, i);
782 if (!elem->string)
783 continue;
784 if (!strcmp(elem->string, cmdstring))
785 return 1;
786 }
787 return 0;
788}
789
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200790/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200791 * \param[in] ntype Node Type
792 * \param[cmd] element to be installed
793 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000794void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200795{
796 struct cmd_node *cnode;
797
798 cnode = vector_slot(cmdvec, ntype);
799
Harald Weltea99d45a2015-11-12 13:48:23 +0100800 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100801 /* ensure no _identical_ command has been registered at this
802 * node so far */
803 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200804
805 vector_set(cnode->cmd_vector, cmd);
806
807 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
808 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
809}
810
811/* Install a command into VIEW and ENABLE node */
812void install_element_ve(struct cmd_element *cmd)
813{
814 install_element(VIEW_NODE, cmd);
815 install_element(ENABLE_NODE, cmd);
816}
817
818#ifdef VTY_CRYPT_PW
819static unsigned char itoa64[] =
820 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
821
822static void to64(char *s, long v, int n)
823{
824 while (--n >= 0) {
825 *s++ = itoa64[v & 0x3f];
826 v >>= 6;
827 }
828}
829
830static char *zencrypt(const char *passwd)
831{
832 char salt[6];
833 struct timeval tv;
834 char *crypt(const char *, const char *);
835
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200836 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200837
838 to64(&salt[0], random(), 3);
839 to64(&salt[3], tv.tv_usec, 3);
840 salt[5] = '\0';
841
842 return crypt(passwd, salt);
843}
844#endif
845
846/* This function write configuration of this host. */
847static int config_write_host(struct vty *vty)
848{
849 if (host.name)
850 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
851
852 if (host.encrypt) {
853 if (host.password_encrypt)
854 vty_out(vty, "password 8 %s%s", host.password_encrypt,
855 VTY_NEWLINE);
856 if (host.enable_encrypt)
857 vty_out(vty, "enable password 8 %s%s",
858 host.enable_encrypt, VTY_NEWLINE);
859 } else {
860 if (host.password)
861 vty_out(vty, "password %s%s", host.password,
862 VTY_NEWLINE);
863 if (host.enable)
864 vty_out(vty, "enable password %s%s", host.enable,
865 VTY_NEWLINE);
866 }
867
868 if (host.advanced)
869 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
870
871 if (host.encrypt)
872 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
873
874 if (host.lines >= 0)
875 vty_out(vty, "service terminal-length %d%s", host.lines,
876 VTY_NEWLINE);
877
878 if (host.motdfile)
879 vty_out(vty, "banner motd file %s%s", host.motdfile,
880 VTY_NEWLINE);
881 else if (!host.motd)
882 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
883
884 return 1;
885}
886
887/* Utility function for getting command vector. */
888static vector cmd_node_vector(vector v, enum node_type ntype)
889{
890 struct cmd_node *cnode = vector_slot(v, ntype);
891 return cnode->cmd_vector;
892}
893
894/* Completion match types. */
895enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200896 NO_MATCH = 0,
897 ANY_MATCH,
898 EXTEND_MATCH,
899 IPV4_PREFIX_MATCH,
900 IPV4_MATCH,
901 IPV6_PREFIX_MATCH,
902 IPV6_MATCH,
903 RANGE_MATCH,
904 VARARG_MATCH,
905 PARTLY_MATCH,
906 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200907};
908
909static enum match_type cmd_ipv4_match(const char *str)
910{
911 const char *sp;
912 int dots = 0, nums = 0;
913 char buf[4];
914
915 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200916 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200917
918 for (;;) {
919 memset(buf, 0, sizeof(buf));
920 sp = str;
921 while (*str != '\0') {
922 if (*str == '.') {
923 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200924 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200925
926 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200927 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200928
929 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200930 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200931
932 dots++;
933 break;
934 }
935 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200936 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200937
938 str++;
939 }
940
941 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200942 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200943
944 strncpy(buf, sp, str - sp);
945 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200946 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200947
948 nums++;
949
950 if (*str == '\0')
951 break;
952
953 str++;
954 }
955
956 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200957 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200958
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200959 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200960}
961
962static enum match_type cmd_ipv4_prefix_match(const char *str)
963{
964 const char *sp;
965 int dots = 0;
966 char buf[4];
967
968 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200969 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200970
971 for (;;) {
972 memset(buf, 0, sizeof(buf));
973 sp = str;
974 while (*str != '\0' && *str != '/') {
975 if (*str == '.') {
976 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200977 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200978
979 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200980 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200981
982 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200983 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200984
985 dots++;
986 break;
987 }
988
989 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200990 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200991
992 str++;
993 }
994
995 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200996 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200997
998 strncpy(buf, sp, str - sp);
999 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001000 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001001
1002 if (dots == 3) {
1003 if (*str == '/') {
1004 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001005 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001006
1007 str++;
1008 break;
1009 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001010 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001011 }
1012
1013 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001014 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001015
1016 str++;
1017 }
1018
1019 sp = str;
1020 while (*str != '\0') {
1021 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001022 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001023
1024 str++;
1025 }
1026
1027 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001028 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001029
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001030 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001031}
1032
1033#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1034#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1035#define STATE_START 1
1036#define STATE_COLON 2
1037#define STATE_DOUBLE 3
1038#define STATE_ADDR 4
1039#define STATE_DOT 5
1040#define STATE_SLASH 6
1041#define STATE_MASK 7
1042
1043#ifdef HAVE_IPV6
1044
1045static enum match_type cmd_ipv6_match(const char *str)
1046{
1047 int state = STATE_START;
1048 int colons = 0, nums = 0, double_colon = 0;
1049 const char *sp = NULL;
1050 struct sockaddr_in6 sin6_dummy;
1051 int ret;
1052
1053 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001054 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001055
1056 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001057 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001058
1059 /* use inet_pton that has a better support,
1060 * for example inet_pton can support the automatic addresses:
1061 * ::1.2.3.4
1062 */
1063 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1064
1065 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001066 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001067
1068 while (*str != '\0') {
1069 switch (state) {
1070 case STATE_START:
1071 if (*str == ':') {
1072 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001073 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001074 colons--;
1075 state = STATE_COLON;
1076 } else {
1077 sp = str;
1078 state = STATE_ADDR;
1079 }
1080
1081 continue;
1082 case STATE_COLON:
1083 colons++;
1084 if (*(str + 1) == ':')
1085 state = STATE_DOUBLE;
1086 else {
1087 sp = str + 1;
1088 state = STATE_ADDR;
1089 }
1090 break;
1091 case STATE_DOUBLE:
1092 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001093 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001094
1095 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001096 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001097 else {
1098 if (*(str + 1) != '\0')
1099 colons++;
1100 sp = str + 1;
1101 state = STATE_ADDR;
1102 }
1103
1104 double_colon++;
1105 nums++;
1106 break;
1107 case STATE_ADDR:
1108 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1109 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001110 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111
1112 nums++;
1113 state = STATE_COLON;
1114 }
1115 if (*(str + 1) == '.')
1116 state = STATE_DOT;
1117 break;
1118 case STATE_DOT:
1119 state = STATE_ADDR;
1120 break;
1121 default:
1122 break;
1123 }
1124
1125 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001126 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001127
1128 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001129 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130
1131 str++;
1132 }
1133
1134#if 0
1135 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001136 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001137#endif /* 0 */
1138
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001139 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001140}
1141
1142static enum match_type cmd_ipv6_prefix_match(const char *str)
1143{
1144 int state = STATE_START;
1145 int colons = 0, nums = 0, double_colon = 0;
1146 int mask;
1147 const char *sp = NULL;
1148 char *endptr = NULL;
1149
1150 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001151 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001152
1153 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001154 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001155
1156 while (*str != '\0' && state != STATE_MASK) {
1157 switch (state) {
1158 case STATE_START:
1159 if (*str == ':') {
1160 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001161 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001162 colons--;
1163 state = STATE_COLON;
1164 } else {
1165 sp = str;
1166 state = STATE_ADDR;
1167 }
1168
1169 continue;
1170 case STATE_COLON:
1171 colons++;
1172 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001173 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001174 else if (*(str + 1) == ':')
1175 state = STATE_DOUBLE;
1176 else {
1177 sp = str + 1;
1178 state = STATE_ADDR;
1179 }
1180 break;
1181 case STATE_DOUBLE:
1182 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001183 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001184
1185 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001186 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001187 else {
1188 if (*(str + 1) != '\0' && *(str + 1) != '/')
1189 colons++;
1190 sp = str + 1;
1191
1192 if (*(str + 1) == '/')
1193 state = STATE_SLASH;
1194 else
1195 state = STATE_ADDR;
1196 }
1197
1198 double_colon++;
1199 nums += 1;
1200 break;
1201 case STATE_ADDR:
1202 if (*(str + 1) == ':' || *(str + 1) == '.'
1203 || *(str + 1) == '\0' || *(str + 1) == '/') {
1204 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001205 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001206
1207 for (; sp <= str; sp++)
1208 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001209 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001210
1211 nums++;
1212
1213 if (*(str + 1) == ':')
1214 state = STATE_COLON;
1215 else if (*(str + 1) == '.')
1216 state = STATE_DOT;
1217 else if (*(str + 1) == '/')
1218 state = STATE_SLASH;
1219 }
1220 break;
1221 case STATE_DOT:
1222 state = STATE_ADDR;
1223 break;
1224 case STATE_SLASH:
1225 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001226 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001227
1228 state = STATE_MASK;
1229 break;
1230 default:
1231 break;
1232 }
1233
1234 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001235 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001236
1237 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001238 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001239
1240 str++;
1241 }
1242
1243 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001244 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001245
1246 mask = strtol(str, &endptr, 10);
1247 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001248 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001249
1250 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001251 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001252
1253/* I don't know why mask < 13 makes command match partly.
1254 Forgive me to make this comments. I Want to set static default route
1255 because of lack of function to originate default in ospf6d; sorry
1256 yasu
1257 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001258 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259*/
1260
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001261 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001262}
1263
1264#endif /* HAVE_IPV6 */
1265
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001266
1267#if ULONG_MAX == 18446744073709551615UL
1268#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1269#elif ULONG_MAX == 4294967295UL
1270#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1271#else
1272#error "ULONG_MAX not defined!"
1273#endif
1274
1275#if LONG_MAX == 9223372036854775807L
1276#define DECIMAL_STRLEN_MAX_SIGNED 19
1277#elif LONG_MAX == 2147483647L
1278#define DECIMAL_STRLEN_MAX_SIGNED 10
1279#else
1280#error "LONG_MAX not defined!"
1281#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001282
1283static int cmd_range_match(const char *range, const char *str)
1284{
1285 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001286 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001287 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001288
1289 if (str == NULL)
1290 return 1;
1291
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001292 if (range[1] == '-') {
1293 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001294
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001295 val = strtol(str, &endptr, 10);
1296 if (*endptr != '\0')
1297 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001298
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001299 range += 2;
1300 p = strchr(range, '-');
1301 if (p == NULL)
1302 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001303 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001304 return 0;
1305 strncpy(buf, range, p - range);
1306 buf[p - range] = '\0';
1307 min = -strtol(buf, &endptr, 10);
1308 if (*endptr != '\0')
1309 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001310
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001311 range = p + 1;
1312 p = strchr(range, '>');
1313 if (p == NULL)
1314 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001315 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001316 return 0;
1317 strncpy(buf, range, p - range);
1318 buf[p - range] = '\0';
1319 max = strtol(buf, &endptr, 10);
1320 if (*endptr != '\0')
1321 return 0;
1322
1323 if (val < min || val > max)
1324 return 0;
1325 } else {
1326 unsigned long min, max, val;
1327
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001328 if (str[0] == '-')
1329 return 0;
1330
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001331 val = strtoul(str, &endptr, 10);
1332 if (*endptr != '\0')
1333 return 0;
1334
1335 range++;
1336 p = strchr(range, '-');
1337 if (p == NULL)
1338 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001339 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001340 return 0;
1341 strncpy(buf, range, p - range);
1342 buf[p - range] = '\0';
1343 min = strtoul(buf, &endptr, 10);
1344 if (*endptr != '\0')
1345 return 0;
1346
1347 range = p + 1;
1348 p = strchr(range, '>');
1349 if (p == NULL)
1350 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001351 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001352 return 0;
1353 strncpy(buf, range, p - range);
1354 buf[p - range] = '\0';
1355 max = strtoul(buf, &endptr, 10);
1356 if (*endptr != '\0')
1357 return 0;
1358
1359 if (val < min || val > max)
1360 return 0;
1361 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001362
1363 return 1;
1364}
1365
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001366/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001367static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001368{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001369 /* we've got "[blah]". We want to strip off the []s and redo the
1370 * match check for "blah"
1371 */
1372 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001373
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001374 if (len < 3)
1375 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001376
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001377 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378}
1379
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001380static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001381cmd_match(const char *str, const char *command,
1382 enum match_type min, bool recur)
1383{
1384
1385 if (recur && CMD_OPTION(str))
1386 {
1387 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001388 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001389
1390 /* this would be a bug in a command, however handle it gracefully
1391 * as it we only discover it if a user tries to run it
1392 */
1393 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001394 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001395
1396 ret = cmd_match(tmp, command, min, false);
1397
1398 talloc_free(tmp);
1399
1400 return ret;
1401 }
1402 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001403 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001404 else if (CMD_RANGE(str))
1405 {
1406 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001407 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001408 }
1409#ifdef HAVE_IPV6
1410 else if (CMD_IPV6(str))
1411 {
1412 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001413 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001414 }
1415 else if (CMD_IPV6_PREFIX(str))
1416 {
1417 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001418 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001419 }
1420#endif /* HAVE_IPV6 */
1421 else if (CMD_IPV4(str))
1422 {
1423 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001424 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001425 }
1426 else if (CMD_IPV4_PREFIX(str))
1427 {
1428 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001429 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001430 }
1431 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001432 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001433 else if (strncmp(command, str, strlen(command)) == 0)
1434 {
1435 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001436 return EXACT_MATCH;
1437 else if (PARTLY_MATCH >= min)
1438 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001439 }
1440
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001441 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001442}
1443
1444/* Filter vector at the specified index and by the given command string, to
1445 * the desired matching level (thus allowing part matches), and return match
1446 * type flag.
1447 */
1448static enum match_type
1449cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001450{
1451 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001452 struct cmd_element *cmd_element;
1453 enum match_type match_type;
1454 vector descvec;
1455 struct desc *desc;
1456
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001457 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001458
1459 /* If command and cmd_element string does not match set NULL to vector */
1460 for (i = 0; i < vector_active(v); i++)
1461 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001462 if (index >= vector_active(cmd_element->strvec))
1463 vector_slot(v, i) = NULL;
1464 else {
1465 unsigned int j;
1466 int matched = 0;
1467
1468 descvec =
1469 vector_slot(cmd_element->strvec, index);
1470
1471 for (j = 0; j < vector_active(descvec); j++)
1472 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001473 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001474
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001475 ret = cmd_match (desc->cmd, command, level, true);
1476
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001477 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001478 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001479
1480 if (match_type < ret)
1481 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001482 }
1483 if (!matched)
1484 vector_slot(v, i) = NULL;
1485 }
1486 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001487
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001488 if (match_type == NO_MATCH)
1489 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001490
1491 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1492 * go again and filter out commands whose argument (at this index) is
1493 * 'weaker'. E.g., if we have 2 commands:
1494 *
1495 * foo bar <1-255>
1496 * foo bar BLAH
1497 *
1498 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001499 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001500 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1501 *
1502 * If we don't do a 2nd pass and filter it out, the higher-layers will
1503 * consider this to be ambiguous.
1504 */
1505 for (i = 0; i < vector_active(v); i++)
1506 if ((cmd_element = vector_slot(v, i)) != NULL) {
1507 if (index >= vector_active(cmd_element->strvec))
1508 vector_slot(v, i) = NULL;
1509 else {
1510 unsigned int j;
1511 int matched = 0;
1512
1513 descvec =
1514 vector_slot(cmd_element->strvec, index);
1515
1516 for (j = 0; j < vector_active(descvec); j++)
1517 if ((desc = vector_slot(descvec, j))) {
1518 enum match_type ret;
1519
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001520 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001521
1522 if (ret >= match_type)
1523 matched++;
1524 }
1525 if (!matched)
1526 vector_slot(v, i) = NULL;
1527 }
1528 }
1529
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001530 return match_type;
1531}
1532
1533/* Check ambiguous match */
1534static int
1535is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1536{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001537 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001538 unsigned int i;
1539 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001540 struct cmd_element *cmd_element;
1541 const char *matched = NULL;
1542 vector descvec;
1543 struct desc *desc;
1544
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001545 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1546 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1547 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1548 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1549 * that case, the string must remain allocated until this function exits or another match comes
1550 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1551 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1552 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1553 void *cmd_deopt_ctx = NULL;
1554
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001555 for (i = 0; i < vector_active(v); i++) {
1556 cmd_element = vector_slot(v, i);
1557 if (!cmd_element)
1558 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001559
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001560 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001561
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001562 descvec = vector_slot(cmd_element->strvec, index);
1563
1564 for (j = 0; j < vector_active(descvec); j++) {
1565 desc = vector_slot(descvec, j);
1566 if (!desc)
1567 continue;
1568
1569 enum match_type mtype;
1570 const char *str = desc->cmd;
1571
1572 if (CMD_OPTION(str)) {
1573 if (!cmd_deopt_ctx)
1574 cmd_deopt_ctx =
1575 talloc_named_const(tall_vty_cmd_ctx, 0,
1576 __func__);
1577 str = cmd_deopt(cmd_deopt_ctx, str);
1578 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001579 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001580 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001581
1582 switch (type) {
1583 case EXACT_MATCH:
1584 if (!(CMD_VARIABLE (str))
1585 && strcmp(command, str) == 0)
1586 match++;
1587 break;
1588 case PARTLY_MATCH:
1589 if (!(CMD_VARIABLE (str))
1590 && strncmp(command, str, strlen (command)) == 0)
1591 {
1592 if (matched
1593 && strcmp(matched,
1594 str) != 0) {
1595 ret = 1; /* There is ambiguous match. */
1596 goto free_and_return;
1597 } else
1598 matched = str;
1599 match++;
1600 }
1601 break;
1602 case RANGE_MATCH:
1603 if (cmd_range_match
1604 (str, command)) {
1605 if (matched
1606 && strcmp(matched,
1607 str) != 0) {
1608 ret = 1;
1609 goto free_and_return;
1610 } else
1611 matched = str;
1612 match++;
1613 }
1614 break;
1615#ifdef HAVE_IPV6
1616 case IPV6_MATCH:
1617 if (CMD_IPV6(str))
1618 match++;
1619 break;
1620 case IPV6_PREFIX_MATCH:
1621 if ((mtype =
1622 cmd_ipv6_prefix_match
1623 (command)) != NO_MATCH) {
1624 if (mtype == PARTLY_MATCH) {
1625 ret = 2; /* There is incomplete match. */
1626 goto free_and_return;
1627 }
1628
1629 match++;
1630 }
1631 break;
1632#endif /* HAVE_IPV6 */
1633 case IPV4_MATCH:
1634 if (CMD_IPV4(str))
1635 match++;
1636 break;
1637 case IPV4_PREFIX_MATCH:
1638 if ((mtype =
1639 cmd_ipv4_prefix_match
1640 (command)) != NO_MATCH) {
1641 if (mtype == PARTLY_MATCH) {
1642 ret = 2; /* There is incomplete match. */
1643 goto free_and_return;
1644 }
1645
1646 match++;
1647 }
1648 break;
1649 case EXTEND_MATCH:
1650 if (CMD_VARIABLE (str))
1651 match++;
1652 break;
1653 case NO_MATCH:
1654 default:
1655 break;
1656 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001657 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001658 if (!match)
1659 vector_slot(v, i) = NULL;
1660 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001661
1662free_and_return:
1663 if (cmd_deopt_ctx)
1664 talloc_free(cmd_deopt_ctx);
1665 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001666}
1667
1668/* If src matches dst return dst string, otherwise return NULL */
1669static const char *cmd_entry_function(const char *src, const char *dst)
1670{
1671 /* Skip variable arguments. */
1672 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1673 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1674 return NULL;
1675
1676 /* In case of 'command \t', given src is NULL string. */
1677 if (src == NULL)
1678 return dst;
1679
1680 /* Matched with input string. */
1681 if (strncmp(src, dst, strlen(src)) == 0)
1682 return dst;
1683
1684 return NULL;
1685}
1686
1687/* If src matches dst return dst string, otherwise return NULL */
1688/* This version will return the dst string always if it is
1689 CMD_VARIABLE for '?' key processing */
1690static const char *cmd_entry_function_desc(const char *src, const char *dst)
1691{
1692 if (CMD_VARARG(dst))
1693 return dst;
1694
1695 if (CMD_RANGE(dst)) {
1696 if (cmd_range_match(dst, src))
1697 return dst;
1698 else
1699 return NULL;
1700 }
1701#ifdef HAVE_IPV6
1702 if (CMD_IPV6(dst)) {
1703 if (cmd_ipv6_match(src))
1704 return dst;
1705 else
1706 return NULL;
1707 }
1708
1709 if (CMD_IPV6_PREFIX(dst)) {
1710 if (cmd_ipv6_prefix_match(src))
1711 return dst;
1712 else
1713 return NULL;
1714 }
1715#endif /* HAVE_IPV6 */
1716
1717 if (CMD_IPV4(dst)) {
1718 if (cmd_ipv4_match(src))
1719 return dst;
1720 else
1721 return NULL;
1722 }
1723
1724 if (CMD_IPV4_PREFIX(dst)) {
1725 if (cmd_ipv4_prefix_match(src))
1726 return dst;
1727 else
1728 return NULL;
1729 }
1730
1731 /* Optional or variable commands always match on '?' */
1732 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1733 return dst;
1734
1735 /* In case of 'command \t', given src is NULL string. */
1736 if (src == NULL)
1737 return dst;
1738
1739 if (strncmp(src, dst, strlen(src)) == 0)
1740 return dst;
1741 else
1742 return NULL;
1743}
1744
1745/* Check same string element existence. If it isn't there return
1746 1. */
1747static int cmd_unique_string(vector v, const char *str)
1748{
1749 unsigned int i;
1750 char *match;
1751
1752 for (i = 0; i < vector_active(v); i++)
1753 if ((match = vector_slot(v, i)) != NULL)
1754 if (strcmp(match, str) == 0)
1755 return 0;
1756 return 1;
1757}
1758
1759/* Compare string to description vector. If there is same string
1760 return 1 else return 0. */
1761static int desc_unique_string(vector v, const char *str)
1762{
1763 unsigned int i;
1764 struct desc *desc;
1765
1766 for (i = 0; i < vector_active(v); i++)
1767 if ((desc = vector_slot(v, i)) != NULL)
1768 if (strcmp(desc->cmd, str) == 0)
1769 return 1;
1770 return 0;
1771}
1772
1773static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1774{
1775 if (first_word != NULL &&
1776 node != AUTH_NODE &&
1777 node != VIEW_NODE &&
1778 node != AUTH_ENABLE_NODE &&
1779 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1780 return 1;
1781 return 0;
1782}
1783
1784/* '?' describe command support. */
1785static vector
1786cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1787{
1788 unsigned int i;
1789 vector cmd_vector;
1790#define INIT_MATCHVEC_SIZE 10
1791 vector matchvec;
1792 struct cmd_element *cmd_element;
1793 unsigned int index;
1794 int ret;
1795 enum match_type match;
1796 char *command;
1797 static struct desc desc_cr = { "<cr>", "" };
1798
1799 /* Set index. */
1800 if (vector_active(vline) == 0) {
1801 *status = CMD_ERR_NO_MATCH;
1802 return NULL;
1803 } else
1804 index = vector_active(vline) - 1;
1805
1806 /* Make copy vector of current node's command vector. */
1807 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1808
1809 /* Prepare match vector */
1810 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1811
1812 /* Filter commands. */
1813 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001814 for (i = 0; i < index; i++) {
1815 command = vector_slot(vline, i);
1816 if (!command)
1817 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001818
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001819 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001820
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001821 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001822 struct cmd_element *cmd_element;
1823 vector descvec;
1824 unsigned int j, k;
1825
1826 for (j = 0; j < vector_active(cmd_vector); j++)
1827 if ((cmd_element =
1828 vector_slot(cmd_vector, j)) != NULL
1829 &&
1830 (vector_active(cmd_element->strvec))) {
1831 descvec =
1832 vector_slot(cmd_element->
1833 strvec,
1834 vector_active
1835 (cmd_element->
1836 strvec) - 1);
1837 for (k = 0;
1838 k < vector_active(descvec);
1839 k++) {
1840 struct desc *desc =
1841 vector_slot(descvec,
1842 k);
1843 vector_set(matchvec,
1844 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001845 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001846 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001847
Harald Welte80d30fe2013-02-12 11:08:57 +01001848 vector_set(matchvec, &desc_cr);
1849 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001850
Harald Welte80d30fe2013-02-12 11:08:57 +01001851 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001852 }
1853
Harald Welte80d30fe2013-02-12 11:08:57 +01001854 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1855 match)) == 1) {
1856 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001857 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001858 *status = CMD_ERR_AMBIGUOUS;
1859 return NULL;
1860 } else if (ret == 2) {
1861 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001862 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001863 *status = CMD_ERR_NO_MATCH;
1864 return NULL;
1865 }
1866 }
1867
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001868 /* Prepare match vector */
1869 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1870
1871 /* Make sure that cmd_vector is filtered based on current word */
1872 command = vector_slot(vline, index);
1873 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001874 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001875
1876 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001877 for (i = 0; i < vector_active(cmd_vector); i++) {
1878 const char *string = NULL;
1879 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001880
Harald Welte80d30fe2013-02-12 11:08:57 +01001881 cmd_element = vector_slot(cmd_vector, i);
1882 if (!cmd_element)
1883 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001884
Harald Welted17aa592013-02-12 11:11:34 +01001885 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1886 continue;
1887
Harald Welte80d30fe2013-02-12 11:08:57 +01001888 strvec = cmd_element->strvec;
1889
1890 /* if command is NULL, index may be equal to vector_active */
1891 if (command && index >= vector_active(strvec))
1892 vector_slot(cmd_vector, i) = NULL;
1893 else {
1894 /* Check if command is completed. */
1895 if (command == NULL
1896 && index == vector_active(strvec)) {
1897 string = "<cr>";
1898 if (!desc_unique_string(matchvec, string))
1899 vector_set(matchvec, &desc_cr);
1900 } else {
1901 unsigned int j;
1902 vector descvec = vector_slot(strvec, index);
1903 struct desc *desc;
1904
1905 for (j = 0; j < vector_active(descvec); j++) {
1906 desc = vector_slot(descvec, j);
1907 if (!desc)
1908 continue;
1909 string = cmd_entry_function_desc
1910 (command, desc->cmd);
1911 if (!string)
1912 continue;
1913 /* Uniqueness check */
1914 if (!desc_unique_string(matchvec, string))
1915 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001916 }
1917 }
1918 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001919 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001920 vector_free(cmd_vector);
1921
1922 if (vector_slot(matchvec, 0) == NULL) {
1923 vector_free(matchvec);
1924 *status = CMD_ERR_NO_MATCH;
1925 } else
1926 *status = CMD_SUCCESS;
1927
1928 return matchvec;
1929}
1930
1931vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1932{
1933 vector ret;
1934
1935 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1936 enum node_type onode;
1937 vector shifted_vline;
1938 unsigned int index;
1939
1940 onode = vty->node;
1941 vty->node = ENABLE_NODE;
1942 /* We can try it on enable node, cos' the vty is authenticated */
1943
1944 shifted_vline = vector_init(vector_count(vline));
1945 /* use memcpy? */
1946 for (index = 1; index < vector_active(vline); index++) {
1947 vector_set_index(shifted_vline, index - 1,
1948 vector_lookup(vline, index));
1949 }
1950
1951 ret = cmd_describe_command_real(shifted_vline, vty, status);
1952
1953 vector_free(shifted_vline);
1954 vty->node = onode;
1955 return ret;
1956 }
1957
1958 return cmd_describe_command_real(vline, vty, status);
1959}
1960
1961/* Check LCD of matched command. */
1962static int cmd_lcd(char **matched)
1963{
1964 int i;
1965 int j;
1966 int lcd = -1;
1967 char *s1, *s2;
1968 char c1, c2;
1969
1970 if (matched[0] == NULL || matched[1] == NULL)
1971 return 0;
1972
1973 for (i = 1; matched[i] != NULL; i++) {
1974 s1 = matched[i - 1];
1975 s2 = matched[i];
1976
1977 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1978 if (c1 != c2)
1979 break;
1980
1981 if (lcd < 0)
1982 lcd = j;
1983 else {
1984 if (lcd > j)
1985 lcd = j;
1986 }
1987 }
1988 return lcd;
1989}
1990
1991/* Command line completion support. */
1992static char **cmd_complete_command_real(vector vline, struct vty *vty,
1993 int *status)
1994{
1995 unsigned int i;
1996 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1997#define INIT_MATCHVEC_SIZE 10
1998 vector matchvec;
1999 struct cmd_element *cmd_element;
2000 unsigned int index;
2001 char **match_str;
2002 struct desc *desc;
2003 vector descvec;
2004 char *command;
2005 int lcd;
2006
2007 if (vector_active(vline) == 0) {
2008 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002009 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002010 return NULL;
2011 } else
2012 index = vector_active(vline) - 1;
2013
2014 /* First, filter by preceeding command string */
2015 for (i = 0; i < index; i++)
2016 if ((command = vector_slot(vline, i))) {
2017 enum match_type match;
2018 int ret;
2019
2020 /* First try completion match, if there is exactly match return 1 */
2021 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002022 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002023
2024 /* If there is exact match then filter ambiguous match else check
2025 ambiguousness. */
2026 if ((ret =
2027 is_cmd_ambiguous(command, cmd_vector, i,
2028 match)) == 1) {
2029 vector_free(cmd_vector);
2030 *status = CMD_ERR_AMBIGUOUS;
2031 return NULL;
2032 }
2033 /*
2034 else if (ret == 2)
2035 {
2036 vector_free (cmd_vector);
2037 *status = CMD_ERR_NO_MATCH;
2038 return NULL;
2039 }
2040 */
2041 }
2042
2043 /* Prepare match vector. */
2044 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2045
2046 /* Now we got into completion */
2047 for (i = 0; i < vector_active(cmd_vector); i++)
2048 if ((cmd_element = vector_slot(cmd_vector, i))) {
2049 const char *string;
2050 vector strvec = cmd_element->strvec;
2051
2052 /* Check field length */
2053 if (index >= vector_active(strvec))
2054 vector_slot(cmd_vector, i) = NULL;
2055 else {
2056 unsigned int j;
2057
2058 descvec = vector_slot(strvec, index);
2059 for (j = 0; j < vector_active(descvec); j++)
2060 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002061 const char *cmd = desc->cmd;
2062 char *tmp = NULL;
2063
2064 if (CMD_OPTION(desc->cmd)) {
2065 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2066 cmd = tmp;
2067 }
2068 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002069 if (cmd_unique_string (matchvec, string))
2070 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002071 if (tmp)
2072 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002073 }
2074 }
2075 }
2076
2077 /* We don't need cmd_vector any more. */
2078 vector_free(cmd_vector);
2079
2080 /* No matched command */
2081 if (vector_slot(matchvec, 0) == NULL) {
2082 vector_free(matchvec);
2083
2084 /* In case of 'command \t' pattern. Do you need '?' command at
2085 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002086 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002087 *status = CMD_ERR_NOTHING_TODO;
2088 else
2089 *status = CMD_ERR_NO_MATCH;
2090 return NULL;
2091 }
2092
2093 /* Only one matched */
2094 if (vector_slot(matchvec, 1) == NULL) {
2095 match_str = (char **)matchvec->index;
2096 vector_only_wrapper_free(matchvec);
2097 *status = CMD_COMPLETE_FULL_MATCH;
2098 return match_str;
2099 }
2100 /* Make it sure last element is NULL. */
2101 vector_set(matchvec, NULL);
2102
2103 /* Check LCD of matched strings. */
2104 if (vector_slot(vline, index) != NULL) {
2105 lcd = cmd_lcd((char **)matchvec->index);
2106
2107 if (lcd) {
2108 int len = strlen(vector_slot(vline, index));
2109
2110 if (len < lcd) {
2111 char *lcdstr;
2112
2113 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2114 "complete-lcdstr");
2115 memcpy(lcdstr, matchvec->index[0], lcd);
2116 lcdstr[lcd] = '\0';
2117
2118 /* match_str = (char **) &lcdstr; */
2119
2120 /* Free matchvec. */
2121 for (i = 0; i < vector_active(matchvec); i++) {
2122 if (vector_slot(matchvec, i))
2123 talloc_free(vector_slot(matchvec, i));
2124 }
2125 vector_free(matchvec);
2126
2127 /* Make new matchvec. */
2128 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2129 vector_set(matchvec, lcdstr);
2130 match_str = (char **)matchvec->index;
2131 vector_only_wrapper_free(matchvec);
2132
2133 *status = CMD_COMPLETE_MATCH;
2134 return match_str;
2135 }
2136 }
2137 }
2138
2139 match_str = (char **)matchvec->index;
2140 vector_only_wrapper_free(matchvec);
2141 *status = CMD_COMPLETE_LIST_MATCH;
2142 return match_str;
2143}
2144
2145char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2146{
2147 char **ret;
2148
2149 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2150 enum node_type onode;
2151 vector shifted_vline;
2152 unsigned int index;
2153
2154 onode = vty->node;
2155 vty->node = ENABLE_NODE;
2156 /* We can try it on enable node, cos' the vty is authenticated */
2157
2158 shifted_vline = vector_init(vector_count(vline));
2159 /* use memcpy? */
2160 for (index = 1; index < vector_active(vline); index++) {
2161 vector_set_index(shifted_vline, index - 1,
2162 vector_lookup(vline, index));
2163 }
2164
2165 ret = cmd_complete_command_real(shifted_vline, vty, status);
2166
2167 vector_free(shifted_vline);
2168 vty->node = onode;
2169 return ret;
2170 }
2171
2172 return cmd_complete_command_real(vline, vty, status);
2173}
2174
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002175static struct vty_parent_node *vty_parent(struct vty *vty)
2176{
2177 return llist_first_entry_or_null(&vty->parent_nodes,
2178 struct vty_parent_node,
2179 entry);
2180}
2181
2182static bool vty_pop_parent(struct vty *vty)
2183{
2184 struct vty_parent_node *parent = vty_parent(vty);
2185 if (!parent)
2186 return false;
2187 llist_del(&parent->entry);
2188 vty->node = parent->node;
2189 vty->priv = parent->priv;
2190 if (vty->indent)
2191 talloc_free(vty->indent);
2192 vty->indent = parent->indent;
2193 talloc_free(parent);
2194 return true;
2195}
2196
2197static void vty_clear_parents(struct vty *vty)
2198{
2199 while (vty_pop_parent(vty));
2200}
2201
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002202/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002203/*
2204 * This function MUST eventually converge on a node when called repeatedly,
2205 * there must not be any cycles.
2206 * All 'config' nodes shall converge on CONFIG_NODE.
2207 * All other 'enable' nodes shall converge on ENABLE_NODE.
2208 * All 'view' only nodes shall converge on VIEW_NODE.
2209 * All other nodes shall converge on themselves or it must be ensured,
2210 * that the user's rights are not extended anyhow by calling this function.
2211 *
2212 * Note that these requirements also apply to all functions that are used
2213 * as go_parent_cb.
2214 * Note also that this function relies on the is_config_child callback to
2215 * recognize non-config nodes if go_parent_cb is not set.
2216 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002217int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002218{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002219 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002220 case AUTH_NODE:
2221 case VIEW_NODE:
2222 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002223 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002224 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002225 break;
2226
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002227 case AUTH_ENABLE_NODE:
2228 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002229 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002230 break;
2231
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002232 default:
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002233 if (host.app_info->go_parent_cb)
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002234 host.app_info->go_parent_cb(vty);
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002235 vty_pop_parent(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002236 break;
2237 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002238
2239 return vty->node;
2240}
2241
2242/* Execute command by argument vline vector. */
2243static int
2244cmd_execute_command_real(vector vline, struct vty *vty,
2245 struct cmd_element **cmd)
2246{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002247 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002248 unsigned int index;
2249 vector cmd_vector;
2250 struct cmd_element *cmd_element;
2251 struct cmd_element *matched_element;
2252 unsigned int matched_count, incomplete_count;
2253 int argc;
2254 const char *argv[CMD_ARGC_MAX];
2255 enum match_type match = 0;
2256 int varflag;
2257 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002258 int rc;
2259 /* Used for temporary storage of cmd_deopt() allocated arguments during
2260 argv[] generation */
2261 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002262
2263 /* Make copy of command elements. */
2264 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2265
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002266 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002267 if ((command = vector_slot(vline, index))) {
2268 int ret;
2269
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002270 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002271 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002272
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002273 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002274 break;
2275
2276 ret =
2277 is_cmd_ambiguous(command, cmd_vector, index, match);
2278
2279 if (ret == 1) {
2280 vector_free(cmd_vector);
2281 return CMD_ERR_AMBIGUOUS;
2282 } else if (ret == 2) {
2283 vector_free(cmd_vector);
2284 return CMD_ERR_NO_MATCH;
2285 }
2286 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002287 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002288
2289 /* Check matched count. */
2290 matched_element = NULL;
2291 matched_count = 0;
2292 incomplete_count = 0;
2293
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002294 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002295 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002296 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002297 || index >= cmd_element->cmdsize) {
2298 matched_element = cmd_element;
2299#if 0
2300 printf("DEBUG: %s\n", cmd_element->string);
2301#endif
2302 matched_count++;
2303 } else {
2304 incomplete_count++;
2305 }
2306 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002307 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002308
2309 /* Finish of using cmd_vector. */
2310 vector_free(cmd_vector);
2311
2312 /* To execute command, matched_count must be 1. */
2313 if (matched_count == 0) {
2314 if (incomplete_count)
2315 return CMD_ERR_INCOMPLETE;
2316 else
2317 return CMD_ERR_NO_MATCH;
2318 }
2319
2320 if (matched_count > 1)
2321 return CMD_ERR_AMBIGUOUS;
2322
2323 /* Argument treatment */
2324 varflag = 0;
2325 argc = 0;
2326
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002327 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2328
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002329 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002330 if (argc == CMD_ARGC_MAX) {
2331 rc = CMD_ERR_EXEED_ARGC_MAX;
2332 goto rc_free_deopt_ctx;
2333 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002334 if (varflag) {
2335 argv[argc++] = vector_slot(vline, i);
2336 continue;
2337 }
2338
2339 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002340 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002341
2342 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002343 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002344 struct desc *desc = vector_slot(descvec, 0);
2345
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002346 if (CMD_OPTION(desc->cmd)) {
2347 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2348 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2349 } else {
2350 tmp_cmd = desc->cmd;
2351 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002352
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002353 if (CMD_VARARG(tmp_cmd))
2354 varflag = 1;
2355 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002356 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002357 else if (CMD_OPTION(desc->cmd))
2358 argv[argc++] = tmp_cmd;
2359 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002360 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002361 /* multi choice argument. look up which choice
2362 the user meant (can only be one after
2363 filtering and checking for ambigous). For instance,
2364 if user typed "th" for "(two|three)" arg, we
2365 want to pass "three" in argv[]. */
2366 for (j = 0; j < vector_active(descvec); j++) {
2367 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002368 if (!desc)
2369 continue;
2370 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2371 continue;
2372 if (CMD_OPTION(desc->cmd)) {
2373 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2374 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2375 } else {
2376 tmp_cmd = desc->cmd;
2377 }
2378
2379 if(CMD_VARIABLE(tmp_cmd)) {
2380 argv[argc++] = vector_slot(vline, i);
2381 } else {
2382 argv[argc++] = tmp_cmd;
2383 }
2384 break;
2385 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002386 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002387 }
2388
2389 /* For vtysh execution. */
2390 if (cmd)
2391 *cmd = matched_element;
2392
2393 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002394 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002395 else {
2396 /* Execute matched command. */
2397 struct vty_parent_node this_node = {
2398 .node = vty->node,
2399 .priv = vty->priv,
2400 .indent = vty->indent,
2401 };
2402 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002403 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002404
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002405 /* If we have stepped down into a child node, push a parent frame.
2406 * The causality is such: we don't expect every single node entry implementation to push
2407 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2408 * a parent node. Hence if the node changed without the parent node changing, we must
2409 * have stepped into a child node. */
2410 if (vty->node != this_node.node && parent == vty_parent(vty)
2411 && vty->node > CONFIG_NODE) {
2412 /* Push the parent node. */
2413 parent = talloc_zero(vty, struct vty_parent_node);
2414 *parent = this_node;
2415 llist_add(&parent->entry, &vty->parent_nodes);
2416 }
2417 }
2418
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002419rc_free_deopt_ctx:
2420 /* Now after we called the command func, we can free temporary strings */
2421 talloc_free(cmd_deopt_ctx);
2422 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002423}
2424
2425int
2426cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2427 int vtysh)
2428{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002429 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002430 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002431
2432 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002433
2434 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2435 vector shifted_vline;
2436 unsigned int index;
2437
2438 vty->node = ENABLE_NODE;
2439 /* We can try it on enable node, cos' the vty is authenticated */
2440
2441 shifted_vline = vector_init(vector_count(vline));
2442 /* use memcpy? */
2443 for (index = 1; index < vector_active(vline); index++) {
2444 vector_set_index(shifted_vline, index - 1,
2445 vector_lookup(vline, index));
2446 }
2447
2448 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2449
2450 vector_free(shifted_vline);
2451 vty->node = onode;
2452 return ret;
2453 }
2454
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002455 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002456}
2457
2458/* Execute command by argument readline. */
2459int
2460cmd_execute_command_strict(vector vline, struct vty *vty,
2461 struct cmd_element **cmd)
2462{
2463 unsigned int i;
2464 unsigned int index;
2465 vector cmd_vector;
2466 struct cmd_element *cmd_element;
2467 struct cmd_element *matched_element;
2468 unsigned int matched_count, incomplete_count;
2469 int argc;
2470 const char *argv[CMD_ARGC_MAX];
2471 int varflag;
2472 enum match_type match = 0;
2473 char *command;
2474
2475 /* Make copy of command element */
2476 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2477
2478 for (index = 0; index < vector_active(vline); index++)
2479 if ((command = vector_slot(vline, index))) {
2480 int ret;
2481
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002482 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002483 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002484
2485 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002486 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002487 break;
2488
2489 ret =
2490 is_cmd_ambiguous(command, cmd_vector, index, match);
2491 if (ret == 1) {
2492 vector_free(cmd_vector);
2493 return CMD_ERR_AMBIGUOUS;
2494 }
2495 if (ret == 2) {
2496 vector_free(cmd_vector);
2497 return CMD_ERR_NO_MATCH;
2498 }
2499 }
2500
2501 /* Check matched count. */
2502 matched_element = NULL;
2503 matched_count = 0;
2504 incomplete_count = 0;
2505 for (i = 0; i < vector_active(cmd_vector); i++)
2506 if (vector_slot(cmd_vector, i) != NULL) {
2507 cmd_element = vector_slot(cmd_vector, i);
2508
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002509 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002510 || index >= cmd_element->cmdsize) {
2511 matched_element = cmd_element;
2512 matched_count++;
2513 } else
2514 incomplete_count++;
2515 }
2516
2517 /* Finish of using cmd_vector. */
2518 vector_free(cmd_vector);
2519
2520 /* To execute command, matched_count must be 1. */
2521 if (matched_count == 0) {
2522 if (incomplete_count)
2523 return CMD_ERR_INCOMPLETE;
2524 else
2525 return CMD_ERR_NO_MATCH;
2526 }
2527
2528 if (matched_count > 1)
2529 return CMD_ERR_AMBIGUOUS;
2530
2531 /* Argument treatment */
2532 varflag = 0;
2533 argc = 0;
2534
2535 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002536 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002537 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002538 if (varflag) {
2539 argv[argc++] = vector_slot(vline, i);
2540 continue;
2541 }
2542
2543 vector descvec = vector_slot(matched_element->strvec, i);
2544
2545 if (vector_active(descvec) == 1) {
2546 struct desc *desc = vector_slot(descvec, 0);
2547
2548 if (CMD_VARARG(desc->cmd))
2549 varflag = 1;
2550
2551 if (varflag || CMD_VARIABLE(desc->cmd)
2552 || CMD_OPTION(desc->cmd))
2553 argv[argc++] = vector_slot(vline, i);
2554 } else {
2555 argv[argc++] = vector_slot(vline, i);
2556 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002557 }
2558
2559 /* For vtysh execution. */
2560 if (cmd)
2561 *cmd = matched_element;
2562
2563 if (matched_element->daemon)
2564 return CMD_SUCCESS_DAEMON;
2565
2566 /* Now execute matched command */
2567 return (*matched_element->func) (matched_element, vty, argc, argv);
2568}
2569
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002570static inline size_t len(const char *str)
2571{
2572 return str? strlen(str) : 0;
2573}
2574
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002575/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2576 * is longer than b, a must start with exactly b, and vice versa.
2577 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2578 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002579static int indent_cmp(const char *a, const char *b)
2580{
2581 size_t al, bl;
2582 al = len(a);
2583 bl = len(b);
2584 if (al > bl) {
2585 if (bl && strncmp(a, b, bl) != 0)
2586 return EINVAL;
2587 return 1;
2588 }
2589 /* al <= bl */
2590 if (al && strncmp(a, b, al) != 0)
2591 return EINVAL;
2592 return (al < bl)? -1 : 0;
2593}
2594
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002595/* Configration make from file. */
2596int config_from_file(struct vty *vty, FILE * fp)
2597{
2598 int ret;
2599 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002600 char *indent;
2601 int cmp;
2602 struct vty_parent_node this_node;
2603 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002604
2605 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002606 indent = NULL;
2607 vline = NULL;
2608 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002609
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002610 if (ret != CMD_SUCCESS)
2611 goto return_invalid_indent;
2612
2613 /* In case of comment or empty line */
2614 if (vline == NULL) {
2615 if (indent) {
2616 talloc_free(indent);
2617 indent = NULL;
2618 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002619 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002620 }
2621
Neels Hofmeyr43063632017-09-19 23:54:01 +02002622 /* We have a nonempty line. */
2623 if (!vty->indent) {
2624 /* We have just entered a node and expecting the first child to come up; but we
2625 * may also skip right back to a parent or ancestor level. */
2626 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002627
Neels Hofmeyr43063632017-09-19 23:54:01 +02002628 /* If there is no parent, record any indentation we encounter. */
2629 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2630
2631 if (cmp == EINVAL)
2632 goto return_invalid_indent;
2633
2634 if (cmp <= 0) {
2635 /* We have gone right back to the parent level or higher, we are skipping
2636 * this child node level entirely. Pop the parent to go back to a node
2637 * that was actually there (to reinstate vty->indent) and re-use below
2638 * go-parent while-loop to find an accurate match of indent in the node
2639 * ancestry. */
2640 vty_go_parent(vty);
2641 } else {
2642 /* The indent is deeper than the just entered parent, record the new
2643 * indentation characters. */
2644 vty->indent = talloc_strdup(vty, indent);
2645 /* This *is* the new indentation. */
2646 cmp = 0;
2647 }
2648 } else {
2649 /* There is a known indentation for this node level, validate and detect node
2650 * exits. */
2651 cmp = indent_cmp(indent, vty->indent);
2652 if (cmp == EINVAL)
2653 goto return_invalid_indent;
2654 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002655
2656 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2657 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2658 while (cmp < 0) {
2659 vty_go_parent(vty);
2660 cmp = indent_cmp(indent, vty->indent);
2661 if (cmp == EINVAL)
2662 goto return_invalid_indent;
2663 }
2664
2665 /* More indent without having entered a child node level? Either the parent node's indent
2666 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2667 * or the indentation increased even though the vty command didn't enter a child. */
2668 if (cmp > 0)
2669 goto return_invalid_indent;
2670
2671 /* Remember the current node before the command possibly changes it. */
2672 this_node = (struct vty_parent_node){
2673 .node = vty->node,
2674 .priv = vty->priv,
2675 .indent = vty->indent,
2676 };
2677
2678 parent = vty_parent(vty);
2679 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002680 cmd_free_strvec(vline);
2681
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002682 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002683 if (indent) {
2684 talloc_free(indent);
2685 indent = NULL;
2686 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002687 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002688 }
2689
2690 /* If we have stepped down into a child node, push a parent frame.
2691 * The causality is such: we don't expect every single node entry implementation to push
2692 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2693 * a parent node. Hence if the node changed without the parent node changing, we must
2694 * have stepped into a child node (and now expect a deeper indent). */
2695 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2696 /* Push the parent node. */
2697 parent = talloc_zero(vty, struct vty_parent_node);
2698 *parent = this_node;
2699 llist_add(&parent->entry, &vty->parent_nodes);
2700
2701 /* The current talloc'ed vty->indent string will now be owned by this parent
2702 * struct. Indicate that we don't know what deeper indent characters the user
2703 * will choose. */
2704 vty->indent = NULL;
2705 }
2706
2707 if (indent) {
2708 talloc_free(indent);
2709 indent = NULL;
2710 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002711 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002712 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2713 while (vty_parent(vty))
2714 vty_go_parent(vty);
2715
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002716 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002717
2718return_invalid_indent:
2719 if (vline)
2720 cmd_free_strvec(vline);
2721 if (indent) {
2722 talloc_free(indent);
2723 indent = NULL;
2724 }
2725 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002726}
2727
2728/* Configration from terminal */
2729DEFUN(config_terminal,
2730 config_terminal_cmd,
2731 "configure terminal",
2732 "Configuration from vty interface\n" "Configuration terminal\n")
2733{
2734 if (vty_config_lock(vty))
2735 vty->node = CONFIG_NODE;
2736 else {
2737 vty_out(vty, "VTY configuration is locked by other VTY%s",
2738 VTY_NEWLINE);
2739 return CMD_WARNING;
2740 }
2741 return CMD_SUCCESS;
2742}
2743
2744/* Enable command */
2745DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2746{
2747 /* If enable password is NULL, change to ENABLE_NODE */
2748 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2749 vty->type == VTY_SHELL_SERV)
2750 vty->node = ENABLE_NODE;
2751 else
2752 vty->node = AUTH_ENABLE_NODE;
2753
2754 return CMD_SUCCESS;
2755}
2756
2757/* Disable command */
2758DEFUN(disable,
2759 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2760{
2761 if (vty->node == ENABLE_NODE)
2762 vty->node = VIEW_NODE;
2763 return CMD_SUCCESS;
2764}
2765
2766/* Down vty node level. */
2767gDEFUN(config_exit,
2768 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2769{
2770 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002771 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002772 case VIEW_NODE:
2773 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002774 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002775 break;
2776 case CONFIG_NODE:
2777 vty->node = ENABLE_NODE;
2778 vty_config_unlock(vty);
2779 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002780 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002781 if (vty->node > CONFIG_NODE)
2782 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002783 break;
2784 }
2785 return CMD_SUCCESS;
2786}
2787
2788/* End of configuration. */
2789 gDEFUN(config_end,
2790 config_end_cmd, "end", "End current mode and change to enable mode.")
2791{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002792 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002793 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002794
2795 /* Repeatedly call go_parent until a top node is reached. */
2796 while (vty->node > CONFIG_NODE) {
2797 if (vty->node == last_node) {
2798 /* Ensure termination, this shouldn't happen. */
2799 break;
2800 }
2801 last_node = vty->node;
2802 vty_go_parent(vty);
2803 }
2804
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002805 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002806 if (vty->node > ENABLE_NODE)
2807 vty->node = ENABLE_NODE;
2808 vty->index = NULL;
2809 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002810 }
2811 return CMD_SUCCESS;
2812}
2813
2814/* Show version. */
2815DEFUN(show_version,
2816 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2817{
Harald Welte237f6242010-05-25 23:00:45 +02002818 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2819 host.app_info->version,
2820 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2821 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002822
2823 return CMD_SUCCESS;
2824}
2825
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002826DEFUN(show_online_help,
2827 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2828{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002829 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002830 return CMD_SUCCESS;
2831}
2832
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002833/* Help display function for all node. */
2834gDEFUN(config_help,
2835 config_help_cmd, "help", "Description of the interactive help system\n")
2836{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002837 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2838 "anytime at the command line please press '?'.%s%s"
2839 "If nothing matches, the help list will be empty and you must backup%s"
2840 " until entering a '?' shows the available options.%s"
2841 "Two styles of help are provided:%s"
2842 "1. Full help is available when you are ready to enter a%s"
2843 "command argument (e.g. 'show ?') and describes each possible%s"
2844 "argument.%s"
2845 "2. Partial help is provided when an abbreviated argument is entered%s"
2846 " and you want to know what arguments match the input%s"
2847 " (e.g. 'show me?'.)%s%s",
2848 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2849 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2850 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2851 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002852 return CMD_SUCCESS;
2853}
2854
2855/* Help display function for all node. */
2856gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2857{
2858 unsigned int i;
2859 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2860 struct cmd_element *cmd;
2861
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002862 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2863 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2864 continue;
2865 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2866 continue;
2867 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2868 }
2869
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002870 return CMD_SUCCESS;
2871}
2872
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002873static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002874{
2875 unsigned int i;
2876 int fd;
2877 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002878 char *config_file_tmp = NULL;
2879 char *config_file_sav = NULL;
2880 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002881 struct stat st;
2882
2883 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002884
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002885 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2886 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2887 * manually instead. */
2888
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002889 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002890 config_file_sav =
2891 _talloc_zero(tall_vty_cmd_ctx,
2892 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2893 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002894 if (!config_file_sav)
2895 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002896 strcpy(config_file_sav, config_file);
2897 strcat(config_file_sav, CONF_BACKUP_EXT);
2898
2899 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002900 "config_file_tmp");
2901 if (!config_file_tmp) {
2902 talloc_free(config_file_sav);
2903 return -1;
2904 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002905 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2906
2907 /* Open file to configuration write. */
2908 fd = mkstemp(config_file_tmp);
2909 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002910 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002911 talloc_free(config_file_tmp);
2912 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002913 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002914 }
2915
2916 /* Make vty for configuration file. */
2917 file_vty = vty_new();
2918 file_vty->fd = fd;
2919 file_vty->type = VTY_FILE;
2920
2921 /* Config file header print. */
2922 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002923 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002924 //vty_time_print (file_vty, 1);
2925 vty_out(file_vty, "!\n");
2926
2927 for (i = 0; i < vector_active(cmdvec); i++)
2928 if ((node = vector_slot(cmdvec, i)) && node->func) {
2929 if ((*node->func) (file_vty))
2930 vty_out(file_vty, "!\n");
2931 }
2932 vty_close(file_vty);
2933
2934 if (unlink(config_file_sav) != 0)
2935 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002936 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002937 talloc_free(config_file_sav);
2938 talloc_free(config_file_tmp);
2939 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002940 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002941 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002942
2943 /* Only link the .sav file if the original file exists */
2944 if (stat(config_file, &st) == 0) {
2945 if (link(config_file, config_file_sav) != 0) {
2946 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2947 talloc_free(config_file_sav);
2948 talloc_free(config_file_tmp);
2949 unlink(config_file_tmp);
2950 return -3;
2951 }
2952 sync();
2953 if (unlink(config_file) != 0) {
2954 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2955 talloc_free(config_file_sav);
2956 talloc_free(config_file_tmp);
2957 unlink(config_file_tmp);
2958 return -4;
2959 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002960 }
2961 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002962 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002963 talloc_free(config_file_sav);
2964 talloc_free(config_file_tmp);
2965 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002966 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002967 }
2968 unlink(config_file_tmp);
2969 sync();
2970
2971 talloc_free(config_file_sav);
2972 talloc_free(config_file_tmp);
2973
2974 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002975 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2976 return -6;
2977 }
2978
2979 return 0;
2980}
2981
2982
2983/* Write current configuration into file. */
2984DEFUN(config_write_file,
2985 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002986 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002987 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002988 "Write to configuration file\n"
2989 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002990{
2991 char *failed_file;
2992 int rc;
2993
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002994 if (host.app_info->config_is_consistent) {
2995 rc = host.app_info->config_is_consistent(vty);
2996 if (!rc) {
2997 vty_out(vty, "Configuration is not consistent%s",
2998 VTY_NEWLINE);
2999 return CMD_WARNING;
3000 }
3001 }
3002
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003003 if (argc == 1)
3004 host_config_set(argv[0]);
3005
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003006 if (host.config == NULL) {
3007 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3008 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003009 return CMD_WARNING;
3010 }
3011
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003012 rc = write_config_file(host.config, &failed_file);
3013 switch (rc) {
3014 case -1:
3015 vty_out(vty, "Can't open configuration file %s.%s",
3016 failed_file, VTY_NEWLINE);
3017 rc = CMD_WARNING;
3018 break;
3019 case -2:
3020 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3021 failed_file, VTY_NEWLINE);
3022 rc = CMD_WARNING;
3023 break;
3024 case -3:
3025 vty_out(vty, "Can't backup old configuration file %s.%s",
3026 failed_file, VTY_NEWLINE);
3027 rc = CMD_WARNING;
3028 break;
3029 case -4:
3030 vty_out(vty, "Can't unlink configuration file %s.%s",
3031 failed_file, VTY_NEWLINE);
3032 rc = CMD_WARNING;
3033 break;
3034 case -5:
3035 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3036 VTY_NEWLINE);
3037 rc = CMD_WARNING;
3038 break;
3039 case -6:
3040 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3041 failed_file, strerror(errno), errno, VTY_NEWLINE);
3042 rc = CMD_WARNING;
3043 break;
3044 default:
3045 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3046 rc = CMD_SUCCESS;
3047 break;
3048 }
3049
3050 talloc_free(failed_file);
3051 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003052}
3053
3054ALIAS(config_write_file,
3055 config_write_cmd,
3056 "write", "Write running configuration to memory, network, or terminal\n")
3057
3058 ALIAS(config_write_file,
3059 config_write_memory_cmd,
3060 "write memory",
3061 "Write running configuration to memory, network, or terminal\n"
3062 "Write configuration to the file (same as write file)\n")
3063
3064 ALIAS(config_write_file,
3065 copy_runningconfig_startupconfig_cmd,
3066 "copy running-config startup-config",
3067 "Copy configuration\n"
3068 "Copy running config to... \n"
3069 "Copy running config to startup config (same as write file)\n")
3070
3071/* Write current configuration into the terminal. */
3072 DEFUN(config_write_terminal,
3073 config_write_terminal_cmd,
3074 "write terminal",
3075 "Write running configuration to memory, network, or terminal\n"
3076 "Write to terminal\n")
3077{
3078 unsigned int i;
3079 struct cmd_node *node;
3080
3081 if (vty->type == VTY_SHELL_SERV) {
3082 for (i = 0; i < vector_active(cmdvec); i++)
3083 if ((node = vector_slot(cmdvec, i)) && node->func
3084 && node->vtysh) {
3085 if ((*node->func) (vty))
3086 vty_out(vty, "!%s", VTY_NEWLINE);
3087 }
3088 } else {
3089 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3090 VTY_NEWLINE);
3091 vty_out(vty, "!%s", VTY_NEWLINE);
3092
3093 for (i = 0; i < vector_active(cmdvec); i++)
3094 if ((node = vector_slot(cmdvec, i)) && node->func) {
3095 if ((*node->func) (vty))
3096 vty_out(vty, "!%s", VTY_NEWLINE);
3097 }
3098 vty_out(vty, "end%s", VTY_NEWLINE);
3099 }
3100 return CMD_SUCCESS;
3101}
3102
3103/* Write current configuration into the terminal. */
3104ALIAS(config_write_terminal,
3105 show_running_config_cmd,
3106 "show running-config", SHOW_STR "running configuration\n")
3107
3108/* Write startup configuration into the terminal. */
3109 DEFUN(show_startup_config,
3110 show_startup_config_cmd,
3111 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3112{
3113 char buf[BUFSIZ];
3114 FILE *confp;
3115
3116 confp = fopen(host.config, "r");
3117 if (confp == NULL) {
3118 vty_out(vty, "Can't open configuration file [%s]%s",
3119 host.config, VTY_NEWLINE);
3120 return CMD_WARNING;
3121 }
3122
3123 while (fgets(buf, BUFSIZ, confp)) {
3124 char *cp = buf;
3125
3126 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3127 cp++;
3128 *cp = '\0';
3129
3130 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3131 }
3132
3133 fclose(confp);
3134
3135 return CMD_SUCCESS;
3136}
3137
3138/* Hostname configuration */
3139DEFUN(config_hostname,
3140 hostname_cmd,
3141 "hostname WORD",
3142 "Set system's network name\n" "This system's network name\n")
3143{
3144 if (!isalpha((int)*argv[0])) {
3145 vty_out(vty, "Please specify string starting with alphabet%s",
3146 VTY_NEWLINE);
3147 return CMD_WARNING;
3148 }
3149
3150 if (host.name)
3151 talloc_free(host.name);
3152
3153 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3154 return CMD_SUCCESS;
3155}
3156
3157DEFUN(config_no_hostname,
3158 no_hostname_cmd,
3159 "no hostname [HOSTNAME]",
3160 NO_STR "Reset system's network name\n" "Host name of this router\n")
3161{
3162 if (host.name)
3163 talloc_free(host.name);
3164 host.name = NULL;
3165 return CMD_SUCCESS;
3166}
3167
3168/* VTY interface password set. */
3169DEFUN(config_password, password_cmd,
3170 "password (8|) WORD",
3171 "Assign the terminal connection password\n"
3172 "Specifies a HIDDEN password will follow\n"
3173 "dummy string \n" "The HIDDEN line password string\n")
3174{
3175 /* Argument check. */
3176 if (argc == 0) {
3177 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3178 return CMD_WARNING;
3179 }
3180
3181 if (argc == 2) {
3182 if (*argv[0] == '8') {
3183 if (host.password)
3184 talloc_free(host.password);
3185 host.password = NULL;
3186 if (host.password_encrypt)
3187 talloc_free(host.password_encrypt);
3188 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3189 return CMD_SUCCESS;
3190 } else {
3191 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3192 return CMD_WARNING;
3193 }
3194 }
3195
3196 if (!isalnum((int)*argv[0])) {
3197 vty_out(vty,
3198 "Please specify string starting with alphanumeric%s",
3199 VTY_NEWLINE);
3200 return CMD_WARNING;
3201 }
3202
3203 if (host.password)
3204 talloc_free(host.password);
3205 host.password = NULL;
3206
3207#ifdef VTY_CRYPT_PW
3208 if (host.encrypt) {
3209 if (host.password_encrypt)
3210 talloc_free(host.password_encrypt);
3211 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3212 } else
3213#endif
3214 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3215
3216 return CMD_SUCCESS;
3217}
3218
3219ALIAS(config_password, password_text_cmd,
3220 "password LINE",
3221 "Assign the terminal connection password\n"
3222 "The UNENCRYPTED (cleartext) line password\n")
3223
3224/* VTY enable password set. */
3225 DEFUN(config_enable_password, enable_password_cmd,
3226 "enable password (8|) WORD",
3227 "Modify enable password parameters\n"
3228 "Assign the privileged level password\n"
3229 "Specifies a HIDDEN password will follow\n"
3230 "dummy string \n" "The HIDDEN 'enable' password string\n")
3231{
3232 /* Argument check. */
3233 if (argc == 0) {
3234 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3235 return CMD_WARNING;
3236 }
3237
3238 /* Crypt type is specified. */
3239 if (argc == 2) {
3240 if (*argv[0] == '8') {
3241 if (host.enable)
3242 talloc_free(host.enable);
3243 host.enable = NULL;
3244
3245 if (host.enable_encrypt)
3246 talloc_free(host.enable_encrypt);
3247 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3248
3249 return CMD_SUCCESS;
3250 } else {
3251 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3252 return CMD_WARNING;
3253 }
3254 }
3255
3256 if (!isalnum((int)*argv[0])) {
3257 vty_out(vty,
3258 "Please specify string starting with alphanumeric%s",
3259 VTY_NEWLINE);
3260 return CMD_WARNING;
3261 }
3262
3263 if (host.enable)
3264 talloc_free(host.enable);
3265 host.enable = NULL;
3266
3267 /* Plain password input. */
3268#ifdef VTY_CRYPT_PW
3269 if (host.encrypt) {
3270 if (host.enable_encrypt)
3271 talloc_free(host.enable_encrypt);
3272 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3273 } else
3274#endif
3275 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3276
3277 return CMD_SUCCESS;
3278}
3279
3280ALIAS(config_enable_password,
3281 enable_password_text_cmd,
3282 "enable password LINE",
3283 "Modify enable password parameters\n"
3284 "Assign the privileged level password\n"
3285 "The UNENCRYPTED (cleartext) 'enable' password\n")
3286
3287/* VTY enable password delete. */
3288 DEFUN(no_config_enable_password, no_enable_password_cmd,
3289 "no enable password",
3290 NO_STR
3291 "Modify enable password parameters\n"
3292 "Assign the privileged level password\n")
3293{
3294 if (host.enable)
3295 talloc_free(host.enable);
3296 host.enable = NULL;
3297
3298 if (host.enable_encrypt)
3299 talloc_free(host.enable_encrypt);
3300 host.enable_encrypt = NULL;
3301
3302 return CMD_SUCCESS;
3303}
3304
3305#ifdef VTY_CRYPT_PW
3306DEFUN(service_password_encrypt,
3307 service_password_encrypt_cmd,
3308 "service password-encryption",
3309 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3310{
3311 if (host.encrypt)
3312 return CMD_SUCCESS;
3313
3314 host.encrypt = 1;
3315
3316 if (host.password) {
3317 if (host.password_encrypt)
3318 talloc_free(host.password_encrypt);
3319 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3320 }
3321 if (host.enable) {
3322 if (host.enable_encrypt)
3323 talloc_free(host.enable_encrypt);
3324 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3325 }
3326
3327 return CMD_SUCCESS;
3328}
3329
3330DEFUN(no_service_password_encrypt,
3331 no_service_password_encrypt_cmd,
3332 "no service password-encryption",
3333 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3334{
3335 if (!host.encrypt)
3336 return CMD_SUCCESS;
3337
3338 host.encrypt = 0;
3339
3340 if (host.password_encrypt)
3341 talloc_free(host.password_encrypt);
3342 host.password_encrypt = NULL;
3343
3344 if (host.enable_encrypt)
3345 talloc_free(host.enable_encrypt);
3346 host.enable_encrypt = NULL;
3347
3348 return CMD_SUCCESS;
3349}
3350#endif
3351
3352DEFUN(config_terminal_length, config_terminal_length_cmd,
3353 "terminal length <0-512>",
3354 "Set terminal line parameters\n"
3355 "Set number of lines on a screen\n"
3356 "Number of lines on screen (0 for no pausing)\n")
3357{
3358 int lines;
3359 char *endptr = NULL;
3360
3361 lines = strtol(argv[0], &endptr, 10);
3362 if (lines < 0 || lines > 512 || *endptr != '\0') {
3363 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3364 return CMD_WARNING;
3365 }
3366 vty->lines = lines;
3367
3368 return CMD_SUCCESS;
3369}
3370
3371DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3372 "terminal no length",
3373 "Set terminal line parameters\n"
3374 NO_STR "Set number of lines on a screen\n")
3375{
3376 vty->lines = -1;
3377 return CMD_SUCCESS;
3378}
3379
3380DEFUN(service_terminal_length, service_terminal_length_cmd,
3381 "service terminal-length <0-512>",
3382 "Set up miscellaneous service\n"
3383 "System wide terminal length configuration\n"
3384 "Number of lines of VTY (0 means no line control)\n")
3385{
3386 int lines;
3387 char *endptr = NULL;
3388
3389 lines = strtol(argv[0], &endptr, 10);
3390 if (lines < 0 || lines > 512 || *endptr != '\0') {
3391 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3392 return CMD_WARNING;
3393 }
3394 host.lines = lines;
3395
3396 return CMD_SUCCESS;
3397}
3398
3399DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3400 "no service terminal-length [<0-512>]",
3401 NO_STR
3402 "Set up miscellaneous service\n"
3403 "System wide terminal length configuration\n"
3404 "Number of lines of VTY (0 means no line control)\n")
3405{
3406 host.lines = -1;
3407 return CMD_SUCCESS;
3408}
3409
3410DEFUN_HIDDEN(do_echo,
3411 echo_cmd,
3412 "echo .MESSAGE",
3413 "Echo a message back to the vty\n" "The message to echo\n")
3414{
3415 char *message;
3416
3417 vty_out(vty, "%s%s",
3418 ((message =
3419 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3420 if (message)
3421 talloc_free(message);
3422 return CMD_SUCCESS;
3423}
3424
3425#if 0
3426DEFUN(config_logmsg,
3427 config_logmsg_cmd,
3428 "logmsg " LOG_LEVELS " .MESSAGE",
3429 "Send a message to enabled logging destinations\n"
3430 LOG_LEVEL_DESC "The message to send\n")
3431{
3432 int level;
3433 char *message;
3434
3435 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3436 return CMD_ERR_NO_MATCH;
3437
3438 zlog(NULL, level,
3439 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3440 if (message)
3441 talloc_free(message);
3442 return CMD_SUCCESS;
3443}
3444
3445DEFUN(show_logging,
3446 show_logging_cmd,
3447 "show logging", SHOW_STR "Show current logging configuration\n")
3448{
3449 struct zlog *zl = zlog_default;
3450
3451 vty_out(vty, "Syslog logging: ");
3452 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3453 vty_out(vty, "disabled");
3454 else
3455 vty_out(vty, "level %s, facility %s, ident %s",
3456 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3457 facility_name(zl->facility), zl->ident);
3458 vty_out(vty, "%s", VTY_NEWLINE);
3459
3460 vty_out(vty, "Stdout logging: ");
3461 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3462 vty_out(vty, "disabled");
3463 else
3464 vty_out(vty, "level %s",
3465 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3466 vty_out(vty, "%s", VTY_NEWLINE);
3467
3468 vty_out(vty, "Monitor logging: ");
3469 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3470 vty_out(vty, "disabled");
3471 else
3472 vty_out(vty, "level %s",
3473 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3474 vty_out(vty, "%s", VTY_NEWLINE);
3475
3476 vty_out(vty, "File logging: ");
3477 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3478 vty_out(vty, "disabled");
3479 else
3480 vty_out(vty, "level %s, filename %s",
3481 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3482 zl->filename);
3483 vty_out(vty, "%s", VTY_NEWLINE);
3484
3485 vty_out(vty, "Protocol name: %s%s",
3486 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3487 vty_out(vty, "Record priority: %s%s",
3488 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3489
3490 return CMD_SUCCESS;
3491}
3492
3493DEFUN(config_log_stdout,
3494 config_log_stdout_cmd,
3495 "log stdout", "Logging control\n" "Set stdout logging level\n")
3496{
3497 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3498 return CMD_SUCCESS;
3499}
3500
3501DEFUN(config_log_stdout_level,
3502 config_log_stdout_level_cmd,
3503 "log stdout " LOG_LEVELS,
3504 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3505{
3506 int level;
3507
3508 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3509 return CMD_ERR_NO_MATCH;
3510 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3511 return CMD_SUCCESS;
3512}
3513
3514DEFUN(no_config_log_stdout,
3515 no_config_log_stdout_cmd,
3516 "no log stdout [LEVEL]",
3517 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3518{
3519 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3520 return CMD_SUCCESS;
3521}
3522
3523DEFUN(config_log_monitor,
3524 config_log_monitor_cmd,
3525 "log monitor",
3526 "Logging control\n" "Set terminal line (monitor) logging level\n")
3527{
3528 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3529 return CMD_SUCCESS;
3530}
3531
3532DEFUN(config_log_monitor_level,
3533 config_log_monitor_level_cmd,
3534 "log monitor " LOG_LEVELS,
3535 "Logging control\n"
3536 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3537{
3538 int level;
3539
3540 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3541 return CMD_ERR_NO_MATCH;
3542 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3543 return CMD_SUCCESS;
3544}
3545
3546DEFUN(no_config_log_monitor,
3547 no_config_log_monitor_cmd,
3548 "no log monitor [LEVEL]",
3549 NO_STR
3550 "Logging control\n"
3551 "Disable terminal line (monitor) logging\n" "Logging level\n")
3552{
3553 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3554 return CMD_SUCCESS;
3555}
3556
3557static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3558{
3559 int ret;
3560 char *p = NULL;
3561 const char *fullpath;
3562
3563 /* Path detection. */
3564 if (!IS_DIRECTORY_SEP(*fname)) {
3565 char cwd[MAXPATHLEN + 1];
3566 cwd[MAXPATHLEN] = '\0';
3567
3568 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3569 zlog_err("config_log_file: Unable to alloc mem!");
3570 return CMD_WARNING;
3571 }
3572
3573 if ((p = _talloc_zero(tall_vcmd_ctx,
3574 strlen(cwd) + strlen(fname) + 2),
3575 "set_log_file")
3576 == NULL) {
3577 zlog_err("config_log_file: Unable to alloc mem!");
3578 return CMD_WARNING;
3579 }
3580 sprintf(p, "%s/%s", cwd, fname);
3581 fullpath = p;
3582 } else
3583 fullpath = fname;
3584
3585 ret = zlog_set_file(NULL, fullpath, loglevel);
3586
3587 if (p)
3588 talloc_free(p);
3589
3590 if (!ret) {
3591 vty_out(vty, "can't open logfile %s\n", fname);
3592 return CMD_WARNING;
3593 }
3594
3595 if (host.logfile)
3596 talloc_free(host.logfile);
3597
3598 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3599
3600 return CMD_SUCCESS;
3601}
3602
3603DEFUN(config_log_file,
3604 config_log_file_cmd,
3605 "log file FILENAME",
3606 "Logging control\n" "Logging to file\n" "Logging filename\n")
3607{
3608 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3609}
3610
3611DEFUN(config_log_file_level,
3612 config_log_file_level_cmd,
3613 "log file FILENAME " LOG_LEVELS,
3614 "Logging control\n"
3615 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3616{
3617 int level;
3618
3619 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3620 return CMD_ERR_NO_MATCH;
3621 return set_log_file(vty, argv[0], level);
3622}
3623
3624DEFUN(no_config_log_file,
3625 no_config_log_file_cmd,
3626 "no log file [FILENAME]",
3627 NO_STR
3628 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3629{
3630 zlog_reset_file(NULL);
3631
3632 if (host.logfile)
3633 talloc_free(host.logfile);
3634
3635 host.logfile = NULL;
3636
3637 return CMD_SUCCESS;
3638}
3639
3640ALIAS(no_config_log_file,
3641 no_config_log_file_level_cmd,
3642 "no log file FILENAME LEVEL",
3643 NO_STR
3644 "Logging control\n"
3645 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3646
3647 DEFUN(config_log_syslog,
3648 config_log_syslog_cmd,
3649 "log syslog", "Logging control\n" "Set syslog logging level\n")
3650{
3651 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3652 return CMD_SUCCESS;
3653}
3654
3655DEFUN(config_log_syslog_level,
3656 config_log_syslog_level_cmd,
3657 "log syslog " LOG_LEVELS,
3658 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3659{
3660 int level;
3661
3662 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3663 return CMD_ERR_NO_MATCH;
3664 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3665 return CMD_SUCCESS;
3666}
3667
3668DEFUN_DEPRECATED(config_log_syslog_facility,
3669 config_log_syslog_facility_cmd,
3670 "log syslog facility " LOG_FACILITIES,
3671 "Logging control\n"
3672 "Logging goes to syslog\n"
3673 "(Deprecated) Facility parameter for syslog messages\n"
3674 LOG_FACILITY_DESC)
3675{
3676 int facility;
3677
3678 if ((facility = facility_match(argv[0])) < 0)
3679 return CMD_ERR_NO_MATCH;
3680
3681 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3682 zlog_default->facility = facility;
3683 return CMD_SUCCESS;
3684}
3685
3686DEFUN(no_config_log_syslog,
3687 no_config_log_syslog_cmd,
3688 "no log syslog [LEVEL]",
3689 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3690{
3691 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3692 return CMD_SUCCESS;
3693}
3694
3695ALIAS(no_config_log_syslog,
3696 no_config_log_syslog_facility_cmd,
3697 "no log syslog facility " LOG_FACILITIES,
3698 NO_STR
3699 "Logging control\n"
3700 "Logging goes to syslog\n"
3701 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3702
3703 DEFUN(config_log_facility,
3704 config_log_facility_cmd,
3705 "log facility " LOG_FACILITIES,
3706 "Logging control\n"
3707 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3708{
3709 int facility;
3710
3711 if ((facility = facility_match(argv[0])) < 0)
3712 return CMD_ERR_NO_MATCH;
3713 zlog_default->facility = facility;
3714 return CMD_SUCCESS;
3715}
3716
3717DEFUN(no_config_log_facility,
3718 no_config_log_facility_cmd,
3719 "no log facility [FACILITY]",
3720 NO_STR
3721 "Logging control\n"
3722 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3723{
3724 zlog_default->facility = LOG_DAEMON;
3725 return CMD_SUCCESS;
3726}
3727
3728DEFUN_DEPRECATED(config_log_trap,
3729 config_log_trap_cmd,
3730 "log trap " LOG_LEVELS,
3731 "Logging control\n"
3732 "(Deprecated) Set logging level and default for all destinations\n"
3733 LOG_LEVEL_DESC)
3734{
3735 int new_level;
3736 int i;
3737
3738 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3739 return CMD_ERR_NO_MATCH;
3740
3741 zlog_default->default_lvl = new_level;
3742 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3743 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3744 zlog_default->maxlvl[i] = new_level;
3745 return CMD_SUCCESS;
3746}
3747
3748DEFUN_DEPRECATED(no_config_log_trap,
3749 no_config_log_trap_cmd,
3750 "no log trap [LEVEL]",
3751 NO_STR
3752 "Logging control\n"
3753 "Permit all logging information\n" "Logging level\n")
3754{
3755 zlog_default->default_lvl = LOG_DEBUG;
3756 return CMD_SUCCESS;
3757}
3758
3759DEFUN(config_log_record_priority,
3760 config_log_record_priority_cmd,
3761 "log record-priority",
3762 "Logging control\n"
3763 "Log the priority of the message within the message\n")
3764{
3765 zlog_default->record_priority = 1;
3766 return CMD_SUCCESS;
3767}
3768
3769DEFUN(no_config_log_record_priority,
3770 no_config_log_record_priority_cmd,
3771 "no log record-priority",
3772 NO_STR
3773 "Logging control\n"
3774 "Do not log the priority of the message within the message\n")
3775{
3776 zlog_default->record_priority = 0;
3777 return CMD_SUCCESS;
3778}
3779#endif
3780
3781DEFUN(banner_motd_file,
3782 banner_motd_file_cmd,
3783 "banner motd file [FILE]",
3784 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3785{
3786 if (host.motdfile)
3787 talloc_free(host.motdfile);
3788 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3789
3790 return CMD_SUCCESS;
3791}
3792
3793DEFUN(banner_motd_default,
3794 banner_motd_default_cmd,
3795 "banner motd default",
3796 "Set banner string\n" "Strings for motd\n" "Default string\n")
3797{
3798 host.motd = default_motd;
3799 return CMD_SUCCESS;
3800}
3801
3802DEFUN(no_banner_motd,
3803 no_banner_motd_cmd,
3804 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3805{
3806 host.motd = NULL;
3807 if (host.motdfile)
3808 talloc_free(host.motdfile);
3809 host.motdfile = NULL;
3810 return CMD_SUCCESS;
3811}
3812
3813/* Set config filename. Called from vty.c */
3814void host_config_set(const char *filename)
3815{
3816 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3817}
3818
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003819/*! Deprecated, now happens implicitly when calling install_node().
3820 * Users of the API may still attempt to call this function, hence
3821 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003822void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003823{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003824}
3825
3826/*! Deprecated, now happens implicitly when calling install_node().
3827 * Users of the API may still attempt to call this function, hence
3828 * leave it here as a no-op. */
3829void vty_install_default(int node)
3830{
3831}
3832
3833/*! Install common commands like 'exit' and 'list'. */
3834static void install_basic_node_commands(int node)
3835{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003836 install_element(node, &config_help_cmd);
3837 install_element(node, &config_list_cmd);
3838
3839 install_element(node, &config_write_terminal_cmd);
3840 install_element(node, &config_write_file_cmd);
3841 install_element(node, &config_write_memory_cmd);
3842 install_element(node, &config_write_cmd);
3843 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003844
3845 install_element(node, &config_exit_cmd);
3846
3847 if (node >= CONFIG_NODE) {
3848 /* It's not a top node. */
3849 install_element(node, &config_end_cmd);
3850 }
3851}
3852
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003853/*! Return true if a node is installed by install_basic_node_commands(), so
3854 * that we can avoid repeating them for each and every node during 'show
3855 * running-config' */
3856static bool vty_command_is_common(struct cmd_element *cmd)
3857{
3858 if (cmd == &config_help_cmd
3859 || cmd == &config_list_cmd
3860 || cmd == &config_write_terminal_cmd
3861 || cmd == &config_write_file_cmd
3862 || cmd == &config_write_memory_cmd
3863 || cmd == &config_write_cmd
3864 || cmd == &show_running_config_cmd
3865 || cmd == &config_exit_cmd
3866 || cmd == &config_end_cmd)
3867 return true;
3868 return false;
3869}
3870
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003871/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003872 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003873 * \param[in] vty the vty of the code
3874 * \param[in] filename where to store the file
3875 * \return 0 in case of success.
3876 *
3877 * If the filename already exists create a filename.sav
3878 * version with the current code.
3879 *
3880 */
3881int osmo_vty_write_config_file(const char *filename)
3882{
3883 char *failed_file;
3884 int rc;
3885
3886 rc = write_config_file(filename, &failed_file);
3887 talloc_free(failed_file);
3888 return rc;
3889}
3890
3891/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003892 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003893 * \return 0 in case of success.
3894 *
3895 * If the filename already exists create a filename.sav
3896 * version with the current code.
3897 *
3898 */
3899int osmo_vty_save_config_file(void)
3900{
3901 char *failed_file;
3902 int rc;
3903
3904 if (host.config == NULL)
3905 return -7;
3906
3907 rc = write_config_file(host.config, &failed_file);
3908 talloc_free(failed_file);
3909 return rc;
3910}
3911
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003912/* Initialize command interface. Install basic nodes and commands. */
3913void cmd_init(int terminal)
3914{
3915 /* Allocate initial top vector of commands. */
3916 cmdvec = vector_init(VECTOR_MIN_SIZE);
3917
3918 /* Default host value settings. */
3919 host.name = NULL;
3920 host.password = NULL;
3921 host.enable = NULL;
3922 host.logfile = NULL;
3923 host.config = NULL;
3924 host.lines = -1;
3925 host.motd = default_motd;
3926 host.motdfile = NULL;
3927
3928 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003929 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003930 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003931 install_node_bare(&auth_node, NULL);
3932 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003933 install_node(&config_node, config_write_host);
3934
3935 /* Each node's basic commands. */
3936 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003937 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003938 if (terminal) {
3939 install_element(VIEW_NODE, &config_list_cmd);
3940 install_element(VIEW_NODE, &config_exit_cmd);
3941 install_element(VIEW_NODE, &config_help_cmd);
3942 install_element(VIEW_NODE, &config_enable_cmd);
3943 install_element(VIEW_NODE, &config_terminal_length_cmd);
3944 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3945 install_element(VIEW_NODE, &echo_cmd);
3946 }
3947
3948 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003949 install_element(ENABLE_NODE, &config_disable_cmd);
3950 install_element(ENABLE_NODE, &config_terminal_cmd);
3951 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3952 }
3953 install_element (ENABLE_NODE, &show_startup_config_cmd);
3954 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003955 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003956
3957 if (terminal) {
3958 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3959 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3960 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003961 }
3962
3963 install_element(CONFIG_NODE, &hostname_cmd);
3964 install_element(CONFIG_NODE, &no_hostname_cmd);
3965
3966 if (terminal) {
3967 install_element(CONFIG_NODE, &password_cmd);
3968 install_element(CONFIG_NODE, &password_text_cmd);
3969 install_element(CONFIG_NODE, &enable_password_cmd);
3970 install_element(CONFIG_NODE, &enable_password_text_cmd);
3971 install_element(CONFIG_NODE, &no_enable_password_cmd);
3972
3973#ifdef VTY_CRYPT_PW
3974 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3975 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3976#endif
3977 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3978 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3979 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3980 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3981 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3982
3983 }
3984 srand(time(NULL));
3985}
Harald Welte7acb30c2011-08-17 17:13:48 +02003986
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003987/*! @} */