blob: 16dd07faf967d64ebd53eab5edabb88e617e0b6a [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
1328 val = strtoul(str, &endptr, 10);
1329 if (*endptr != '\0')
1330 return 0;
1331
1332 range++;
1333 p = strchr(range, '-');
1334 if (p == NULL)
1335 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001336 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001337 return 0;
1338 strncpy(buf, range, p - range);
1339 buf[p - range] = '\0';
1340 min = strtoul(buf, &endptr, 10);
1341 if (*endptr != '\0')
1342 return 0;
1343
1344 range = p + 1;
1345 p = strchr(range, '>');
1346 if (p == NULL)
1347 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001348 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001349 return 0;
1350 strncpy(buf, range, p - range);
1351 buf[p - range] = '\0';
1352 max = strtoul(buf, &endptr, 10);
1353 if (*endptr != '\0')
1354 return 0;
1355
1356 if (val < min || val > max)
1357 return 0;
1358 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001359
1360 return 1;
1361}
1362
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001363/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001364static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001365{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001366 /* we've got "[blah]". We want to strip off the []s and redo the
1367 * match check for "blah"
1368 */
1369 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001370
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001371 if (len < 3)
1372 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001373
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001374 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001375}
1376
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001377static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001378cmd_match(const char *str, const char *command,
1379 enum match_type min, bool recur)
1380{
1381
1382 if (recur && CMD_OPTION(str))
1383 {
1384 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001385 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001386
1387 /* this would be a bug in a command, however handle it gracefully
1388 * as it we only discover it if a user tries to run it
1389 */
1390 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001391 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001392
1393 ret = cmd_match(tmp, command, min, false);
1394
1395 talloc_free(tmp);
1396
1397 return ret;
1398 }
1399 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001400 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001401 else if (CMD_RANGE(str))
1402 {
1403 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001404 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001405 }
1406#ifdef HAVE_IPV6
1407 else if (CMD_IPV6(str))
1408 {
1409 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001410 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001411 }
1412 else if (CMD_IPV6_PREFIX(str))
1413 {
1414 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001415 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001416 }
1417#endif /* HAVE_IPV6 */
1418 else if (CMD_IPV4(str))
1419 {
1420 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001421 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001422 }
1423 else if (CMD_IPV4_PREFIX(str))
1424 {
1425 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001426 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001427 }
1428 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001429 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001430 else if (strncmp(command, str, strlen(command)) == 0)
1431 {
1432 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001433 return EXACT_MATCH;
1434 else if (PARTLY_MATCH >= min)
1435 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001436 }
1437
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001438 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001439}
1440
1441/* Filter vector at the specified index and by the given command string, to
1442 * the desired matching level (thus allowing part matches), and return match
1443 * type flag.
1444 */
1445static enum match_type
1446cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001447{
1448 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001449 struct cmd_element *cmd_element;
1450 enum match_type match_type;
1451 vector descvec;
1452 struct desc *desc;
1453
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001454 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001455
1456 /* If command and cmd_element string does not match set NULL to vector */
1457 for (i = 0; i < vector_active(v); i++)
1458 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459 if (index >= vector_active(cmd_element->strvec))
1460 vector_slot(v, i) = NULL;
1461 else {
1462 unsigned int j;
1463 int matched = 0;
1464
1465 descvec =
1466 vector_slot(cmd_element->strvec, index);
1467
1468 for (j = 0; j < vector_active(descvec); j++)
1469 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001470 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001471
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001472 ret = cmd_match (desc->cmd, command, level, true);
1473
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001474 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001475 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001476
1477 if (match_type < ret)
1478 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001479 }
1480 if (!matched)
1481 vector_slot(v, i) = NULL;
1482 }
1483 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001484
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001485 if (match_type == NO_MATCH)
1486 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001487
1488 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1489 * go again and filter out commands whose argument (at this index) is
1490 * 'weaker'. E.g., if we have 2 commands:
1491 *
1492 * foo bar <1-255>
1493 * foo bar BLAH
1494 *
1495 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001496 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001497 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1498 *
1499 * If we don't do a 2nd pass and filter it out, the higher-layers will
1500 * consider this to be ambiguous.
1501 */
1502 for (i = 0; i < vector_active(v); i++)
1503 if ((cmd_element = vector_slot(v, i)) != NULL) {
1504 if (index >= vector_active(cmd_element->strvec))
1505 vector_slot(v, i) = NULL;
1506 else {
1507 unsigned int j;
1508 int matched = 0;
1509
1510 descvec =
1511 vector_slot(cmd_element->strvec, index);
1512
1513 for (j = 0; j < vector_active(descvec); j++)
1514 if ((desc = vector_slot(descvec, j))) {
1515 enum match_type ret;
1516
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001517 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001518
1519 if (ret >= match_type)
1520 matched++;
1521 }
1522 if (!matched)
1523 vector_slot(v, i) = NULL;
1524 }
1525 }
1526
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001527 return match_type;
1528}
1529
1530/* Check ambiguous match */
1531static int
1532is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1533{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001534 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001535 unsigned int i;
1536 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001537 struct cmd_element *cmd_element;
1538 const char *matched = NULL;
1539 vector descvec;
1540 struct desc *desc;
1541
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001542 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1543 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1544 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1545 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1546 * that case, the string must remain allocated until this function exits or another match comes
1547 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1548 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1549 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1550 void *cmd_deopt_ctx = NULL;
1551
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001552 for (i = 0; i < vector_active(v); i++) {
1553 cmd_element = vector_slot(v, i);
1554 if (!cmd_element)
1555 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001556
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001557 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001558
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001559 descvec = vector_slot(cmd_element->strvec, index);
1560
1561 for (j = 0; j < vector_active(descvec); j++) {
1562 desc = vector_slot(descvec, j);
1563 if (!desc)
1564 continue;
1565
1566 enum match_type mtype;
1567 const char *str = desc->cmd;
1568
1569 if (CMD_OPTION(str)) {
1570 if (!cmd_deopt_ctx)
1571 cmd_deopt_ctx =
1572 talloc_named_const(tall_vty_cmd_ctx, 0,
1573 __func__);
1574 str = cmd_deopt(cmd_deopt_ctx, str);
1575 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001576 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001577 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001578
1579 switch (type) {
1580 case EXACT_MATCH:
1581 if (!(CMD_VARIABLE (str))
1582 && strcmp(command, str) == 0)
1583 match++;
1584 break;
1585 case PARTLY_MATCH:
1586 if (!(CMD_VARIABLE (str))
1587 && strncmp(command, str, strlen (command)) == 0)
1588 {
1589 if (matched
1590 && strcmp(matched,
1591 str) != 0) {
1592 ret = 1; /* There is ambiguous match. */
1593 goto free_and_return;
1594 } else
1595 matched = str;
1596 match++;
1597 }
1598 break;
1599 case RANGE_MATCH:
1600 if (cmd_range_match
1601 (str, command)) {
1602 if (matched
1603 && strcmp(matched,
1604 str) != 0) {
1605 ret = 1;
1606 goto free_and_return;
1607 } else
1608 matched = str;
1609 match++;
1610 }
1611 break;
1612#ifdef HAVE_IPV6
1613 case IPV6_MATCH:
1614 if (CMD_IPV6(str))
1615 match++;
1616 break;
1617 case IPV6_PREFIX_MATCH:
1618 if ((mtype =
1619 cmd_ipv6_prefix_match
1620 (command)) != NO_MATCH) {
1621 if (mtype == PARTLY_MATCH) {
1622 ret = 2; /* There is incomplete match. */
1623 goto free_and_return;
1624 }
1625
1626 match++;
1627 }
1628 break;
1629#endif /* HAVE_IPV6 */
1630 case IPV4_MATCH:
1631 if (CMD_IPV4(str))
1632 match++;
1633 break;
1634 case IPV4_PREFIX_MATCH:
1635 if ((mtype =
1636 cmd_ipv4_prefix_match
1637 (command)) != NO_MATCH) {
1638 if (mtype == PARTLY_MATCH) {
1639 ret = 2; /* There is incomplete match. */
1640 goto free_and_return;
1641 }
1642
1643 match++;
1644 }
1645 break;
1646 case EXTEND_MATCH:
1647 if (CMD_VARIABLE (str))
1648 match++;
1649 break;
1650 case NO_MATCH:
1651 default:
1652 break;
1653 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001654 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001655 if (!match)
1656 vector_slot(v, i) = NULL;
1657 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001658
1659free_and_return:
1660 if (cmd_deopt_ctx)
1661 talloc_free(cmd_deopt_ctx);
1662 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001663}
1664
1665/* If src matches dst return dst string, otherwise return NULL */
1666static const char *cmd_entry_function(const char *src, const char *dst)
1667{
1668 /* Skip variable arguments. */
1669 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1670 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1671 return NULL;
1672
1673 /* In case of 'command \t', given src is NULL string. */
1674 if (src == NULL)
1675 return dst;
1676
1677 /* Matched with input string. */
1678 if (strncmp(src, dst, strlen(src)) == 0)
1679 return dst;
1680
1681 return NULL;
1682}
1683
1684/* If src matches dst return dst string, otherwise return NULL */
1685/* This version will return the dst string always if it is
1686 CMD_VARIABLE for '?' key processing */
1687static const char *cmd_entry_function_desc(const char *src, const char *dst)
1688{
1689 if (CMD_VARARG(dst))
1690 return dst;
1691
1692 if (CMD_RANGE(dst)) {
1693 if (cmd_range_match(dst, src))
1694 return dst;
1695 else
1696 return NULL;
1697 }
1698#ifdef HAVE_IPV6
1699 if (CMD_IPV6(dst)) {
1700 if (cmd_ipv6_match(src))
1701 return dst;
1702 else
1703 return NULL;
1704 }
1705
1706 if (CMD_IPV6_PREFIX(dst)) {
1707 if (cmd_ipv6_prefix_match(src))
1708 return dst;
1709 else
1710 return NULL;
1711 }
1712#endif /* HAVE_IPV6 */
1713
1714 if (CMD_IPV4(dst)) {
1715 if (cmd_ipv4_match(src))
1716 return dst;
1717 else
1718 return NULL;
1719 }
1720
1721 if (CMD_IPV4_PREFIX(dst)) {
1722 if (cmd_ipv4_prefix_match(src))
1723 return dst;
1724 else
1725 return NULL;
1726 }
1727
1728 /* Optional or variable commands always match on '?' */
1729 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1730 return dst;
1731
1732 /* In case of 'command \t', given src is NULL string. */
1733 if (src == NULL)
1734 return dst;
1735
1736 if (strncmp(src, dst, strlen(src)) == 0)
1737 return dst;
1738 else
1739 return NULL;
1740}
1741
1742/* Check same string element existence. If it isn't there return
1743 1. */
1744static int cmd_unique_string(vector v, const char *str)
1745{
1746 unsigned int i;
1747 char *match;
1748
1749 for (i = 0; i < vector_active(v); i++)
1750 if ((match = vector_slot(v, i)) != NULL)
1751 if (strcmp(match, str) == 0)
1752 return 0;
1753 return 1;
1754}
1755
1756/* Compare string to description vector. If there is same string
1757 return 1 else return 0. */
1758static int desc_unique_string(vector v, const char *str)
1759{
1760 unsigned int i;
1761 struct desc *desc;
1762
1763 for (i = 0; i < vector_active(v); i++)
1764 if ((desc = vector_slot(v, i)) != NULL)
1765 if (strcmp(desc->cmd, str) == 0)
1766 return 1;
1767 return 0;
1768}
1769
1770static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1771{
1772 if (first_word != NULL &&
1773 node != AUTH_NODE &&
1774 node != VIEW_NODE &&
1775 node != AUTH_ENABLE_NODE &&
1776 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1777 return 1;
1778 return 0;
1779}
1780
1781/* '?' describe command support. */
1782static vector
1783cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1784{
1785 unsigned int i;
1786 vector cmd_vector;
1787#define INIT_MATCHVEC_SIZE 10
1788 vector matchvec;
1789 struct cmd_element *cmd_element;
1790 unsigned int index;
1791 int ret;
1792 enum match_type match;
1793 char *command;
1794 static struct desc desc_cr = { "<cr>", "" };
1795
1796 /* Set index. */
1797 if (vector_active(vline) == 0) {
1798 *status = CMD_ERR_NO_MATCH;
1799 return NULL;
1800 } else
1801 index = vector_active(vline) - 1;
1802
1803 /* Make copy vector of current node's command vector. */
1804 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1805
1806 /* Prepare match vector */
1807 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1808
1809 /* Filter commands. */
1810 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001811 for (i = 0; i < index; i++) {
1812 command = vector_slot(vline, i);
1813 if (!command)
1814 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001815
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001816 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001817
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001818 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001819 struct cmd_element *cmd_element;
1820 vector descvec;
1821 unsigned int j, k;
1822
1823 for (j = 0; j < vector_active(cmd_vector); j++)
1824 if ((cmd_element =
1825 vector_slot(cmd_vector, j)) != NULL
1826 &&
1827 (vector_active(cmd_element->strvec))) {
1828 descvec =
1829 vector_slot(cmd_element->
1830 strvec,
1831 vector_active
1832 (cmd_element->
1833 strvec) - 1);
1834 for (k = 0;
1835 k < vector_active(descvec);
1836 k++) {
1837 struct desc *desc =
1838 vector_slot(descvec,
1839 k);
1840 vector_set(matchvec,
1841 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001842 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001843 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001844
Harald Welte80d30fe2013-02-12 11:08:57 +01001845 vector_set(matchvec, &desc_cr);
1846 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001847
Harald Welte80d30fe2013-02-12 11:08:57 +01001848 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001849 }
1850
Harald Welte80d30fe2013-02-12 11:08:57 +01001851 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1852 match)) == 1) {
1853 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001854 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001855 *status = CMD_ERR_AMBIGUOUS;
1856 return NULL;
1857 } else if (ret == 2) {
1858 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001859 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001860 *status = CMD_ERR_NO_MATCH;
1861 return NULL;
1862 }
1863 }
1864
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001865 /* Prepare match vector */
1866 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1867
1868 /* Make sure that cmd_vector is filtered based on current word */
1869 command = vector_slot(vline, index);
1870 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001871 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001872
1873 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001874 for (i = 0; i < vector_active(cmd_vector); i++) {
1875 const char *string = NULL;
1876 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001877
Harald Welte80d30fe2013-02-12 11:08:57 +01001878 cmd_element = vector_slot(cmd_vector, i);
1879 if (!cmd_element)
1880 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001881
Harald Welted17aa592013-02-12 11:11:34 +01001882 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1883 continue;
1884
Harald Welte80d30fe2013-02-12 11:08:57 +01001885 strvec = cmd_element->strvec;
1886
1887 /* if command is NULL, index may be equal to vector_active */
1888 if (command && index >= vector_active(strvec))
1889 vector_slot(cmd_vector, i) = NULL;
1890 else {
1891 /* Check if command is completed. */
1892 if (command == NULL
1893 && index == vector_active(strvec)) {
1894 string = "<cr>";
1895 if (!desc_unique_string(matchvec, string))
1896 vector_set(matchvec, &desc_cr);
1897 } else {
1898 unsigned int j;
1899 vector descvec = vector_slot(strvec, index);
1900 struct desc *desc;
1901
1902 for (j = 0; j < vector_active(descvec); j++) {
1903 desc = vector_slot(descvec, j);
1904 if (!desc)
1905 continue;
1906 string = cmd_entry_function_desc
1907 (command, desc->cmd);
1908 if (!string)
1909 continue;
1910 /* Uniqueness check */
1911 if (!desc_unique_string(matchvec, string))
1912 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001913 }
1914 }
1915 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001916 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001917 vector_free(cmd_vector);
1918
1919 if (vector_slot(matchvec, 0) == NULL) {
1920 vector_free(matchvec);
1921 *status = CMD_ERR_NO_MATCH;
1922 } else
1923 *status = CMD_SUCCESS;
1924
1925 return matchvec;
1926}
1927
1928vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1929{
1930 vector ret;
1931
1932 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1933 enum node_type onode;
1934 vector shifted_vline;
1935 unsigned int index;
1936
1937 onode = vty->node;
1938 vty->node = ENABLE_NODE;
1939 /* We can try it on enable node, cos' the vty is authenticated */
1940
1941 shifted_vline = vector_init(vector_count(vline));
1942 /* use memcpy? */
1943 for (index = 1; index < vector_active(vline); index++) {
1944 vector_set_index(shifted_vline, index - 1,
1945 vector_lookup(vline, index));
1946 }
1947
1948 ret = cmd_describe_command_real(shifted_vline, vty, status);
1949
1950 vector_free(shifted_vline);
1951 vty->node = onode;
1952 return ret;
1953 }
1954
1955 return cmd_describe_command_real(vline, vty, status);
1956}
1957
1958/* Check LCD of matched command. */
1959static int cmd_lcd(char **matched)
1960{
1961 int i;
1962 int j;
1963 int lcd = -1;
1964 char *s1, *s2;
1965 char c1, c2;
1966
1967 if (matched[0] == NULL || matched[1] == NULL)
1968 return 0;
1969
1970 for (i = 1; matched[i] != NULL; i++) {
1971 s1 = matched[i - 1];
1972 s2 = matched[i];
1973
1974 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1975 if (c1 != c2)
1976 break;
1977
1978 if (lcd < 0)
1979 lcd = j;
1980 else {
1981 if (lcd > j)
1982 lcd = j;
1983 }
1984 }
1985 return lcd;
1986}
1987
1988/* Command line completion support. */
1989static char **cmd_complete_command_real(vector vline, struct vty *vty,
1990 int *status)
1991{
1992 unsigned int i;
1993 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1994#define INIT_MATCHVEC_SIZE 10
1995 vector matchvec;
1996 struct cmd_element *cmd_element;
1997 unsigned int index;
1998 char **match_str;
1999 struct desc *desc;
2000 vector descvec;
2001 char *command;
2002 int lcd;
2003
2004 if (vector_active(vline) == 0) {
2005 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002006 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002007 return NULL;
2008 } else
2009 index = vector_active(vline) - 1;
2010
2011 /* First, filter by preceeding command string */
2012 for (i = 0; i < index; i++)
2013 if ((command = vector_slot(vline, i))) {
2014 enum match_type match;
2015 int ret;
2016
2017 /* First try completion match, if there is exactly match return 1 */
2018 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002019 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002020
2021 /* If there is exact match then filter ambiguous match else check
2022 ambiguousness. */
2023 if ((ret =
2024 is_cmd_ambiguous(command, cmd_vector, i,
2025 match)) == 1) {
2026 vector_free(cmd_vector);
2027 *status = CMD_ERR_AMBIGUOUS;
2028 return NULL;
2029 }
2030 /*
2031 else if (ret == 2)
2032 {
2033 vector_free (cmd_vector);
2034 *status = CMD_ERR_NO_MATCH;
2035 return NULL;
2036 }
2037 */
2038 }
2039
2040 /* Prepare match vector. */
2041 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2042
2043 /* Now we got into completion */
2044 for (i = 0; i < vector_active(cmd_vector); i++)
2045 if ((cmd_element = vector_slot(cmd_vector, i))) {
2046 const char *string;
2047 vector strvec = cmd_element->strvec;
2048
2049 /* Check field length */
2050 if (index >= vector_active(strvec))
2051 vector_slot(cmd_vector, i) = NULL;
2052 else {
2053 unsigned int j;
2054
2055 descvec = vector_slot(strvec, index);
2056 for (j = 0; j < vector_active(descvec); j++)
2057 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002058 const char *cmd = desc->cmd;
2059 char *tmp = NULL;
2060
2061 if (CMD_OPTION(desc->cmd)) {
2062 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2063 cmd = tmp;
2064 }
2065 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002066 if (cmd_unique_string (matchvec, string))
2067 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002068 if (tmp)
2069 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002070 }
2071 }
2072 }
2073
2074 /* We don't need cmd_vector any more. */
2075 vector_free(cmd_vector);
2076
2077 /* No matched command */
2078 if (vector_slot(matchvec, 0) == NULL) {
2079 vector_free(matchvec);
2080
2081 /* In case of 'command \t' pattern. Do you need '?' command at
2082 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002083 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002084 *status = CMD_ERR_NOTHING_TODO;
2085 else
2086 *status = CMD_ERR_NO_MATCH;
2087 return NULL;
2088 }
2089
2090 /* Only one matched */
2091 if (vector_slot(matchvec, 1) == NULL) {
2092 match_str = (char **)matchvec->index;
2093 vector_only_wrapper_free(matchvec);
2094 *status = CMD_COMPLETE_FULL_MATCH;
2095 return match_str;
2096 }
2097 /* Make it sure last element is NULL. */
2098 vector_set(matchvec, NULL);
2099
2100 /* Check LCD of matched strings. */
2101 if (vector_slot(vline, index) != NULL) {
2102 lcd = cmd_lcd((char **)matchvec->index);
2103
2104 if (lcd) {
2105 int len = strlen(vector_slot(vline, index));
2106
2107 if (len < lcd) {
2108 char *lcdstr;
2109
2110 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2111 "complete-lcdstr");
2112 memcpy(lcdstr, matchvec->index[0], lcd);
2113 lcdstr[lcd] = '\0';
2114
2115 /* match_str = (char **) &lcdstr; */
2116
2117 /* Free matchvec. */
2118 for (i = 0; i < vector_active(matchvec); i++) {
2119 if (vector_slot(matchvec, i))
2120 talloc_free(vector_slot(matchvec, i));
2121 }
2122 vector_free(matchvec);
2123
2124 /* Make new matchvec. */
2125 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2126 vector_set(matchvec, lcdstr);
2127 match_str = (char **)matchvec->index;
2128 vector_only_wrapper_free(matchvec);
2129
2130 *status = CMD_COMPLETE_MATCH;
2131 return match_str;
2132 }
2133 }
2134 }
2135
2136 match_str = (char **)matchvec->index;
2137 vector_only_wrapper_free(matchvec);
2138 *status = CMD_COMPLETE_LIST_MATCH;
2139 return match_str;
2140}
2141
2142char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2143{
2144 char **ret;
2145
2146 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2147 enum node_type onode;
2148 vector shifted_vline;
2149 unsigned int index;
2150
2151 onode = vty->node;
2152 vty->node = ENABLE_NODE;
2153 /* We can try it on enable node, cos' the vty is authenticated */
2154
2155 shifted_vline = vector_init(vector_count(vline));
2156 /* use memcpy? */
2157 for (index = 1; index < vector_active(vline); index++) {
2158 vector_set_index(shifted_vline, index - 1,
2159 vector_lookup(vline, index));
2160 }
2161
2162 ret = cmd_complete_command_real(shifted_vline, vty, status);
2163
2164 vector_free(shifted_vline);
2165 vty->node = onode;
2166 return ret;
2167 }
2168
2169 return cmd_complete_command_real(vline, vty, status);
2170}
2171
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002172static struct vty_parent_node *vty_parent(struct vty *vty)
2173{
2174 return llist_first_entry_or_null(&vty->parent_nodes,
2175 struct vty_parent_node,
2176 entry);
2177}
2178
2179static bool vty_pop_parent(struct vty *vty)
2180{
2181 struct vty_parent_node *parent = vty_parent(vty);
2182 if (!parent)
2183 return false;
2184 llist_del(&parent->entry);
2185 vty->node = parent->node;
2186 vty->priv = parent->priv;
2187 if (vty->indent)
2188 talloc_free(vty->indent);
2189 vty->indent = parent->indent;
2190 talloc_free(parent);
2191 return true;
2192}
2193
2194static void vty_clear_parents(struct vty *vty)
2195{
2196 while (vty_pop_parent(vty));
2197}
2198
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002199/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002200/*
2201 * This function MUST eventually converge on a node when called repeatedly,
2202 * there must not be any cycles.
2203 * All 'config' nodes shall converge on CONFIG_NODE.
2204 * All other 'enable' nodes shall converge on ENABLE_NODE.
2205 * All 'view' only nodes shall converge on VIEW_NODE.
2206 * All other nodes shall converge on themselves or it must be ensured,
2207 * that the user's rights are not extended anyhow by calling this function.
2208 *
2209 * Note that these requirements also apply to all functions that are used
2210 * as go_parent_cb.
2211 * Note also that this function relies on the is_config_child callback to
2212 * recognize non-config nodes if go_parent_cb is not set.
2213 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002214int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002215{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002216 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002217 case AUTH_NODE:
2218 case VIEW_NODE:
2219 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002220 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002221 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002222 break;
2223
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002224 case AUTH_ENABLE_NODE:
2225 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002226 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002227 break;
2228
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002229 default:
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002230 if (host.app_info->go_parent_cb)
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002231 host.app_info->go_parent_cb(vty);
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002232 vty_pop_parent(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002233 break;
2234 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002235
2236 return vty->node;
2237}
2238
2239/* Execute command by argument vline vector. */
2240static int
2241cmd_execute_command_real(vector vline, struct vty *vty,
2242 struct cmd_element **cmd)
2243{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002244 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002245 unsigned int index;
2246 vector cmd_vector;
2247 struct cmd_element *cmd_element;
2248 struct cmd_element *matched_element;
2249 unsigned int matched_count, incomplete_count;
2250 int argc;
2251 const char *argv[CMD_ARGC_MAX];
2252 enum match_type match = 0;
2253 int varflag;
2254 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002255 int rc;
2256 /* Used for temporary storage of cmd_deopt() allocated arguments during
2257 argv[] generation */
2258 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002259
2260 /* Make copy of command elements. */
2261 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2262
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002263 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002264 if ((command = vector_slot(vline, index))) {
2265 int ret;
2266
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002267 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002268 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002269
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002270 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002271 break;
2272
2273 ret =
2274 is_cmd_ambiguous(command, cmd_vector, index, match);
2275
2276 if (ret == 1) {
2277 vector_free(cmd_vector);
2278 return CMD_ERR_AMBIGUOUS;
2279 } else if (ret == 2) {
2280 vector_free(cmd_vector);
2281 return CMD_ERR_NO_MATCH;
2282 }
2283 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002284 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002285
2286 /* Check matched count. */
2287 matched_element = NULL;
2288 matched_count = 0;
2289 incomplete_count = 0;
2290
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002291 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002292 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002293 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002294 || index >= cmd_element->cmdsize) {
2295 matched_element = cmd_element;
2296#if 0
2297 printf("DEBUG: %s\n", cmd_element->string);
2298#endif
2299 matched_count++;
2300 } else {
2301 incomplete_count++;
2302 }
2303 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002304 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002305
2306 /* Finish of using cmd_vector. */
2307 vector_free(cmd_vector);
2308
2309 /* To execute command, matched_count must be 1. */
2310 if (matched_count == 0) {
2311 if (incomplete_count)
2312 return CMD_ERR_INCOMPLETE;
2313 else
2314 return CMD_ERR_NO_MATCH;
2315 }
2316
2317 if (matched_count > 1)
2318 return CMD_ERR_AMBIGUOUS;
2319
2320 /* Argument treatment */
2321 varflag = 0;
2322 argc = 0;
2323
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002324 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2325
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002326 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002327 if (argc == CMD_ARGC_MAX) {
2328 rc = CMD_ERR_EXEED_ARGC_MAX;
2329 goto rc_free_deopt_ctx;
2330 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002331 if (varflag) {
2332 argv[argc++] = vector_slot(vline, i);
2333 continue;
2334 }
2335
2336 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002337 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002338
2339 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002340 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002341 struct desc *desc = vector_slot(descvec, 0);
2342
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002343 if (CMD_OPTION(desc->cmd)) {
2344 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2345 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2346 } else {
2347 tmp_cmd = desc->cmd;
2348 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002349
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002350 if (CMD_VARARG(tmp_cmd))
2351 varflag = 1;
2352 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002353 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002354 else if (CMD_OPTION(desc->cmd))
2355 argv[argc++] = tmp_cmd;
2356 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002357 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002358 /* multi choice argument. look up which choice
2359 the user meant (can only be one after
2360 filtering and checking for ambigous). For instance,
2361 if user typed "th" for "(two|three)" arg, we
2362 want to pass "three" in argv[]. */
2363 for (j = 0; j < vector_active(descvec); j++) {
2364 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002365 if (!desc)
2366 continue;
2367 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2368 continue;
2369 if (CMD_OPTION(desc->cmd)) {
2370 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2371 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2372 } else {
2373 tmp_cmd = desc->cmd;
2374 }
2375
2376 if(CMD_VARIABLE(tmp_cmd)) {
2377 argv[argc++] = vector_slot(vline, i);
2378 } else {
2379 argv[argc++] = tmp_cmd;
2380 }
2381 break;
2382 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002383 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002384 }
2385
2386 /* For vtysh execution. */
2387 if (cmd)
2388 *cmd = matched_element;
2389
2390 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002391 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002392 else {
2393 /* Execute matched command. */
2394 struct vty_parent_node this_node = {
2395 .node = vty->node,
2396 .priv = vty->priv,
2397 .indent = vty->indent,
2398 };
2399 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002400 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002401
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002402 /* If we have stepped down into a child node, push a parent frame.
2403 * The causality is such: we don't expect every single node entry implementation to push
2404 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2405 * a parent node. Hence if the node changed without the parent node changing, we must
2406 * have stepped into a child node. */
2407 if (vty->node != this_node.node && parent == vty_parent(vty)
2408 && vty->node > CONFIG_NODE) {
2409 /* Push the parent node. */
2410 parent = talloc_zero(vty, struct vty_parent_node);
2411 *parent = this_node;
2412 llist_add(&parent->entry, &vty->parent_nodes);
2413 }
2414 }
2415
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002416rc_free_deopt_ctx:
2417 /* Now after we called the command func, we can free temporary strings */
2418 talloc_free(cmd_deopt_ctx);
2419 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002420}
2421
2422int
2423cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2424 int vtysh)
2425{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002426 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002427 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002428
2429 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002430
2431 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2432 vector shifted_vline;
2433 unsigned int index;
2434
2435 vty->node = ENABLE_NODE;
2436 /* We can try it on enable node, cos' the vty is authenticated */
2437
2438 shifted_vline = vector_init(vector_count(vline));
2439 /* use memcpy? */
2440 for (index = 1; index < vector_active(vline); index++) {
2441 vector_set_index(shifted_vline, index - 1,
2442 vector_lookup(vline, index));
2443 }
2444
2445 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2446
2447 vector_free(shifted_vline);
2448 vty->node = onode;
2449 return ret;
2450 }
2451
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002452 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002453}
2454
2455/* Execute command by argument readline. */
2456int
2457cmd_execute_command_strict(vector vline, struct vty *vty,
2458 struct cmd_element **cmd)
2459{
2460 unsigned int i;
2461 unsigned int index;
2462 vector cmd_vector;
2463 struct cmd_element *cmd_element;
2464 struct cmd_element *matched_element;
2465 unsigned int matched_count, incomplete_count;
2466 int argc;
2467 const char *argv[CMD_ARGC_MAX];
2468 int varflag;
2469 enum match_type match = 0;
2470 char *command;
2471
2472 /* Make copy of command element */
2473 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2474
2475 for (index = 0; index < vector_active(vline); index++)
2476 if ((command = vector_slot(vline, index))) {
2477 int ret;
2478
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002479 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002480 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002481
2482 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002483 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002484 break;
2485
2486 ret =
2487 is_cmd_ambiguous(command, cmd_vector, index, match);
2488 if (ret == 1) {
2489 vector_free(cmd_vector);
2490 return CMD_ERR_AMBIGUOUS;
2491 }
2492 if (ret == 2) {
2493 vector_free(cmd_vector);
2494 return CMD_ERR_NO_MATCH;
2495 }
2496 }
2497
2498 /* Check matched count. */
2499 matched_element = NULL;
2500 matched_count = 0;
2501 incomplete_count = 0;
2502 for (i = 0; i < vector_active(cmd_vector); i++)
2503 if (vector_slot(cmd_vector, i) != NULL) {
2504 cmd_element = vector_slot(cmd_vector, i);
2505
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002506 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002507 || index >= cmd_element->cmdsize) {
2508 matched_element = cmd_element;
2509 matched_count++;
2510 } else
2511 incomplete_count++;
2512 }
2513
2514 /* Finish of using cmd_vector. */
2515 vector_free(cmd_vector);
2516
2517 /* To execute command, matched_count must be 1. */
2518 if (matched_count == 0) {
2519 if (incomplete_count)
2520 return CMD_ERR_INCOMPLETE;
2521 else
2522 return CMD_ERR_NO_MATCH;
2523 }
2524
2525 if (matched_count > 1)
2526 return CMD_ERR_AMBIGUOUS;
2527
2528 /* Argument treatment */
2529 varflag = 0;
2530 argc = 0;
2531
2532 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002533 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002534 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002535 if (varflag) {
2536 argv[argc++] = vector_slot(vline, i);
2537 continue;
2538 }
2539
2540 vector descvec = vector_slot(matched_element->strvec, i);
2541
2542 if (vector_active(descvec) == 1) {
2543 struct desc *desc = vector_slot(descvec, 0);
2544
2545 if (CMD_VARARG(desc->cmd))
2546 varflag = 1;
2547
2548 if (varflag || CMD_VARIABLE(desc->cmd)
2549 || CMD_OPTION(desc->cmd))
2550 argv[argc++] = vector_slot(vline, i);
2551 } else {
2552 argv[argc++] = vector_slot(vline, i);
2553 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002554 }
2555
2556 /* For vtysh execution. */
2557 if (cmd)
2558 *cmd = matched_element;
2559
2560 if (matched_element->daemon)
2561 return CMD_SUCCESS_DAEMON;
2562
2563 /* Now execute matched command */
2564 return (*matched_element->func) (matched_element, vty, argc, argv);
2565}
2566
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002567static inline size_t len(const char *str)
2568{
2569 return str? strlen(str) : 0;
2570}
2571
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002572/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2573 * is longer than b, a must start with exactly b, and vice versa.
2574 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2575 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002576static int indent_cmp(const char *a, const char *b)
2577{
2578 size_t al, bl;
2579 al = len(a);
2580 bl = len(b);
2581 if (al > bl) {
2582 if (bl && strncmp(a, b, bl) != 0)
2583 return EINVAL;
2584 return 1;
2585 }
2586 /* al <= bl */
2587 if (al && strncmp(a, b, al) != 0)
2588 return EINVAL;
2589 return (al < bl)? -1 : 0;
2590}
2591
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002592/* Configration make from file. */
2593int config_from_file(struct vty *vty, FILE * fp)
2594{
2595 int ret;
2596 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002597 char *indent;
2598 int cmp;
2599 struct vty_parent_node this_node;
2600 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002601
2602 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002603 indent = NULL;
2604 vline = NULL;
2605 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002606
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002607 if (ret != CMD_SUCCESS)
2608 goto return_invalid_indent;
2609
2610 /* In case of comment or empty line */
2611 if (vline == NULL) {
2612 if (indent) {
2613 talloc_free(indent);
2614 indent = NULL;
2615 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002616 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002617 }
2618
Neels Hofmeyr43063632017-09-19 23:54:01 +02002619 /* We have a nonempty line. */
2620 if (!vty->indent) {
2621 /* We have just entered a node and expecting the first child to come up; but we
2622 * may also skip right back to a parent or ancestor level. */
2623 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002624
Neels Hofmeyr43063632017-09-19 23:54:01 +02002625 /* If there is no parent, record any indentation we encounter. */
2626 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2627
2628 if (cmp == EINVAL)
2629 goto return_invalid_indent;
2630
2631 if (cmp <= 0) {
2632 /* We have gone right back to the parent level or higher, we are skipping
2633 * this child node level entirely. Pop the parent to go back to a node
2634 * that was actually there (to reinstate vty->indent) and re-use below
2635 * go-parent while-loop to find an accurate match of indent in the node
2636 * ancestry. */
2637 vty_go_parent(vty);
2638 } else {
2639 /* The indent is deeper than the just entered parent, record the new
2640 * indentation characters. */
2641 vty->indent = talloc_strdup(vty, indent);
2642 /* This *is* the new indentation. */
2643 cmp = 0;
2644 }
2645 } else {
2646 /* There is a known indentation for this node level, validate and detect node
2647 * exits. */
2648 cmp = indent_cmp(indent, vty->indent);
2649 if (cmp == EINVAL)
2650 goto return_invalid_indent;
2651 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002652
2653 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2654 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2655 while (cmp < 0) {
2656 vty_go_parent(vty);
2657 cmp = indent_cmp(indent, vty->indent);
2658 if (cmp == EINVAL)
2659 goto return_invalid_indent;
2660 }
2661
2662 /* More indent without having entered a child node level? Either the parent node's indent
2663 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2664 * or the indentation increased even though the vty command didn't enter a child. */
2665 if (cmp > 0)
2666 goto return_invalid_indent;
2667
2668 /* Remember the current node before the command possibly changes it. */
2669 this_node = (struct vty_parent_node){
2670 .node = vty->node,
2671 .priv = vty->priv,
2672 .indent = vty->indent,
2673 };
2674
2675 parent = vty_parent(vty);
2676 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002677 cmd_free_strvec(vline);
2678
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002679 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002680 if (indent) {
2681 talloc_free(indent);
2682 indent = NULL;
2683 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002684 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002685 }
2686
2687 /* If we have stepped down into a child node, push a parent frame.
2688 * The causality is such: we don't expect every single node entry implementation to push
2689 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2690 * a parent node. Hence if the node changed without the parent node changing, we must
2691 * have stepped into a child node (and now expect a deeper indent). */
2692 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2693 /* Push the parent node. */
2694 parent = talloc_zero(vty, struct vty_parent_node);
2695 *parent = this_node;
2696 llist_add(&parent->entry, &vty->parent_nodes);
2697
2698 /* The current talloc'ed vty->indent string will now be owned by this parent
2699 * struct. Indicate that we don't know what deeper indent characters the user
2700 * will choose. */
2701 vty->indent = NULL;
2702 }
2703
2704 if (indent) {
2705 talloc_free(indent);
2706 indent = NULL;
2707 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002708 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002709 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2710 while (vty_parent(vty))
2711 vty_go_parent(vty);
2712
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002713 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002714
2715return_invalid_indent:
2716 if (vline)
2717 cmd_free_strvec(vline);
2718 if (indent) {
2719 talloc_free(indent);
2720 indent = NULL;
2721 }
2722 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002723}
2724
2725/* Configration from terminal */
2726DEFUN(config_terminal,
2727 config_terminal_cmd,
2728 "configure terminal",
2729 "Configuration from vty interface\n" "Configuration terminal\n")
2730{
2731 if (vty_config_lock(vty))
2732 vty->node = CONFIG_NODE;
2733 else {
2734 vty_out(vty, "VTY configuration is locked by other VTY%s",
2735 VTY_NEWLINE);
2736 return CMD_WARNING;
2737 }
2738 return CMD_SUCCESS;
2739}
2740
2741/* Enable command */
2742DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2743{
2744 /* If enable password is NULL, change to ENABLE_NODE */
2745 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2746 vty->type == VTY_SHELL_SERV)
2747 vty->node = ENABLE_NODE;
2748 else
2749 vty->node = AUTH_ENABLE_NODE;
2750
2751 return CMD_SUCCESS;
2752}
2753
2754/* Disable command */
2755DEFUN(disable,
2756 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2757{
2758 if (vty->node == ENABLE_NODE)
2759 vty->node = VIEW_NODE;
2760 return CMD_SUCCESS;
2761}
2762
2763/* Down vty node level. */
2764gDEFUN(config_exit,
2765 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2766{
2767 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002768 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002769 case VIEW_NODE:
2770 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002771 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002772 break;
2773 case CONFIG_NODE:
2774 vty->node = ENABLE_NODE;
2775 vty_config_unlock(vty);
2776 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002777 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002778 if (vty->node > CONFIG_NODE)
2779 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002780 break;
2781 }
2782 return CMD_SUCCESS;
2783}
2784
2785/* End of configuration. */
2786 gDEFUN(config_end,
2787 config_end_cmd, "end", "End current mode and change to enable mode.")
2788{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002789 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002790 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002791
2792 /* Repeatedly call go_parent until a top node is reached. */
2793 while (vty->node > CONFIG_NODE) {
2794 if (vty->node == last_node) {
2795 /* Ensure termination, this shouldn't happen. */
2796 break;
2797 }
2798 last_node = vty->node;
2799 vty_go_parent(vty);
2800 }
2801
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002802 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002803 if (vty->node > ENABLE_NODE)
2804 vty->node = ENABLE_NODE;
2805 vty->index = NULL;
2806 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002807 }
2808 return CMD_SUCCESS;
2809}
2810
2811/* Show version. */
2812DEFUN(show_version,
2813 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2814{
Harald Welte237f6242010-05-25 23:00:45 +02002815 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2816 host.app_info->version,
2817 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2818 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002819
2820 return CMD_SUCCESS;
2821}
2822
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002823DEFUN(show_online_help,
2824 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2825{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002826 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002827 return CMD_SUCCESS;
2828}
2829
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002830/* Help display function for all node. */
2831gDEFUN(config_help,
2832 config_help_cmd, "help", "Description of the interactive help system\n")
2833{
2834 vty_out(vty,
2835 "This VTY provides advanced help features. When you need help,%s\
2836anytime at the command line please press '?'.%s\
2837%s\
2838If nothing matches, the help list will be empty and you must backup%s\
2839 until entering a '?' shows the available options.%s\
2840Two styles of help are provided:%s\
28411. Full help is available when you are ready to enter a%s\
2842command argument (e.g. 'show ?') and describes each possible%s\
2843argument.%s\
28442. Partial help is provided when an abbreviated argument is entered%s\
2845 and you want to know what arguments match the input%s\
2846 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2847 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2848 return CMD_SUCCESS;
2849}
2850
2851/* Help display function for all node. */
2852gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2853{
2854 unsigned int i;
2855 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2856 struct cmd_element *cmd;
2857
2858 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2859 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002860 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002861 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2862 return CMD_SUCCESS;
2863}
2864
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002865static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002866{
2867 unsigned int i;
2868 int fd;
2869 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002870 char *config_file_tmp = NULL;
2871 char *config_file_sav = NULL;
2872 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002873 struct stat st;
2874
2875 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002876
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002877 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2878 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2879 * manually instead. */
2880
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002881 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002882 config_file_sav =
2883 _talloc_zero(tall_vty_cmd_ctx,
2884 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2885 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002886 if (!config_file_sav)
2887 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002888 strcpy(config_file_sav, config_file);
2889 strcat(config_file_sav, CONF_BACKUP_EXT);
2890
2891 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002892 "config_file_tmp");
2893 if (!config_file_tmp) {
2894 talloc_free(config_file_sav);
2895 return -1;
2896 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002897 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2898
2899 /* Open file to configuration write. */
2900 fd = mkstemp(config_file_tmp);
2901 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002902 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002903 talloc_free(config_file_tmp);
2904 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002905 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002906 }
2907
2908 /* Make vty for configuration file. */
2909 file_vty = vty_new();
2910 file_vty->fd = fd;
2911 file_vty->type = VTY_FILE;
2912
2913 /* Config file header print. */
2914 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002915 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002916 //vty_time_print (file_vty, 1);
2917 vty_out(file_vty, "!\n");
2918
2919 for (i = 0; i < vector_active(cmdvec); i++)
2920 if ((node = vector_slot(cmdvec, i)) && node->func) {
2921 if ((*node->func) (file_vty))
2922 vty_out(file_vty, "!\n");
2923 }
2924 vty_close(file_vty);
2925
2926 if (unlink(config_file_sav) != 0)
2927 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002928 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002929 talloc_free(config_file_sav);
2930 talloc_free(config_file_tmp);
2931 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002932 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002933 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002934
2935 /* Only link the .sav file if the original file exists */
2936 if (stat(config_file, &st) == 0) {
2937 if (link(config_file, config_file_sav) != 0) {
2938 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2939 talloc_free(config_file_sav);
2940 talloc_free(config_file_tmp);
2941 unlink(config_file_tmp);
2942 return -3;
2943 }
2944 sync();
2945 if (unlink(config_file) != 0) {
2946 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2947 talloc_free(config_file_sav);
2948 talloc_free(config_file_tmp);
2949 unlink(config_file_tmp);
2950 return -4;
2951 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002952 }
2953 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002954 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002955 talloc_free(config_file_sav);
2956 talloc_free(config_file_tmp);
2957 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002958 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002959 }
2960 unlink(config_file_tmp);
2961 sync();
2962
2963 talloc_free(config_file_sav);
2964 talloc_free(config_file_tmp);
2965
2966 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002967 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2968 return -6;
2969 }
2970
2971 return 0;
2972}
2973
2974
2975/* Write current configuration into file. */
2976DEFUN(config_write_file,
2977 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002978 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002979 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002980 "Write to configuration file\n"
2981 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002982{
2983 char *failed_file;
2984 int rc;
2985
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002986 if (host.app_info->config_is_consistent) {
2987 rc = host.app_info->config_is_consistent(vty);
2988 if (!rc) {
2989 vty_out(vty, "Configuration is not consistent%s",
2990 VTY_NEWLINE);
2991 return CMD_WARNING;
2992 }
2993 }
2994
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002995 if (argc == 1)
2996 host_config_set(argv[0]);
2997
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002998 if (host.config == NULL) {
2999 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3000 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003001 return CMD_WARNING;
3002 }
3003
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003004 rc = write_config_file(host.config, &failed_file);
3005 switch (rc) {
3006 case -1:
3007 vty_out(vty, "Can't open configuration file %s.%s",
3008 failed_file, VTY_NEWLINE);
3009 rc = CMD_WARNING;
3010 break;
3011 case -2:
3012 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3013 failed_file, VTY_NEWLINE);
3014 rc = CMD_WARNING;
3015 break;
3016 case -3:
3017 vty_out(vty, "Can't backup old configuration file %s.%s",
3018 failed_file, VTY_NEWLINE);
3019 rc = CMD_WARNING;
3020 break;
3021 case -4:
3022 vty_out(vty, "Can't unlink configuration file %s.%s",
3023 failed_file, VTY_NEWLINE);
3024 rc = CMD_WARNING;
3025 break;
3026 case -5:
3027 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3028 VTY_NEWLINE);
3029 rc = CMD_WARNING;
3030 break;
3031 case -6:
3032 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3033 failed_file, strerror(errno), errno, VTY_NEWLINE);
3034 rc = CMD_WARNING;
3035 break;
3036 default:
3037 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3038 rc = CMD_SUCCESS;
3039 break;
3040 }
3041
3042 talloc_free(failed_file);
3043 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003044}
3045
3046ALIAS(config_write_file,
3047 config_write_cmd,
3048 "write", "Write running configuration to memory, network, or terminal\n")
3049
3050 ALIAS(config_write_file,
3051 config_write_memory_cmd,
3052 "write memory",
3053 "Write running configuration to memory, network, or terminal\n"
3054 "Write configuration to the file (same as write file)\n")
3055
3056 ALIAS(config_write_file,
3057 copy_runningconfig_startupconfig_cmd,
3058 "copy running-config startup-config",
3059 "Copy configuration\n"
3060 "Copy running config to... \n"
3061 "Copy running config to startup config (same as write file)\n")
3062
3063/* Write current configuration into the terminal. */
3064 DEFUN(config_write_terminal,
3065 config_write_terminal_cmd,
3066 "write terminal",
3067 "Write running configuration to memory, network, or terminal\n"
3068 "Write to terminal\n")
3069{
3070 unsigned int i;
3071 struct cmd_node *node;
3072
3073 if (vty->type == VTY_SHELL_SERV) {
3074 for (i = 0; i < vector_active(cmdvec); i++)
3075 if ((node = vector_slot(cmdvec, i)) && node->func
3076 && node->vtysh) {
3077 if ((*node->func) (vty))
3078 vty_out(vty, "!%s", VTY_NEWLINE);
3079 }
3080 } else {
3081 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3082 VTY_NEWLINE);
3083 vty_out(vty, "!%s", VTY_NEWLINE);
3084
3085 for (i = 0; i < vector_active(cmdvec); i++)
3086 if ((node = vector_slot(cmdvec, i)) && node->func) {
3087 if ((*node->func) (vty))
3088 vty_out(vty, "!%s", VTY_NEWLINE);
3089 }
3090 vty_out(vty, "end%s", VTY_NEWLINE);
3091 }
3092 return CMD_SUCCESS;
3093}
3094
3095/* Write current configuration into the terminal. */
3096ALIAS(config_write_terminal,
3097 show_running_config_cmd,
3098 "show running-config", SHOW_STR "running configuration\n")
3099
3100/* Write startup configuration into the terminal. */
3101 DEFUN(show_startup_config,
3102 show_startup_config_cmd,
3103 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3104{
3105 char buf[BUFSIZ];
3106 FILE *confp;
3107
3108 confp = fopen(host.config, "r");
3109 if (confp == NULL) {
3110 vty_out(vty, "Can't open configuration file [%s]%s",
3111 host.config, VTY_NEWLINE);
3112 return CMD_WARNING;
3113 }
3114
3115 while (fgets(buf, BUFSIZ, confp)) {
3116 char *cp = buf;
3117
3118 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3119 cp++;
3120 *cp = '\0';
3121
3122 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3123 }
3124
3125 fclose(confp);
3126
3127 return CMD_SUCCESS;
3128}
3129
3130/* Hostname configuration */
3131DEFUN(config_hostname,
3132 hostname_cmd,
3133 "hostname WORD",
3134 "Set system's network name\n" "This system's network name\n")
3135{
3136 if (!isalpha((int)*argv[0])) {
3137 vty_out(vty, "Please specify string starting with alphabet%s",
3138 VTY_NEWLINE);
3139 return CMD_WARNING;
3140 }
3141
3142 if (host.name)
3143 talloc_free(host.name);
3144
3145 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3146 return CMD_SUCCESS;
3147}
3148
3149DEFUN(config_no_hostname,
3150 no_hostname_cmd,
3151 "no hostname [HOSTNAME]",
3152 NO_STR "Reset system's network name\n" "Host name of this router\n")
3153{
3154 if (host.name)
3155 talloc_free(host.name);
3156 host.name = NULL;
3157 return CMD_SUCCESS;
3158}
3159
3160/* VTY interface password set. */
3161DEFUN(config_password, password_cmd,
3162 "password (8|) WORD",
3163 "Assign the terminal connection password\n"
3164 "Specifies a HIDDEN password will follow\n"
3165 "dummy string \n" "The HIDDEN line password string\n")
3166{
3167 /* Argument check. */
3168 if (argc == 0) {
3169 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3170 return CMD_WARNING;
3171 }
3172
3173 if (argc == 2) {
3174 if (*argv[0] == '8') {
3175 if (host.password)
3176 talloc_free(host.password);
3177 host.password = NULL;
3178 if (host.password_encrypt)
3179 talloc_free(host.password_encrypt);
3180 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3181 return CMD_SUCCESS;
3182 } else {
3183 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3184 return CMD_WARNING;
3185 }
3186 }
3187
3188 if (!isalnum((int)*argv[0])) {
3189 vty_out(vty,
3190 "Please specify string starting with alphanumeric%s",
3191 VTY_NEWLINE);
3192 return CMD_WARNING;
3193 }
3194
3195 if (host.password)
3196 talloc_free(host.password);
3197 host.password = NULL;
3198
3199#ifdef VTY_CRYPT_PW
3200 if (host.encrypt) {
3201 if (host.password_encrypt)
3202 talloc_free(host.password_encrypt);
3203 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3204 } else
3205#endif
3206 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3207
3208 return CMD_SUCCESS;
3209}
3210
3211ALIAS(config_password, password_text_cmd,
3212 "password LINE",
3213 "Assign the terminal connection password\n"
3214 "The UNENCRYPTED (cleartext) line password\n")
3215
3216/* VTY enable password set. */
3217 DEFUN(config_enable_password, enable_password_cmd,
3218 "enable password (8|) WORD",
3219 "Modify enable password parameters\n"
3220 "Assign the privileged level password\n"
3221 "Specifies a HIDDEN password will follow\n"
3222 "dummy string \n" "The HIDDEN 'enable' password string\n")
3223{
3224 /* Argument check. */
3225 if (argc == 0) {
3226 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3227 return CMD_WARNING;
3228 }
3229
3230 /* Crypt type is specified. */
3231 if (argc == 2) {
3232 if (*argv[0] == '8') {
3233 if (host.enable)
3234 talloc_free(host.enable);
3235 host.enable = NULL;
3236
3237 if (host.enable_encrypt)
3238 talloc_free(host.enable_encrypt);
3239 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3240
3241 return CMD_SUCCESS;
3242 } else {
3243 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3244 return CMD_WARNING;
3245 }
3246 }
3247
3248 if (!isalnum((int)*argv[0])) {
3249 vty_out(vty,
3250 "Please specify string starting with alphanumeric%s",
3251 VTY_NEWLINE);
3252 return CMD_WARNING;
3253 }
3254
3255 if (host.enable)
3256 talloc_free(host.enable);
3257 host.enable = NULL;
3258
3259 /* Plain password input. */
3260#ifdef VTY_CRYPT_PW
3261 if (host.encrypt) {
3262 if (host.enable_encrypt)
3263 talloc_free(host.enable_encrypt);
3264 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3265 } else
3266#endif
3267 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3268
3269 return CMD_SUCCESS;
3270}
3271
3272ALIAS(config_enable_password,
3273 enable_password_text_cmd,
3274 "enable password LINE",
3275 "Modify enable password parameters\n"
3276 "Assign the privileged level password\n"
3277 "The UNENCRYPTED (cleartext) 'enable' password\n")
3278
3279/* VTY enable password delete. */
3280 DEFUN(no_config_enable_password, no_enable_password_cmd,
3281 "no enable password",
3282 NO_STR
3283 "Modify enable password parameters\n"
3284 "Assign the privileged level password\n")
3285{
3286 if (host.enable)
3287 talloc_free(host.enable);
3288 host.enable = NULL;
3289
3290 if (host.enable_encrypt)
3291 talloc_free(host.enable_encrypt);
3292 host.enable_encrypt = NULL;
3293
3294 return CMD_SUCCESS;
3295}
3296
3297#ifdef VTY_CRYPT_PW
3298DEFUN(service_password_encrypt,
3299 service_password_encrypt_cmd,
3300 "service password-encryption",
3301 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3302{
3303 if (host.encrypt)
3304 return CMD_SUCCESS;
3305
3306 host.encrypt = 1;
3307
3308 if (host.password) {
3309 if (host.password_encrypt)
3310 talloc_free(host.password_encrypt);
3311 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3312 }
3313 if (host.enable) {
3314 if (host.enable_encrypt)
3315 talloc_free(host.enable_encrypt);
3316 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3317 }
3318
3319 return CMD_SUCCESS;
3320}
3321
3322DEFUN(no_service_password_encrypt,
3323 no_service_password_encrypt_cmd,
3324 "no service password-encryption",
3325 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3326{
3327 if (!host.encrypt)
3328 return CMD_SUCCESS;
3329
3330 host.encrypt = 0;
3331
3332 if (host.password_encrypt)
3333 talloc_free(host.password_encrypt);
3334 host.password_encrypt = NULL;
3335
3336 if (host.enable_encrypt)
3337 talloc_free(host.enable_encrypt);
3338 host.enable_encrypt = NULL;
3339
3340 return CMD_SUCCESS;
3341}
3342#endif
3343
3344DEFUN(config_terminal_length, config_terminal_length_cmd,
3345 "terminal length <0-512>",
3346 "Set terminal line parameters\n"
3347 "Set number of lines on a screen\n"
3348 "Number of lines on screen (0 for no pausing)\n")
3349{
3350 int lines;
3351 char *endptr = NULL;
3352
3353 lines = strtol(argv[0], &endptr, 10);
3354 if (lines < 0 || lines > 512 || *endptr != '\0') {
3355 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3356 return CMD_WARNING;
3357 }
3358 vty->lines = lines;
3359
3360 return CMD_SUCCESS;
3361}
3362
3363DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3364 "terminal no length",
3365 "Set terminal line parameters\n"
3366 NO_STR "Set number of lines on a screen\n")
3367{
3368 vty->lines = -1;
3369 return CMD_SUCCESS;
3370}
3371
3372DEFUN(service_terminal_length, service_terminal_length_cmd,
3373 "service terminal-length <0-512>",
3374 "Set up miscellaneous service\n"
3375 "System wide terminal length configuration\n"
3376 "Number of lines of VTY (0 means no line control)\n")
3377{
3378 int lines;
3379 char *endptr = NULL;
3380
3381 lines = strtol(argv[0], &endptr, 10);
3382 if (lines < 0 || lines > 512 || *endptr != '\0') {
3383 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3384 return CMD_WARNING;
3385 }
3386 host.lines = lines;
3387
3388 return CMD_SUCCESS;
3389}
3390
3391DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3392 "no service terminal-length [<0-512>]",
3393 NO_STR
3394 "Set up miscellaneous service\n"
3395 "System wide terminal length configuration\n"
3396 "Number of lines of VTY (0 means no line control)\n")
3397{
3398 host.lines = -1;
3399 return CMD_SUCCESS;
3400}
3401
3402DEFUN_HIDDEN(do_echo,
3403 echo_cmd,
3404 "echo .MESSAGE",
3405 "Echo a message back to the vty\n" "The message to echo\n")
3406{
3407 char *message;
3408
3409 vty_out(vty, "%s%s",
3410 ((message =
3411 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3412 if (message)
3413 talloc_free(message);
3414 return CMD_SUCCESS;
3415}
3416
3417#if 0
3418DEFUN(config_logmsg,
3419 config_logmsg_cmd,
3420 "logmsg " LOG_LEVELS " .MESSAGE",
3421 "Send a message to enabled logging destinations\n"
3422 LOG_LEVEL_DESC "The message to send\n")
3423{
3424 int level;
3425 char *message;
3426
3427 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3428 return CMD_ERR_NO_MATCH;
3429
3430 zlog(NULL, level,
3431 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3432 if (message)
3433 talloc_free(message);
3434 return CMD_SUCCESS;
3435}
3436
3437DEFUN(show_logging,
3438 show_logging_cmd,
3439 "show logging", SHOW_STR "Show current logging configuration\n")
3440{
3441 struct zlog *zl = zlog_default;
3442
3443 vty_out(vty, "Syslog logging: ");
3444 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3445 vty_out(vty, "disabled");
3446 else
3447 vty_out(vty, "level %s, facility %s, ident %s",
3448 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3449 facility_name(zl->facility), zl->ident);
3450 vty_out(vty, "%s", VTY_NEWLINE);
3451
3452 vty_out(vty, "Stdout logging: ");
3453 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3454 vty_out(vty, "disabled");
3455 else
3456 vty_out(vty, "level %s",
3457 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3458 vty_out(vty, "%s", VTY_NEWLINE);
3459
3460 vty_out(vty, "Monitor logging: ");
3461 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3462 vty_out(vty, "disabled");
3463 else
3464 vty_out(vty, "level %s",
3465 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3466 vty_out(vty, "%s", VTY_NEWLINE);
3467
3468 vty_out(vty, "File logging: ");
3469 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3470 vty_out(vty, "disabled");
3471 else
3472 vty_out(vty, "level %s, filename %s",
3473 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3474 zl->filename);
3475 vty_out(vty, "%s", VTY_NEWLINE);
3476
3477 vty_out(vty, "Protocol name: %s%s",
3478 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3479 vty_out(vty, "Record priority: %s%s",
3480 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3481
3482 return CMD_SUCCESS;
3483}
3484
3485DEFUN(config_log_stdout,
3486 config_log_stdout_cmd,
3487 "log stdout", "Logging control\n" "Set stdout logging level\n")
3488{
3489 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3490 return CMD_SUCCESS;
3491}
3492
3493DEFUN(config_log_stdout_level,
3494 config_log_stdout_level_cmd,
3495 "log stdout " LOG_LEVELS,
3496 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3497{
3498 int level;
3499
3500 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3501 return CMD_ERR_NO_MATCH;
3502 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3503 return CMD_SUCCESS;
3504}
3505
3506DEFUN(no_config_log_stdout,
3507 no_config_log_stdout_cmd,
3508 "no log stdout [LEVEL]",
3509 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3510{
3511 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3512 return CMD_SUCCESS;
3513}
3514
3515DEFUN(config_log_monitor,
3516 config_log_monitor_cmd,
3517 "log monitor",
3518 "Logging control\n" "Set terminal line (monitor) logging level\n")
3519{
3520 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3521 return CMD_SUCCESS;
3522}
3523
3524DEFUN(config_log_monitor_level,
3525 config_log_monitor_level_cmd,
3526 "log monitor " LOG_LEVELS,
3527 "Logging control\n"
3528 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3529{
3530 int level;
3531
3532 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3533 return CMD_ERR_NO_MATCH;
3534 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3535 return CMD_SUCCESS;
3536}
3537
3538DEFUN(no_config_log_monitor,
3539 no_config_log_monitor_cmd,
3540 "no log monitor [LEVEL]",
3541 NO_STR
3542 "Logging control\n"
3543 "Disable terminal line (monitor) logging\n" "Logging level\n")
3544{
3545 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3546 return CMD_SUCCESS;
3547}
3548
3549static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3550{
3551 int ret;
3552 char *p = NULL;
3553 const char *fullpath;
3554
3555 /* Path detection. */
3556 if (!IS_DIRECTORY_SEP(*fname)) {
3557 char cwd[MAXPATHLEN + 1];
3558 cwd[MAXPATHLEN] = '\0';
3559
3560 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3561 zlog_err("config_log_file: Unable to alloc mem!");
3562 return CMD_WARNING;
3563 }
3564
3565 if ((p = _talloc_zero(tall_vcmd_ctx,
3566 strlen(cwd) + strlen(fname) + 2),
3567 "set_log_file")
3568 == NULL) {
3569 zlog_err("config_log_file: Unable to alloc mem!");
3570 return CMD_WARNING;
3571 }
3572 sprintf(p, "%s/%s", cwd, fname);
3573 fullpath = p;
3574 } else
3575 fullpath = fname;
3576
3577 ret = zlog_set_file(NULL, fullpath, loglevel);
3578
3579 if (p)
3580 talloc_free(p);
3581
3582 if (!ret) {
3583 vty_out(vty, "can't open logfile %s\n", fname);
3584 return CMD_WARNING;
3585 }
3586
3587 if (host.logfile)
3588 talloc_free(host.logfile);
3589
3590 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3591
3592 return CMD_SUCCESS;
3593}
3594
3595DEFUN(config_log_file,
3596 config_log_file_cmd,
3597 "log file FILENAME",
3598 "Logging control\n" "Logging to file\n" "Logging filename\n")
3599{
3600 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3601}
3602
3603DEFUN(config_log_file_level,
3604 config_log_file_level_cmd,
3605 "log file FILENAME " LOG_LEVELS,
3606 "Logging control\n"
3607 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3608{
3609 int level;
3610
3611 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3612 return CMD_ERR_NO_MATCH;
3613 return set_log_file(vty, argv[0], level);
3614}
3615
3616DEFUN(no_config_log_file,
3617 no_config_log_file_cmd,
3618 "no log file [FILENAME]",
3619 NO_STR
3620 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3621{
3622 zlog_reset_file(NULL);
3623
3624 if (host.logfile)
3625 talloc_free(host.logfile);
3626
3627 host.logfile = NULL;
3628
3629 return CMD_SUCCESS;
3630}
3631
3632ALIAS(no_config_log_file,
3633 no_config_log_file_level_cmd,
3634 "no log file FILENAME LEVEL",
3635 NO_STR
3636 "Logging control\n"
3637 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3638
3639 DEFUN(config_log_syslog,
3640 config_log_syslog_cmd,
3641 "log syslog", "Logging control\n" "Set syslog logging level\n")
3642{
3643 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3644 return CMD_SUCCESS;
3645}
3646
3647DEFUN(config_log_syslog_level,
3648 config_log_syslog_level_cmd,
3649 "log syslog " LOG_LEVELS,
3650 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3651{
3652 int level;
3653
3654 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3655 return CMD_ERR_NO_MATCH;
3656 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3657 return CMD_SUCCESS;
3658}
3659
3660DEFUN_DEPRECATED(config_log_syslog_facility,
3661 config_log_syslog_facility_cmd,
3662 "log syslog facility " LOG_FACILITIES,
3663 "Logging control\n"
3664 "Logging goes to syslog\n"
3665 "(Deprecated) Facility parameter for syslog messages\n"
3666 LOG_FACILITY_DESC)
3667{
3668 int facility;
3669
3670 if ((facility = facility_match(argv[0])) < 0)
3671 return CMD_ERR_NO_MATCH;
3672
3673 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3674 zlog_default->facility = facility;
3675 return CMD_SUCCESS;
3676}
3677
3678DEFUN(no_config_log_syslog,
3679 no_config_log_syslog_cmd,
3680 "no log syslog [LEVEL]",
3681 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3682{
3683 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3684 return CMD_SUCCESS;
3685}
3686
3687ALIAS(no_config_log_syslog,
3688 no_config_log_syslog_facility_cmd,
3689 "no log syslog facility " LOG_FACILITIES,
3690 NO_STR
3691 "Logging control\n"
3692 "Logging goes to syslog\n"
3693 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3694
3695 DEFUN(config_log_facility,
3696 config_log_facility_cmd,
3697 "log facility " LOG_FACILITIES,
3698 "Logging control\n"
3699 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3700{
3701 int facility;
3702
3703 if ((facility = facility_match(argv[0])) < 0)
3704 return CMD_ERR_NO_MATCH;
3705 zlog_default->facility = facility;
3706 return CMD_SUCCESS;
3707}
3708
3709DEFUN(no_config_log_facility,
3710 no_config_log_facility_cmd,
3711 "no log facility [FACILITY]",
3712 NO_STR
3713 "Logging control\n"
3714 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3715{
3716 zlog_default->facility = LOG_DAEMON;
3717 return CMD_SUCCESS;
3718}
3719
3720DEFUN_DEPRECATED(config_log_trap,
3721 config_log_trap_cmd,
3722 "log trap " LOG_LEVELS,
3723 "Logging control\n"
3724 "(Deprecated) Set logging level and default for all destinations\n"
3725 LOG_LEVEL_DESC)
3726{
3727 int new_level;
3728 int i;
3729
3730 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3731 return CMD_ERR_NO_MATCH;
3732
3733 zlog_default->default_lvl = new_level;
3734 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3735 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3736 zlog_default->maxlvl[i] = new_level;
3737 return CMD_SUCCESS;
3738}
3739
3740DEFUN_DEPRECATED(no_config_log_trap,
3741 no_config_log_trap_cmd,
3742 "no log trap [LEVEL]",
3743 NO_STR
3744 "Logging control\n"
3745 "Permit all logging information\n" "Logging level\n")
3746{
3747 zlog_default->default_lvl = LOG_DEBUG;
3748 return CMD_SUCCESS;
3749}
3750
3751DEFUN(config_log_record_priority,
3752 config_log_record_priority_cmd,
3753 "log record-priority",
3754 "Logging control\n"
3755 "Log the priority of the message within the message\n")
3756{
3757 zlog_default->record_priority = 1;
3758 return CMD_SUCCESS;
3759}
3760
3761DEFUN(no_config_log_record_priority,
3762 no_config_log_record_priority_cmd,
3763 "no log record-priority",
3764 NO_STR
3765 "Logging control\n"
3766 "Do not log the priority of the message within the message\n")
3767{
3768 zlog_default->record_priority = 0;
3769 return CMD_SUCCESS;
3770}
3771#endif
3772
3773DEFUN(banner_motd_file,
3774 banner_motd_file_cmd,
3775 "banner motd file [FILE]",
3776 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3777{
3778 if (host.motdfile)
3779 talloc_free(host.motdfile);
3780 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3781
3782 return CMD_SUCCESS;
3783}
3784
3785DEFUN(banner_motd_default,
3786 banner_motd_default_cmd,
3787 "banner motd default",
3788 "Set banner string\n" "Strings for motd\n" "Default string\n")
3789{
3790 host.motd = default_motd;
3791 return CMD_SUCCESS;
3792}
3793
3794DEFUN(no_banner_motd,
3795 no_banner_motd_cmd,
3796 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3797{
3798 host.motd = NULL;
3799 if (host.motdfile)
3800 talloc_free(host.motdfile);
3801 host.motdfile = NULL;
3802 return CMD_SUCCESS;
3803}
3804
3805/* Set config filename. Called from vty.c */
3806void host_config_set(const char *filename)
3807{
3808 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3809}
3810
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003811/*! Deprecated, now happens implicitly when calling install_node().
3812 * Users of the API may still attempt to call this function, hence
3813 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003814void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003815{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003816}
3817
3818/*! 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. */
3821void vty_install_default(int node)
3822{
3823}
3824
3825/*! Install common commands like 'exit' and 'list'. */
3826static void install_basic_node_commands(int node)
3827{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003828 install_element(node, &config_help_cmd);
3829 install_element(node, &config_list_cmd);
3830
3831 install_element(node, &config_write_terminal_cmd);
3832 install_element(node, &config_write_file_cmd);
3833 install_element(node, &config_write_memory_cmd);
3834 install_element(node, &config_write_cmd);
3835 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003836
3837 install_element(node, &config_exit_cmd);
3838
3839 if (node >= CONFIG_NODE) {
3840 /* It's not a top node. */
3841 install_element(node, &config_end_cmd);
3842 }
3843}
3844
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003845/*! Return true if a node is installed by install_basic_node_commands(), so
3846 * that we can avoid repeating them for each and every node during 'show
3847 * running-config' */
3848static bool vty_command_is_common(struct cmd_element *cmd)
3849{
3850 if (cmd == &config_help_cmd
3851 || cmd == &config_list_cmd
3852 || cmd == &config_write_terminal_cmd
3853 || cmd == &config_write_file_cmd
3854 || cmd == &config_write_memory_cmd
3855 || cmd == &config_write_cmd
3856 || cmd == &show_running_config_cmd
3857 || cmd == &config_exit_cmd
3858 || cmd == &config_end_cmd)
3859 return true;
3860 return false;
3861}
3862
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003863/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003864 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003865 * \param[in] vty the vty of the code
3866 * \param[in] filename where to store the file
3867 * \return 0 in case of success.
3868 *
3869 * If the filename already exists create a filename.sav
3870 * version with the current code.
3871 *
3872 */
3873int osmo_vty_write_config_file(const char *filename)
3874{
3875 char *failed_file;
3876 int rc;
3877
3878 rc = write_config_file(filename, &failed_file);
3879 talloc_free(failed_file);
3880 return rc;
3881}
3882
3883/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003884 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003885 * \return 0 in case of success.
3886 *
3887 * If the filename already exists create a filename.sav
3888 * version with the current code.
3889 *
3890 */
3891int osmo_vty_save_config_file(void)
3892{
3893 char *failed_file;
3894 int rc;
3895
3896 if (host.config == NULL)
3897 return -7;
3898
3899 rc = write_config_file(host.config, &failed_file);
3900 talloc_free(failed_file);
3901 return rc;
3902}
3903
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003904/* Initialize command interface. Install basic nodes and commands. */
3905void cmd_init(int terminal)
3906{
3907 /* Allocate initial top vector of commands. */
3908 cmdvec = vector_init(VECTOR_MIN_SIZE);
3909
3910 /* Default host value settings. */
3911 host.name = NULL;
3912 host.password = NULL;
3913 host.enable = NULL;
3914 host.logfile = NULL;
3915 host.config = NULL;
3916 host.lines = -1;
3917 host.motd = default_motd;
3918 host.motdfile = NULL;
3919
3920 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003921 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003922 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003923 install_node_bare(&auth_node, NULL);
3924 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003925 install_node(&config_node, config_write_host);
3926
3927 /* Each node's basic commands. */
3928 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003929 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003930 if (terminal) {
3931 install_element(VIEW_NODE, &config_list_cmd);
3932 install_element(VIEW_NODE, &config_exit_cmd);
3933 install_element(VIEW_NODE, &config_help_cmd);
3934 install_element(VIEW_NODE, &config_enable_cmd);
3935 install_element(VIEW_NODE, &config_terminal_length_cmd);
3936 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3937 install_element(VIEW_NODE, &echo_cmd);
3938 }
3939
3940 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003941 install_element(ENABLE_NODE, &config_disable_cmd);
3942 install_element(ENABLE_NODE, &config_terminal_cmd);
3943 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3944 }
3945 install_element (ENABLE_NODE, &show_startup_config_cmd);
3946 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003947 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003948
3949 if (terminal) {
3950 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3951 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3952 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003953 }
3954
3955 install_element(CONFIG_NODE, &hostname_cmd);
3956 install_element(CONFIG_NODE, &no_hostname_cmd);
3957
3958 if (terminal) {
3959 install_element(CONFIG_NODE, &password_cmd);
3960 install_element(CONFIG_NODE, &password_text_cmd);
3961 install_element(CONFIG_NODE, &enable_password_cmd);
3962 install_element(CONFIG_NODE, &enable_password_text_cmd);
3963 install_element(CONFIG_NODE, &no_enable_password_cmd);
3964
3965#ifdef VTY_CRYPT_PW
3966 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3967 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3968#endif
3969 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3970 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3971 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3972 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3973 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3974
3975 }
3976 srand(time(NULL));
3977}
Harald Welte7acb30c2011-08-17 17:13:48 +02003978
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003979/*! @} */