blob: 75bc00debbecb1146bae30e4b456f7bcd6b059c9 [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{
2837 vty_out(vty,
2838 "This VTY provides advanced help features. When you need help,%s\
2839anytime at the command line please press '?'.%s\
2840%s\
2841If nothing matches, the help list will be empty and you must backup%s\
2842 until entering a '?' shows the available options.%s\
2843Two styles of help are provided:%s\
28441. Full help is available when you are ready to enter a%s\
2845command argument (e.g. 'show ?') and describes each possible%s\
2846argument.%s\
28472. Partial help is provided when an abbreviated argument is entered%s\
2848 and you want to know what arguments match the input%s\
2849 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2850 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2851 return CMD_SUCCESS;
2852}
2853
2854/* Help display function for all node. */
2855gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2856{
2857 unsigned int i;
2858 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2859 struct cmd_element *cmd;
2860
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002861 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2862 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2863 continue;
2864 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2865 continue;
2866 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2867 }
2868
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002869 return CMD_SUCCESS;
2870}
2871
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002872static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002873{
2874 unsigned int i;
2875 int fd;
2876 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002877 char *config_file_tmp = NULL;
2878 char *config_file_sav = NULL;
2879 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002880 struct stat st;
2881
2882 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002883
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002884 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2885 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2886 * manually instead. */
2887
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002888 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002889 config_file_sav =
2890 _talloc_zero(tall_vty_cmd_ctx,
2891 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2892 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002893 if (!config_file_sav)
2894 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002895 strcpy(config_file_sav, config_file);
2896 strcat(config_file_sav, CONF_BACKUP_EXT);
2897
2898 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002899 "config_file_tmp");
2900 if (!config_file_tmp) {
2901 talloc_free(config_file_sav);
2902 return -1;
2903 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002904 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2905
2906 /* Open file to configuration write. */
2907 fd = mkstemp(config_file_tmp);
2908 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002909 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002910 talloc_free(config_file_tmp);
2911 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002912 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002913 }
2914
2915 /* Make vty for configuration file. */
2916 file_vty = vty_new();
2917 file_vty->fd = fd;
2918 file_vty->type = VTY_FILE;
2919
2920 /* Config file header print. */
2921 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002922 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002923 //vty_time_print (file_vty, 1);
2924 vty_out(file_vty, "!\n");
2925
2926 for (i = 0; i < vector_active(cmdvec); i++)
2927 if ((node = vector_slot(cmdvec, i)) && node->func) {
2928 if ((*node->func) (file_vty))
2929 vty_out(file_vty, "!\n");
2930 }
2931 vty_close(file_vty);
2932
2933 if (unlink(config_file_sav) != 0)
2934 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002935 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002936 talloc_free(config_file_sav);
2937 talloc_free(config_file_tmp);
2938 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002939 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002940 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002941
2942 /* Only link the .sav file if the original file exists */
2943 if (stat(config_file, &st) == 0) {
2944 if (link(config_file, config_file_sav) != 0) {
2945 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2946 talloc_free(config_file_sav);
2947 talloc_free(config_file_tmp);
2948 unlink(config_file_tmp);
2949 return -3;
2950 }
2951 sync();
2952 if (unlink(config_file) != 0) {
2953 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2954 talloc_free(config_file_sav);
2955 talloc_free(config_file_tmp);
2956 unlink(config_file_tmp);
2957 return -4;
2958 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002959 }
2960 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002961 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002962 talloc_free(config_file_sav);
2963 talloc_free(config_file_tmp);
2964 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002965 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002966 }
2967 unlink(config_file_tmp);
2968 sync();
2969
2970 talloc_free(config_file_sav);
2971 talloc_free(config_file_tmp);
2972
2973 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002974 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2975 return -6;
2976 }
2977
2978 return 0;
2979}
2980
2981
2982/* Write current configuration into file. */
2983DEFUN(config_write_file,
2984 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002985 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002986 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002987 "Write to configuration file\n"
2988 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002989{
2990 char *failed_file;
2991 int rc;
2992
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002993 if (host.app_info->config_is_consistent) {
2994 rc = host.app_info->config_is_consistent(vty);
2995 if (!rc) {
2996 vty_out(vty, "Configuration is not consistent%s",
2997 VTY_NEWLINE);
2998 return CMD_WARNING;
2999 }
3000 }
3001
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003002 if (argc == 1)
3003 host_config_set(argv[0]);
3004
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003005 if (host.config == NULL) {
3006 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3007 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003008 return CMD_WARNING;
3009 }
3010
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003011 rc = write_config_file(host.config, &failed_file);
3012 switch (rc) {
3013 case -1:
3014 vty_out(vty, "Can't open configuration file %s.%s",
3015 failed_file, VTY_NEWLINE);
3016 rc = CMD_WARNING;
3017 break;
3018 case -2:
3019 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3020 failed_file, VTY_NEWLINE);
3021 rc = CMD_WARNING;
3022 break;
3023 case -3:
3024 vty_out(vty, "Can't backup old configuration file %s.%s",
3025 failed_file, VTY_NEWLINE);
3026 rc = CMD_WARNING;
3027 break;
3028 case -4:
3029 vty_out(vty, "Can't unlink configuration file %s.%s",
3030 failed_file, VTY_NEWLINE);
3031 rc = CMD_WARNING;
3032 break;
3033 case -5:
3034 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3035 VTY_NEWLINE);
3036 rc = CMD_WARNING;
3037 break;
3038 case -6:
3039 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3040 failed_file, strerror(errno), errno, VTY_NEWLINE);
3041 rc = CMD_WARNING;
3042 break;
3043 default:
3044 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3045 rc = CMD_SUCCESS;
3046 break;
3047 }
3048
3049 talloc_free(failed_file);
3050 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003051}
3052
3053ALIAS(config_write_file,
3054 config_write_cmd,
3055 "write", "Write running configuration to memory, network, or terminal\n")
3056
3057 ALIAS(config_write_file,
3058 config_write_memory_cmd,
3059 "write memory",
3060 "Write running configuration to memory, network, or terminal\n"
3061 "Write configuration to the file (same as write file)\n")
3062
3063 ALIAS(config_write_file,
3064 copy_runningconfig_startupconfig_cmd,
3065 "copy running-config startup-config",
3066 "Copy configuration\n"
3067 "Copy running config to... \n"
3068 "Copy running config to startup config (same as write file)\n")
3069
3070/* Write current configuration into the terminal. */
3071 DEFUN(config_write_terminal,
3072 config_write_terminal_cmd,
3073 "write terminal",
3074 "Write running configuration to memory, network, or terminal\n"
3075 "Write to terminal\n")
3076{
3077 unsigned int i;
3078 struct cmd_node *node;
3079
3080 if (vty->type == VTY_SHELL_SERV) {
3081 for (i = 0; i < vector_active(cmdvec); i++)
3082 if ((node = vector_slot(cmdvec, i)) && node->func
3083 && node->vtysh) {
3084 if ((*node->func) (vty))
3085 vty_out(vty, "!%s", VTY_NEWLINE);
3086 }
3087 } else {
3088 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3089 VTY_NEWLINE);
3090 vty_out(vty, "!%s", VTY_NEWLINE);
3091
3092 for (i = 0; i < vector_active(cmdvec); i++)
3093 if ((node = vector_slot(cmdvec, i)) && node->func) {
3094 if ((*node->func) (vty))
3095 vty_out(vty, "!%s", VTY_NEWLINE);
3096 }
3097 vty_out(vty, "end%s", VTY_NEWLINE);
3098 }
3099 return CMD_SUCCESS;
3100}
3101
3102/* Write current configuration into the terminal. */
3103ALIAS(config_write_terminal,
3104 show_running_config_cmd,
3105 "show running-config", SHOW_STR "running configuration\n")
3106
3107/* Write startup configuration into the terminal. */
3108 DEFUN(show_startup_config,
3109 show_startup_config_cmd,
3110 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3111{
3112 char buf[BUFSIZ];
3113 FILE *confp;
3114
3115 confp = fopen(host.config, "r");
3116 if (confp == NULL) {
3117 vty_out(vty, "Can't open configuration file [%s]%s",
3118 host.config, VTY_NEWLINE);
3119 return CMD_WARNING;
3120 }
3121
3122 while (fgets(buf, BUFSIZ, confp)) {
3123 char *cp = buf;
3124
3125 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3126 cp++;
3127 *cp = '\0';
3128
3129 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3130 }
3131
3132 fclose(confp);
3133
3134 return CMD_SUCCESS;
3135}
3136
3137/* Hostname configuration */
3138DEFUN(config_hostname,
3139 hostname_cmd,
3140 "hostname WORD",
3141 "Set system's network name\n" "This system's network name\n")
3142{
3143 if (!isalpha((int)*argv[0])) {
3144 vty_out(vty, "Please specify string starting with alphabet%s",
3145 VTY_NEWLINE);
3146 return CMD_WARNING;
3147 }
3148
3149 if (host.name)
3150 talloc_free(host.name);
3151
3152 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3153 return CMD_SUCCESS;
3154}
3155
3156DEFUN(config_no_hostname,
3157 no_hostname_cmd,
3158 "no hostname [HOSTNAME]",
3159 NO_STR "Reset system's network name\n" "Host name of this router\n")
3160{
3161 if (host.name)
3162 talloc_free(host.name);
3163 host.name = NULL;
3164 return CMD_SUCCESS;
3165}
3166
3167/* VTY interface password set. */
3168DEFUN(config_password, password_cmd,
3169 "password (8|) WORD",
3170 "Assign the terminal connection password\n"
3171 "Specifies a HIDDEN password will follow\n"
3172 "dummy string \n" "The HIDDEN line password string\n")
3173{
3174 /* Argument check. */
3175 if (argc == 0) {
3176 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3177 return CMD_WARNING;
3178 }
3179
3180 if (argc == 2) {
3181 if (*argv[0] == '8') {
3182 if (host.password)
3183 talloc_free(host.password);
3184 host.password = NULL;
3185 if (host.password_encrypt)
3186 talloc_free(host.password_encrypt);
3187 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3188 return CMD_SUCCESS;
3189 } else {
3190 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3191 return CMD_WARNING;
3192 }
3193 }
3194
3195 if (!isalnum((int)*argv[0])) {
3196 vty_out(vty,
3197 "Please specify string starting with alphanumeric%s",
3198 VTY_NEWLINE);
3199 return CMD_WARNING;
3200 }
3201
3202 if (host.password)
3203 talloc_free(host.password);
3204 host.password = NULL;
3205
3206#ifdef VTY_CRYPT_PW
3207 if (host.encrypt) {
3208 if (host.password_encrypt)
3209 talloc_free(host.password_encrypt);
3210 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3211 } else
3212#endif
3213 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3214
3215 return CMD_SUCCESS;
3216}
3217
3218ALIAS(config_password, password_text_cmd,
3219 "password LINE",
3220 "Assign the terminal connection password\n"
3221 "The UNENCRYPTED (cleartext) line password\n")
3222
3223/* VTY enable password set. */
3224 DEFUN(config_enable_password, enable_password_cmd,
3225 "enable password (8|) WORD",
3226 "Modify enable password parameters\n"
3227 "Assign the privileged level password\n"
3228 "Specifies a HIDDEN password will follow\n"
3229 "dummy string \n" "The HIDDEN 'enable' password string\n")
3230{
3231 /* Argument check. */
3232 if (argc == 0) {
3233 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3234 return CMD_WARNING;
3235 }
3236
3237 /* Crypt type is specified. */
3238 if (argc == 2) {
3239 if (*argv[0] == '8') {
3240 if (host.enable)
3241 talloc_free(host.enable);
3242 host.enable = NULL;
3243
3244 if (host.enable_encrypt)
3245 talloc_free(host.enable_encrypt);
3246 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3247
3248 return CMD_SUCCESS;
3249 } else {
3250 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3251 return CMD_WARNING;
3252 }
3253 }
3254
3255 if (!isalnum((int)*argv[0])) {
3256 vty_out(vty,
3257 "Please specify string starting with alphanumeric%s",
3258 VTY_NEWLINE);
3259 return CMD_WARNING;
3260 }
3261
3262 if (host.enable)
3263 talloc_free(host.enable);
3264 host.enable = NULL;
3265
3266 /* Plain password input. */
3267#ifdef VTY_CRYPT_PW
3268 if (host.encrypt) {
3269 if (host.enable_encrypt)
3270 talloc_free(host.enable_encrypt);
3271 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3272 } else
3273#endif
3274 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3275
3276 return CMD_SUCCESS;
3277}
3278
3279ALIAS(config_enable_password,
3280 enable_password_text_cmd,
3281 "enable password LINE",
3282 "Modify enable password parameters\n"
3283 "Assign the privileged level password\n"
3284 "The UNENCRYPTED (cleartext) 'enable' password\n")
3285
3286/* VTY enable password delete. */
3287 DEFUN(no_config_enable_password, no_enable_password_cmd,
3288 "no enable password",
3289 NO_STR
3290 "Modify enable password parameters\n"
3291 "Assign the privileged level password\n")
3292{
3293 if (host.enable)
3294 talloc_free(host.enable);
3295 host.enable = NULL;
3296
3297 if (host.enable_encrypt)
3298 talloc_free(host.enable_encrypt);
3299 host.enable_encrypt = NULL;
3300
3301 return CMD_SUCCESS;
3302}
3303
3304#ifdef VTY_CRYPT_PW
3305DEFUN(service_password_encrypt,
3306 service_password_encrypt_cmd,
3307 "service password-encryption",
3308 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3309{
3310 if (host.encrypt)
3311 return CMD_SUCCESS;
3312
3313 host.encrypt = 1;
3314
3315 if (host.password) {
3316 if (host.password_encrypt)
3317 talloc_free(host.password_encrypt);
3318 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3319 }
3320 if (host.enable) {
3321 if (host.enable_encrypt)
3322 talloc_free(host.enable_encrypt);
3323 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3324 }
3325
3326 return CMD_SUCCESS;
3327}
3328
3329DEFUN(no_service_password_encrypt,
3330 no_service_password_encrypt_cmd,
3331 "no service password-encryption",
3332 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3333{
3334 if (!host.encrypt)
3335 return CMD_SUCCESS;
3336
3337 host.encrypt = 0;
3338
3339 if (host.password_encrypt)
3340 talloc_free(host.password_encrypt);
3341 host.password_encrypt = NULL;
3342
3343 if (host.enable_encrypt)
3344 talloc_free(host.enable_encrypt);
3345 host.enable_encrypt = NULL;
3346
3347 return CMD_SUCCESS;
3348}
3349#endif
3350
3351DEFUN(config_terminal_length, config_terminal_length_cmd,
3352 "terminal length <0-512>",
3353 "Set terminal line parameters\n"
3354 "Set number of lines on a screen\n"
3355 "Number of lines on screen (0 for no pausing)\n")
3356{
3357 int lines;
3358 char *endptr = NULL;
3359
3360 lines = strtol(argv[0], &endptr, 10);
3361 if (lines < 0 || lines > 512 || *endptr != '\0') {
3362 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3363 return CMD_WARNING;
3364 }
3365 vty->lines = lines;
3366
3367 return CMD_SUCCESS;
3368}
3369
3370DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3371 "terminal no length",
3372 "Set terminal line parameters\n"
3373 NO_STR "Set number of lines on a screen\n")
3374{
3375 vty->lines = -1;
3376 return CMD_SUCCESS;
3377}
3378
3379DEFUN(service_terminal_length, service_terminal_length_cmd,
3380 "service terminal-length <0-512>",
3381 "Set up miscellaneous service\n"
3382 "System wide terminal length configuration\n"
3383 "Number of lines of VTY (0 means no line control)\n")
3384{
3385 int lines;
3386 char *endptr = NULL;
3387
3388 lines = strtol(argv[0], &endptr, 10);
3389 if (lines < 0 || lines > 512 || *endptr != '\0') {
3390 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3391 return CMD_WARNING;
3392 }
3393 host.lines = lines;
3394
3395 return CMD_SUCCESS;
3396}
3397
3398DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3399 "no service terminal-length [<0-512>]",
3400 NO_STR
3401 "Set up miscellaneous service\n"
3402 "System wide terminal length configuration\n"
3403 "Number of lines of VTY (0 means no line control)\n")
3404{
3405 host.lines = -1;
3406 return CMD_SUCCESS;
3407}
3408
3409DEFUN_HIDDEN(do_echo,
3410 echo_cmd,
3411 "echo .MESSAGE",
3412 "Echo a message back to the vty\n" "The message to echo\n")
3413{
3414 char *message;
3415
3416 vty_out(vty, "%s%s",
3417 ((message =
3418 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3419 if (message)
3420 talloc_free(message);
3421 return CMD_SUCCESS;
3422}
3423
3424#if 0
3425DEFUN(config_logmsg,
3426 config_logmsg_cmd,
3427 "logmsg " LOG_LEVELS " .MESSAGE",
3428 "Send a message to enabled logging destinations\n"
3429 LOG_LEVEL_DESC "The message to send\n")
3430{
3431 int level;
3432 char *message;
3433
3434 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3435 return CMD_ERR_NO_MATCH;
3436
3437 zlog(NULL, level,
3438 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3439 if (message)
3440 talloc_free(message);
3441 return CMD_SUCCESS;
3442}
3443
3444DEFUN(show_logging,
3445 show_logging_cmd,
3446 "show logging", SHOW_STR "Show current logging configuration\n")
3447{
3448 struct zlog *zl = zlog_default;
3449
3450 vty_out(vty, "Syslog logging: ");
3451 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3452 vty_out(vty, "disabled");
3453 else
3454 vty_out(vty, "level %s, facility %s, ident %s",
3455 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3456 facility_name(zl->facility), zl->ident);
3457 vty_out(vty, "%s", VTY_NEWLINE);
3458
3459 vty_out(vty, "Stdout logging: ");
3460 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3461 vty_out(vty, "disabled");
3462 else
3463 vty_out(vty, "level %s",
3464 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3465 vty_out(vty, "%s", VTY_NEWLINE);
3466
3467 vty_out(vty, "Monitor logging: ");
3468 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3469 vty_out(vty, "disabled");
3470 else
3471 vty_out(vty, "level %s",
3472 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3473 vty_out(vty, "%s", VTY_NEWLINE);
3474
3475 vty_out(vty, "File logging: ");
3476 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3477 vty_out(vty, "disabled");
3478 else
3479 vty_out(vty, "level %s, filename %s",
3480 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3481 zl->filename);
3482 vty_out(vty, "%s", VTY_NEWLINE);
3483
3484 vty_out(vty, "Protocol name: %s%s",
3485 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3486 vty_out(vty, "Record priority: %s%s",
3487 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3488
3489 return CMD_SUCCESS;
3490}
3491
3492DEFUN(config_log_stdout,
3493 config_log_stdout_cmd,
3494 "log stdout", "Logging control\n" "Set stdout logging level\n")
3495{
3496 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3497 return CMD_SUCCESS;
3498}
3499
3500DEFUN(config_log_stdout_level,
3501 config_log_stdout_level_cmd,
3502 "log stdout " LOG_LEVELS,
3503 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3504{
3505 int level;
3506
3507 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3508 return CMD_ERR_NO_MATCH;
3509 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3510 return CMD_SUCCESS;
3511}
3512
3513DEFUN(no_config_log_stdout,
3514 no_config_log_stdout_cmd,
3515 "no log stdout [LEVEL]",
3516 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3517{
3518 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3519 return CMD_SUCCESS;
3520}
3521
3522DEFUN(config_log_monitor,
3523 config_log_monitor_cmd,
3524 "log monitor",
3525 "Logging control\n" "Set terminal line (monitor) logging level\n")
3526{
3527 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3528 return CMD_SUCCESS;
3529}
3530
3531DEFUN(config_log_monitor_level,
3532 config_log_monitor_level_cmd,
3533 "log monitor " LOG_LEVELS,
3534 "Logging control\n"
3535 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3536{
3537 int level;
3538
3539 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3540 return CMD_ERR_NO_MATCH;
3541 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3542 return CMD_SUCCESS;
3543}
3544
3545DEFUN(no_config_log_monitor,
3546 no_config_log_monitor_cmd,
3547 "no log monitor [LEVEL]",
3548 NO_STR
3549 "Logging control\n"
3550 "Disable terminal line (monitor) logging\n" "Logging level\n")
3551{
3552 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3553 return CMD_SUCCESS;
3554}
3555
3556static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3557{
3558 int ret;
3559 char *p = NULL;
3560 const char *fullpath;
3561
3562 /* Path detection. */
3563 if (!IS_DIRECTORY_SEP(*fname)) {
3564 char cwd[MAXPATHLEN + 1];
3565 cwd[MAXPATHLEN] = '\0';
3566
3567 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3568 zlog_err("config_log_file: Unable to alloc mem!");
3569 return CMD_WARNING;
3570 }
3571
3572 if ((p = _talloc_zero(tall_vcmd_ctx,
3573 strlen(cwd) + strlen(fname) + 2),
3574 "set_log_file")
3575 == NULL) {
3576 zlog_err("config_log_file: Unable to alloc mem!");
3577 return CMD_WARNING;
3578 }
3579 sprintf(p, "%s/%s", cwd, fname);
3580 fullpath = p;
3581 } else
3582 fullpath = fname;
3583
3584 ret = zlog_set_file(NULL, fullpath, loglevel);
3585
3586 if (p)
3587 talloc_free(p);
3588
3589 if (!ret) {
3590 vty_out(vty, "can't open logfile %s\n", fname);
3591 return CMD_WARNING;
3592 }
3593
3594 if (host.logfile)
3595 talloc_free(host.logfile);
3596
3597 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3598
3599 return CMD_SUCCESS;
3600}
3601
3602DEFUN(config_log_file,
3603 config_log_file_cmd,
3604 "log file FILENAME",
3605 "Logging control\n" "Logging to file\n" "Logging filename\n")
3606{
3607 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3608}
3609
3610DEFUN(config_log_file_level,
3611 config_log_file_level_cmd,
3612 "log file FILENAME " LOG_LEVELS,
3613 "Logging control\n"
3614 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3615{
3616 int level;
3617
3618 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3619 return CMD_ERR_NO_MATCH;
3620 return set_log_file(vty, argv[0], level);
3621}
3622
3623DEFUN(no_config_log_file,
3624 no_config_log_file_cmd,
3625 "no log file [FILENAME]",
3626 NO_STR
3627 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3628{
3629 zlog_reset_file(NULL);
3630
3631 if (host.logfile)
3632 talloc_free(host.logfile);
3633
3634 host.logfile = NULL;
3635
3636 return CMD_SUCCESS;
3637}
3638
3639ALIAS(no_config_log_file,
3640 no_config_log_file_level_cmd,
3641 "no log file FILENAME LEVEL",
3642 NO_STR
3643 "Logging control\n"
3644 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3645
3646 DEFUN(config_log_syslog,
3647 config_log_syslog_cmd,
3648 "log syslog", "Logging control\n" "Set syslog logging level\n")
3649{
3650 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3651 return CMD_SUCCESS;
3652}
3653
3654DEFUN(config_log_syslog_level,
3655 config_log_syslog_level_cmd,
3656 "log syslog " LOG_LEVELS,
3657 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3658{
3659 int level;
3660
3661 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3662 return CMD_ERR_NO_MATCH;
3663 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3664 return CMD_SUCCESS;
3665}
3666
3667DEFUN_DEPRECATED(config_log_syslog_facility,
3668 config_log_syslog_facility_cmd,
3669 "log syslog facility " LOG_FACILITIES,
3670 "Logging control\n"
3671 "Logging goes to syslog\n"
3672 "(Deprecated) Facility parameter for syslog messages\n"
3673 LOG_FACILITY_DESC)
3674{
3675 int facility;
3676
3677 if ((facility = facility_match(argv[0])) < 0)
3678 return CMD_ERR_NO_MATCH;
3679
3680 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3681 zlog_default->facility = facility;
3682 return CMD_SUCCESS;
3683}
3684
3685DEFUN(no_config_log_syslog,
3686 no_config_log_syslog_cmd,
3687 "no log syslog [LEVEL]",
3688 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3689{
3690 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3691 return CMD_SUCCESS;
3692}
3693
3694ALIAS(no_config_log_syslog,
3695 no_config_log_syslog_facility_cmd,
3696 "no log syslog facility " LOG_FACILITIES,
3697 NO_STR
3698 "Logging control\n"
3699 "Logging goes to syslog\n"
3700 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3701
3702 DEFUN(config_log_facility,
3703 config_log_facility_cmd,
3704 "log facility " LOG_FACILITIES,
3705 "Logging control\n"
3706 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3707{
3708 int facility;
3709
3710 if ((facility = facility_match(argv[0])) < 0)
3711 return CMD_ERR_NO_MATCH;
3712 zlog_default->facility = facility;
3713 return CMD_SUCCESS;
3714}
3715
3716DEFUN(no_config_log_facility,
3717 no_config_log_facility_cmd,
3718 "no log facility [FACILITY]",
3719 NO_STR
3720 "Logging control\n"
3721 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3722{
3723 zlog_default->facility = LOG_DAEMON;
3724 return CMD_SUCCESS;
3725}
3726
3727DEFUN_DEPRECATED(config_log_trap,
3728 config_log_trap_cmd,
3729 "log trap " LOG_LEVELS,
3730 "Logging control\n"
3731 "(Deprecated) Set logging level and default for all destinations\n"
3732 LOG_LEVEL_DESC)
3733{
3734 int new_level;
3735 int i;
3736
3737 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3738 return CMD_ERR_NO_MATCH;
3739
3740 zlog_default->default_lvl = new_level;
3741 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3742 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3743 zlog_default->maxlvl[i] = new_level;
3744 return CMD_SUCCESS;
3745}
3746
3747DEFUN_DEPRECATED(no_config_log_trap,
3748 no_config_log_trap_cmd,
3749 "no log trap [LEVEL]",
3750 NO_STR
3751 "Logging control\n"
3752 "Permit all logging information\n" "Logging level\n")
3753{
3754 zlog_default->default_lvl = LOG_DEBUG;
3755 return CMD_SUCCESS;
3756}
3757
3758DEFUN(config_log_record_priority,
3759 config_log_record_priority_cmd,
3760 "log record-priority",
3761 "Logging control\n"
3762 "Log the priority of the message within the message\n")
3763{
3764 zlog_default->record_priority = 1;
3765 return CMD_SUCCESS;
3766}
3767
3768DEFUN(no_config_log_record_priority,
3769 no_config_log_record_priority_cmd,
3770 "no log record-priority",
3771 NO_STR
3772 "Logging control\n"
3773 "Do not log the priority of the message within the message\n")
3774{
3775 zlog_default->record_priority = 0;
3776 return CMD_SUCCESS;
3777}
3778#endif
3779
3780DEFUN(banner_motd_file,
3781 banner_motd_file_cmd,
3782 "banner motd file [FILE]",
3783 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3784{
3785 if (host.motdfile)
3786 talloc_free(host.motdfile);
3787 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3788
3789 return CMD_SUCCESS;
3790}
3791
3792DEFUN(banner_motd_default,
3793 banner_motd_default_cmd,
3794 "banner motd default",
3795 "Set banner string\n" "Strings for motd\n" "Default string\n")
3796{
3797 host.motd = default_motd;
3798 return CMD_SUCCESS;
3799}
3800
3801DEFUN(no_banner_motd,
3802 no_banner_motd_cmd,
3803 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3804{
3805 host.motd = NULL;
3806 if (host.motdfile)
3807 talloc_free(host.motdfile);
3808 host.motdfile = NULL;
3809 return CMD_SUCCESS;
3810}
3811
3812/* Set config filename. Called from vty.c */
3813void host_config_set(const char *filename)
3814{
3815 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3816}
3817
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003818/*! Deprecated, now happens implicitly when calling install_node().
3819 * Users of the API may still attempt to call this function, hence
3820 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003821void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003822{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003823}
3824
3825/*! Deprecated, now happens implicitly when calling install_node().
3826 * Users of the API may still attempt to call this function, hence
3827 * leave it here as a no-op. */
3828void vty_install_default(int node)
3829{
3830}
3831
3832/*! Install common commands like 'exit' and 'list'. */
3833static void install_basic_node_commands(int node)
3834{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003835 install_element(node, &config_help_cmd);
3836 install_element(node, &config_list_cmd);
3837
3838 install_element(node, &config_write_terminal_cmd);
3839 install_element(node, &config_write_file_cmd);
3840 install_element(node, &config_write_memory_cmd);
3841 install_element(node, &config_write_cmd);
3842 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003843
3844 install_element(node, &config_exit_cmd);
3845
3846 if (node >= CONFIG_NODE) {
3847 /* It's not a top node. */
3848 install_element(node, &config_end_cmd);
3849 }
3850}
3851
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003852/*! Return true if a node is installed by install_basic_node_commands(), so
3853 * that we can avoid repeating them for each and every node during 'show
3854 * running-config' */
3855static bool vty_command_is_common(struct cmd_element *cmd)
3856{
3857 if (cmd == &config_help_cmd
3858 || cmd == &config_list_cmd
3859 || cmd == &config_write_terminal_cmd
3860 || cmd == &config_write_file_cmd
3861 || cmd == &config_write_memory_cmd
3862 || cmd == &config_write_cmd
3863 || cmd == &show_running_config_cmd
3864 || cmd == &config_exit_cmd
3865 || cmd == &config_end_cmd)
3866 return true;
3867 return false;
3868}
3869
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003870/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003871 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003872 * \param[in] vty the vty of the code
3873 * \param[in] filename where to store the file
3874 * \return 0 in case of success.
3875 *
3876 * If the filename already exists create a filename.sav
3877 * version with the current code.
3878 *
3879 */
3880int osmo_vty_write_config_file(const char *filename)
3881{
3882 char *failed_file;
3883 int rc;
3884
3885 rc = write_config_file(filename, &failed_file);
3886 talloc_free(failed_file);
3887 return rc;
3888}
3889
3890/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003891 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003892 * \return 0 in case of success.
3893 *
3894 * If the filename already exists create a filename.sav
3895 * version with the current code.
3896 *
3897 */
3898int osmo_vty_save_config_file(void)
3899{
3900 char *failed_file;
3901 int rc;
3902
3903 if (host.config == NULL)
3904 return -7;
3905
3906 rc = write_config_file(host.config, &failed_file);
3907 talloc_free(failed_file);
3908 return rc;
3909}
3910
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003911/* Initialize command interface. Install basic nodes and commands. */
3912void cmd_init(int terminal)
3913{
3914 /* Allocate initial top vector of commands. */
3915 cmdvec = vector_init(VECTOR_MIN_SIZE);
3916
3917 /* Default host value settings. */
3918 host.name = NULL;
3919 host.password = NULL;
3920 host.enable = NULL;
3921 host.logfile = NULL;
3922 host.config = NULL;
3923 host.lines = -1;
3924 host.motd = default_motd;
3925 host.motdfile = NULL;
3926
3927 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003928 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003929 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003930 install_node_bare(&auth_node, NULL);
3931 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003932 install_node(&config_node, config_write_host);
3933
3934 /* Each node's basic commands. */
3935 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003936 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003937 if (terminal) {
3938 install_element(VIEW_NODE, &config_list_cmd);
3939 install_element(VIEW_NODE, &config_exit_cmd);
3940 install_element(VIEW_NODE, &config_help_cmd);
3941 install_element(VIEW_NODE, &config_enable_cmd);
3942 install_element(VIEW_NODE, &config_terminal_length_cmd);
3943 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3944 install_element(VIEW_NODE, &echo_cmd);
3945 }
3946
3947 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003948 install_element(ENABLE_NODE, &config_disable_cmd);
3949 install_element(ENABLE_NODE, &config_terminal_cmd);
3950 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3951 }
3952 install_element (ENABLE_NODE, &show_startup_config_cmd);
3953 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003954 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003955
3956 if (terminal) {
3957 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3958 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3959 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003960 }
3961
3962 install_element(CONFIG_NODE, &hostname_cmd);
3963 install_element(CONFIG_NODE, &no_hostname_cmd);
3964
3965 if (terminal) {
3966 install_element(CONFIG_NODE, &password_cmd);
3967 install_element(CONFIG_NODE, &password_text_cmd);
3968 install_element(CONFIG_NODE, &enable_password_cmd);
3969 install_element(CONFIG_NODE, &enable_password_text_cmd);
3970 install_element(CONFIG_NODE, &no_enable_password_cmd);
3971
3972#ifdef VTY_CRYPT_PW
3973 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3974 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3975#endif
3976 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3977 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3978 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3979 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3980 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3981
3982 }
3983 srand(time(NULL));
3984}
Harald Welte7acb30c2011-08-17 17:13:48 +02003985
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003986/*! @} */