blob: 9b32d22dfe8c54a08169fe78592c7a8af73545b9 [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>
40#include <sys/time.h>
41#include <sys/stat.h>
42
43#include <osmocom/vty/vector.h>
44#include <osmocom/vty/vty.h>
45#include <osmocom/vty/command.h>
46
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010047#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010048#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020049
Ruben Undheim766f77c2018-11-18 13:02:47 +010050#ifndef MAXPATHLEN
51 #define MAXPATHLEN 4096
52#endif
53
54
Harald Weltee881b1b2011-08-17 18:52:30 +020055/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020056 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020057 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020058 *
59 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020060
Harald Welte3fb0b6f2010-05-19 19:02:52 +020061#define CONFIGFILE_MASK 022
62
63void *tall_vty_cmd_ctx;
64
65/* Command vector which includes some level of command lists. Normally
66 each daemon maintains each own cmdvec. */
67vector cmdvec;
68
69/* Host information structure. */
70struct host host;
71
72/* Standard command node structures. */
73struct cmd_node auth_node = {
74 AUTH_NODE,
75 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010076 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020077};
78
79struct cmd_node view_node = {
80 VIEW_NODE,
81 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010082 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020083};
84
85struct cmd_node auth_enable_node = {
86 AUTH_ENABLE_NODE,
87 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010088 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020089};
90
91struct cmd_node enable_node = {
92 ENABLE_NODE,
93 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010094 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020095};
96
97struct cmd_node config_node = {
98 CONFIG_NODE,
99 "%s(config)# ",
100 1
101};
102
103/* Default motd string. */
104const char *default_motd = "";
105
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200106/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200107 *
108 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200109void print_version(int print_copyright)
110{
Harald Welte237f6242010-05-25 23:00:45 +0200111 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200112 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200113 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200114}
115
116/* Utility function to concatenate argv argument into a single string
117 with inserting ' ' character between each argument. */
118char *argv_concat(const char **argv, int argc, int shift)
119{
120 int i;
121 size_t len;
122 char *str;
123 char *p;
124
125 len = 0;
126 for (i = shift; i < argc; i++)
127 len += strlen(argv[i]) + 1;
128 if (!len)
129 return NULL;
130 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
131 for (i = shift; i < argc; i++) {
132 size_t arglen;
133 memcpy(p, argv[i], (arglen = strlen(argv[i])));
134 p += arglen;
135 *p++ = ' ';
136 }
137 *(p - 1) = '\0';
138 return str;
139}
140
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200141/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
142 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
143 * in turn, this name us used for XML IDs in 'show online-help'. */
144static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
145{
146 const char *pos;
147 int dest = 0;
148
149 if (!prompt || !*prompt)
150 return "";
151
152 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
153 if (pos[0] == '%' && pos[1]) {
154 /* skip "%s"; loop pos++ does the second one. */
155 pos++;
156 continue;
157 }
158 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
159 continue;
160 name_buf[dest] = pos[0];
161 dest++;
162 }
163 name_buf[dest] = '\0';
164 return name_buf;
165}
166
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200167static void install_basic_node_commands(int node);
168
169/*! Install top node of command vector, without adding basic node commands. */
170static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200171{
172 vector_set_index(cmdvec, node->node, node);
173 node->func = func;
174 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200175 if (!*node->name)
176 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200177}
178
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200179/*! Install top node of command vector. */
180void install_node(struct cmd_node *node, int (*func) (struct vty *))
181{
182 install_node_bare(node, func);
183 install_basic_node_commands(node->node);
184}
185
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200186/* Compare two command's string. Used in sort_node (). */
187static int cmp_node(const void *p, const void *q)
188{
189 struct cmd_element *a = *(struct cmd_element **)p;
190 struct cmd_element *b = *(struct cmd_element **)q;
191
192 return strcmp(a->string, b->string);
193}
194
195static int cmp_desc(const void *p, const void *q)
196{
197 struct desc *a = *(struct desc **)p;
198 struct desc *b = *(struct desc **)q;
199
200 return strcmp(a->cmd, b->cmd);
201}
202
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200203/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200204void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200205{
206 unsigned int i, j;
207 struct cmd_node *cnode;
208 vector descvec;
209 struct cmd_element *cmd_element;
210
211 for (i = 0; i < vector_active(cmdvec); i++)
212 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
213 vector cmd_vector = cnode->cmd_vector;
214 qsort(cmd_vector->index, vector_active(cmd_vector),
215 sizeof(void *), cmp_node);
216
217 for (j = 0; j < vector_active(cmd_vector); j++)
218 if ((cmd_element =
219 vector_slot(cmd_vector, j)) != NULL
220 && vector_active(cmd_element->strvec)) {
221 descvec =
222 vector_slot(cmd_element->strvec,
223 vector_active
224 (cmd_element->strvec) -
225 1);
226 qsort(descvec->index,
227 vector_active(descvec),
228 sizeof(void *), cmp_desc);
229 }
230 }
231}
232
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200233/*! Break up string in command tokens. Return leading indents.
234 * \param[in] string String to split.
235 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
236 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
237 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
238 *
239 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
240 * so that \a indent can simply return the count of leading spaces.
241 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
242 */
243int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200244{
245 const char *cp, *start;
246 char *token;
247 int strlen;
248 vector strvec;
249
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200250 *strvec_p = NULL;
251 if (indent)
252 *indent = 0;
253
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200254 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200255 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200256
257 cp = string;
258
259 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200260 while (isspace((int)*cp) && *cp != '\0') {
261 /* if we're counting indents, we need to be strict about them */
262 if (indent && (*cp != ' ') && (*cp != '\t')) {
263 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
264 if (*cp == '\n' || *cp == '\r') {
265 cp++;
266 string = cp;
267 continue;
268 }
269 return CMD_ERR_INVALID_INDENT;
270 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200271 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200272 }
273
274 if (indent)
275 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200276
277 /* Return if there is only white spaces */
278 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200279 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200280
281 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200282 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200283
284 /* Prepare return vector. */
285 strvec = vector_init(VECTOR_MIN_SIZE);
286
287 /* Copy each command piece and set into vector. */
288 while (1) {
289 start = cp;
290 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
291 *cp != '\0')
292 cp++;
293 strlen = cp - start;
294 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
295 memcpy(token, start, strlen);
296 *(token + strlen) = '\0';
297 vector_set(strvec, token);
298
299 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
300 *cp != '\0')
301 cp++;
302
303 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200304 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200305 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200306
307 *strvec_p = strvec;
308 return CMD_SUCCESS;
309}
310
311/*! Breaking up string into each command piece. I assume given
312 character is separated by a space character. Return value is a
313 vector which includes char ** data element. */
314vector cmd_make_strvec(const char *string)
315{
316 vector strvec;
317 cmd_make_strvec2(string, NULL, &strvec);
318 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200319}
320
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200321/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200322void cmd_free_strvec(vector v)
323{
324 unsigned int i;
325 char *cp;
326
327 if (!v)
328 return;
329
330 for (i = 0; i < vector_active(v); i++)
331 if ((cp = vector_slot(v, i)) != NULL)
332 talloc_free(cp);
333
334 vector_free(v);
335}
336
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200337/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200338static char *cmd_desc_str(const char **string)
339{
340 const char *cp, *start;
341 char *token;
342 int strlen;
343
344 cp = *string;
345
346 if (cp == NULL)
347 return NULL;
348
349 /* Skip white spaces. */
350 while (isspace((int)*cp) && *cp != '\0')
351 cp++;
352
353 /* Return if there is only white spaces */
354 if (*cp == '\0')
355 return NULL;
356
357 start = cp;
358
359 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
360 cp++;
361
362 strlen = cp - start;
363 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
364 memcpy(token, start, strlen);
365 *(token + strlen) = '\0';
366
367 *string = cp;
368
369 return token;
370}
371
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200372/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200373static vector cmd_make_descvec(const char *string, const char *descstr)
374{
375 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100376 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200377 const char *sp;
378 char *token;
379 int len;
380 const char *cp;
381 const char *dp;
382 vector allvec;
383 vector strvec = NULL;
384 struct desc *desc;
385
386 cp = string;
387 dp = descstr;
388
389 if (cp == NULL)
390 return NULL;
391
392 allvec = vector_init(VECTOR_MIN_SIZE);
393
394 while (1) {
395 while (isspace((int)*cp) && *cp != '\0')
396 cp++;
397
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100398 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
399 if (cp[0] == '[' && cp[1] == '(') {
400 optional_brace = 1;
401 cp++;
402 }
403
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200404 if (*cp == '(') {
405 multiple = 1;
406 cp++;
407 }
408 if (*cp == ')') {
409 multiple = 0;
410 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100411 if (*cp == ']')
412 cp++;
413 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200414 }
415 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100416 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200417 cp++;
418 }
419
420 while (isspace((int)*cp) && *cp != '\0')
421 cp++;
422
423 if (*cp == '(') {
424 multiple = 1;
425 cp++;
426 }
427
428 if (*cp == '\0')
429 return allvec;
430
431 sp = cp;
432
433 while (!
434 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
435 || *cp == ')' || *cp == '|') && *cp != '\0')
436 cp++;
437
438 len = cp - sp;
439
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100440 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
441 if (optional_brace) {
442 /* Place each individual multi-choice token in its own square braces */
443 token[0] = '[';
444 memcpy(token + 1, sp, len);
445 token[1 + len] = ']';
446 token[2 + len] = '\0';
447 } else {
448 memcpy(token, sp, len);
449 *(token + len) = '\0';
450 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200451
452 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
453 desc->cmd = token;
454 desc->str = cmd_desc_str(&dp);
455
456 if (multiple) {
457 if (multiple == 1) {
458 strvec = vector_init(VECTOR_MIN_SIZE);
459 vector_set(allvec, strvec);
460 }
461 multiple++;
462 } else {
463 strvec = vector_init(VECTOR_MIN_SIZE);
464 vector_set(allvec, strvec);
465 }
466 vector_set(strvec, desc);
467 }
468}
469
470/* Count mandantory string vector size. This is to determine inputed
471 command has enough command length. */
472static int cmd_cmdsize(vector strvec)
473{
474 unsigned int i;
475 int size = 0;
476 vector descvec;
477 struct desc *desc;
478
479 for (i = 0; i < vector_active(strvec); i++)
480 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100481 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200482 && (desc = vector_slot(descvec, 0)) != NULL) {
483 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
484 return size;
485 else
486 size++;
487 } else
488 size++;
489 }
490 return size;
491}
492
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200493/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200494const char *cmd_prompt(enum node_type node)
495{
496 struct cmd_node *cnode;
497
498 cnode = vector_slot(cmdvec, node);
499 return cnode->prompt;
500}
501
Alexander Couzensad580ba2016-05-16 16:01:45 +0200502/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200503 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200504 * \param unsafe string
505 * \return a new talloc char *
506 */
507char *osmo_asciidoc_escape(const char *inp)
508{
509 int _strlen;
510 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200511 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200512
513 if (!inp)
514 return NULL;
515 _strlen = strlen(inp);
516
517 for (i = 0; i < _strlen; ++i) {
518 switch (inp[i]) {
519 case '|':
520 len += 2;
521 break;
522 default:
523 len += 1;
524 break;
525 }
526 }
527
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200528 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200529 if (!out)
530 return NULL;
531
532 out_ptr = out;
533
Alexander Couzensad580ba2016-05-16 16:01:45 +0200534 for (i = 0; i < _strlen; ++i) {
535 switch (inp[i]) {
536 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200537 /* Prepend escape character "\": */
538 *(out_ptr++) = '\\';
539 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200540 default:
541 *(out_ptr++) = inp[i];
542 break;
543 }
544 }
545
Alexander Couzensad580ba2016-05-16 16:01:45 +0200546 out_ptr[0] = '\0';
547 return out;
548}
549
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100550static char *xml_escape(const char *inp)
551{
552 int _strlen;
553 char *out, *out_ptr;
554 int len = 0, i, j;
555
556 if (!inp)
557 return NULL;
558 _strlen = strlen(inp);
559
560 for (i = 0; i < _strlen; ++i) {
561 switch (inp[i]) {
562 case '"':
563 len += 6;
564 break;
565 case '\'':
566 len += 6;
567 break;
568 case '<':
569 len += 4;
570 break;
571 case '>':
572 len += 4;
573 break;
574 case '&':
575 len += 5;
576 break;
577 default:
578 len += 1;
579 break;
580 }
581 }
582
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200583 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100584 if (!out)
585 return NULL;
586
587 out_ptr = out;
588
589#define ADD(out, str) \
590 for (j = 0; j < strlen(str); ++j) \
591 *(out++) = str[j];
592
593 for (i = 0; i < _strlen; ++i) {
594 switch (inp[i]) {
595 case '"':
596 ADD(out_ptr, "&quot;");
597 break;
598 case '\'':
599 ADD(out_ptr, "&apos;");
600 break;
601 case '<':
602 ADD(out_ptr, "&lt;");
603 break;
604 case '>':
605 ADD(out_ptr, "&gt;");
606 break;
607 case '&':
608 ADD(out_ptr, "&amp;");
609 break;
610 default:
611 *(out_ptr++) = inp[i];
612 break;
613 }
614 }
615
616#undef ADD
617
618 out_ptr[0] = '\0';
619 return out;
620}
621
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200622typedef int (*print_func_t)(void *data, const char *fmt, ...);
623
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100624/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200625 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100626 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200627static 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 +0100628{
629 char *xml_string = xml_escape(cmd->string);
630
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200631 print_func(data, " <command id='%s'>%s", xml_string, newline);
632 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100633
634 int j;
635 for (j = 0; j < vector_count(cmd->strvec); ++j) {
636 vector descvec = vector_slot(cmd->strvec, j);
637 int i;
638 for (i = 0; i < vector_active(descvec); ++i) {
639 char *xml_param, *xml_doc;
640 struct desc *desc = vector_slot(descvec, i);
641 if (desc == NULL)
642 continue;
643
644 xml_param = xml_escape(desc->cmd);
645 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200646 print_func(data, " <param name='%s' doc='%s' />%s",
647 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100648 talloc_free(xml_param);
649 talloc_free(xml_doc);
650 }
651 }
652
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200653 print_func(data, " </params>%s", newline);
654 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100655
656 talloc_free(xml_string);
657 return 0;
658}
659
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200660static bool vty_command_is_common(struct cmd_element *cmd);
661
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100662/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200663 * 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 +0100664 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200665static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100666{
667 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200668 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100669
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200670 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100671
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200672 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200673 print_func(data, " <node id='_common_cmds_'>%s", newline);
674 print_func(data, " <name>Common Commands</name>%s", newline);
675 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
676 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200677 for (i = 0; i < vector_active(cmdvec); ++i) {
678 struct cmd_node *cnode;
679 cnode = vector_slot(cmdvec, i);
680 if (!cnode)
681 continue;
682 if (cnode->node != CONFIG_NODE)
683 continue;
684
685 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
686 struct cmd_element *elem;
687 elem = vector_slot(cnode->cmd_vector, j);
688 if (!vty_command_is_common(elem))
689 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200690 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200691 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200692 }
693 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200694 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200695
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100696 for (i = 0; i < vector_active(cmdvec); ++i) {
697 struct cmd_node *cnode;
698 cnode = vector_slot(cmdvec, i);
699 if (!cnode)
700 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200701 if (vector_active(cnode->cmd_vector) < 1)
702 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100703
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200704 /* De-dup node IDs: how many times has this same name been used before? Count the first
705 * occurence as _1 and omit that first suffix, so that the first occurence is called
706 * 'name', the second becomes 'name_2', then 'name_3', ... */
707 same_name_count = 1;
708 for (j = 0; j < i; ++j) {
709 struct cmd_node *cnode2;
710 cnode2 = vector_slot(cmdvec, j);
711 if (!cnode2)
712 continue;
713 if (strcmp(cnode->name, cnode2->name) == 0)
714 same_name_count ++;
715 }
716
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200717 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200718 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200719 print_func(data, "_%d", same_name_count);
720 print_func(data, "'>%s", newline);
721 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100722
723 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
724 struct cmd_element *elem;
725 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200726 if (vty_command_is_common(elem))
727 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200728 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200729 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100730 }
731
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200732 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100733 }
734
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200735 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100736
737 return 0;
738}
739
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200740static int print_func_vty(void *data, const char *format, ...)
741{
742 struct vty *vty = data;
743 va_list args;
744 int rc;
745 va_start(args, format);
746 rc = vty_out_va(vty, format, args);
747 va_end(args);
748 return rc;
749}
750
751static int vty_dump_xml_ref_to_vty(struct vty *vty)
752{
753 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
754}
755
756static int print_func_stream(void *data, const char *format, ...)
757{
758 va_list args;
759 int rc;
760 va_start(args, format);
761 rc = vfprintf((FILE*)data, format, args);
762 va_end(args);
763 return rc;
764}
765
766/*! Print the XML reference of all VTY nodes to the given stream.
767 */
768int vty_dump_xml_ref(FILE *stream)
769{
770 return vty_dump_nodes(print_func_stream, stream, "\n");
771}
772
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200773/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100774static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
775{
776 int i;
777
778 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
779 struct cmd_element *elem;
780 elem = vector_slot(cnode->cmd_vector, i);
781 if (!elem->string)
782 continue;
783 if (!strcmp(elem->string, cmdstring))
784 return 1;
785 }
786 return 0;
787}
788
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200789/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200790 * \param[in] ntype Node Type
791 * \param[cmd] element to be installed
792 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000793void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200794{
795 struct cmd_node *cnode;
796
797 cnode = vector_slot(cmdvec, ntype);
798
Harald Weltea99d45a2015-11-12 13:48:23 +0100799 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100800 /* ensure no _identical_ command has been registered at this
801 * node so far */
802 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200803
804 vector_set(cnode->cmd_vector, cmd);
805
806 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
807 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
808}
809
810/* Install a command into VIEW and ENABLE node */
811void install_element_ve(struct cmd_element *cmd)
812{
813 install_element(VIEW_NODE, cmd);
814 install_element(ENABLE_NODE, cmd);
815}
816
817#ifdef VTY_CRYPT_PW
818static unsigned char itoa64[] =
819 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
820
821static void to64(char *s, long v, int n)
822{
823 while (--n >= 0) {
824 *s++ = itoa64[v & 0x3f];
825 v >>= 6;
826 }
827}
828
829static char *zencrypt(const char *passwd)
830{
831 char salt[6];
832 struct timeval tv;
833 char *crypt(const char *, const char *);
834
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200835 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200836
837 to64(&salt[0], random(), 3);
838 to64(&salt[3], tv.tv_usec, 3);
839 salt[5] = '\0';
840
841 return crypt(passwd, salt);
842}
843#endif
844
845/* This function write configuration of this host. */
846static int config_write_host(struct vty *vty)
847{
848 if (host.name)
849 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
850
851 if (host.encrypt) {
852 if (host.password_encrypt)
853 vty_out(vty, "password 8 %s%s", host.password_encrypt,
854 VTY_NEWLINE);
855 if (host.enable_encrypt)
856 vty_out(vty, "enable password 8 %s%s",
857 host.enable_encrypt, VTY_NEWLINE);
858 } else {
859 if (host.password)
860 vty_out(vty, "password %s%s", host.password,
861 VTY_NEWLINE);
862 if (host.enable)
863 vty_out(vty, "enable password %s%s", host.enable,
864 VTY_NEWLINE);
865 }
866
867 if (host.advanced)
868 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
869
870 if (host.encrypt)
871 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
872
873 if (host.lines >= 0)
874 vty_out(vty, "service terminal-length %d%s", host.lines,
875 VTY_NEWLINE);
876
877 if (host.motdfile)
878 vty_out(vty, "banner motd file %s%s", host.motdfile,
879 VTY_NEWLINE);
880 else if (!host.motd)
881 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
882
883 return 1;
884}
885
886/* Utility function for getting command vector. */
887static vector cmd_node_vector(vector v, enum node_type ntype)
888{
889 struct cmd_node *cnode = vector_slot(v, ntype);
890 return cnode->cmd_vector;
891}
892
893/* Completion match types. */
894enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200895 NO_MATCH = 0,
896 ANY_MATCH,
897 EXTEND_MATCH,
898 IPV4_PREFIX_MATCH,
899 IPV4_MATCH,
900 IPV6_PREFIX_MATCH,
901 IPV6_MATCH,
902 RANGE_MATCH,
903 VARARG_MATCH,
904 PARTLY_MATCH,
905 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200906};
907
908static enum match_type cmd_ipv4_match(const char *str)
909{
910 const char *sp;
911 int dots = 0, nums = 0;
912 char buf[4];
913
914 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200915 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200916
917 for (;;) {
918 memset(buf, 0, sizeof(buf));
919 sp = str;
920 while (*str != '\0') {
921 if (*str == '.') {
922 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200923 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200924
925 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200926 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200927
928 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200929 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200930
931 dots++;
932 break;
933 }
934 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200935 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200936
937 str++;
938 }
939
940 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200941 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200942
943 strncpy(buf, sp, str - sp);
944 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200945 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200946
947 nums++;
948
949 if (*str == '\0')
950 break;
951
952 str++;
953 }
954
955 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200956 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200957
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200958 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200959}
960
961static enum match_type cmd_ipv4_prefix_match(const char *str)
962{
963 const char *sp;
964 int dots = 0;
965 char buf[4];
966
967 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200968 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200969
970 for (;;) {
971 memset(buf, 0, sizeof(buf));
972 sp = str;
973 while (*str != '\0' && *str != '/') {
974 if (*str == '.') {
975 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200976 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200977
978 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200979 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200980
981 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200982 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200983
984 dots++;
985 break;
986 }
987
988 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200989 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200990
991 str++;
992 }
993
994 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200995 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200996
997 strncpy(buf, sp, str - sp);
998 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200999 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001000
1001 if (dots == 3) {
1002 if (*str == '/') {
1003 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001004 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001005
1006 str++;
1007 break;
1008 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001009 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001010 }
1011
1012 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001013 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001014
1015 str++;
1016 }
1017
1018 sp = str;
1019 while (*str != '\0') {
1020 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001021 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022
1023 str++;
1024 }
1025
1026 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001027 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001028
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001029 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001030}
1031
1032#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1033#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1034#define STATE_START 1
1035#define STATE_COLON 2
1036#define STATE_DOUBLE 3
1037#define STATE_ADDR 4
1038#define STATE_DOT 5
1039#define STATE_SLASH 6
1040#define STATE_MASK 7
1041
1042#ifdef HAVE_IPV6
1043
1044static enum match_type cmd_ipv6_match(const char *str)
1045{
1046 int state = STATE_START;
1047 int colons = 0, nums = 0, double_colon = 0;
1048 const char *sp = NULL;
1049 struct sockaddr_in6 sin6_dummy;
1050 int ret;
1051
1052 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001053 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001054
1055 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001056 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001057
1058 /* use inet_pton that has a better support,
1059 * for example inet_pton can support the automatic addresses:
1060 * ::1.2.3.4
1061 */
1062 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1063
1064 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001065 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001066
1067 while (*str != '\0') {
1068 switch (state) {
1069 case STATE_START:
1070 if (*str == ':') {
1071 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001072 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001073 colons--;
1074 state = STATE_COLON;
1075 } else {
1076 sp = str;
1077 state = STATE_ADDR;
1078 }
1079
1080 continue;
1081 case STATE_COLON:
1082 colons++;
1083 if (*(str + 1) == ':')
1084 state = STATE_DOUBLE;
1085 else {
1086 sp = str + 1;
1087 state = STATE_ADDR;
1088 }
1089 break;
1090 case STATE_DOUBLE:
1091 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001092 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001093
1094 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001095 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001096 else {
1097 if (*(str + 1) != '\0')
1098 colons++;
1099 sp = str + 1;
1100 state = STATE_ADDR;
1101 }
1102
1103 double_colon++;
1104 nums++;
1105 break;
1106 case STATE_ADDR:
1107 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1108 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001109 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001110
1111 nums++;
1112 state = STATE_COLON;
1113 }
1114 if (*(str + 1) == '.')
1115 state = STATE_DOT;
1116 break;
1117 case STATE_DOT:
1118 state = STATE_ADDR;
1119 break;
1120 default:
1121 break;
1122 }
1123
1124 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126
1127 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001128 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001129
1130 str++;
1131 }
1132
1133#if 0
1134 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001135 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001136#endif /* 0 */
1137
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001138 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001139}
1140
1141static enum match_type cmd_ipv6_prefix_match(const char *str)
1142{
1143 int state = STATE_START;
1144 int colons = 0, nums = 0, double_colon = 0;
1145 int mask;
1146 const char *sp = NULL;
1147 char *endptr = NULL;
1148
1149 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001153 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001154
1155 while (*str != '\0' && state != STATE_MASK) {
1156 switch (state) {
1157 case STATE_START:
1158 if (*str == ':') {
1159 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001160 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001161 colons--;
1162 state = STATE_COLON;
1163 } else {
1164 sp = str;
1165 state = STATE_ADDR;
1166 }
1167
1168 continue;
1169 case STATE_COLON:
1170 colons++;
1171 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001172 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001173 else if (*(str + 1) == ':')
1174 state = STATE_DOUBLE;
1175 else {
1176 sp = str + 1;
1177 state = STATE_ADDR;
1178 }
1179 break;
1180 case STATE_DOUBLE:
1181 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001182 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001183
1184 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001185 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186 else {
1187 if (*(str + 1) != '\0' && *(str + 1) != '/')
1188 colons++;
1189 sp = str + 1;
1190
1191 if (*(str + 1) == '/')
1192 state = STATE_SLASH;
1193 else
1194 state = STATE_ADDR;
1195 }
1196
1197 double_colon++;
1198 nums += 1;
1199 break;
1200 case STATE_ADDR:
1201 if (*(str + 1) == ':' || *(str + 1) == '.'
1202 || *(str + 1) == '\0' || *(str + 1) == '/') {
1203 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001204 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001205
1206 for (; sp <= str; sp++)
1207 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001208 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001209
1210 nums++;
1211
1212 if (*(str + 1) == ':')
1213 state = STATE_COLON;
1214 else if (*(str + 1) == '.')
1215 state = STATE_DOT;
1216 else if (*(str + 1) == '/')
1217 state = STATE_SLASH;
1218 }
1219 break;
1220 case STATE_DOT:
1221 state = STATE_ADDR;
1222 break;
1223 case STATE_SLASH:
1224 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226
1227 state = STATE_MASK;
1228 break;
1229 default:
1230 break;
1231 }
1232
1233 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001234 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235
1236 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001237 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001238
1239 str++;
1240 }
1241
1242 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001243 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001244
1245 mask = strtol(str, &endptr, 10);
1246 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001247 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001248
1249 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001250 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001251
1252/* I don't know why mask < 13 makes command match partly.
1253 Forgive me to make this comments. I Want to set static default route
1254 because of lack of function to originate default in ospf6d; sorry
1255 yasu
1256 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001257 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001258*/
1259
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001260 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001261}
1262
1263#endif /* HAVE_IPV6 */
1264
1265#define DECIMAL_STRLEN_MAX 10
1266
1267static int cmd_range_match(const char *range, const char *str)
1268{
1269 char *p;
1270 char buf[DECIMAL_STRLEN_MAX + 1];
1271 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001272
1273 if (str == NULL)
1274 return 1;
1275
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001276 if (range[1] == '-') {
1277 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001279 val = strtol(str, &endptr, 10);
1280 if (*endptr != '\0')
1281 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001282
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001283 range += 2;
1284 p = strchr(range, '-');
1285 if (p == NULL)
1286 return 0;
1287 if (p - range > DECIMAL_STRLEN_MAX)
1288 return 0;
1289 strncpy(buf, range, p - range);
1290 buf[p - range] = '\0';
1291 min = -strtol(buf, &endptr, 10);
1292 if (*endptr != '\0')
1293 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001294
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001295 range = p + 1;
1296 p = strchr(range, '>');
1297 if (p == NULL)
1298 return 0;
1299 if (p - range > DECIMAL_STRLEN_MAX)
1300 return 0;
1301 strncpy(buf, range, p - range);
1302 buf[p - range] = '\0';
1303 max = strtol(buf, &endptr, 10);
1304 if (*endptr != '\0')
1305 return 0;
1306
1307 if (val < min || val > max)
1308 return 0;
1309 } else {
1310 unsigned long min, max, val;
1311
1312 val = strtoul(str, &endptr, 10);
1313 if (*endptr != '\0')
1314 return 0;
1315
1316 range++;
1317 p = strchr(range, '-');
1318 if (p == NULL)
1319 return 0;
1320 if (p - range > DECIMAL_STRLEN_MAX)
1321 return 0;
1322 strncpy(buf, range, p - range);
1323 buf[p - range] = '\0';
1324 min = strtoul(buf, &endptr, 10);
1325 if (*endptr != '\0')
1326 return 0;
1327
1328 range = p + 1;
1329 p = strchr(range, '>');
1330 if (p == NULL)
1331 return 0;
1332 if (p - range > DECIMAL_STRLEN_MAX)
1333 return 0;
1334 strncpy(buf, range, p - range);
1335 buf[p - range] = '\0';
1336 max = strtoul(buf, &endptr, 10);
1337 if (*endptr != '\0')
1338 return 0;
1339
1340 if (val < min || val > max)
1341 return 0;
1342 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001343
1344 return 1;
1345}
1346
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001347/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001348static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001349{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001350 /* we've got "[blah]". We want to strip off the []s and redo the
1351 * match check for "blah"
1352 */
1353 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001354
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001355 if (len < 3)
1356 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001357
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001358 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001359}
1360
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001361static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001362cmd_match(const char *str, const char *command,
1363 enum match_type min, bool recur)
1364{
1365
1366 if (recur && CMD_OPTION(str))
1367 {
1368 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001369 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001370
1371 /* this would be a bug in a command, however handle it gracefully
1372 * as it we only discover it if a user tries to run it
1373 */
1374 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001375 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001376
1377 ret = cmd_match(tmp, command, min, false);
1378
1379 talloc_free(tmp);
1380
1381 return ret;
1382 }
1383 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001384 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001385 else if (CMD_RANGE(str))
1386 {
1387 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001388 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001389 }
1390#ifdef HAVE_IPV6
1391 else if (CMD_IPV6(str))
1392 {
1393 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001394 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001395 }
1396 else if (CMD_IPV6_PREFIX(str))
1397 {
1398 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001399 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001400 }
1401#endif /* HAVE_IPV6 */
1402 else if (CMD_IPV4(str))
1403 {
1404 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001405 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001406 }
1407 else if (CMD_IPV4_PREFIX(str))
1408 {
1409 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001410 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001411 }
1412 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001413 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001414 else if (strncmp(command, str, strlen(command)) == 0)
1415 {
1416 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001417 return EXACT_MATCH;
1418 else if (PARTLY_MATCH >= min)
1419 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001420 }
1421
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001422 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001423}
1424
1425/* Filter vector at the specified index and by the given command string, to
1426 * the desired matching level (thus allowing part matches), and return match
1427 * type flag.
1428 */
1429static enum match_type
1430cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001431{
1432 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001433 struct cmd_element *cmd_element;
1434 enum match_type match_type;
1435 vector descvec;
1436 struct desc *desc;
1437
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001438 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001439
1440 /* If command and cmd_element string does not match set NULL to vector */
1441 for (i = 0; i < vector_active(v); i++)
1442 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001443 if (index >= vector_active(cmd_element->strvec))
1444 vector_slot(v, i) = NULL;
1445 else {
1446 unsigned int j;
1447 int matched = 0;
1448
1449 descvec =
1450 vector_slot(cmd_element->strvec, index);
1451
1452 for (j = 0; j < vector_active(descvec); j++)
1453 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001454 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001455
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001456 ret = cmd_match (desc->cmd, command, level, true);
1457
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001458 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001460
1461 if (match_type < ret)
1462 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001463 }
1464 if (!matched)
1465 vector_slot(v, i) = NULL;
1466 }
1467 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001468
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001469 if (match_type == NO_MATCH)
1470 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001471
1472 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1473 * go again and filter out commands whose argument (at this index) is
1474 * 'weaker'. E.g., if we have 2 commands:
1475 *
1476 * foo bar <1-255>
1477 * foo bar BLAH
1478 *
1479 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001480 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001481 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1482 *
1483 * If we don't do a 2nd pass and filter it out, the higher-layers will
1484 * consider this to be ambiguous.
1485 */
1486 for (i = 0; i < vector_active(v); i++)
1487 if ((cmd_element = vector_slot(v, i)) != NULL) {
1488 if (index >= vector_active(cmd_element->strvec))
1489 vector_slot(v, i) = NULL;
1490 else {
1491 unsigned int j;
1492 int matched = 0;
1493
1494 descvec =
1495 vector_slot(cmd_element->strvec, index);
1496
1497 for (j = 0; j < vector_active(descvec); j++)
1498 if ((desc = vector_slot(descvec, j))) {
1499 enum match_type ret;
1500
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001501 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001502
1503 if (ret >= match_type)
1504 matched++;
1505 }
1506 if (!matched)
1507 vector_slot(v, i) = NULL;
1508 }
1509 }
1510
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001511 return match_type;
1512}
1513
1514/* Check ambiguous match */
1515static int
1516is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1517{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001518 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001519 unsigned int i;
1520 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001521 struct cmd_element *cmd_element;
1522 const char *matched = NULL;
1523 vector descvec;
1524 struct desc *desc;
1525
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001526 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1527 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1528 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1529 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1530 * that case, the string must remain allocated until this function exits or another match comes
1531 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1532 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1533 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1534 void *cmd_deopt_ctx = NULL;
1535
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001536 for (i = 0; i < vector_active(v); i++) {
1537 cmd_element = vector_slot(v, i);
1538 if (!cmd_element)
1539 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001540
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001541 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001542
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001543 descvec = vector_slot(cmd_element->strvec, index);
1544
1545 for (j = 0; j < vector_active(descvec); j++) {
1546 desc = vector_slot(descvec, j);
1547 if (!desc)
1548 continue;
1549
1550 enum match_type mtype;
1551 const char *str = desc->cmd;
1552
1553 if (CMD_OPTION(str)) {
1554 if (!cmd_deopt_ctx)
1555 cmd_deopt_ctx =
1556 talloc_named_const(tall_vty_cmd_ctx, 0,
1557 __func__);
1558 str = cmd_deopt(cmd_deopt_ctx, str);
1559 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001560 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001561 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001562
1563 switch (type) {
1564 case EXACT_MATCH:
1565 if (!(CMD_VARIABLE (str))
1566 && strcmp(command, str) == 0)
1567 match++;
1568 break;
1569 case PARTLY_MATCH:
1570 if (!(CMD_VARIABLE (str))
1571 && strncmp(command, str, strlen (command)) == 0)
1572 {
1573 if (matched
1574 && strcmp(matched,
1575 str) != 0) {
1576 ret = 1; /* There is ambiguous match. */
1577 goto free_and_return;
1578 } else
1579 matched = str;
1580 match++;
1581 }
1582 break;
1583 case RANGE_MATCH:
1584 if (cmd_range_match
1585 (str, command)) {
1586 if (matched
1587 && strcmp(matched,
1588 str) != 0) {
1589 ret = 1;
1590 goto free_and_return;
1591 } else
1592 matched = str;
1593 match++;
1594 }
1595 break;
1596#ifdef HAVE_IPV6
1597 case IPV6_MATCH:
1598 if (CMD_IPV6(str))
1599 match++;
1600 break;
1601 case IPV6_PREFIX_MATCH:
1602 if ((mtype =
1603 cmd_ipv6_prefix_match
1604 (command)) != NO_MATCH) {
1605 if (mtype == PARTLY_MATCH) {
1606 ret = 2; /* There is incomplete match. */
1607 goto free_and_return;
1608 }
1609
1610 match++;
1611 }
1612 break;
1613#endif /* HAVE_IPV6 */
1614 case IPV4_MATCH:
1615 if (CMD_IPV4(str))
1616 match++;
1617 break;
1618 case IPV4_PREFIX_MATCH:
1619 if ((mtype =
1620 cmd_ipv4_prefix_match
1621 (command)) != NO_MATCH) {
1622 if (mtype == PARTLY_MATCH) {
1623 ret = 2; /* There is incomplete match. */
1624 goto free_and_return;
1625 }
1626
1627 match++;
1628 }
1629 break;
1630 case EXTEND_MATCH:
1631 if (CMD_VARIABLE (str))
1632 match++;
1633 break;
1634 case NO_MATCH:
1635 default:
1636 break;
1637 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001638 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001639 if (!match)
1640 vector_slot(v, i) = NULL;
1641 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001642
1643free_and_return:
1644 if (cmd_deopt_ctx)
1645 talloc_free(cmd_deopt_ctx);
1646 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001647}
1648
1649/* If src matches dst return dst string, otherwise return NULL */
1650static const char *cmd_entry_function(const char *src, const char *dst)
1651{
1652 /* Skip variable arguments. */
1653 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1654 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1655 return NULL;
1656
1657 /* In case of 'command \t', given src is NULL string. */
1658 if (src == NULL)
1659 return dst;
1660
1661 /* Matched with input string. */
1662 if (strncmp(src, dst, strlen(src)) == 0)
1663 return dst;
1664
1665 return NULL;
1666}
1667
1668/* If src matches dst return dst string, otherwise return NULL */
1669/* This version will return the dst string always if it is
1670 CMD_VARIABLE for '?' key processing */
1671static const char *cmd_entry_function_desc(const char *src, const char *dst)
1672{
1673 if (CMD_VARARG(dst))
1674 return dst;
1675
1676 if (CMD_RANGE(dst)) {
1677 if (cmd_range_match(dst, src))
1678 return dst;
1679 else
1680 return NULL;
1681 }
1682#ifdef HAVE_IPV6
1683 if (CMD_IPV6(dst)) {
1684 if (cmd_ipv6_match(src))
1685 return dst;
1686 else
1687 return NULL;
1688 }
1689
1690 if (CMD_IPV6_PREFIX(dst)) {
1691 if (cmd_ipv6_prefix_match(src))
1692 return dst;
1693 else
1694 return NULL;
1695 }
1696#endif /* HAVE_IPV6 */
1697
1698 if (CMD_IPV4(dst)) {
1699 if (cmd_ipv4_match(src))
1700 return dst;
1701 else
1702 return NULL;
1703 }
1704
1705 if (CMD_IPV4_PREFIX(dst)) {
1706 if (cmd_ipv4_prefix_match(src))
1707 return dst;
1708 else
1709 return NULL;
1710 }
1711
1712 /* Optional or variable commands always match on '?' */
1713 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1714 return dst;
1715
1716 /* In case of 'command \t', given src is NULL string. */
1717 if (src == NULL)
1718 return dst;
1719
1720 if (strncmp(src, dst, strlen(src)) == 0)
1721 return dst;
1722 else
1723 return NULL;
1724}
1725
1726/* Check same string element existence. If it isn't there return
1727 1. */
1728static int cmd_unique_string(vector v, const char *str)
1729{
1730 unsigned int i;
1731 char *match;
1732
1733 for (i = 0; i < vector_active(v); i++)
1734 if ((match = vector_slot(v, i)) != NULL)
1735 if (strcmp(match, str) == 0)
1736 return 0;
1737 return 1;
1738}
1739
1740/* Compare string to description vector. If there is same string
1741 return 1 else return 0. */
1742static int desc_unique_string(vector v, const char *str)
1743{
1744 unsigned int i;
1745 struct desc *desc;
1746
1747 for (i = 0; i < vector_active(v); i++)
1748 if ((desc = vector_slot(v, i)) != NULL)
1749 if (strcmp(desc->cmd, str) == 0)
1750 return 1;
1751 return 0;
1752}
1753
1754static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1755{
1756 if (first_word != NULL &&
1757 node != AUTH_NODE &&
1758 node != VIEW_NODE &&
1759 node != AUTH_ENABLE_NODE &&
1760 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1761 return 1;
1762 return 0;
1763}
1764
1765/* '?' describe command support. */
1766static vector
1767cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1768{
1769 unsigned int i;
1770 vector cmd_vector;
1771#define INIT_MATCHVEC_SIZE 10
1772 vector matchvec;
1773 struct cmd_element *cmd_element;
1774 unsigned int index;
1775 int ret;
1776 enum match_type match;
1777 char *command;
1778 static struct desc desc_cr = { "<cr>", "" };
1779
1780 /* Set index. */
1781 if (vector_active(vline) == 0) {
1782 *status = CMD_ERR_NO_MATCH;
1783 return NULL;
1784 } else
1785 index = vector_active(vline) - 1;
1786
1787 /* Make copy vector of current node's command vector. */
1788 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1789
1790 /* Prepare match vector */
1791 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1792
1793 /* Filter commands. */
1794 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001795 for (i = 0; i < index; i++) {
1796 command = vector_slot(vline, i);
1797 if (!command)
1798 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001799
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001800 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001801
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001802 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001803 struct cmd_element *cmd_element;
1804 vector descvec;
1805 unsigned int j, k;
1806
1807 for (j = 0; j < vector_active(cmd_vector); j++)
1808 if ((cmd_element =
1809 vector_slot(cmd_vector, j)) != NULL
1810 &&
1811 (vector_active(cmd_element->strvec))) {
1812 descvec =
1813 vector_slot(cmd_element->
1814 strvec,
1815 vector_active
1816 (cmd_element->
1817 strvec) - 1);
1818 for (k = 0;
1819 k < vector_active(descvec);
1820 k++) {
1821 struct desc *desc =
1822 vector_slot(descvec,
1823 k);
1824 vector_set(matchvec,
1825 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001826 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001827 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001828
Harald Welte80d30fe2013-02-12 11:08:57 +01001829 vector_set(matchvec, &desc_cr);
1830 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001831
Harald Welte80d30fe2013-02-12 11:08:57 +01001832 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001833 }
1834
Harald Welte80d30fe2013-02-12 11:08:57 +01001835 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1836 match)) == 1) {
1837 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001838 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001839 *status = CMD_ERR_AMBIGUOUS;
1840 return NULL;
1841 } else if (ret == 2) {
1842 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001843 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001844 *status = CMD_ERR_NO_MATCH;
1845 return NULL;
1846 }
1847 }
1848
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001849 /* Prepare match vector */
1850 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1851
1852 /* Make sure that cmd_vector is filtered based on current word */
1853 command = vector_slot(vline, index);
1854 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001855 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001856
1857 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001858 for (i = 0; i < vector_active(cmd_vector); i++) {
1859 const char *string = NULL;
1860 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001861
Harald Welte80d30fe2013-02-12 11:08:57 +01001862 cmd_element = vector_slot(cmd_vector, i);
1863 if (!cmd_element)
1864 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001865
Harald Welted17aa592013-02-12 11:11:34 +01001866 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1867 continue;
1868
Harald Welte80d30fe2013-02-12 11:08:57 +01001869 strvec = cmd_element->strvec;
1870
1871 /* if command is NULL, index may be equal to vector_active */
1872 if (command && index >= vector_active(strvec))
1873 vector_slot(cmd_vector, i) = NULL;
1874 else {
1875 /* Check if command is completed. */
1876 if (command == NULL
1877 && index == vector_active(strvec)) {
1878 string = "<cr>";
1879 if (!desc_unique_string(matchvec, string))
1880 vector_set(matchvec, &desc_cr);
1881 } else {
1882 unsigned int j;
1883 vector descvec = vector_slot(strvec, index);
1884 struct desc *desc;
1885
1886 for (j = 0; j < vector_active(descvec); j++) {
1887 desc = vector_slot(descvec, j);
1888 if (!desc)
1889 continue;
1890 string = cmd_entry_function_desc
1891 (command, desc->cmd);
1892 if (!string)
1893 continue;
1894 /* Uniqueness check */
1895 if (!desc_unique_string(matchvec, string))
1896 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001897 }
1898 }
1899 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001900 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001901 vector_free(cmd_vector);
1902
1903 if (vector_slot(matchvec, 0) == NULL) {
1904 vector_free(matchvec);
1905 *status = CMD_ERR_NO_MATCH;
1906 } else
1907 *status = CMD_SUCCESS;
1908
1909 return matchvec;
1910}
1911
1912vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1913{
1914 vector ret;
1915
1916 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1917 enum node_type onode;
1918 vector shifted_vline;
1919 unsigned int index;
1920
1921 onode = vty->node;
1922 vty->node = ENABLE_NODE;
1923 /* We can try it on enable node, cos' the vty is authenticated */
1924
1925 shifted_vline = vector_init(vector_count(vline));
1926 /* use memcpy? */
1927 for (index = 1; index < vector_active(vline); index++) {
1928 vector_set_index(shifted_vline, index - 1,
1929 vector_lookup(vline, index));
1930 }
1931
1932 ret = cmd_describe_command_real(shifted_vline, vty, status);
1933
1934 vector_free(shifted_vline);
1935 vty->node = onode;
1936 return ret;
1937 }
1938
1939 return cmd_describe_command_real(vline, vty, status);
1940}
1941
1942/* Check LCD of matched command. */
1943static int cmd_lcd(char **matched)
1944{
1945 int i;
1946 int j;
1947 int lcd = -1;
1948 char *s1, *s2;
1949 char c1, c2;
1950
1951 if (matched[0] == NULL || matched[1] == NULL)
1952 return 0;
1953
1954 for (i = 1; matched[i] != NULL; i++) {
1955 s1 = matched[i - 1];
1956 s2 = matched[i];
1957
1958 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1959 if (c1 != c2)
1960 break;
1961
1962 if (lcd < 0)
1963 lcd = j;
1964 else {
1965 if (lcd > j)
1966 lcd = j;
1967 }
1968 }
1969 return lcd;
1970}
1971
1972/* Command line completion support. */
1973static char **cmd_complete_command_real(vector vline, struct vty *vty,
1974 int *status)
1975{
1976 unsigned int i;
1977 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1978#define INIT_MATCHVEC_SIZE 10
1979 vector matchvec;
1980 struct cmd_element *cmd_element;
1981 unsigned int index;
1982 char **match_str;
1983 struct desc *desc;
1984 vector descvec;
1985 char *command;
1986 int lcd;
1987
1988 if (vector_active(vline) == 0) {
1989 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001990 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001991 return NULL;
1992 } else
1993 index = vector_active(vline) - 1;
1994
1995 /* First, filter by preceeding command string */
1996 for (i = 0; i < index; i++)
1997 if ((command = vector_slot(vline, i))) {
1998 enum match_type match;
1999 int ret;
2000
2001 /* First try completion match, if there is exactly match return 1 */
2002 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002003 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002004
2005 /* If there is exact match then filter ambiguous match else check
2006 ambiguousness. */
2007 if ((ret =
2008 is_cmd_ambiguous(command, cmd_vector, i,
2009 match)) == 1) {
2010 vector_free(cmd_vector);
2011 *status = CMD_ERR_AMBIGUOUS;
2012 return NULL;
2013 }
2014 /*
2015 else if (ret == 2)
2016 {
2017 vector_free (cmd_vector);
2018 *status = CMD_ERR_NO_MATCH;
2019 return NULL;
2020 }
2021 */
2022 }
2023
2024 /* Prepare match vector. */
2025 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2026
2027 /* Now we got into completion */
2028 for (i = 0; i < vector_active(cmd_vector); i++)
2029 if ((cmd_element = vector_slot(cmd_vector, i))) {
2030 const char *string;
2031 vector strvec = cmd_element->strvec;
2032
2033 /* Check field length */
2034 if (index >= vector_active(strvec))
2035 vector_slot(cmd_vector, i) = NULL;
2036 else {
2037 unsigned int j;
2038
2039 descvec = vector_slot(strvec, index);
2040 for (j = 0; j < vector_active(descvec); j++)
2041 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002042 const char *cmd = desc->cmd;
2043 char *tmp = NULL;
2044
2045 if (CMD_OPTION(desc->cmd)) {
2046 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2047 cmd = tmp;
2048 }
2049 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002050 if (cmd_unique_string (matchvec, string))
2051 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002052 if (tmp)
2053 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002054 }
2055 }
2056 }
2057
2058 /* We don't need cmd_vector any more. */
2059 vector_free(cmd_vector);
2060
2061 /* No matched command */
2062 if (vector_slot(matchvec, 0) == NULL) {
2063 vector_free(matchvec);
2064
2065 /* In case of 'command \t' pattern. Do you need '?' command at
2066 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002067 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002068 *status = CMD_ERR_NOTHING_TODO;
2069 else
2070 *status = CMD_ERR_NO_MATCH;
2071 return NULL;
2072 }
2073
2074 /* Only one matched */
2075 if (vector_slot(matchvec, 1) == NULL) {
2076 match_str = (char **)matchvec->index;
2077 vector_only_wrapper_free(matchvec);
2078 *status = CMD_COMPLETE_FULL_MATCH;
2079 return match_str;
2080 }
2081 /* Make it sure last element is NULL. */
2082 vector_set(matchvec, NULL);
2083
2084 /* Check LCD of matched strings. */
2085 if (vector_slot(vline, index) != NULL) {
2086 lcd = cmd_lcd((char **)matchvec->index);
2087
2088 if (lcd) {
2089 int len = strlen(vector_slot(vline, index));
2090
2091 if (len < lcd) {
2092 char *lcdstr;
2093
2094 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2095 "complete-lcdstr");
2096 memcpy(lcdstr, matchvec->index[0], lcd);
2097 lcdstr[lcd] = '\0';
2098
2099 /* match_str = (char **) &lcdstr; */
2100
2101 /* Free matchvec. */
2102 for (i = 0; i < vector_active(matchvec); i++) {
2103 if (vector_slot(matchvec, i))
2104 talloc_free(vector_slot(matchvec, i));
2105 }
2106 vector_free(matchvec);
2107
2108 /* Make new matchvec. */
2109 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2110 vector_set(matchvec, lcdstr);
2111 match_str = (char **)matchvec->index;
2112 vector_only_wrapper_free(matchvec);
2113
2114 *status = CMD_COMPLETE_MATCH;
2115 return match_str;
2116 }
2117 }
2118 }
2119
2120 match_str = (char **)matchvec->index;
2121 vector_only_wrapper_free(matchvec);
2122 *status = CMD_COMPLETE_LIST_MATCH;
2123 return match_str;
2124}
2125
2126char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2127{
2128 char **ret;
2129
2130 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2131 enum node_type onode;
2132 vector shifted_vline;
2133 unsigned int index;
2134
2135 onode = vty->node;
2136 vty->node = ENABLE_NODE;
2137 /* We can try it on enable node, cos' the vty is authenticated */
2138
2139 shifted_vline = vector_init(vector_count(vline));
2140 /* use memcpy? */
2141 for (index = 1; index < vector_active(vline); index++) {
2142 vector_set_index(shifted_vline, index - 1,
2143 vector_lookup(vline, index));
2144 }
2145
2146 ret = cmd_complete_command_real(shifted_vline, vty, status);
2147
2148 vector_free(shifted_vline);
2149 vty->node = onode;
2150 return ret;
2151 }
2152
2153 return cmd_complete_command_real(vline, vty, status);
2154}
2155
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002156static struct vty_parent_node *vty_parent(struct vty *vty)
2157{
2158 return llist_first_entry_or_null(&vty->parent_nodes,
2159 struct vty_parent_node,
2160 entry);
2161}
2162
2163static bool vty_pop_parent(struct vty *vty)
2164{
2165 struct vty_parent_node *parent = vty_parent(vty);
2166 if (!parent)
2167 return false;
2168 llist_del(&parent->entry);
2169 vty->node = parent->node;
2170 vty->priv = parent->priv;
2171 if (vty->indent)
2172 talloc_free(vty->indent);
2173 vty->indent = parent->indent;
2174 talloc_free(parent);
2175 return true;
2176}
2177
2178static void vty_clear_parents(struct vty *vty)
2179{
2180 while (vty_pop_parent(vty));
2181}
2182
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002183/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002184/*
2185 * This function MUST eventually converge on a node when called repeatedly,
2186 * there must not be any cycles.
2187 * All 'config' nodes shall converge on CONFIG_NODE.
2188 * All other 'enable' nodes shall converge on ENABLE_NODE.
2189 * All 'view' only nodes shall converge on VIEW_NODE.
2190 * All other nodes shall converge on themselves or it must be ensured,
2191 * that the user's rights are not extended anyhow by calling this function.
2192 *
2193 * Note that these requirements also apply to all functions that are used
2194 * as go_parent_cb.
2195 * Note also that this function relies on the is_config_child callback to
2196 * recognize non-config nodes if go_parent_cb is not set.
2197 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002198int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002199{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002200 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002201 case AUTH_NODE:
2202 case VIEW_NODE:
2203 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002204 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002205 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002206 break;
2207
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002208 case AUTH_ENABLE_NODE:
2209 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002210 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002211 break;
2212
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002213 default:
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002214 if (host.app_info->go_parent_cb)
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002215 host.app_info->go_parent_cb(vty);
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002216 vty_pop_parent(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002217 break;
2218 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002219
2220 return vty->node;
2221}
2222
2223/* Execute command by argument vline vector. */
2224static int
2225cmd_execute_command_real(vector vline, struct vty *vty,
2226 struct cmd_element **cmd)
2227{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002228 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002229 unsigned int index;
2230 vector cmd_vector;
2231 struct cmd_element *cmd_element;
2232 struct cmd_element *matched_element;
2233 unsigned int matched_count, incomplete_count;
2234 int argc;
2235 const char *argv[CMD_ARGC_MAX];
2236 enum match_type match = 0;
2237 int varflag;
2238 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002239 int rc;
2240 /* Used for temporary storage of cmd_deopt() allocated arguments during
2241 argv[] generation */
2242 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002243
2244 /* Make copy of command elements. */
2245 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2246
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002247 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002248 if ((command = vector_slot(vline, index))) {
2249 int ret;
2250
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002251 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002252 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002253
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002254 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002255 break;
2256
2257 ret =
2258 is_cmd_ambiguous(command, cmd_vector, index, match);
2259
2260 if (ret == 1) {
2261 vector_free(cmd_vector);
2262 return CMD_ERR_AMBIGUOUS;
2263 } else if (ret == 2) {
2264 vector_free(cmd_vector);
2265 return CMD_ERR_NO_MATCH;
2266 }
2267 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002268 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002269
2270 /* Check matched count. */
2271 matched_element = NULL;
2272 matched_count = 0;
2273 incomplete_count = 0;
2274
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002275 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002276 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002277 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002278 || index >= cmd_element->cmdsize) {
2279 matched_element = cmd_element;
2280#if 0
2281 printf("DEBUG: %s\n", cmd_element->string);
2282#endif
2283 matched_count++;
2284 } else {
2285 incomplete_count++;
2286 }
2287 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002288 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002289
2290 /* Finish of using cmd_vector. */
2291 vector_free(cmd_vector);
2292
2293 /* To execute command, matched_count must be 1. */
2294 if (matched_count == 0) {
2295 if (incomplete_count)
2296 return CMD_ERR_INCOMPLETE;
2297 else
2298 return CMD_ERR_NO_MATCH;
2299 }
2300
2301 if (matched_count > 1)
2302 return CMD_ERR_AMBIGUOUS;
2303
2304 /* Argument treatment */
2305 varflag = 0;
2306 argc = 0;
2307
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002308 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2309
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002310 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002311 if (argc == CMD_ARGC_MAX) {
2312 rc = CMD_ERR_EXEED_ARGC_MAX;
2313 goto rc_free_deopt_ctx;
2314 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002315 if (varflag) {
2316 argv[argc++] = vector_slot(vline, i);
2317 continue;
2318 }
2319
2320 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002321 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002322
2323 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002324 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002325 struct desc *desc = vector_slot(descvec, 0);
2326
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002327 if (CMD_OPTION(desc->cmd)) {
2328 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2329 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2330 } else {
2331 tmp_cmd = desc->cmd;
2332 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002333
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002334 if (CMD_VARARG(tmp_cmd))
2335 varflag = 1;
2336 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002337 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002338 else if (CMD_OPTION(desc->cmd))
2339 argv[argc++] = tmp_cmd;
2340 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002341 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002342 /* multi choice argument. look up which choice
2343 the user meant (can only be one after
2344 filtering and checking for ambigous). For instance,
2345 if user typed "th" for "(two|three)" arg, we
2346 want to pass "three" in argv[]. */
2347 for (j = 0; j < vector_active(descvec); j++) {
2348 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002349 if (!desc)
2350 continue;
2351 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2352 continue;
2353 if (CMD_OPTION(desc->cmd)) {
2354 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2355 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2356 } else {
2357 tmp_cmd = desc->cmd;
2358 }
2359
2360 if(CMD_VARIABLE(tmp_cmd)) {
2361 argv[argc++] = vector_slot(vline, i);
2362 } else {
2363 argv[argc++] = tmp_cmd;
2364 }
2365 break;
2366 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002367 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002368 }
2369
2370 /* For vtysh execution. */
2371 if (cmd)
2372 *cmd = matched_element;
2373
2374 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002375 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002376 else {
2377 /* Execute matched command. */
2378 struct vty_parent_node this_node = {
2379 .node = vty->node,
2380 .priv = vty->priv,
2381 .indent = vty->indent,
2382 };
2383 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002384 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002385
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002386 /* If we have stepped down into a child node, push a parent frame.
2387 * The causality is such: we don't expect every single node entry implementation to push
2388 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2389 * a parent node. Hence if the node changed without the parent node changing, we must
2390 * have stepped into a child node. */
2391 if (vty->node != this_node.node && parent == vty_parent(vty)
2392 && vty->node > CONFIG_NODE) {
2393 /* Push the parent node. */
2394 parent = talloc_zero(vty, struct vty_parent_node);
2395 *parent = this_node;
2396 llist_add(&parent->entry, &vty->parent_nodes);
2397 }
2398 }
2399
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002400rc_free_deopt_ctx:
2401 /* Now after we called the command func, we can free temporary strings */
2402 talloc_free(cmd_deopt_ctx);
2403 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002404}
2405
2406int
2407cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2408 int vtysh)
2409{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002410 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002411 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002412
2413 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002414
2415 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2416 vector shifted_vline;
2417 unsigned int index;
2418
2419 vty->node = ENABLE_NODE;
2420 /* We can try it on enable node, cos' the vty is authenticated */
2421
2422 shifted_vline = vector_init(vector_count(vline));
2423 /* use memcpy? */
2424 for (index = 1; index < vector_active(vline); index++) {
2425 vector_set_index(shifted_vline, index - 1,
2426 vector_lookup(vline, index));
2427 }
2428
2429 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2430
2431 vector_free(shifted_vline);
2432 vty->node = onode;
2433 return ret;
2434 }
2435
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002436 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002437}
2438
2439/* Execute command by argument readline. */
2440int
2441cmd_execute_command_strict(vector vline, struct vty *vty,
2442 struct cmd_element **cmd)
2443{
2444 unsigned int i;
2445 unsigned int index;
2446 vector cmd_vector;
2447 struct cmd_element *cmd_element;
2448 struct cmd_element *matched_element;
2449 unsigned int matched_count, incomplete_count;
2450 int argc;
2451 const char *argv[CMD_ARGC_MAX];
2452 int varflag;
2453 enum match_type match = 0;
2454 char *command;
2455
2456 /* Make copy of command element */
2457 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2458
2459 for (index = 0; index < vector_active(vline); index++)
2460 if ((command = vector_slot(vline, index))) {
2461 int ret;
2462
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002463 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002464 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002465
2466 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002467 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002468 break;
2469
2470 ret =
2471 is_cmd_ambiguous(command, cmd_vector, index, match);
2472 if (ret == 1) {
2473 vector_free(cmd_vector);
2474 return CMD_ERR_AMBIGUOUS;
2475 }
2476 if (ret == 2) {
2477 vector_free(cmd_vector);
2478 return CMD_ERR_NO_MATCH;
2479 }
2480 }
2481
2482 /* Check matched count. */
2483 matched_element = NULL;
2484 matched_count = 0;
2485 incomplete_count = 0;
2486 for (i = 0; i < vector_active(cmd_vector); i++)
2487 if (vector_slot(cmd_vector, i) != NULL) {
2488 cmd_element = vector_slot(cmd_vector, i);
2489
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002490 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002491 || index >= cmd_element->cmdsize) {
2492 matched_element = cmd_element;
2493 matched_count++;
2494 } else
2495 incomplete_count++;
2496 }
2497
2498 /* Finish of using cmd_vector. */
2499 vector_free(cmd_vector);
2500
2501 /* To execute command, matched_count must be 1. */
2502 if (matched_count == 0) {
2503 if (incomplete_count)
2504 return CMD_ERR_INCOMPLETE;
2505 else
2506 return CMD_ERR_NO_MATCH;
2507 }
2508
2509 if (matched_count > 1)
2510 return CMD_ERR_AMBIGUOUS;
2511
2512 /* Argument treatment */
2513 varflag = 0;
2514 argc = 0;
2515
2516 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002517 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002518 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002519 if (varflag) {
2520 argv[argc++] = vector_slot(vline, i);
2521 continue;
2522 }
2523
2524 vector descvec = vector_slot(matched_element->strvec, i);
2525
2526 if (vector_active(descvec) == 1) {
2527 struct desc *desc = vector_slot(descvec, 0);
2528
2529 if (CMD_VARARG(desc->cmd))
2530 varflag = 1;
2531
2532 if (varflag || CMD_VARIABLE(desc->cmd)
2533 || CMD_OPTION(desc->cmd))
2534 argv[argc++] = vector_slot(vline, i);
2535 } else {
2536 argv[argc++] = vector_slot(vline, i);
2537 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002538 }
2539
2540 /* For vtysh execution. */
2541 if (cmd)
2542 *cmd = matched_element;
2543
2544 if (matched_element->daemon)
2545 return CMD_SUCCESS_DAEMON;
2546
2547 /* Now execute matched command */
2548 return (*matched_element->func) (matched_element, vty, argc, argv);
2549}
2550
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002551static inline size_t len(const char *str)
2552{
2553 return str? strlen(str) : 0;
2554}
2555
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002556/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2557 * is longer than b, a must start with exactly b, and vice versa.
2558 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2559 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002560static int indent_cmp(const char *a, const char *b)
2561{
2562 size_t al, bl;
2563 al = len(a);
2564 bl = len(b);
2565 if (al > bl) {
2566 if (bl && strncmp(a, b, bl) != 0)
2567 return EINVAL;
2568 return 1;
2569 }
2570 /* al <= bl */
2571 if (al && strncmp(a, b, al) != 0)
2572 return EINVAL;
2573 return (al < bl)? -1 : 0;
2574}
2575
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002576/* Configration make from file. */
2577int config_from_file(struct vty *vty, FILE * fp)
2578{
2579 int ret;
2580 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002581 char *indent;
2582 int cmp;
2583 struct vty_parent_node this_node;
2584 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002585
2586 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002587 indent = NULL;
2588 vline = NULL;
2589 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002590
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002591 if (ret != CMD_SUCCESS)
2592 goto return_invalid_indent;
2593
2594 /* In case of comment or empty line */
2595 if (vline == NULL) {
2596 if (indent) {
2597 talloc_free(indent);
2598 indent = NULL;
2599 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002600 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002601 }
2602
Neels Hofmeyr43063632017-09-19 23:54:01 +02002603 /* We have a nonempty line. */
2604 if (!vty->indent) {
2605 /* We have just entered a node and expecting the first child to come up; but we
2606 * may also skip right back to a parent or ancestor level. */
2607 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002608
Neels Hofmeyr43063632017-09-19 23:54:01 +02002609 /* If there is no parent, record any indentation we encounter. */
2610 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2611
2612 if (cmp == EINVAL)
2613 goto return_invalid_indent;
2614
2615 if (cmp <= 0) {
2616 /* We have gone right back to the parent level or higher, we are skipping
2617 * this child node level entirely. Pop the parent to go back to a node
2618 * that was actually there (to reinstate vty->indent) and re-use below
2619 * go-parent while-loop to find an accurate match of indent in the node
2620 * ancestry. */
2621 vty_go_parent(vty);
2622 } else {
2623 /* The indent is deeper than the just entered parent, record the new
2624 * indentation characters. */
2625 vty->indent = talloc_strdup(vty, indent);
2626 /* This *is* the new indentation. */
2627 cmp = 0;
2628 }
2629 } else {
2630 /* There is a known indentation for this node level, validate and detect node
2631 * exits. */
2632 cmp = indent_cmp(indent, vty->indent);
2633 if (cmp == EINVAL)
2634 goto return_invalid_indent;
2635 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002636
2637 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2638 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2639 while (cmp < 0) {
2640 vty_go_parent(vty);
2641 cmp = indent_cmp(indent, vty->indent);
2642 if (cmp == EINVAL)
2643 goto return_invalid_indent;
2644 }
2645
2646 /* More indent without having entered a child node level? Either the parent node's indent
2647 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2648 * or the indentation increased even though the vty command didn't enter a child. */
2649 if (cmp > 0)
2650 goto return_invalid_indent;
2651
2652 /* Remember the current node before the command possibly changes it. */
2653 this_node = (struct vty_parent_node){
2654 .node = vty->node,
2655 .priv = vty->priv,
2656 .indent = vty->indent,
2657 };
2658
2659 parent = vty_parent(vty);
2660 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002661 cmd_free_strvec(vline);
2662
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002663 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002664 if (indent) {
2665 talloc_free(indent);
2666 indent = NULL;
2667 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002668 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002669 }
2670
2671 /* If we have stepped down into a child node, push a parent frame.
2672 * The causality is such: we don't expect every single node entry implementation to push
2673 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2674 * a parent node. Hence if the node changed without the parent node changing, we must
2675 * have stepped into a child node (and now expect a deeper indent). */
2676 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2677 /* Push the parent node. */
2678 parent = talloc_zero(vty, struct vty_parent_node);
2679 *parent = this_node;
2680 llist_add(&parent->entry, &vty->parent_nodes);
2681
2682 /* The current talloc'ed vty->indent string will now be owned by this parent
2683 * struct. Indicate that we don't know what deeper indent characters the user
2684 * will choose. */
2685 vty->indent = NULL;
2686 }
2687
2688 if (indent) {
2689 talloc_free(indent);
2690 indent = NULL;
2691 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002692 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002693 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2694 while (vty_parent(vty))
2695 vty_go_parent(vty);
2696
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002697 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002698
2699return_invalid_indent:
2700 if (vline)
2701 cmd_free_strvec(vline);
2702 if (indent) {
2703 talloc_free(indent);
2704 indent = NULL;
2705 }
2706 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002707}
2708
2709/* Configration from terminal */
2710DEFUN(config_terminal,
2711 config_terminal_cmd,
2712 "configure terminal",
2713 "Configuration from vty interface\n" "Configuration terminal\n")
2714{
2715 if (vty_config_lock(vty))
2716 vty->node = CONFIG_NODE;
2717 else {
2718 vty_out(vty, "VTY configuration is locked by other VTY%s",
2719 VTY_NEWLINE);
2720 return CMD_WARNING;
2721 }
2722 return CMD_SUCCESS;
2723}
2724
2725/* Enable command */
2726DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2727{
2728 /* If enable password is NULL, change to ENABLE_NODE */
2729 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2730 vty->type == VTY_SHELL_SERV)
2731 vty->node = ENABLE_NODE;
2732 else
2733 vty->node = AUTH_ENABLE_NODE;
2734
2735 return CMD_SUCCESS;
2736}
2737
2738/* Disable command */
2739DEFUN(disable,
2740 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2741{
2742 if (vty->node == ENABLE_NODE)
2743 vty->node = VIEW_NODE;
2744 return CMD_SUCCESS;
2745}
2746
2747/* Down vty node level. */
2748gDEFUN(config_exit,
2749 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2750{
2751 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002752 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002753 case VIEW_NODE:
2754 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002755 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002756 break;
2757 case CONFIG_NODE:
2758 vty->node = ENABLE_NODE;
2759 vty_config_unlock(vty);
2760 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002761 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002762 if (vty->node > CONFIG_NODE)
2763 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002764 break;
2765 }
2766 return CMD_SUCCESS;
2767}
2768
2769/* End of configuration. */
2770 gDEFUN(config_end,
2771 config_end_cmd, "end", "End current mode and change to enable mode.")
2772{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002773 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002774 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002775
2776 /* Repeatedly call go_parent until a top node is reached. */
2777 while (vty->node > CONFIG_NODE) {
2778 if (vty->node == last_node) {
2779 /* Ensure termination, this shouldn't happen. */
2780 break;
2781 }
2782 last_node = vty->node;
2783 vty_go_parent(vty);
2784 }
2785
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002786 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002787 if (vty->node > ENABLE_NODE)
2788 vty->node = ENABLE_NODE;
2789 vty->index = NULL;
2790 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002791 }
2792 return CMD_SUCCESS;
2793}
2794
2795/* Show version. */
2796DEFUN(show_version,
2797 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2798{
Harald Welte237f6242010-05-25 23:00:45 +02002799 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2800 host.app_info->version,
2801 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2802 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002803
2804 return CMD_SUCCESS;
2805}
2806
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002807DEFUN(show_online_help,
2808 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2809{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002810 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002811 return CMD_SUCCESS;
2812}
2813
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002814/* Help display function for all node. */
2815gDEFUN(config_help,
2816 config_help_cmd, "help", "Description of the interactive help system\n")
2817{
2818 vty_out(vty,
2819 "This VTY provides advanced help features. When you need help,%s\
2820anytime at the command line please press '?'.%s\
2821%s\
2822If nothing matches, the help list will be empty and you must backup%s\
2823 until entering a '?' shows the available options.%s\
2824Two styles of help are provided:%s\
28251. Full help is available when you are ready to enter a%s\
2826command argument (e.g. 'show ?') and describes each possible%s\
2827argument.%s\
28282. Partial help is provided when an abbreviated argument is entered%s\
2829 and you want to know what arguments match the input%s\
2830 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2831 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2832 return CMD_SUCCESS;
2833}
2834
2835/* Help display function for all node. */
2836gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2837{
2838 unsigned int i;
2839 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2840 struct cmd_element *cmd;
2841
2842 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2843 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002844 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002845 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2846 return CMD_SUCCESS;
2847}
2848
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002849static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002850{
2851 unsigned int i;
2852 int fd;
2853 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002854 char *config_file_tmp = NULL;
2855 char *config_file_sav = NULL;
2856 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002857 struct stat st;
2858
2859 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002860
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002861 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2862 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2863 * manually instead. */
2864
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002865 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002866 config_file_sav =
2867 _talloc_zero(tall_vty_cmd_ctx,
2868 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2869 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002870 if (!config_file_sav)
2871 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002872 strcpy(config_file_sav, config_file);
2873 strcat(config_file_sav, CONF_BACKUP_EXT);
2874
2875 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002876 "config_file_tmp");
2877 if (!config_file_tmp) {
2878 talloc_free(config_file_sav);
2879 return -1;
2880 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002881 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2882
2883 /* Open file to configuration write. */
2884 fd = mkstemp(config_file_tmp);
2885 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002886 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002887 talloc_free(config_file_tmp);
2888 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002889 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002890 }
2891
2892 /* Make vty for configuration file. */
2893 file_vty = vty_new();
2894 file_vty->fd = fd;
2895 file_vty->type = VTY_FILE;
2896
2897 /* Config file header print. */
2898 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002899 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002900 //vty_time_print (file_vty, 1);
2901 vty_out(file_vty, "!\n");
2902
2903 for (i = 0; i < vector_active(cmdvec); i++)
2904 if ((node = vector_slot(cmdvec, i)) && node->func) {
2905 if ((*node->func) (file_vty))
2906 vty_out(file_vty, "!\n");
2907 }
2908 vty_close(file_vty);
2909
2910 if (unlink(config_file_sav) != 0)
2911 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002912 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002913 talloc_free(config_file_sav);
2914 talloc_free(config_file_tmp);
2915 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002916 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002917 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002918
2919 /* Only link the .sav file if the original file exists */
2920 if (stat(config_file, &st) == 0) {
2921 if (link(config_file, config_file_sav) != 0) {
2922 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2923 talloc_free(config_file_sav);
2924 talloc_free(config_file_tmp);
2925 unlink(config_file_tmp);
2926 return -3;
2927 }
2928 sync();
2929 if (unlink(config_file) != 0) {
2930 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2931 talloc_free(config_file_sav);
2932 talloc_free(config_file_tmp);
2933 unlink(config_file_tmp);
2934 return -4;
2935 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002936 }
2937 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002938 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002939 talloc_free(config_file_sav);
2940 talloc_free(config_file_tmp);
2941 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002942 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002943 }
2944 unlink(config_file_tmp);
2945 sync();
2946
2947 talloc_free(config_file_sav);
2948 talloc_free(config_file_tmp);
2949
2950 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002951 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2952 return -6;
2953 }
2954
2955 return 0;
2956}
2957
2958
2959/* Write current configuration into file. */
2960DEFUN(config_write_file,
2961 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002962 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002963 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002964 "Write to configuration file\n"
2965 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002966{
2967 char *failed_file;
2968 int rc;
2969
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002970 if (host.app_info->config_is_consistent) {
2971 rc = host.app_info->config_is_consistent(vty);
2972 if (!rc) {
2973 vty_out(vty, "Configuration is not consistent%s",
2974 VTY_NEWLINE);
2975 return CMD_WARNING;
2976 }
2977 }
2978
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002979 if (argc == 1)
2980 host_config_set(argv[0]);
2981
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002982 if (host.config == NULL) {
2983 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2984 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002985 return CMD_WARNING;
2986 }
2987
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002988 rc = write_config_file(host.config, &failed_file);
2989 switch (rc) {
2990 case -1:
2991 vty_out(vty, "Can't open configuration file %s.%s",
2992 failed_file, VTY_NEWLINE);
2993 rc = CMD_WARNING;
2994 break;
2995 case -2:
2996 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2997 failed_file, VTY_NEWLINE);
2998 rc = CMD_WARNING;
2999 break;
3000 case -3:
3001 vty_out(vty, "Can't backup old configuration file %s.%s",
3002 failed_file, VTY_NEWLINE);
3003 rc = CMD_WARNING;
3004 break;
3005 case -4:
3006 vty_out(vty, "Can't unlink configuration file %s.%s",
3007 failed_file, VTY_NEWLINE);
3008 rc = CMD_WARNING;
3009 break;
3010 case -5:
3011 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3012 VTY_NEWLINE);
3013 rc = CMD_WARNING;
3014 break;
3015 case -6:
3016 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3017 failed_file, strerror(errno), errno, VTY_NEWLINE);
3018 rc = CMD_WARNING;
3019 break;
3020 default:
3021 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3022 rc = CMD_SUCCESS;
3023 break;
3024 }
3025
3026 talloc_free(failed_file);
3027 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003028}
3029
3030ALIAS(config_write_file,
3031 config_write_cmd,
3032 "write", "Write running configuration to memory, network, or terminal\n")
3033
3034 ALIAS(config_write_file,
3035 config_write_memory_cmd,
3036 "write memory",
3037 "Write running configuration to memory, network, or terminal\n"
3038 "Write configuration to the file (same as write file)\n")
3039
3040 ALIAS(config_write_file,
3041 copy_runningconfig_startupconfig_cmd,
3042 "copy running-config startup-config",
3043 "Copy configuration\n"
3044 "Copy running config to... \n"
3045 "Copy running config to startup config (same as write file)\n")
3046
3047/* Write current configuration into the terminal. */
3048 DEFUN(config_write_terminal,
3049 config_write_terminal_cmd,
3050 "write terminal",
3051 "Write running configuration to memory, network, or terminal\n"
3052 "Write to terminal\n")
3053{
3054 unsigned int i;
3055 struct cmd_node *node;
3056
3057 if (vty->type == VTY_SHELL_SERV) {
3058 for (i = 0; i < vector_active(cmdvec); i++)
3059 if ((node = vector_slot(cmdvec, i)) && node->func
3060 && node->vtysh) {
3061 if ((*node->func) (vty))
3062 vty_out(vty, "!%s", VTY_NEWLINE);
3063 }
3064 } else {
3065 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3066 VTY_NEWLINE);
3067 vty_out(vty, "!%s", VTY_NEWLINE);
3068
3069 for (i = 0; i < vector_active(cmdvec); i++)
3070 if ((node = vector_slot(cmdvec, i)) && node->func) {
3071 if ((*node->func) (vty))
3072 vty_out(vty, "!%s", VTY_NEWLINE);
3073 }
3074 vty_out(vty, "end%s", VTY_NEWLINE);
3075 }
3076 return CMD_SUCCESS;
3077}
3078
3079/* Write current configuration into the terminal. */
3080ALIAS(config_write_terminal,
3081 show_running_config_cmd,
3082 "show running-config", SHOW_STR "running configuration\n")
3083
3084/* Write startup configuration into the terminal. */
3085 DEFUN(show_startup_config,
3086 show_startup_config_cmd,
3087 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3088{
3089 char buf[BUFSIZ];
3090 FILE *confp;
3091
3092 confp = fopen(host.config, "r");
3093 if (confp == NULL) {
3094 vty_out(vty, "Can't open configuration file [%s]%s",
3095 host.config, VTY_NEWLINE);
3096 return CMD_WARNING;
3097 }
3098
3099 while (fgets(buf, BUFSIZ, confp)) {
3100 char *cp = buf;
3101
3102 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3103 cp++;
3104 *cp = '\0';
3105
3106 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3107 }
3108
3109 fclose(confp);
3110
3111 return CMD_SUCCESS;
3112}
3113
3114/* Hostname configuration */
3115DEFUN(config_hostname,
3116 hostname_cmd,
3117 "hostname WORD",
3118 "Set system's network name\n" "This system's network name\n")
3119{
3120 if (!isalpha((int)*argv[0])) {
3121 vty_out(vty, "Please specify string starting with alphabet%s",
3122 VTY_NEWLINE);
3123 return CMD_WARNING;
3124 }
3125
3126 if (host.name)
3127 talloc_free(host.name);
3128
3129 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3130 return CMD_SUCCESS;
3131}
3132
3133DEFUN(config_no_hostname,
3134 no_hostname_cmd,
3135 "no hostname [HOSTNAME]",
3136 NO_STR "Reset system's network name\n" "Host name of this router\n")
3137{
3138 if (host.name)
3139 talloc_free(host.name);
3140 host.name = NULL;
3141 return CMD_SUCCESS;
3142}
3143
3144/* VTY interface password set. */
3145DEFUN(config_password, password_cmd,
3146 "password (8|) WORD",
3147 "Assign the terminal connection password\n"
3148 "Specifies a HIDDEN password will follow\n"
3149 "dummy string \n" "The HIDDEN line password string\n")
3150{
3151 /* Argument check. */
3152 if (argc == 0) {
3153 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3154 return CMD_WARNING;
3155 }
3156
3157 if (argc == 2) {
3158 if (*argv[0] == '8') {
3159 if (host.password)
3160 talloc_free(host.password);
3161 host.password = NULL;
3162 if (host.password_encrypt)
3163 talloc_free(host.password_encrypt);
3164 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3165 return CMD_SUCCESS;
3166 } else {
3167 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3168 return CMD_WARNING;
3169 }
3170 }
3171
3172 if (!isalnum((int)*argv[0])) {
3173 vty_out(vty,
3174 "Please specify string starting with alphanumeric%s",
3175 VTY_NEWLINE);
3176 return CMD_WARNING;
3177 }
3178
3179 if (host.password)
3180 talloc_free(host.password);
3181 host.password = NULL;
3182
3183#ifdef VTY_CRYPT_PW
3184 if (host.encrypt) {
3185 if (host.password_encrypt)
3186 talloc_free(host.password_encrypt);
3187 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3188 } else
3189#endif
3190 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3191
3192 return CMD_SUCCESS;
3193}
3194
3195ALIAS(config_password, password_text_cmd,
3196 "password LINE",
3197 "Assign the terminal connection password\n"
3198 "The UNENCRYPTED (cleartext) line password\n")
3199
3200/* VTY enable password set. */
3201 DEFUN(config_enable_password, enable_password_cmd,
3202 "enable password (8|) WORD",
3203 "Modify enable password parameters\n"
3204 "Assign the privileged level password\n"
3205 "Specifies a HIDDEN password will follow\n"
3206 "dummy string \n" "The HIDDEN 'enable' password string\n")
3207{
3208 /* Argument check. */
3209 if (argc == 0) {
3210 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3211 return CMD_WARNING;
3212 }
3213
3214 /* Crypt type is specified. */
3215 if (argc == 2) {
3216 if (*argv[0] == '8') {
3217 if (host.enable)
3218 talloc_free(host.enable);
3219 host.enable = NULL;
3220
3221 if (host.enable_encrypt)
3222 talloc_free(host.enable_encrypt);
3223 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3224
3225 return CMD_SUCCESS;
3226 } else {
3227 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3228 return CMD_WARNING;
3229 }
3230 }
3231
3232 if (!isalnum((int)*argv[0])) {
3233 vty_out(vty,
3234 "Please specify string starting with alphanumeric%s",
3235 VTY_NEWLINE);
3236 return CMD_WARNING;
3237 }
3238
3239 if (host.enable)
3240 talloc_free(host.enable);
3241 host.enable = NULL;
3242
3243 /* Plain password input. */
3244#ifdef VTY_CRYPT_PW
3245 if (host.encrypt) {
3246 if (host.enable_encrypt)
3247 talloc_free(host.enable_encrypt);
3248 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3249 } else
3250#endif
3251 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3252
3253 return CMD_SUCCESS;
3254}
3255
3256ALIAS(config_enable_password,
3257 enable_password_text_cmd,
3258 "enable password LINE",
3259 "Modify enable password parameters\n"
3260 "Assign the privileged level password\n"
3261 "The UNENCRYPTED (cleartext) 'enable' password\n")
3262
3263/* VTY enable password delete. */
3264 DEFUN(no_config_enable_password, no_enable_password_cmd,
3265 "no enable password",
3266 NO_STR
3267 "Modify enable password parameters\n"
3268 "Assign the privileged level password\n")
3269{
3270 if (host.enable)
3271 talloc_free(host.enable);
3272 host.enable = NULL;
3273
3274 if (host.enable_encrypt)
3275 talloc_free(host.enable_encrypt);
3276 host.enable_encrypt = NULL;
3277
3278 return CMD_SUCCESS;
3279}
3280
3281#ifdef VTY_CRYPT_PW
3282DEFUN(service_password_encrypt,
3283 service_password_encrypt_cmd,
3284 "service password-encryption",
3285 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3286{
3287 if (host.encrypt)
3288 return CMD_SUCCESS;
3289
3290 host.encrypt = 1;
3291
3292 if (host.password) {
3293 if (host.password_encrypt)
3294 talloc_free(host.password_encrypt);
3295 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3296 }
3297 if (host.enable) {
3298 if (host.enable_encrypt)
3299 talloc_free(host.enable_encrypt);
3300 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3301 }
3302
3303 return CMD_SUCCESS;
3304}
3305
3306DEFUN(no_service_password_encrypt,
3307 no_service_password_encrypt_cmd,
3308 "no service password-encryption",
3309 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3310{
3311 if (!host.encrypt)
3312 return CMD_SUCCESS;
3313
3314 host.encrypt = 0;
3315
3316 if (host.password_encrypt)
3317 talloc_free(host.password_encrypt);
3318 host.password_encrypt = NULL;
3319
3320 if (host.enable_encrypt)
3321 talloc_free(host.enable_encrypt);
3322 host.enable_encrypt = NULL;
3323
3324 return CMD_SUCCESS;
3325}
3326#endif
3327
3328DEFUN(config_terminal_length, config_terminal_length_cmd,
3329 "terminal length <0-512>",
3330 "Set terminal line parameters\n"
3331 "Set number of lines on a screen\n"
3332 "Number of lines on screen (0 for no pausing)\n")
3333{
3334 int lines;
3335 char *endptr = NULL;
3336
3337 lines = strtol(argv[0], &endptr, 10);
3338 if (lines < 0 || lines > 512 || *endptr != '\0') {
3339 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3340 return CMD_WARNING;
3341 }
3342 vty->lines = lines;
3343
3344 return CMD_SUCCESS;
3345}
3346
3347DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3348 "terminal no length",
3349 "Set terminal line parameters\n"
3350 NO_STR "Set number of lines on a screen\n")
3351{
3352 vty->lines = -1;
3353 return CMD_SUCCESS;
3354}
3355
3356DEFUN(service_terminal_length, service_terminal_length_cmd,
3357 "service terminal-length <0-512>",
3358 "Set up miscellaneous service\n"
3359 "System wide terminal length configuration\n"
3360 "Number of lines of VTY (0 means no line control)\n")
3361{
3362 int lines;
3363 char *endptr = NULL;
3364
3365 lines = strtol(argv[0], &endptr, 10);
3366 if (lines < 0 || lines > 512 || *endptr != '\0') {
3367 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3368 return CMD_WARNING;
3369 }
3370 host.lines = lines;
3371
3372 return CMD_SUCCESS;
3373}
3374
3375DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3376 "no service terminal-length [<0-512>]",
3377 NO_STR
3378 "Set up miscellaneous service\n"
3379 "System wide terminal length configuration\n"
3380 "Number of lines of VTY (0 means no line control)\n")
3381{
3382 host.lines = -1;
3383 return CMD_SUCCESS;
3384}
3385
3386DEFUN_HIDDEN(do_echo,
3387 echo_cmd,
3388 "echo .MESSAGE",
3389 "Echo a message back to the vty\n" "The message to echo\n")
3390{
3391 char *message;
3392
3393 vty_out(vty, "%s%s",
3394 ((message =
3395 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3396 if (message)
3397 talloc_free(message);
3398 return CMD_SUCCESS;
3399}
3400
3401#if 0
3402DEFUN(config_logmsg,
3403 config_logmsg_cmd,
3404 "logmsg " LOG_LEVELS " .MESSAGE",
3405 "Send a message to enabled logging destinations\n"
3406 LOG_LEVEL_DESC "The message to send\n")
3407{
3408 int level;
3409 char *message;
3410
3411 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3412 return CMD_ERR_NO_MATCH;
3413
3414 zlog(NULL, level,
3415 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3416 if (message)
3417 talloc_free(message);
3418 return CMD_SUCCESS;
3419}
3420
3421DEFUN(show_logging,
3422 show_logging_cmd,
3423 "show logging", SHOW_STR "Show current logging configuration\n")
3424{
3425 struct zlog *zl = zlog_default;
3426
3427 vty_out(vty, "Syslog logging: ");
3428 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3429 vty_out(vty, "disabled");
3430 else
3431 vty_out(vty, "level %s, facility %s, ident %s",
3432 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3433 facility_name(zl->facility), zl->ident);
3434 vty_out(vty, "%s", VTY_NEWLINE);
3435
3436 vty_out(vty, "Stdout logging: ");
3437 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3438 vty_out(vty, "disabled");
3439 else
3440 vty_out(vty, "level %s",
3441 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3442 vty_out(vty, "%s", VTY_NEWLINE);
3443
3444 vty_out(vty, "Monitor logging: ");
3445 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3446 vty_out(vty, "disabled");
3447 else
3448 vty_out(vty, "level %s",
3449 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3450 vty_out(vty, "%s", VTY_NEWLINE);
3451
3452 vty_out(vty, "File logging: ");
3453 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3454 vty_out(vty, "disabled");
3455 else
3456 vty_out(vty, "level %s, filename %s",
3457 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3458 zl->filename);
3459 vty_out(vty, "%s", VTY_NEWLINE);
3460
3461 vty_out(vty, "Protocol name: %s%s",
3462 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3463 vty_out(vty, "Record priority: %s%s",
3464 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3465
3466 return CMD_SUCCESS;
3467}
3468
3469DEFUN(config_log_stdout,
3470 config_log_stdout_cmd,
3471 "log stdout", "Logging control\n" "Set stdout logging level\n")
3472{
3473 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3474 return CMD_SUCCESS;
3475}
3476
3477DEFUN(config_log_stdout_level,
3478 config_log_stdout_level_cmd,
3479 "log stdout " LOG_LEVELS,
3480 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3481{
3482 int level;
3483
3484 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3485 return CMD_ERR_NO_MATCH;
3486 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3487 return CMD_SUCCESS;
3488}
3489
3490DEFUN(no_config_log_stdout,
3491 no_config_log_stdout_cmd,
3492 "no log stdout [LEVEL]",
3493 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3494{
3495 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3496 return CMD_SUCCESS;
3497}
3498
3499DEFUN(config_log_monitor,
3500 config_log_monitor_cmd,
3501 "log monitor",
3502 "Logging control\n" "Set terminal line (monitor) logging level\n")
3503{
3504 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3505 return CMD_SUCCESS;
3506}
3507
3508DEFUN(config_log_monitor_level,
3509 config_log_monitor_level_cmd,
3510 "log monitor " LOG_LEVELS,
3511 "Logging control\n"
3512 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3513{
3514 int level;
3515
3516 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3517 return CMD_ERR_NO_MATCH;
3518 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3519 return CMD_SUCCESS;
3520}
3521
3522DEFUN(no_config_log_monitor,
3523 no_config_log_monitor_cmd,
3524 "no log monitor [LEVEL]",
3525 NO_STR
3526 "Logging control\n"
3527 "Disable terminal line (monitor) logging\n" "Logging level\n")
3528{
3529 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3530 return CMD_SUCCESS;
3531}
3532
3533static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3534{
3535 int ret;
3536 char *p = NULL;
3537 const char *fullpath;
3538
3539 /* Path detection. */
3540 if (!IS_DIRECTORY_SEP(*fname)) {
3541 char cwd[MAXPATHLEN + 1];
3542 cwd[MAXPATHLEN] = '\0';
3543
3544 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3545 zlog_err("config_log_file: Unable to alloc mem!");
3546 return CMD_WARNING;
3547 }
3548
3549 if ((p = _talloc_zero(tall_vcmd_ctx,
3550 strlen(cwd) + strlen(fname) + 2),
3551 "set_log_file")
3552 == NULL) {
3553 zlog_err("config_log_file: Unable to alloc mem!");
3554 return CMD_WARNING;
3555 }
3556 sprintf(p, "%s/%s", cwd, fname);
3557 fullpath = p;
3558 } else
3559 fullpath = fname;
3560
3561 ret = zlog_set_file(NULL, fullpath, loglevel);
3562
3563 if (p)
3564 talloc_free(p);
3565
3566 if (!ret) {
3567 vty_out(vty, "can't open logfile %s\n", fname);
3568 return CMD_WARNING;
3569 }
3570
3571 if (host.logfile)
3572 talloc_free(host.logfile);
3573
3574 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3575
3576 return CMD_SUCCESS;
3577}
3578
3579DEFUN(config_log_file,
3580 config_log_file_cmd,
3581 "log file FILENAME",
3582 "Logging control\n" "Logging to file\n" "Logging filename\n")
3583{
3584 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3585}
3586
3587DEFUN(config_log_file_level,
3588 config_log_file_level_cmd,
3589 "log file FILENAME " LOG_LEVELS,
3590 "Logging control\n"
3591 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3592{
3593 int level;
3594
3595 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3596 return CMD_ERR_NO_MATCH;
3597 return set_log_file(vty, argv[0], level);
3598}
3599
3600DEFUN(no_config_log_file,
3601 no_config_log_file_cmd,
3602 "no log file [FILENAME]",
3603 NO_STR
3604 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3605{
3606 zlog_reset_file(NULL);
3607
3608 if (host.logfile)
3609 talloc_free(host.logfile);
3610
3611 host.logfile = NULL;
3612
3613 return CMD_SUCCESS;
3614}
3615
3616ALIAS(no_config_log_file,
3617 no_config_log_file_level_cmd,
3618 "no log file FILENAME LEVEL",
3619 NO_STR
3620 "Logging control\n"
3621 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3622
3623 DEFUN(config_log_syslog,
3624 config_log_syslog_cmd,
3625 "log syslog", "Logging control\n" "Set syslog logging level\n")
3626{
3627 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3628 return CMD_SUCCESS;
3629}
3630
3631DEFUN(config_log_syslog_level,
3632 config_log_syslog_level_cmd,
3633 "log syslog " LOG_LEVELS,
3634 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3635{
3636 int level;
3637
3638 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3639 return CMD_ERR_NO_MATCH;
3640 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3641 return CMD_SUCCESS;
3642}
3643
3644DEFUN_DEPRECATED(config_log_syslog_facility,
3645 config_log_syslog_facility_cmd,
3646 "log syslog facility " LOG_FACILITIES,
3647 "Logging control\n"
3648 "Logging goes to syslog\n"
3649 "(Deprecated) Facility parameter for syslog messages\n"
3650 LOG_FACILITY_DESC)
3651{
3652 int facility;
3653
3654 if ((facility = facility_match(argv[0])) < 0)
3655 return CMD_ERR_NO_MATCH;
3656
3657 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3658 zlog_default->facility = facility;
3659 return CMD_SUCCESS;
3660}
3661
3662DEFUN(no_config_log_syslog,
3663 no_config_log_syslog_cmd,
3664 "no log syslog [LEVEL]",
3665 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3666{
3667 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3668 return CMD_SUCCESS;
3669}
3670
3671ALIAS(no_config_log_syslog,
3672 no_config_log_syslog_facility_cmd,
3673 "no log syslog facility " LOG_FACILITIES,
3674 NO_STR
3675 "Logging control\n"
3676 "Logging goes to syslog\n"
3677 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3678
3679 DEFUN(config_log_facility,
3680 config_log_facility_cmd,
3681 "log facility " LOG_FACILITIES,
3682 "Logging control\n"
3683 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3684{
3685 int facility;
3686
3687 if ((facility = facility_match(argv[0])) < 0)
3688 return CMD_ERR_NO_MATCH;
3689 zlog_default->facility = facility;
3690 return CMD_SUCCESS;
3691}
3692
3693DEFUN(no_config_log_facility,
3694 no_config_log_facility_cmd,
3695 "no log facility [FACILITY]",
3696 NO_STR
3697 "Logging control\n"
3698 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3699{
3700 zlog_default->facility = LOG_DAEMON;
3701 return CMD_SUCCESS;
3702}
3703
3704DEFUN_DEPRECATED(config_log_trap,
3705 config_log_trap_cmd,
3706 "log trap " LOG_LEVELS,
3707 "Logging control\n"
3708 "(Deprecated) Set logging level and default for all destinations\n"
3709 LOG_LEVEL_DESC)
3710{
3711 int new_level;
3712 int i;
3713
3714 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3715 return CMD_ERR_NO_MATCH;
3716
3717 zlog_default->default_lvl = new_level;
3718 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3719 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3720 zlog_default->maxlvl[i] = new_level;
3721 return CMD_SUCCESS;
3722}
3723
3724DEFUN_DEPRECATED(no_config_log_trap,
3725 no_config_log_trap_cmd,
3726 "no log trap [LEVEL]",
3727 NO_STR
3728 "Logging control\n"
3729 "Permit all logging information\n" "Logging level\n")
3730{
3731 zlog_default->default_lvl = LOG_DEBUG;
3732 return CMD_SUCCESS;
3733}
3734
3735DEFUN(config_log_record_priority,
3736 config_log_record_priority_cmd,
3737 "log record-priority",
3738 "Logging control\n"
3739 "Log the priority of the message within the message\n")
3740{
3741 zlog_default->record_priority = 1;
3742 return CMD_SUCCESS;
3743}
3744
3745DEFUN(no_config_log_record_priority,
3746 no_config_log_record_priority_cmd,
3747 "no log record-priority",
3748 NO_STR
3749 "Logging control\n"
3750 "Do not log the priority of the message within the message\n")
3751{
3752 zlog_default->record_priority = 0;
3753 return CMD_SUCCESS;
3754}
3755#endif
3756
3757DEFUN(banner_motd_file,
3758 banner_motd_file_cmd,
3759 "banner motd file [FILE]",
3760 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3761{
3762 if (host.motdfile)
3763 talloc_free(host.motdfile);
3764 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3765
3766 return CMD_SUCCESS;
3767}
3768
3769DEFUN(banner_motd_default,
3770 banner_motd_default_cmd,
3771 "banner motd default",
3772 "Set banner string\n" "Strings for motd\n" "Default string\n")
3773{
3774 host.motd = default_motd;
3775 return CMD_SUCCESS;
3776}
3777
3778DEFUN(no_banner_motd,
3779 no_banner_motd_cmd,
3780 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3781{
3782 host.motd = NULL;
3783 if (host.motdfile)
3784 talloc_free(host.motdfile);
3785 host.motdfile = NULL;
3786 return CMD_SUCCESS;
3787}
3788
3789/* Set config filename. Called from vty.c */
3790void host_config_set(const char *filename)
3791{
3792 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3793}
3794
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003795/*! Deprecated, now happens implicitly when calling install_node().
3796 * Users of the API may still attempt to call this function, hence
3797 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003798void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003799{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003800}
3801
3802/*! Deprecated, now happens implicitly when calling install_node().
3803 * Users of the API may still attempt to call this function, hence
3804 * leave it here as a no-op. */
3805void vty_install_default(int node)
3806{
3807}
3808
3809/*! Install common commands like 'exit' and 'list'. */
3810static void install_basic_node_commands(int node)
3811{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003812 install_element(node, &config_help_cmd);
3813 install_element(node, &config_list_cmd);
3814
3815 install_element(node, &config_write_terminal_cmd);
3816 install_element(node, &config_write_file_cmd);
3817 install_element(node, &config_write_memory_cmd);
3818 install_element(node, &config_write_cmd);
3819 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003820
3821 install_element(node, &config_exit_cmd);
3822
3823 if (node >= CONFIG_NODE) {
3824 /* It's not a top node. */
3825 install_element(node, &config_end_cmd);
3826 }
3827}
3828
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003829/*! Return true if a node is installed by install_basic_node_commands(), so
3830 * that we can avoid repeating them for each and every node during 'show
3831 * running-config' */
3832static bool vty_command_is_common(struct cmd_element *cmd)
3833{
3834 if (cmd == &config_help_cmd
3835 || cmd == &config_list_cmd
3836 || cmd == &config_write_terminal_cmd
3837 || cmd == &config_write_file_cmd
3838 || cmd == &config_write_memory_cmd
3839 || cmd == &config_write_cmd
3840 || cmd == &show_running_config_cmd
3841 || cmd == &config_exit_cmd
3842 || cmd == &config_end_cmd)
3843 return true;
3844 return false;
3845}
3846
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003847/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003848 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003849 * \param[in] vty the vty of the code
3850 * \param[in] filename where to store the file
3851 * \return 0 in case of success.
3852 *
3853 * If the filename already exists create a filename.sav
3854 * version with the current code.
3855 *
3856 */
3857int osmo_vty_write_config_file(const char *filename)
3858{
3859 char *failed_file;
3860 int rc;
3861
3862 rc = write_config_file(filename, &failed_file);
3863 talloc_free(failed_file);
3864 return rc;
3865}
3866
3867/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003868 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003869 * \return 0 in case of success.
3870 *
3871 * If the filename already exists create a filename.sav
3872 * version with the current code.
3873 *
3874 */
3875int osmo_vty_save_config_file(void)
3876{
3877 char *failed_file;
3878 int rc;
3879
3880 if (host.config == NULL)
3881 return -7;
3882
3883 rc = write_config_file(host.config, &failed_file);
3884 talloc_free(failed_file);
3885 return rc;
3886}
3887
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003888/* Initialize command interface. Install basic nodes and commands. */
3889void cmd_init(int terminal)
3890{
3891 /* Allocate initial top vector of commands. */
3892 cmdvec = vector_init(VECTOR_MIN_SIZE);
3893
3894 /* Default host value settings. */
3895 host.name = NULL;
3896 host.password = NULL;
3897 host.enable = NULL;
3898 host.logfile = NULL;
3899 host.config = NULL;
3900 host.lines = -1;
3901 host.motd = default_motd;
3902 host.motdfile = NULL;
3903
3904 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003905 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003906 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003907 install_node_bare(&auth_node, NULL);
3908 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003909 install_node(&config_node, config_write_host);
3910
3911 /* Each node's basic commands. */
3912 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003913 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003914 if (terminal) {
3915 install_element(VIEW_NODE, &config_list_cmd);
3916 install_element(VIEW_NODE, &config_exit_cmd);
3917 install_element(VIEW_NODE, &config_help_cmd);
3918 install_element(VIEW_NODE, &config_enable_cmd);
3919 install_element(VIEW_NODE, &config_terminal_length_cmd);
3920 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3921 install_element(VIEW_NODE, &echo_cmd);
3922 }
3923
3924 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003925 install_element(ENABLE_NODE, &config_disable_cmd);
3926 install_element(ENABLE_NODE, &config_terminal_cmd);
3927 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3928 }
3929 install_element (ENABLE_NODE, &show_startup_config_cmd);
3930 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003931 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003932
3933 if (terminal) {
3934 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3935 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3936 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003937 }
3938
3939 install_element(CONFIG_NODE, &hostname_cmd);
3940 install_element(CONFIG_NODE, &no_hostname_cmd);
3941
3942 if (terminal) {
3943 install_element(CONFIG_NODE, &password_cmd);
3944 install_element(CONFIG_NODE, &password_text_cmd);
3945 install_element(CONFIG_NODE, &enable_password_cmd);
3946 install_element(CONFIG_NODE, &enable_password_text_cmd);
3947 install_element(CONFIG_NODE, &no_enable_password_cmd);
3948
3949#ifdef VTY_CRYPT_PW
3950 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3951 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3952#endif
3953 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3954 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3955 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3956 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3957 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3958
3959 }
3960 srand(time(NULL));
3961}
Harald Welte7acb30c2011-08-17 17:13:48 +02003962
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003963/*! @} */