blob: b26e991935d08613b97929a0e9e61db17dd97606 [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
Harald Weltee881b1b2011-08-17 18:52:30 +020050/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020051 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020052 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020053 *
54 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020055
Harald Welte3fb0b6f2010-05-19 19:02:52 +020056#define CONFIGFILE_MASK 022
57
58void *tall_vty_cmd_ctx;
59
60/* Command vector which includes some level of command lists. Normally
61 each daemon maintains each own cmdvec. */
62vector cmdvec;
63
64/* Host information structure. */
65struct host host;
66
67/* Standard command node structures. */
68struct cmd_node auth_node = {
69 AUTH_NODE,
70 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010071 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020072};
73
74struct cmd_node view_node = {
75 VIEW_NODE,
76 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node auth_enable_node = {
81 AUTH_ENABLE_NODE,
82 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node enable_node = {
87 ENABLE_NODE,
88 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node config_node = {
93 CONFIG_NODE,
94 "%s(config)# ",
95 1
96};
97
98/* Default motd string. */
99const char *default_motd = "";
100
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200101/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200102 *
103 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200104void print_version(int print_copyright)
105{
Harald Welte237f6242010-05-25 23:00:45 +0200106 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200107 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200108 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200109}
110
111/* Utility function to concatenate argv argument into a single string
112 with inserting ' ' character between each argument. */
113char *argv_concat(const char **argv, int argc, int shift)
114{
115 int i;
116 size_t len;
117 char *str;
118 char *p;
119
120 len = 0;
121 for (i = shift; i < argc; i++)
122 len += strlen(argv[i]) + 1;
123 if (!len)
124 return NULL;
125 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
126 for (i = shift; i < argc; i++) {
127 size_t arglen;
128 memcpy(p, argv[i], (arglen = strlen(argv[i])));
129 p += arglen;
130 *p++ = ' ';
131 }
132 *(p - 1) = '\0';
133 return str;
134}
135
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200136/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
137 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
138 * in turn, this name us used for XML IDs in 'show online-help'. */
139static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
140{
141 const char *pos;
142 int dest = 0;
143
144 if (!prompt || !*prompt)
145 return "";
146
147 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
148 if (pos[0] == '%' && pos[1]) {
149 /* skip "%s"; loop pos++ does the second one. */
150 pos++;
151 continue;
152 }
153 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
154 continue;
155 name_buf[dest] = pos[0];
156 dest++;
157 }
158 name_buf[dest] = '\0';
159 return name_buf;
160}
161
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200162static void install_basic_node_commands(int node);
163
164/*! Install top node of command vector, without adding basic node commands. */
165static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200166{
167 vector_set_index(cmdvec, node->node, node);
168 node->func = func;
169 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200170 if (!*node->name)
171 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172}
173
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200174/*! Install top node of command vector. */
175void install_node(struct cmd_node *node, int (*func) (struct vty *))
176{
177 install_node_bare(node, func);
178 install_basic_node_commands(node->node);
179}
180
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200181/* Compare two command's string. Used in sort_node (). */
182static int cmp_node(const void *p, const void *q)
183{
184 struct cmd_element *a = *(struct cmd_element **)p;
185 struct cmd_element *b = *(struct cmd_element **)q;
186
187 return strcmp(a->string, b->string);
188}
189
190static int cmp_desc(const void *p, const void *q)
191{
192 struct desc *a = *(struct desc **)p;
193 struct desc *b = *(struct desc **)q;
194
195 return strcmp(a->cmd, b->cmd);
196}
197
Jacob Erlbeck2442e092013-09-06 16:51:58 +0200198static int is_config_child(struct vty *vty)
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800199{
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800200 if (vty->node <= CONFIG_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800201 return 0;
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800202 else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800203 return 1;
204 else if (host.app_info->is_config_node)
Holger Hans Peter Freyther8f09f012010-08-25 17:34:56 +0800205 return host.app_info->is_config_node(vty, vty->node);
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800206 else
207 return vty->node > CONFIG_NODE;
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200211void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200212{
213 unsigned int i, j;
214 struct cmd_node *cnode;
215 vector descvec;
216 struct cmd_element *cmd_element;
217
218 for (i = 0; i < vector_active(cmdvec); i++)
219 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
220 vector cmd_vector = cnode->cmd_vector;
221 qsort(cmd_vector->index, vector_active(cmd_vector),
222 sizeof(void *), cmp_node);
223
224 for (j = 0; j < vector_active(cmd_vector); j++)
225 if ((cmd_element =
226 vector_slot(cmd_vector, j)) != NULL
227 && vector_active(cmd_element->strvec)) {
228 descvec =
229 vector_slot(cmd_element->strvec,
230 vector_active
231 (cmd_element->strvec) -
232 1);
233 qsort(descvec->index,
234 vector_active(descvec),
235 sizeof(void *), cmp_desc);
236 }
237 }
238}
239
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200240/*! Break up string in command tokens. Return leading indents.
241 * \param[in] string String to split.
242 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
243 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
244 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
245 *
246 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
247 * so that \a indent can simply return the count of leading spaces.
248 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
249 */
250int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200251{
252 const char *cp, *start;
253 char *token;
254 int strlen;
255 vector strvec;
256
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200257 *strvec_p = NULL;
258 if (indent)
259 *indent = 0;
260
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200262 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200263
264 cp = string;
265
266 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200267 while (isspace((int)*cp) && *cp != '\0') {
268 /* if we're counting indents, we need to be strict about them */
269 if (indent && (*cp != ' ') && (*cp != '\t')) {
270 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
271 if (*cp == '\n' || *cp == '\r') {
272 cp++;
273 string = cp;
274 continue;
275 }
276 return CMD_ERR_INVALID_INDENT;
277 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200278 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200279 }
280
281 if (indent)
282 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200283
284 /* Return if there is only white spaces */
285 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200286 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200287
288 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200289 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200290
291 /* Prepare return vector. */
292 strvec = vector_init(VECTOR_MIN_SIZE);
293
294 /* Copy each command piece and set into vector. */
295 while (1) {
296 start = cp;
297 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
298 *cp != '\0')
299 cp++;
300 strlen = cp - start;
301 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
302 memcpy(token, start, strlen);
303 *(token + strlen) = '\0';
304 vector_set(strvec, token);
305
306 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
307 *cp != '\0')
308 cp++;
309
310 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200311 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200312 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200313
314 *strvec_p = strvec;
315 return CMD_SUCCESS;
316}
317
318/*! Breaking up string into each command piece. I assume given
319 character is separated by a space character. Return value is a
320 vector which includes char ** data element. */
321vector cmd_make_strvec(const char *string)
322{
323 vector strvec;
324 cmd_make_strvec2(string, NULL, &strvec);
325 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200326}
327
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200328/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200329void cmd_free_strvec(vector v)
330{
331 unsigned int i;
332 char *cp;
333
334 if (!v)
335 return;
336
337 for (i = 0; i < vector_active(v); i++)
338 if ((cp = vector_slot(v, i)) != NULL)
339 talloc_free(cp);
340
341 vector_free(v);
342}
343
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200344/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200345static char *cmd_desc_str(const char **string)
346{
347 const char *cp, *start;
348 char *token;
349 int strlen;
350
351 cp = *string;
352
353 if (cp == NULL)
354 return NULL;
355
356 /* Skip white spaces. */
357 while (isspace((int)*cp) && *cp != '\0')
358 cp++;
359
360 /* Return if there is only white spaces */
361 if (*cp == '\0')
362 return NULL;
363
364 start = cp;
365
366 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
367 cp++;
368
369 strlen = cp - start;
370 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
371 memcpy(token, start, strlen);
372 *(token + strlen) = '\0';
373
374 *string = cp;
375
376 return token;
377}
378
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200379/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200380static vector cmd_make_descvec(const char *string, const char *descstr)
381{
382 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100383 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200384 const char *sp;
385 char *token;
386 int len;
387 const char *cp;
388 const char *dp;
389 vector allvec;
390 vector strvec = NULL;
391 struct desc *desc;
392
393 cp = string;
394 dp = descstr;
395
396 if (cp == NULL)
397 return NULL;
398
399 allvec = vector_init(VECTOR_MIN_SIZE);
400
401 while (1) {
402 while (isspace((int)*cp) && *cp != '\0')
403 cp++;
404
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100405 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
406 if (cp[0] == '[' && cp[1] == '(') {
407 optional_brace = 1;
408 cp++;
409 }
410
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200411 if (*cp == '(') {
412 multiple = 1;
413 cp++;
414 }
415 if (*cp == ')') {
416 multiple = 0;
417 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100418 if (*cp == ']')
419 cp++;
420 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200421 }
422 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100423 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200424 cp++;
425 }
426
427 while (isspace((int)*cp) && *cp != '\0')
428 cp++;
429
430 if (*cp == '(') {
431 multiple = 1;
432 cp++;
433 }
434
435 if (*cp == '\0')
436 return allvec;
437
438 sp = cp;
439
440 while (!
441 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
442 || *cp == ')' || *cp == '|') && *cp != '\0')
443 cp++;
444
445 len = cp - sp;
446
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100447 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
448 if (optional_brace) {
449 /* Place each individual multi-choice token in its own square braces */
450 token[0] = '[';
451 memcpy(token + 1, sp, len);
452 token[1 + len] = ']';
453 token[2 + len] = '\0';
454 } else {
455 memcpy(token, sp, len);
456 *(token + len) = '\0';
457 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200458
459 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
460 desc->cmd = token;
461 desc->str = cmd_desc_str(&dp);
462
463 if (multiple) {
464 if (multiple == 1) {
465 strvec = vector_init(VECTOR_MIN_SIZE);
466 vector_set(allvec, strvec);
467 }
468 multiple++;
469 } else {
470 strvec = vector_init(VECTOR_MIN_SIZE);
471 vector_set(allvec, strvec);
472 }
473 vector_set(strvec, desc);
474 }
475}
476
477/* Count mandantory string vector size. This is to determine inputed
478 command has enough command length. */
479static int cmd_cmdsize(vector strvec)
480{
481 unsigned int i;
482 int size = 0;
483 vector descvec;
484 struct desc *desc;
485
486 for (i = 0; i < vector_active(strvec); i++)
487 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100488 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200489 && (desc = vector_slot(descvec, 0)) != NULL) {
490 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
491 return size;
492 else
493 size++;
494 } else
495 size++;
496 }
497 return size;
498}
499
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200500/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200501const char *cmd_prompt(enum node_type node)
502{
503 struct cmd_node *cnode;
504
505 cnode = vector_slot(cmdvec, node);
506 return cnode->prompt;
507}
508
Alexander Couzensad580ba2016-05-16 16:01:45 +0200509/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200510 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200511 * \param unsafe string
512 * \return a new talloc char *
513 */
514char *osmo_asciidoc_escape(const char *inp)
515{
516 int _strlen;
517 char *out, *out_ptr;
518 int len = 0, i, j;
519
520 if (!inp)
521 return NULL;
522 _strlen = strlen(inp);
523
524 for (i = 0; i < _strlen; ++i) {
525 switch (inp[i]) {
526 case '|':
527 len += 2;
528 break;
529 default:
530 len += 1;
531 break;
532 }
533 }
534
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200535 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200536 if (!out)
537 return NULL;
538
539 out_ptr = out;
540
541#define ADD(out, str) \
542 for (j = 0; j < strlen(str); ++j) \
543 *(out++) = str[j];
544
545 for (i = 0; i < _strlen; ++i) {
546 switch (inp[i]) {
547 case '|':
548 ADD(out_ptr, "\\|");
549 break;
550 default:
551 *(out_ptr++) = inp[i];
552 break;
553 }
554 }
555
556#undef ADD
557
558 out_ptr[0] = '\0';
559 return out;
560}
561
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100562static char *xml_escape(const char *inp)
563{
564 int _strlen;
565 char *out, *out_ptr;
566 int len = 0, i, j;
567
568 if (!inp)
569 return NULL;
570 _strlen = strlen(inp);
571
572 for (i = 0; i < _strlen; ++i) {
573 switch (inp[i]) {
574 case '"':
575 len += 6;
576 break;
577 case '\'':
578 len += 6;
579 break;
580 case '<':
581 len += 4;
582 break;
583 case '>':
584 len += 4;
585 break;
586 case '&':
587 len += 5;
588 break;
589 default:
590 len += 1;
591 break;
592 }
593 }
594
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200595 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100596 if (!out)
597 return NULL;
598
599 out_ptr = out;
600
601#define ADD(out, str) \
602 for (j = 0; j < strlen(str); ++j) \
603 *(out++) = str[j];
604
605 for (i = 0; i < _strlen; ++i) {
606 switch (inp[i]) {
607 case '"':
608 ADD(out_ptr, "&quot;");
609 break;
610 case '\'':
611 ADD(out_ptr, "&apos;");
612 break;
613 case '<':
614 ADD(out_ptr, "&lt;");
615 break;
616 case '>':
617 ADD(out_ptr, "&gt;");
618 break;
619 case '&':
620 ADD(out_ptr, "&amp;");
621 break;
622 default:
623 *(out_ptr++) = inp[i];
624 break;
625 }
626 }
627
628#undef ADD
629
630 out_ptr[0] = '\0';
631 return out;
632}
633
634/*
635 * Write one cmd_element as XML to the given VTY.
636 */
637static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
638{
639 char *xml_string = xml_escape(cmd->string);
640
641 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
642 vty_out(vty, " <params>%s", VTY_NEWLINE);
643
644 int j;
645 for (j = 0; j < vector_count(cmd->strvec); ++j) {
646 vector descvec = vector_slot(cmd->strvec, j);
647 int i;
648 for (i = 0; i < vector_active(descvec); ++i) {
649 char *xml_param, *xml_doc;
650 struct desc *desc = vector_slot(descvec, i);
651 if (desc == NULL)
652 continue;
653
654 xml_param = xml_escape(desc->cmd);
655 xml_doc = xml_escape(desc->str);
656 vty_out(vty, " <param name='%s' doc='%s' />%s",
657 xml_param, xml_doc, VTY_NEWLINE);
658 talloc_free(xml_param);
659 talloc_free(xml_doc);
660 }
661 }
662
663 vty_out(vty, " </params>%s", VTY_NEWLINE);
664 vty_out(vty, " </command>%s", VTY_NEWLINE);
665
666 talloc_free(xml_string);
667 return 0;
668}
669
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200670static bool vty_command_is_common(struct cmd_element *cmd);
671
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100672/*
673 * Dump all nodes and commands associated with a given node as XML to the VTY.
674 */
675static int vty_dump_nodes(struct vty *vty)
676{
677 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200678 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100679
680 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
681
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200682 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
683 vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
684 vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
685 vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
686 " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
687 for (i = 0; i < vector_active(cmdvec); ++i) {
688 struct cmd_node *cnode;
689 cnode = vector_slot(cmdvec, i);
690 if (!cnode)
691 continue;
692 if (cnode->node != CONFIG_NODE)
693 continue;
694
695 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
696 struct cmd_element *elem;
697 elem = vector_slot(cnode->cmd_vector, j);
698 if (!vty_command_is_common(elem))
699 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200700 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200701 vty_dump_element(elem, vty);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200702 }
703 }
704 vty_out(vty, " </node>%s", VTY_NEWLINE);
705
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100706 for (i = 0; i < vector_active(cmdvec); ++i) {
707 struct cmd_node *cnode;
708 cnode = vector_slot(cmdvec, i);
709 if (!cnode)
710 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200711 if (vector_active(cnode->cmd_vector) < 1)
712 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100713
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200714 /* De-dup node IDs: how many times has this same name been used before? Count the first
715 * occurence as _1 and omit that first suffix, so that the first occurence is called
716 * 'name', the second becomes 'name_2', then 'name_3', ... */
717 same_name_count = 1;
718 for (j = 0; j < i; ++j) {
719 struct cmd_node *cnode2;
720 cnode2 = vector_slot(cmdvec, j);
721 if (!cnode2)
722 continue;
723 if (strcmp(cnode->name, cnode2->name) == 0)
724 same_name_count ++;
725 }
726
727 vty_out(vty, " <node id='%s", cnode->name);
728 if (same_name_count > 1 || !*cnode->name)
729 vty_out(vty, "_%d", same_name_count);
730 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200731 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100732
733 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
734 struct cmd_element *elem;
735 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200736 if (vty_command_is_common(elem))
737 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200738 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200739 vty_dump_element(elem, vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100740 }
741
742 vty_out(vty, " </node>%s", VTY_NEWLINE);
743 }
744
745 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
746
747 return 0;
748}
749
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200750/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100751static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
752{
753 int i;
754
755 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
756 struct cmd_element *elem;
757 elem = vector_slot(cnode->cmd_vector, i);
758 if (!elem->string)
759 continue;
760 if (!strcmp(elem->string, cmdstring))
761 return 1;
762 }
763 return 0;
764}
765
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200766/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200767 * \param[in] ntype Node Type
768 * \param[cmd] element to be installed
769 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000770void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200771{
772 struct cmd_node *cnode;
773
774 cnode = vector_slot(cmdvec, ntype);
775
Harald Weltea99d45a2015-11-12 13:48:23 +0100776 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100777 /* ensure no _identical_ command has been registered at this
778 * node so far */
779 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200780
781 vector_set(cnode->cmd_vector, cmd);
782
783 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
784 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
785}
786
787/* Install a command into VIEW and ENABLE node */
788void install_element_ve(struct cmd_element *cmd)
789{
790 install_element(VIEW_NODE, cmd);
791 install_element(ENABLE_NODE, cmd);
792}
793
794#ifdef VTY_CRYPT_PW
795static unsigned char itoa64[] =
796 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
797
798static void to64(char *s, long v, int n)
799{
800 while (--n >= 0) {
801 *s++ = itoa64[v & 0x3f];
802 v >>= 6;
803 }
804}
805
806static char *zencrypt(const char *passwd)
807{
808 char salt[6];
809 struct timeval tv;
810 char *crypt(const char *, const char *);
811
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200812 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200813
814 to64(&salt[0], random(), 3);
815 to64(&salt[3], tv.tv_usec, 3);
816 salt[5] = '\0';
817
818 return crypt(passwd, salt);
819}
820#endif
821
822/* This function write configuration of this host. */
823static int config_write_host(struct vty *vty)
824{
825 if (host.name)
826 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
827
828 if (host.encrypt) {
829 if (host.password_encrypt)
830 vty_out(vty, "password 8 %s%s", host.password_encrypt,
831 VTY_NEWLINE);
832 if (host.enable_encrypt)
833 vty_out(vty, "enable password 8 %s%s",
834 host.enable_encrypt, VTY_NEWLINE);
835 } else {
836 if (host.password)
837 vty_out(vty, "password %s%s", host.password,
838 VTY_NEWLINE);
839 if (host.enable)
840 vty_out(vty, "enable password %s%s", host.enable,
841 VTY_NEWLINE);
842 }
843
844 if (host.advanced)
845 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
846
847 if (host.encrypt)
848 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
849
850 if (host.lines >= 0)
851 vty_out(vty, "service terminal-length %d%s", host.lines,
852 VTY_NEWLINE);
853
854 if (host.motdfile)
855 vty_out(vty, "banner motd file %s%s", host.motdfile,
856 VTY_NEWLINE);
857 else if (!host.motd)
858 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
859
860 return 1;
861}
862
863/* Utility function for getting command vector. */
864static vector cmd_node_vector(vector v, enum node_type ntype)
865{
866 struct cmd_node *cnode = vector_slot(v, ntype);
867 return cnode->cmd_vector;
868}
869
870/* Completion match types. */
871enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200872 NO_MATCH = 0,
873 ANY_MATCH,
874 EXTEND_MATCH,
875 IPV4_PREFIX_MATCH,
876 IPV4_MATCH,
877 IPV6_PREFIX_MATCH,
878 IPV6_MATCH,
879 RANGE_MATCH,
880 VARARG_MATCH,
881 PARTLY_MATCH,
882 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200883};
884
885static enum match_type cmd_ipv4_match(const char *str)
886{
887 const char *sp;
888 int dots = 0, nums = 0;
889 char buf[4];
890
891 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200892 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200893
894 for (;;) {
895 memset(buf, 0, sizeof(buf));
896 sp = str;
897 while (*str != '\0') {
898 if (*str == '.') {
899 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200900 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200901
902 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200903 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200904
905 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200906 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200907
908 dots++;
909 break;
910 }
911 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200912 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200913
914 str++;
915 }
916
917 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200918 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200919
920 strncpy(buf, sp, str - sp);
921 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200922 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200923
924 nums++;
925
926 if (*str == '\0')
927 break;
928
929 str++;
930 }
931
932 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200933 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200934
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200935 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200936}
937
938static enum match_type cmd_ipv4_prefix_match(const char *str)
939{
940 const char *sp;
941 int dots = 0;
942 char buf[4];
943
944 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200945 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200946
947 for (;;) {
948 memset(buf, 0, sizeof(buf));
949 sp = str;
950 while (*str != '\0' && *str != '/') {
951 if (*str == '.') {
952 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200953 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200954
955 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200956 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200957
958 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200959 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200960
961 dots++;
962 break;
963 }
964
965 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200966 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200967
968 str++;
969 }
970
971 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200972 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200973
974 strncpy(buf, sp, str - sp);
975 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200976 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200977
978 if (dots == 3) {
979 if (*str == '/') {
980 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200981 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200982
983 str++;
984 break;
985 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200986 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200987 }
988
989 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200990 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200991
992 str++;
993 }
994
995 sp = str;
996 while (*str != '\0') {
997 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200998 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200999
1000 str++;
1001 }
1002
1003 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001004 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001005
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001006 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001007}
1008
1009#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1010#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1011#define STATE_START 1
1012#define STATE_COLON 2
1013#define STATE_DOUBLE 3
1014#define STATE_ADDR 4
1015#define STATE_DOT 5
1016#define STATE_SLASH 6
1017#define STATE_MASK 7
1018
1019#ifdef HAVE_IPV6
1020
1021static enum match_type cmd_ipv6_match(const char *str)
1022{
1023 int state = STATE_START;
1024 int colons = 0, nums = 0, double_colon = 0;
1025 const char *sp = NULL;
1026 struct sockaddr_in6 sin6_dummy;
1027 int ret;
1028
1029 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001030 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001031
1032 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001033 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001034
1035 /* use inet_pton that has a better support,
1036 * for example inet_pton can support the automatic addresses:
1037 * ::1.2.3.4
1038 */
1039 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1040
1041 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001042 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001043
1044 while (*str != '\0') {
1045 switch (state) {
1046 case STATE_START:
1047 if (*str == ':') {
1048 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001049 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001050 colons--;
1051 state = STATE_COLON;
1052 } else {
1053 sp = str;
1054 state = STATE_ADDR;
1055 }
1056
1057 continue;
1058 case STATE_COLON:
1059 colons++;
1060 if (*(str + 1) == ':')
1061 state = STATE_DOUBLE;
1062 else {
1063 sp = str + 1;
1064 state = STATE_ADDR;
1065 }
1066 break;
1067 case STATE_DOUBLE:
1068 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001069 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001070
1071 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001072 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001073 else {
1074 if (*(str + 1) != '\0')
1075 colons++;
1076 sp = str + 1;
1077 state = STATE_ADDR;
1078 }
1079
1080 double_colon++;
1081 nums++;
1082 break;
1083 case STATE_ADDR:
1084 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1085 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001086 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001087
1088 nums++;
1089 state = STATE_COLON;
1090 }
1091 if (*(str + 1) == '.')
1092 state = STATE_DOT;
1093 break;
1094 case STATE_DOT:
1095 state = STATE_ADDR;
1096 break;
1097 default:
1098 break;
1099 }
1100
1101 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001102 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001103
1104 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001105 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001106
1107 str++;
1108 }
1109
1110#if 0
1111 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001112 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001113#endif /* 0 */
1114
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001115 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116}
1117
1118static enum match_type cmd_ipv6_prefix_match(const char *str)
1119{
1120 int state = STATE_START;
1121 int colons = 0, nums = 0, double_colon = 0;
1122 int mask;
1123 const char *sp = NULL;
1124 char *endptr = NULL;
1125
1126 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001127 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001128
1129 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001130 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001131
1132 while (*str != '\0' && state != STATE_MASK) {
1133 switch (state) {
1134 case STATE_START:
1135 if (*str == ':') {
1136 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001137 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001138 colons--;
1139 state = STATE_COLON;
1140 } else {
1141 sp = str;
1142 state = STATE_ADDR;
1143 }
1144
1145 continue;
1146 case STATE_COLON:
1147 colons++;
1148 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001149 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001150 else if (*(str + 1) == ':')
1151 state = STATE_DOUBLE;
1152 else {
1153 sp = str + 1;
1154 state = STATE_ADDR;
1155 }
1156 break;
1157 case STATE_DOUBLE:
1158 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001159 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001160
1161 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001162 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001163 else {
1164 if (*(str + 1) != '\0' && *(str + 1) != '/')
1165 colons++;
1166 sp = str + 1;
1167
1168 if (*(str + 1) == '/')
1169 state = STATE_SLASH;
1170 else
1171 state = STATE_ADDR;
1172 }
1173
1174 double_colon++;
1175 nums += 1;
1176 break;
1177 case STATE_ADDR:
1178 if (*(str + 1) == ':' || *(str + 1) == '.'
1179 || *(str + 1) == '\0' || *(str + 1) == '/') {
1180 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001181 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001182
1183 for (; sp <= str; sp++)
1184 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001185 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186
1187 nums++;
1188
1189 if (*(str + 1) == ':')
1190 state = STATE_COLON;
1191 else if (*(str + 1) == '.')
1192 state = STATE_DOT;
1193 else if (*(str + 1) == '/')
1194 state = STATE_SLASH;
1195 }
1196 break;
1197 case STATE_DOT:
1198 state = STATE_ADDR;
1199 break;
1200 case STATE_SLASH:
1201 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001202 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001203
1204 state = STATE_MASK;
1205 break;
1206 default:
1207 break;
1208 }
1209
1210 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001211 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001212
1213 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001214 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001215
1216 str++;
1217 }
1218
1219 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001220 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001221
1222 mask = strtol(str, &endptr, 10);
1223 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001224 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001225
1226 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001227 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001228
1229/* I don't know why mask < 13 makes command match partly.
1230 Forgive me to make this comments. I Want to set static default route
1231 because of lack of function to originate default in ospf6d; sorry
1232 yasu
1233 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001234 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235*/
1236
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001237 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001238}
1239
1240#endif /* HAVE_IPV6 */
1241
1242#define DECIMAL_STRLEN_MAX 10
1243
1244static int cmd_range_match(const char *range, const char *str)
1245{
1246 char *p;
1247 char buf[DECIMAL_STRLEN_MAX + 1];
1248 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001249
1250 if (str == NULL)
1251 return 1;
1252
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001253 if (range[1] == '-') {
1254 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001255
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001256 val = strtol(str, &endptr, 10);
1257 if (*endptr != '\0')
1258 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001260 range += 2;
1261 p = strchr(range, '-');
1262 if (p == NULL)
1263 return 0;
1264 if (p - range > DECIMAL_STRLEN_MAX)
1265 return 0;
1266 strncpy(buf, range, p - range);
1267 buf[p - range] = '\0';
1268 min = -strtol(buf, &endptr, 10);
1269 if (*endptr != '\0')
1270 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001271
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001272 range = p + 1;
1273 p = strchr(range, '>');
1274 if (p == NULL)
1275 return 0;
1276 if (p - range > DECIMAL_STRLEN_MAX)
1277 return 0;
1278 strncpy(buf, range, p - range);
1279 buf[p - range] = '\0';
1280 max = strtol(buf, &endptr, 10);
1281 if (*endptr != '\0')
1282 return 0;
1283
1284 if (val < min || val > max)
1285 return 0;
1286 } else {
1287 unsigned long min, max, val;
1288
1289 val = strtoul(str, &endptr, 10);
1290 if (*endptr != '\0')
1291 return 0;
1292
1293 range++;
1294 p = strchr(range, '-');
1295 if (p == NULL)
1296 return 0;
1297 if (p - range > DECIMAL_STRLEN_MAX)
1298 return 0;
1299 strncpy(buf, range, p - range);
1300 buf[p - range] = '\0';
1301 min = strtoul(buf, &endptr, 10);
1302 if (*endptr != '\0')
1303 return 0;
1304
1305 range = p + 1;
1306 p = strchr(range, '>');
1307 if (p == NULL)
1308 return 0;
1309 if (p - range > DECIMAL_STRLEN_MAX)
1310 return 0;
1311 strncpy(buf, range, p - range);
1312 buf[p - range] = '\0';
1313 max = strtoul(buf, &endptr, 10);
1314 if (*endptr != '\0')
1315 return 0;
1316
1317 if (val < min || val > max)
1318 return 0;
1319 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001320
1321 return 1;
1322}
1323
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001324/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001325static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001326{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001327 /* we've got "[blah]". We want to strip off the []s and redo the
1328 * match check for "blah"
1329 */
1330 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001332 if (len < 3)
1333 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001334
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001335 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001336}
1337
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001338static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001339cmd_match(const char *str, const char *command,
1340 enum match_type min, bool recur)
1341{
1342
1343 if (recur && CMD_OPTION(str))
1344 {
1345 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001346 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001347
1348 /* this would be a bug in a command, however handle it gracefully
1349 * as it we only discover it if a user tries to run it
1350 */
1351 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001352 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001353
1354 ret = cmd_match(tmp, command, min, false);
1355
1356 talloc_free(tmp);
1357
1358 return ret;
1359 }
1360 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001361 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001362 else if (CMD_RANGE(str))
1363 {
1364 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001365 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001366 }
1367#ifdef HAVE_IPV6
1368 else if (CMD_IPV6(str))
1369 {
1370 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001371 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001372 }
1373 else if (CMD_IPV6_PREFIX(str))
1374 {
1375 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001376 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001377 }
1378#endif /* HAVE_IPV6 */
1379 else if (CMD_IPV4(str))
1380 {
1381 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001382 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001383 }
1384 else if (CMD_IPV4_PREFIX(str))
1385 {
1386 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001387 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001388 }
1389 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001390 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001391 else if (strncmp(command, str, strlen(command)) == 0)
1392 {
1393 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001394 return EXACT_MATCH;
1395 else if (PARTLY_MATCH >= min)
1396 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001397 }
1398
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001399 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001400}
1401
1402/* Filter vector at the specified index and by the given command string, to
1403 * the desired matching level (thus allowing part matches), and return match
1404 * type flag.
1405 */
1406static enum match_type
1407cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001408{
1409 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001410 struct cmd_element *cmd_element;
1411 enum match_type match_type;
1412 vector descvec;
1413 struct desc *desc;
1414
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001415 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001416
1417 /* If command and cmd_element string does not match set NULL to vector */
1418 for (i = 0; i < vector_active(v); i++)
1419 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001420 if (index >= vector_active(cmd_element->strvec))
1421 vector_slot(v, i) = NULL;
1422 else {
1423 unsigned int j;
1424 int matched = 0;
1425
1426 descvec =
1427 vector_slot(cmd_element->strvec, index);
1428
1429 for (j = 0; j < vector_active(descvec); j++)
1430 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001431 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001432
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001433 ret = cmd_match (desc->cmd, command, level, true);
1434
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001435 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001436 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001437
1438 if (match_type < ret)
1439 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001440 }
1441 if (!matched)
1442 vector_slot(v, i) = NULL;
1443 }
1444 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001445
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001446 if (match_type == NO_MATCH)
1447 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001448
1449 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1450 * go again and filter out commands whose argument (at this index) is
1451 * 'weaker'. E.g., if we have 2 commands:
1452 *
1453 * foo bar <1-255>
1454 * foo bar BLAH
1455 *
1456 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001457 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001458 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1459 *
1460 * If we don't do a 2nd pass and filter it out, the higher-layers will
1461 * consider this to be ambiguous.
1462 */
1463 for (i = 0; i < vector_active(v); i++)
1464 if ((cmd_element = vector_slot(v, i)) != NULL) {
1465 if (index >= vector_active(cmd_element->strvec))
1466 vector_slot(v, i) = NULL;
1467 else {
1468 unsigned int j;
1469 int matched = 0;
1470
1471 descvec =
1472 vector_slot(cmd_element->strvec, index);
1473
1474 for (j = 0; j < vector_active(descvec); j++)
1475 if ((desc = vector_slot(descvec, j))) {
1476 enum match_type ret;
1477
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001478 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001479
1480 if (ret >= match_type)
1481 matched++;
1482 }
1483 if (!matched)
1484 vector_slot(v, i) = NULL;
1485 }
1486 }
1487
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001488 return match_type;
1489}
1490
1491/* Check ambiguous match */
1492static int
1493is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1494{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001495 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001496 unsigned int i;
1497 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001498 struct cmd_element *cmd_element;
1499 const char *matched = NULL;
1500 vector descvec;
1501 struct desc *desc;
1502
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001503 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1504 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1505 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1506 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1507 * that case, the string must remain allocated until this function exits or another match comes
1508 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1509 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1510 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1511 void *cmd_deopt_ctx = NULL;
1512
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001513 for (i = 0; i < vector_active(v); i++) {
1514 cmd_element = vector_slot(v, i);
1515 if (!cmd_element)
1516 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001517
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001518 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001519
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001520 descvec = vector_slot(cmd_element->strvec, index);
1521
1522 for (j = 0; j < vector_active(descvec); j++) {
1523 desc = vector_slot(descvec, j);
1524 if (!desc)
1525 continue;
1526
1527 enum match_type mtype;
1528 const char *str = desc->cmd;
1529
1530 if (CMD_OPTION(str)) {
1531 if (!cmd_deopt_ctx)
1532 cmd_deopt_ctx =
1533 talloc_named_const(tall_vty_cmd_ctx, 0,
1534 __func__);
1535 str = cmd_deopt(cmd_deopt_ctx, str);
1536 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001537 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001538 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001539
1540 switch (type) {
1541 case EXACT_MATCH:
1542 if (!(CMD_VARIABLE (str))
1543 && strcmp(command, str) == 0)
1544 match++;
1545 break;
1546 case PARTLY_MATCH:
1547 if (!(CMD_VARIABLE (str))
1548 && strncmp(command, str, strlen (command)) == 0)
1549 {
1550 if (matched
1551 && strcmp(matched,
1552 str) != 0) {
1553 ret = 1; /* There is ambiguous match. */
1554 goto free_and_return;
1555 } else
1556 matched = str;
1557 match++;
1558 }
1559 break;
1560 case RANGE_MATCH:
1561 if (cmd_range_match
1562 (str, command)) {
1563 if (matched
1564 && strcmp(matched,
1565 str) != 0) {
1566 ret = 1;
1567 goto free_and_return;
1568 } else
1569 matched = str;
1570 match++;
1571 }
1572 break;
1573#ifdef HAVE_IPV6
1574 case IPV6_MATCH:
1575 if (CMD_IPV6(str))
1576 match++;
1577 break;
1578 case IPV6_PREFIX_MATCH:
1579 if ((mtype =
1580 cmd_ipv6_prefix_match
1581 (command)) != NO_MATCH) {
1582 if (mtype == PARTLY_MATCH) {
1583 ret = 2; /* There is incomplete match. */
1584 goto free_and_return;
1585 }
1586
1587 match++;
1588 }
1589 break;
1590#endif /* HAVE_IPV6 */
1591 case IPV4_MATCH:
1592 if (CMD_IPV4(str))
1593 match++;
1594 break;
1595 case IPV4_PREFIX_MATCH:
1596 if ((mtype =
1597 cmd_ipv4_prefix_match
1598 (command)) != NO_MATCH) {
1599 if (mtype == PARTLY_MATCH) {
1600 ret = 2; /* There is incomplete match. */
1601 goto free_and_return;
1602 }
1603
1604 match++;
1605 }
1606 break;
1607 case EXTEND_MATCH:
1608 if (CMD_VARIABLE (str))
1609 match++;
1610 break;
1611 case NO_MATCH:
1612 default:
1613 break;
1614 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001615 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001616 if (!match)
1617 vector_slot(v, i) = NULL;
1618 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001619
1620free_and_return:
1621 if (cmd_deopt_ctx)
1622 talloc_free(cmd_deopt_ctx);
1623 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001624}
1625
1626/* If src matches dst return dst string, otherwise return NULL */
1627static const char *cmd_entry_function(const char *src, const char *dst)
1628{
1629 /* Skip variable arguments. */
1630 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1631 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1632 return NULL;
1633
1634 /* In case of 'command \t', given src is NULL string. */
1635 if (src == NULL)
1636 return dst;
1637
1638 /* Matched with input string. */
1639 if (strncmp(src, dst, strlen(src)) == 0)
1640 return dst;
1641
1642 return NULL;
1643}
1644
1645/* If src matches dst return dst string, otherwise return NULL */
1646/* This version will return the dst string always if it is
1647 CMD_VARIABLE for '?' key processing */
1648static const char *cmd_entry_function_desc(const char *src, const char *dst)
1649{
1650 if (CMD_VARARG(dst))
1651 return dst;
1652
1653 if (CMD_RANGE(dst)) {
1654 if (cmd_range_match(dst, src))
1655 return dst;
1656 else
1657 return NULL;
1658 }
1659#ifdef HAVE_IPV6
1660 if (CMD_IPV6(dst)) {
1661 if (cmd_ipv6_match(src))
1662 return dst;
1663 else
1664 return NULL;
1665 }
1666
1667 if (CMD_IPV6_PREFIX(dst)) {
1668 if (cmd_ipv6_prefix_match(src))
1669 return dst;
1670 else
1671 return NULL;
1672 }
1673#endif /* HAVE_IPV6 */
1674
1675 if (CMD_IPV4(dst)) {
1676 if (cmd_ipv4_match(src))
1677 return dst;
1678 else
1679 return NULL;
1680 }
1681
1682 if (CMD_IPV4_PREFIX(dst)) {
1683 if (cmd_ipv4_prefix_match(src))
1684 return dst;
1685 else
1686 return NULL;
1687 }
1688
1689 /* Optional or variable commands always match on '?' */
1690 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1691 return dst;
1692
1693 /* In case of 'command \t', given src is NULL string. */
1694 if (src == NULL)
1695 return dst;
1696
1697 if (strncmp(src, dst, strlen(src)) == 0)
1698 return dst;
1699 else
1700 return NULL;
1701}
1702
1703/* Check same string element existence. If it isn't there return
1704 1. */
1705static int cmd_unique_string(vector v, const char *str)
1706{
1707 unsigned int i;
1708 char *match;
1709
1710 for (i = 0; i < vector_active(v); i++)
1711 if ((match = vector_slot(v, i)) != NULL)
1712 if (strcmp(match, str) == 0)
1713 return 0;
1714 return 1;
1715}
1716
1717/* Compare string to description vector. If there is same string
1718 return 1 else return 0. */
1719static int desc_unique_string(vector v, const char *str)
1720{
1721 unsigned int i;
1722 struct desc *desc;
1723
1724 for (i = 0; i < vector_active(v); i++)
1725 if ((desc = vector_slot(v, i)) != NULL)
1726 if (strcmp(desc->cmd, str) == 0)
1727 return 1;
1728 return 0;
1729}
1730
1731static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1732{
1733 if (first_word != NULL &&
1734 node != AUTH_NODE &&
1735 node != VIEW_NODE &&
1736 node != AUTH_ENABLE_NODE &&
1737 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1738 return 1;
1739 return 0;
1740}
1741
1742/* '?' describe command support. */
1743static vector
1744cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1745{
1746 unsigned int i;
1747 vector cmd_vector;
1748#define INIT_MATCHVEC_SIZE 10
1749 vector matchvec;
1750 struct cmd_element *cmd_element;
1751 unsigned int index;
1752 int ret;
1753 enum match_type match;
1754 char *command;
1755 static struct desc desc_cr = { "<cr>", "" };
1756
1757 /* Set index. */
1758 if (vector_active(vline) == 0) {
1759 *status = CMD_ERR_NO_MATCH;
1760 return NULL;
1761 } else
1762 index = vector_active(vline) - 1;
1763
1764 /* Make copy vector of current node's command vector. */
1765 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1766
1767 /* Prepare match vector */
1768 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1769
1770 /* Filter commands. */
1771 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001772 for (i = 0; i < index; i++) {
1773 command = vector_slot(vline, i);
1774 if (!command)
1775 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001776
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001777 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001778
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001779 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001780 struct cmd_element *cmd_element;
1781 vector descvec;
1782 unsigned int j, k;
1783
1784 for (j = 0; j < vector_active(cmd_vector); j++)
1785 if ((cmd_element =
1786 vector_slot(cmd_vector, j)) != NULL
1787 &&
1788 (vector_active(cmd_element->strvec))) {
1789 descvec =
1790 vector_slot(cmd_element->
1791 strvec,
1792 vector_active
1793 (cmd_element->
1794 strvec) - 1);
1795 for (k = 0;
1796 k < vector_active(descvec);
1797 k++) {
1798 struct desc *desc =
1799 vector_slot(descvec,
1800 k);
1801 vector_set(matchvec,
1802 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001803 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001804 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001805
Harald Welte80d30fe2013-02-12 11:08:57 +01001806 vector_set(matchvec, &desc_cr);
1807 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001808
Harald Welte80d30fe2013-02-12 11:08:57 +01001809 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001810 }
1811
Harald Welte80d30fe2013-02-12 11:08:57 +01001812 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1813 match)) == 1) {
1814 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001815 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001816 *status = CMD_ERR_AMBIGUOUS;
1817 return NULL;
1818 } else if (ret == 2) {
1819 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001820 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001821 *status = CMD_ERR_NO_MATCH;
1822 return NULL;
1823 }
1824 }
1825
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001826 /* Prepare match vector */
1827 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1828
1829 /* Make sure that cmd_vector is filtered based on current word */
1830 command = vector_slot(vline, index);
1831 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001832 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001833
1834 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001835 for (i = 0; i < vector_active(cmd_vector); i++) {
1836 const char *string = NULL;
1837 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001838
Harald Welte80d30fe2013-02-12 11:08:57 +01001839 cmd_element = vector_slot(cmd_vector, i);
1840 if (!cmd_element)
1841 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001842
Harald Welted17aa592013-02-12 11:11:34 +01001843 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1844 continue;
1845
Harald Welte80d30fe2013-02-12 11:08:57 +01001846 strvec = cmd_element->strvec;
1847
1848 /* if command is NULL, index may be equal to vector_active */
1849 if (command && index >= vector_active(strvec))
1850 vector_slot(cmd_vector, i) = NULL;
1851 else {
1852 /* Check if command is completed. */
1853 if (command == NULL
1854 && index == vector_active(strvec)) {
1855 string = "<cr>";
1856 if (!desc_unique_string(matchvec, string))
1857 vector_set(matchvec, &desc_cr);
1858 } else {
1859 unsigned int j;
1860 vector descvec = vector_slot(strvec, index);
1861 struct desc *desc;
1862
1863 for (j = 0; j < vector_active(descvec); j++) {
1864 desc = vector_slot(descvec, j);
1865 if (!desc)
1866 continue;
1867 string = cmd_entry_function_desc
1868 (command, desc->cmd);
1869 if (!string)
1870 continue;
1871 /* Uniqueness check */
1872 if (!desc_unique_string(matchvec, string))
1873 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001874 }
1875 }
1876 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001877 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001878 vector_free(cmd_vector);
1879
1880 if (vector_slot(matchvec, 0) == NULL) {
1881 vector_free(matchvec);
1882 *status = CMD_ERR_NO_MATCH;
1883 } else
1884 *status = CMD_SUCCESS;
1885
1886 return matchvec;
1887}
1888
1889vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1890{
1891 vector ret;
1892
1893 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1894 enum node_type onode;
1895 vector shifted_vline;
1896 unsigned int index;
1897
1898 onode = vty->node;
1899 vty->node = ENABLE_NODE;
1900 /* We can try it on enable node, cos' the vty is authenticated */
1901
1902 shifted_vline = vector_init(vector_count(vline));
1903 /* use memcpy? */
1904 for (index = 1; index < vector_active(vline); index++) {
1905 vector_set_index(shifted_vline, index - 1,
1906 vector_lookup(vline, index));
1907 }
1908
1909 ret = cmd_describe_command_real(shifted_vline, vty, status);
1910
1911 vector_free(shifted_vline);
1912 vty->node = onode;
1913 return ret;
1914 }
1915
1916 return cmd_describe_command_real(vline, vty, status);
1917}
1918
1919/* Check LCD of matched command. */
1920static int cmd_lcd(char **matched)
1921{
1922 int i;
1923 int j;
1924 int lcd = -1;
1925 char *s1, *s2;
1926 char c1, c2;
1927
1928 if (matched[0] == NULL || matched[1] == NULL)
1929 return 0;
1930
1931 for (i = 1; matched[i] != NULL; i++) {
1932 s1 = matched[i - 1];
1933 s2 = matched[i];
1934
1935 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1936 if (c1 != c2)
1937 break;
1938
1939 if (lcd < 0)
1940 lcd = j;
1941 else {
1942 if (lcd > j)
1943 lcd = j;
1944 }
1945 }
1946 return lcd;
1947}
1948
1949/* Command line completion support. */
1950static char **cmd_complete_command_real(vector vline, struct vty *vty,
1951 int *status)
1952{
1953 unsigned int i;
1954 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1955#define INIT_MATCHVEC_SIZE 10
1956 vector matchvec;
1957 struct cmd_element *cmd_element;
1958 unsigned int index;
1959 char **match_str;
1960 struct desc *desc;
1961 vector descvec;
1962 char *command;
1963 int lcd;
1964
1965 if (vector_active(vline) == 0) {
1966 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001967 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001968 return NULL;
1969 } else
1970 index = vector_active(vline) - 1;
1971
1972 /* First, filter by preceeding command string */
1973 for (i = 0; i < index; i++)
1974 if ((command = vector_slot(vline, i))) {
1975 enum match_type match;
1976 int ret;
1977
1978 /* First try completion match, if there is exactly match return 1 */
1979 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001980 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001981
1982 /* If there is exact match then filter ambiguous match else check
1983 ambiguousness. */
1984 if ((ret =
1985 is_cmd_ambiguous(command, cmd_vector, i,
1986 match)) == 1) {
1987 vector_free(cmd_vector);
1988 *status = CMD_ERR_AMBIGUOUS;
1989 return NULL;
1990 }
1991 /*
1992 else if (ret == 2)
1993 {
1994 vector_free (cmd_vector);
1995 *status = CMD_ERR_NO_MATCH;
1996 return NULL;
1997 }
1998 */
1999 }
2000
2001 /* Prepare match vector. */
2002 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2003
2004 /* Now we got into completion */
2005 for (i = 0; i < vector_active(cmd_vector); i++)
2006 if ((cmd_element = vector_slot(cmd_vector, i))) {
2007 const char *string;
2008 vector strvec = cmd_element->strvec;
2009
2010 /* Check field length */
2011 if (index >= vector_active(strvec))
2012 vector_slot(cmd_vector, i) = NULL;
2013 else {
2014 unsigned int j;
2015
2016 descvec = vector_slot(strvec, index);
2017 for (j = 0; j < vector_active(descvec); j++)
2018 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002019 const char *cmd = desc->cmd;
2020 char *tmp = NULL;
2021
2022 if (CMD_OPTION(desc->cmd)) {
2023 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2024 cmd = tmp;
2025 }
2026 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002027 if (cmd_unique_string (matchvec, string))
2028 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002029 if (tmp)
2030 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002031 }
2032 }
2033 }
2034
2035 /* We don't need cmd_vector any more. */
2036 vector_free(cmd_vector);
2037
2038 /* No matched command */
2039 if (vector_slot(matchvec, 0) == NULL) {
2040 vector_free(matchvec);
2041
2042 /* In case of 'command \t' pattern. Do you need '?' command at
2043 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002044 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002045 *status = CMD_ERR_NOTHING_TODO;
2046 else
2047 *status = CMD_ERR_NO_MATCH;
2048 return NULL;
2049 }
2050
2051 /* Only one matched */
2052 if (vector_slot(matchvec, 1) == NULL) {
2053 match_str = (char **)matchvec->index;
2054 vector_only_wrapper_free(matchvec);
2055 *status = CMD_COMPLETE_FULL_MATCH;
2056 return match_str;
2057 }
2058 /* Make it sure last element is NULL. */
2059 vector_set(matchvec, NULL);
2060
2061 /* Check LCD of matched strings. */
2062 if (vector_slot(vline, index) != NULL) {
2063 lcd = cmd_lcd((char **)matchvec->index);
2064
2065 if (lcd) {
2066 int len = strlen(vector_slot(vline, index));
2067
2068 if (len < lcd) {
2069 char *lcdstr;
2070
2071 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2072 "complete-lcdstr");
2073 memcpy(lcdstr, matchvec->index[0], lcd);
2074 lcdstr[lcd] = '\0';
2075
2076 /* match_str = (char **) &lcdstr; */
2077
2078 /* Free matchvec. */
2079 for (i = 0; i < vector_active(matchvec); i++) {
2080 if (vector_slot(matchvec, i))
2081 talloc_free(vector_slot(matchvec, i));
2082 }
2083 vector_free(matchvec);
2084
2085 /* Make new matchvec. */
2086 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2087 vector_set(matchvec, lcdstr);
2088 match_str = (char **)matchvec->index;
2089 vector_only_wrapper_free(matchvec);
2090
2091 *status = CMD_COMPLETE_MATCH;
2092 return match_str;
2093 }
2094 }
2095 }
2096
2097 match_str = (char **)matchvec->index;
2098 vector_only_wrapper_free(matchvec);
2099 *status = CMD_COMPLETE_LIST_MATCH;
2100 return match_str;
2101}
2102
2103char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2104{
2105 char **ret;
2106
2107 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2108 enum node_type onode;
2109 vector shifted_vline;
2110 unsigned int index;
2111
2112 onode = vty->node;
2113 vty->node = ENABLE_NODE;
2114 /* We can try it on enable node, cos' the vty is authenticated */
2115
2116 shifted_vline = vector_init(vector_count(vline));
2117 /* use memcpy? */
2118 for (index = 1; index < vector_active(vline); index++) {
2119 vector_set_index(shifted_vline, index - 1,
2120 vector_lookup(vline, index));
2121 }
2122
2123 ret = cmd_complete_command_real(shifted_vline, vty, status);
2124
2125 vector_free(shifted_vline);
2126 vty->node = onode;
2127 return ret;
2128 }
2129
2130 return cmd_complete_command_real(vline, vty, status);
2131}
2132
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002133static struct vty_parent_node *vty_parent(struct vty *vty)
2134{
2135 return llist_first_entry_or_null(&vty->parent_nodes,
2136 struct vty_parent_node,
2137 entry);
2138}
2139
2140static bool vty_pop_parent(struct vty *vty)
2141{
2142 struct vty_parent_node *parent = vty_parent(vty);
2143 if (!parent)
2144 return false;
2145 llist_del(&parent->entry);
2146 vty->node = parent->node;
2147 vty->priv = parent->priv;
2148 if (vty->indent)
2149 talloc_free(vty->indent);
2150 vty->indent = parent->indent;
2151 talloc_free(parent);
2152 return true;
2153}
2154
2155static void vty_clear_parents(struct vty *vty)
2156{
2157 while (vty_pop_parent(vty));
2158}
2159
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002160/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002161/*
2162 * This function MUST eventually converge on a node when called repeatedly,
2163 * there must not be any cycles.
2164 * All 'config' nodes shall converge on CONFIG_NODE.
2165 * All other 'enable' nodes shall converge on ENABLE_NODE.
2166 * All 'view' only nodes shall converge on VIEW_NODE.
2167 * All other nodes shall converge on themselves or it must be ensured,
2168 * that the user's rights are not extended anyhow by calling this function.
2169 *
2170 * Note that these requirements also apply to all functions that are used
2171 * as go_parent_cb.
2172 * Note also that this function relies on the is_config_child callback to
2173 * recognize non-config nodes if go_parent_cb is not set.
2174 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002175int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002176{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002177 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002178 case AUTH_NODE:
2179 case VIEW_NODE:
2180 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002181 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002182 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002183 break;
2184
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002185 case AUTH_ENABLE_NODE:
2186 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002187 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002188 break;
2189
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002190 case CFG_LOG_NODE:
2191 case VTY_NODE:
2192 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002193 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002194 break;
2195
2196 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002197 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002198 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002199 vty_pop_parent(vty);
2200 }
2201 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002202 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002203 vty_clear_parents(vty);
2204 }
2205 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002206 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002207 vty_clear_parents(vty);
2208 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002209 break;
2210 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002211
2212 return vty->node;
2213}
2214
2215/* Execute command by argument vline vector. */
2216static int
2217cmd_execute_command_real(vector vline, struct vty *vty,
2218 struct cmd_element **cmd)
2219{
2220 unsigned int i;
2221 unsigned int index;
2222 vector cmd_vector;
2223 struct cmd_element *cmd_element;
2224 struct cmd_element *matched_element;
2225 unsigned int matched_count, incomplete_count;
2226 int argc;
2227 const char *argv[CMD_ARGC_MAX];
2228 enum match_type match = 0;
2229 int varflag;
2230 char *command;
2231
2232 /* Make copy of command elements. */
2233 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2234
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002235 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002236 if ((command = vector_slot(vline, index))) {
2237 int ret;
2238
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002239 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002240 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002241
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002242 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002243 break;
2244
2245 ret =
2246 is_cmd_ambiguous(command, cmd_vector, index, match);
2247
2248 if (ret == 1) {
2249 vector_free(cmd_vector);
2250 return CMD_ERR_AMBIGUOUS;
2251 } else if (ret == 2) {
2252 vector_free(cmd_vector);
2253 return CMD_ERR_NO_MATCH;
2254 }
2255 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002256 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002257
2258 /* Check matched count. */
2259 matched_element = NULL;
2260 matched_count = 0;
2261 incomplete_count = 0;
2262
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002263 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002264 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002265 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002266 || index >= cmd_element->cmdsize) {
2267 matched_element = cmd_element;
2268#if 0
2269 printf("DEBUG: %s\n", cmd_element->string);
2270#endif
2271 matched_count++;
2272 } else {
2273 incomplete_count++;
2274 }
2275 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002276 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002277
2278 /* Finish of using cmd_vector. */
2279 vector_free(cmd_vector);
2280
2281 /* To execute command, matched_count must be 1. */
2282 if (matched_count == 0) {
2283 if (incomplete_count)
2284 return CMD_ERR_INCOMPLETE;
2285 else
2286 return CMD_ERR_NO_MATCH;
2287 }
2288
2289 if (matched_count > 1)
2290 return CMD_ERR_AMBIGUOUS;
2291
2292 /* Argument treatment */
2293 varflag = 0;
2294 argc = 0;
2295
2296 for (i = 0; i < vector_active(vline); i++) {
2297 if (varflag)
2298 argv[argc++] = vector_slot(vline, i);
2299 else {
2300 vector descvec =
2301 vector_slot(matched_element->strvec, i);
2302
2303 if (vector_active(descvec) == 1) {
2304 struct desc *desc = vector_slot(descvec, 0);
2305
2306 if (CMD_VARARG(desc->cmd))
2307 varflag = 1;
2308
2309 if (varflag || CMD_VARIABLE(desc->cmd)
2310 || CMD_OPTION(desc->cmd))
2311 argv[argc++] = vector_slot(vline, i);
2312 } else
2313 argv[argc++] = vector_slot(vline, i);
2314 }
2315
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002316 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002317 return CMD_ERR_EXEED_ARGC_MAX;
2318 }
2319
2320 /* For vtysh execution. */
2321 if (cmd)
2322 *cmd = matched_element;
2323
2324 if (matched_element->daemon)
2325 return CMD_SUCCESS_DAEMON;
2326
2327 /* Execute matched command. */
2328 return (*matched_element->func) (matched_element, vty, argc, argv);
2329}
2330
2331int
2332cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2333 int vtysh)
2334{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002335 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002336 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002337
2338 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002339
2340 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2341 vector shifted_vline;
2342 unsigned int index;
2343
2344 vty->node = ENABLE_NODE;
2345 /* We can try it on enable node, cos' the vty is authenticated */
2346
2347 shifted_vline = vector_init(vector_count(vline));
2348 /* use memcpy? */
2349 for (index = 1; index < vector_active(vline); index++) {
2350 vector_set_index(shifted_vline, index - 1,
2351 vector_lookup(vline, index));
2352 }
2353
2354 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2355
2356 vector_free(shifted_vline);
2357 vty->node = onode;
2358 return ret;
2359 }
2360
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002361 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002362}
2363
2364/* Execute command by argument readline. */
2365int
2366cmd_execute_command_strict(vector vline, struct vty *vty,
2367 struct cmd_element **cmd)
2368{
2369 unsigned int i;
2370 unsigned int index;
2371 vector cmd_vector;
2372 struct cmd_element *cmd_element;
2373 struct cmd_element *matched_element;
2374 unsigned int matched_count, incomplete_count;
2375 int argc;
2376 const char *argv[CMD_ARGC_MAX];
2377 int varflag;
2378 enum match_type match = 0;
2379 char *command;
2380
2381 /* Make copy of command element */
2382 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2383
2384 for (index = 0; index < vector_active(vline); index++)
2385 if ((command = vector_slot(vline, index))) {
2386 int ret;
2387
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002388 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002389 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002390
2391 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002392 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002393 break;
2394
2395 ret =
2396 is_cmd_ambiguous(command, cmd_vector, index, match);
2397 if (ret == 1) {
2398 vector_free(cmd_vector);
2399 return CMD_ERR_AMBIGUOUS;
2400 }
2401 if (ret == 2) {
2402 vector_free(cmd_vector);
2403 return CMD_ERR_NO_MATCH;
2404 }
2405 }
2406
2407 /* Check matched count. */
2408 matched_element = NULL;
2409 matched_count = 0;
2410 incomplete_count = 0;
2411 for (i = 0; i < vector_active(cmd_vector); i++)
2412 if (vector_slot(cmd_vector, i) != NULL) {
2413 cmd_element = vector_slot(cmd_vector, i);
2414
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002415 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002416 || index >= cmd_element->cmdsize) {
2417 matched_element = cmd_element;
2418 matched_count++;
2419 } else
2420 incomplete_count++;
2421 }
2422
2423 /* Finish of using cmd_vector. */
2424 vector_free(cmd_vector);
2425
2426 /* To execute command, matched_count must be 1. */
2427 if (matched_count == 0) {
2428 if (incomplete_count)
2429 return CMD_ERR_INCOMPLETE;
2430 else
2431 return CMD_ERR_NO_MATCH;
2432 }
2433
2434 if (matched_count > 1)
2435 return CMD_ERR_AMBIGUOUS;
2436
2437 /* Argument treatment */
2438 varflag = 0;
2439 argc = 0;
2440
2441 for (i = 0; i < vector_active(vline); i++) {
2442 if (varflag)
2443 argv[argc++] = vector_slot(vline, i);
2444 else {
2445 vector descvec =
2446 vector_slot(matched_element->strvec, i);
2447
2448 if (vector_active(descvec) == 1) {
2449 struct desc *desc = vector_slot(descvec, 0);
2450
2451 if (CMD_VARARG(desc->cmd))
2452 varflag = 1;
2453
2454 if (varflag || CMD_VARIABLE(desc->cmd)
2455 || CMD_OPTION(desc->cmd))
2456 argv[argc++] = vector_slot(vline, i);
2457 } else
2458 argv[argc++] = vector_slot(vline, i);
2459 }
2460
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002461 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002462 return CMD_ERR_EXEED_ARGC_MAX;
2463 }
2464
2465 /* For vtysh execution. */
2466 if (cmd)
2467 *cmd = matched_element;
2468
2469 if (matched_element->daemon)
2470 return CMD_SUCCESS_DAEMON;
2471
2472 /* Now execute matched command */
2473 return (*matched_element->func) (matched_element, vty, argc, argv);
2474}
2475
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002476static inline size_t len(const char *str)
2477{
2478 return str? strlen(str) : 0;
2479}
2480
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002481/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2482 * is longer than b, a must start with exactly b, and vice versa.
2483 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2484 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002485static int indent_cmp(const char *a, const char *b)
2486{
2487 size_t al, bl;
2488 al = len(a);
2489 bl = len(b);
2490 if (al > bl) {
2491 if (bl && strncmp(a, b, bl) != 0)
2492 return EINVAL;
2493 return 1;
2494 }
2495 /* al <= bl */
2496 if (al && strncmp(a, b, al) != 0)
2497 return EINVAL;
2498 return (al < bl)? -1 : 0;
2499}
2500
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002501/* Configration make from file. */
2502int config_from_file(struct vty *vty, FILE * fp)
2503{
2504 int ret;
2505 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002506 char *indent;
2507 int cmp;
2508 struct vty_parent_node this_node;
2509 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002510
2511 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002512 indent = NULL;
2513 vline = NULL;
2514 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002515
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002516 if (ret != CMD_SUCCESS)
2517 goto return_invalid_indent;
2518
2519 /* In case of comment or empty line */
2520 if (vline == NULL) {
2521 if (indent) {
2522 talloc_free(indent);
2523 indent = NULL;
2524 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002525 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002526 }
2527
Neels Hofmeyr43063632017-09-19 23:54:01 +02002528 /* We have a nonempty line. */
2529 if (!vty->indent) {
2530 /* We have just entered a node and expecting the first child to come up; but we
2531 * may also skip right back to a parent or ancestor level. */
2532 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002533
Neels Hofmeyr43063632017-09-19 23:54:01 +02002534 /* If there is no parent, record any indentation we encounter. */
2535 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2536
2537 if (cmp == EINVAL)
2538 goto return_invalid_indent;
2539
2540 if (cmp <= 0) {
2541 /* We have gone right back to the parent level or higher, we are skipping
2542 * this child node level entirely. Pop the parent to go back to a node
2543 * that was actually there (to reinstate vty->indent) and re-use below
2544 * go-parent while-loop to find an accurate match of indent in the node
2545 * ancestry. */
2546 vty_go_parent(vty);
2547 } else {
2548 /* The indent is deeper than the just entered parent, record the new
2549 * indentation characters. */
2550 vty->indent = talloc_strdup(vty, indent);
2551 /* This *is* the new indentation. */
2552 cmp = 0;
2553 }
2554 } else {
2555 /* There is a known indentation for this node level, validate and detect node
2556 * exits. */
2557 cmp = indent_cmp(indent, vty->indent);
2558 if (cmp == EINVAL)
2559 goto return_invalid_indent;
2560 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002561
2562 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2563 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2564 while (cmp < 0) {
2565 vty_go_parent(vty);
2566 cmp = indent_cmp(indent, vty->indent);
2567 if (cmp == EINVAL)
2568 goto return_invalid_indent;
2569 }
2570
2571 /* More indent without having entered a child node level? Either the parent node's indent
2572 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2573 * or the indentation increased even though the vty command didn't enter a child. */
2574 if (cmp > 0)
2575 goto return_invalid_indent;
2576
2577 /* Remember the current node before the command possibly changes it. */
2578 this_node = (struct vty_parent_node){
2579 .node = vty->node,
2580 .priv = vty->priv,
2581 .indent = vty->indent,
2582 };
2583
2584 parent = vty_parent(vty);
2585 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002586 cmd_free_strvec(vline);
2587
2588 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002589 && ret != CMD_ERR_NOTHING_TODO) {
2590 if (indent) {
2591 talloc_free(indent);
2592 indent = NULL;
2593 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002594 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002595 }
2596
2597 /* If we have stepped down into a child node, push a parent frame.
2598 * The causality is such: we don't expect every single node entry implementation to push
2599 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2600 * a parent node. Hence if the node changed without the parent node changing, we must
2601 * have stepped into a child node (and now expect a deeper indent). */
2602 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2603 /* Push the parent node. */
2604 parent = talloc_zero(vty, struct vty_parent_node);
2605 *parent = this_node;
2606 llist_add(&parent->entry, &vty->parent_nodes);
2607
2608 /* The current talloc'ed vty->indent string will now be owned by this parent
2609 * struct. Indicate that we don't know what deeper indent characters the user
2610 * will choose. */
2611 vty->indent = NULL;
2612 }
2613
2614 if (indent) {
2615 talloc_free(indent);
2616 indent = NULL;
2617 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002618 }
2619 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002620
2621return_invalid_indent:
2622 if (vline)
2623 cmd_free_strvec(vline);
2624 if (indent) {
2625 talloc_free(indent);
2626 indent = NULL;
2627 }
2628 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002629}
2630
2631/* Configration from terminal */
2632DEFUN(config_terminal,
2633 config_terminal_cmd,
2634 "configure terminal",
2635 "Configuration from vty interface\n" "Configuration terminal\n")
2636{
2637 if (vty_config_lock(vty))
2638 vty->node = CONFIG_NODE;
2639 else {
2640 vty_out(vty, "VTY configuration is locked by other VTY%s",
2641 VTY_NEWLINE);
2642 return CMD_WARNING;
2643 }
2644 return CMD_SUCCESS;
2645}
2646
2647/* Enable command */
2648DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2649{
2650 /* If enable password is NULL, change to ENABLE_NODE */
2651 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2652 vty->type == VTY_SHELL_SERV)
2653 vty->node = ENABLE_NODE;
2654 else
2655 vty->node = AUTH_ENABLE_NODE;
2656
2657 return CMD_SUCCESS;
2658}
2659
2660/* Disable command */
2661DEFUN(disable,
2662 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2663{
2664 if (vty->node == ENABLE_NODE)
2665 vty->node = VIEW_NODE;
2666 return CMD_SUCCESS;
2667}
2668
2669/* Down vty node level. */
2670gDEFUN(config_exit,
2671 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2672{
2673 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002674 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002675 case VIEW_NODE:
2676 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002677 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002678 break;
2679 case CONFIG_NODE:
2680 vty->node = ENABLE_NODE;
2681 vty_config_unlock(vty);
2682 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002683 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002684 if (vty->node > CONFIG_NODE)
2685 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002686 break;
2687 }
2688 return CMD_SUCCESS;
2689}
2690
2691/* End of configuration. */
2692 gDEFUN(config_end,
2693 config_end_cmd, "end", "End current mode and change to enable mode.")
2694{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002695 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002696 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002697
2698 /* Repeatedly call go_parent until a top node is reached. */
2699 while (vty->node > CONFIG_NODE) {
2700 if (vty->node == last_node) {
2701 /* Ensure termination, this shouldn't happen. */
2702 break;
2703 }
2704 last_node = vty->node;
2705 vty_go_parent(vty);
2706 }
2707
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002708 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002709 if (vty->node > ENABLE_NODE)
2710 vty->node = ENABLE_NODE;
2711 vty->index = NULL;
2712 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002713 }
2714 return CMD_SUCCESS;
2715}
2716
2717/* Show version. */
2718DEFUN(show_version,
2719 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2720{
Harald Welte237f6242010-05-25 23:00:45 +02002721 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2722 host.app_info->version,
2723 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2724 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002725
2726 return CMD_SUCCESS;
2727}
2728
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002729DEFUN(show_online_help,
2730 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2731{
2732 vty_dump_nodes(vty);
2733 return CMD_SUCCESS;
2734}
2735
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002736/* Help display function for all node. */
2737gDEFUN(config_help,
2738 config_help_cmd, "help", "Description of the interactive help system\n")
2739{
2740 vty_out(vty,
2741 "This VTY provides advanced help features. When you need help,%s\
2742anytime at the command line please press '?'.%s\
2743%s\
2744If nothing matches, the help list will be empty and you must backup%s\
2745 until entering a '?' shows the available options.%s\
2746Two styles of help are provided:%s\
27471. Full help is available when you are ready to enter a%s\
2748command argument (e.g. 'show ?') and describes each possible%s\
2749argument.%s\
27502. Partial help is provided when an abbreviated argument is entered%s\
2751 and you want to know what arguments match the input%s\
2752 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2753 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2754 return CMD_SUCCESS;
2755}
2756
2757/* Help display function for all node. */
2758gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2759{
2760 unsigned int i;
2761 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2762 struct cmd_element *cmd;
2763
2764 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2765 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002766 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002767 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2768 return CMD_SUCCESS;
2769}
2770
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002771static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002772{
2773 unsigned int i;
2774 int fd;
2775 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002776 char *config_file_tmp = NULL;
2777 char *config_file_sav = NULL;
2778 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002779 struct stat st;
2780
2781 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002782
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002783 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2784 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2785 * manually instead. */
2786
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002787 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002788 config_file_sav =
2789 _talloc_zero(tall_vty_cmd_ctx,
2790 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2791 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002792 if (!config_file_sav)
2793 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002794 strcpy(config_file_sav, config_file);
2795 strcat(config_file_sav, CONF_BACKUP_EXT);
2796
2797 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002798 "config_file_tmp");
2799 if (!config_file_tmp) {
2800 talloc_free(config_file_sav);
2801 return -1;
2802 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002803 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2804
2805 /* Open file to configuration write. */
2806 fd = mkstemp(config_file_tmp);
2807 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002808 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002809 talloc_free(config_file_tmp);
2810 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002811 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002812 }
2813
2814 /* Make vty for configuration file. */
2815 file_vty = vty_new();
2816 file_vty->fd = fd;
2817 file_vty->type = VTY_FILE;
2818
2819 /* Config file header print. */
2820 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002821 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002822 //vty_time_print (file_vty, 1);
2823 vty_out(file_vty, "!\n");
2824
2825 for (i = 0; i < vector_active(cmdvec); i++)
2826 if ((node = vector_slot(cmdvec, i)) && node->func) {
2827 if ((*node->func) (file_vty))
2828 vty_out(file_vty, "!\n");
2829 }
2830 vty_close(file_vty);
2831
2832 if (unlink(config_file_sav) != 0)
2833 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002834 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002835 talloc_free(config_file_sav);
2836 talloc_free(config_file_tmp);
2837 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002838 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002839 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002840
2841 /* Only link the .sav file if the original file exists */
2842 if (stat(config_file, &st) == 0) {
2843 if (link(config_file, config_file_sav) != 0) {
2844 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2845 talloc_free(config_file_sav);
2846 talloc_free(config_file_tmp);
2847 unlink(config_file_tmp);
2848 return -3;
2849 }
2850 sync();
2851 if (unlink(config_file) != 0) {
2852 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2853 talloc_free(config_file_sav);
2854 talloc_free(config_file_tmp);
2855 unlink(config_file_tmp);
2856 return -4;
2857 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002858 }
2859 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002860 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002861 talloc_free(config_file_sav);
2862 talloc_free(config_file_tmp);
2863 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002864 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002865 }
2866 unlink(config_file_tmp);
2867 sync();
2868
2869 talloc_free(config_file_sav);
2870 talloc_free(config_file_tmp);
2871
2872 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002873 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2874 return -6;
2875 }
2876
2877 return 0;
2878}
2879
2880
2881/* Write current configuration into file. */
2882DEFUN(config_write_file,
2883 config_write_file_cmd,
2884 "write file",
2885 "Write running configuration to memory, network, or terminal\n"
2886 "Write to configuration file\n")
2887{
2888 char *failed_file;
2889 int rc;
2890
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002891 if (host.app_info->config_is_consistent) {
2892 rc = host.app_info->config_is_consistent(vty);
2893 if (!rc) {
2894 vty_out(vty, "Configuration is not consistent%s",
2895 VTY_NEWLINE);
2896 return CMD_WARNING;
2897 }
2898 }
2899
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002900 if (host.config == NULL) {
2901 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2902 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002903 return CMD_WARNING;
2904 }
2905
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002906 rc = write_config_file(host.config, &failed_file);
2907 switch (rc) {
2908 case -1:
2909 vty_out(vty, "Can't open configuration file %s.%s",
2910 failed_file, VTY_NEWLINE);
2911 rc = CMD_WARNING;
2912 break;
2913 case -2:
2914 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2915 failed_file, VTY_NEWLINE);
2916 rc = CMD_WARNING;
2917 break;
2918 case -3:
2919 vty_out(vty, "Can't backup old configuration file %s.%s",
2920 failed_file, VTY_NEWLINE);
2921 rc = CMD_WARNING;
2922 break;
2923 case -4:
2924 vty_out(vty, "Can't unlink configuration file %s.%s",
2925 failed_file, VTY_NEWLINE);
2926 rc = CMD_WARNING;
2927 break;
2928 case -5:
2929 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2930 VTY_NEWLINE);
2931 rc = CMD_WARNING;
2932 break;
2933 case -6:
2934 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2935 failed_file, strerror(errno), errno, VTY_NEWLINE);
2936 rc = CMD_WARNING;
2937 break;
2938 default:
2939 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2940 rc = CMD_SUCCESS;
2941 break;
2942 }
2943
2944 talloc_free(failed_file);
2945 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002946}
2947
2948ALIAS(config_write_file,
2949 config_write_cmd,
2950 "write", "Write running configuration to memory, network, or terminal\n")
2951
2952 ALIAS(config_write_file,
2953 config_write_memory_cmd,
2954 "write memory",
2955 "Write running configuration to memory, network, or terminal\n"
2956 "Write configuration to the file (same as write file)\n")
2957
2958 ALIAS(config_write_file,
2959 copy_runningconfig_startupconfig_cmd,
2960 "copy running-config startup-config",
2961 "Copy configuration\n"
2962 "Copy running config to... \n"
2963 "Copy running config to startup config (same as write file)\n")
2964
2965/* Write current configuration into the terminal. */
2966 DEFUN(config_write_terminal,
2967 config_write_terminal_cmd,
2968 "write terminal",
2969 "Write running configuration to memory, network, or terminal\n"
2970 "Write to terminal\n")
2971{
2972 unsigned int i;
2973 struct cmd_node *node;
2974
2975 if (vty->type == VTY_SHELL_SERV) {
2976 for (i = 0; i < vector_active(cmdvec); i++)
2977 if ((node = vector_slot(cmdvec, i)) && node->func
2978 && node->vtysh) {
2979 if ((*node->func) (vty))
2980 vty_out(vty, "!%s", VTY_NEWLINE);
2981 }
2982 } else {
2983 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2984 VTY_NEWLINE);
2985 vty_out(vty, "!%s", VTY_NEWLINE);
2986
2987 for (i = 0; i < vector_active(cmdvec); i++)
2988 if ((node = vector_slot(cmdvec, i)) && node->func) {
2989 if ((*node->func) (vty))
2990 vty_out(vty, "!%s", VTY_NEWLINE);
2991 }
2992 vty_out(vty, "end%s", VTY_NEWLINE);
2993 }
2994 return CMD_SUCCESS;
2995}
2996
2997/* Write current configuration into the terminal. */
2998ALIAS(config_write_terminal,
2999 show_running_config_cmd,
3000 "show running-config", SHOW_STR "running configuration\n")
3001
3002/* Write startup configuration into the terminal. */
3003 DEFUN(show_startup_config,
3004 show_startup_config_cmd,
3005 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3006{
3007 char buf[BUFSIZ];
3008 FILE *confp;
3009
3010 confp = fopen(host.config, "r");
3011 if (confp == NULL) {
3012 vty_out(vty, "Can't open configuration file [%s]%s",
3013 host.config, VTY_NEWLINE);
3014 return CMD_WARNING;
3015 }
3016
3017 while (fgets(buf, BUFSIZ, confp)) {
3018 char *cp = buf;
3019
3020 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3021 cp++;
3022 *cp = '\0';
3023
3024 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3025 }
3026
3027 fclose(confp);
3028
3029 return CMD_SUCCESS;
3030}
3031
3032/* Hostname configuration */
3033DEFUN(config_hostname,
3034 hostname_cmd,
3035 "hostname WORD",
3036 "Set system's network name\n" "This system's network name\n")
3037{
3038 if (!isalpha((int)*argv[0])) {
3039 vty_out(vty, "Please specify string starting with alphabet%s",
3040 VTY_NEWLINE);
3041 return CMD_WARNING;
3042 }
3043
3044 if (host.name)
3045 talloc_free(host.name);
3046
3047 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3048 return CMD_SUCCESS;
3049}
3050
3051DEFUN(config_no_hostname,
3052 no_hostname_cmd,
3053 "no hostname [HOSTNAME]",
3054 NO_STR "Reset system's network name\n" "Host name of this router\n")
3055{
3056 if (host.name)
3057 talloc_free(host.name);
3058 host.name = NULL;
3059 return CMD_SUCCESS;
3060}
3061
3062/* VTY interface password set. */
3063DEFUN(config_password, password_cmd,
3064 "password (8|) WORD",
3065 "Assign the terminal connection password\n"
3066 "Specifies a HIDDEN password will follow\n"
3067 "dummy string \n" "The HIDDEN line password string\n")
3068{
3069 /* Argument check. */
3070 if (argc == 0) {
3071 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3072 return CMD_WARNING;
3073 }
3074
3075 if (argc == 2) {
3076 if (*argv[0] == '8') {
3077 if (host.password)
3078 talloc_free(host.password);
3079 host.password = NULL;
3080 if (host.password_encrypt)
3081 talloc_free(host.password_encrypt);
3082 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3083 return CMD_SUCCESS;
3084 } else {
3085 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3086 return CMD_WARNING;
3087 }
3088 }
3089
3090 if (!isalnum((int)*argv[0])) {
3091 vty_out(vty,
3092 "Please specify string starting with alphanumeric%s",
3093 VTY_NEWLINE);
3094 return CMD_WARNING;
3095 }
3096
3097 if (host.password)
3098 talloc_free(host.password);
3099 host.password = NULL;
3100
3101#ifdef VTY_CRYPT_PW
3102 if (host.encrypt) {
3103 if (host.password_encrypt)
3104 talloc_free(host.password_encrypt);
3105 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3106 } else
3107#endif
3108 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3109
3110 return CMD_SUCCESS;
3111}
3112
3113ALIAS(config_password, password_text_cmd,
3114 "password LINE",
3115 "Assign the terminal connection password\n"
3116 "The UNENCRYPTED (cleartext) line password\n")
3117
3118/* VTY enable password set. */
3119 DEFUN(config_enable_password, enable_password_cmd,
3120 "enable password (8|) WORD",
3121 "Modify enable password parameters\n"
3122 "Assign the privileged level password\n"
3123 "Specifies a HIDDEN password will follow\n"
3124 "dummy string \n" "The HIDDEN 'enable' password string\n")
3125{
3126 /* Argument check. */
3127 if (argc == 0) {
3128 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3129 return CMD_WARNING;
3130 }
3131
3132 /* Crypt type is specified. */
3133 if (argc == 2) {
3134 if (*argv[0] == '8') {
3135 if (host.enable)
3136 talloc_free(host.enable);
3137 host.enable = NULL;
3138
3139 if (host.enable_encrypt)
3140 talloc_free(host.enable_encrypt);
3141 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3142
3143 return CMD_SUCCESS;
3144 } else {
3145 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3146 return CMD_WARNING;
3147 }
3148 }
3149
3150 if (!isalnum((int)*argv[0])) {
3151 vty_out(vty,
3152 "Please specify string starting with alphanumeric%s",
3153 VTY_NEWLINE);
3154 return CMD_WARNING;
3155 }
3156
3157 if (host.enable)
3158 talloc_free(host.enable);
3159 host.enable = NULL;
3160
3161 /* Plain password input. */
3162#ifdef VTY_CRYPT_PW
3163 if (host.encrypt) {
3164 if (host.enable_encrypt)
3165 talloc_free(host.enable_encrypt);
3166 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3167 } else
3168#endif
3169 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3170
3171 return CMD_SUCCESS;
3172}
3173
3174ALIAS(config_enable_password,
3175 enable_password_text_cmd,
3176 "enable password LINE",
3177 "Modify enable password parameters\n"
3178 "Assign the privileged level password\n"
3179 "The UNENCRYPTED (cleartext) 'enable' password\n")
3180
3181/* VTY enable password delete. */
3182 DEFUN(no_config_enable_password, no_enable_password_cmd,
3183 "no enable password",
3184 NO_STR
3185 "Modify enable password parameters\n"
3186 "Assign the privileged level password\n")
3187{
3188 if (host.enable)
3189 talloc_free(host.enable);
3190 host.enable = NULL;
3191
3192 if (host.enable_encrypt)
3193 talloc_free(host.enable_encrypt);
3194 host.enable_encrypt = NULL;
3195
3196 return CMD_SUCCESS;
3197}
3198
3199#ifdef VTY_CRYPT_PW
3200DEFUN(service_password_encrypt,
3201 service_password_encrypt_cmd,
3202 "service password-encryption",
3203 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3204{
3205 if (host.encrypt)
3206 return CMD_SUCCESS;
3207
3208 host.encrypt = 1;
3209
3210 if (host.password) {
3211 if (host.password_encrypt)
3212 talloc_free(host.password_encrypt);
3213 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3214 }
3215 if (host.enable) {
3216 if (host.enable_encrypt)
3217 talloc_free(host.enable_encrypt);
3218 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3219 }
3220
3221 return CMD_SUCCESS;
3222}
3223
3224DEFUN(no_service_password_encrypt,
3225 no_service_password_encrypt_cmd,
3226 "no service password-encryption",
3227 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3228{
3229 if (!host.encrypt)
3230 return CMD_SUCCESS;
3231
3232 host.encrypt = 0;
3233
3234 if (host.password_encrypt)
3235 talloc_free(host.password_encrypt);
3236 host.password_encrypt = NULL;
3237
3238 if (host.enable_encrypt)
3239 talloc_free(host.enable_encrypt);
3240 host.enable_encrypt = NULL;
3241
3242 return CMD_SUCCESS;
3243}
3244#endif
3245
3246DEFUN(config_terminal_length, config_terminal_length_cmd,
3247 "terminal length <0-512>",
3248 "Set terminal line parameters\n"
3249 "Set number of lines on a screen\n"
3250 "Number of lines on screen (0 for no pausing)\n")
3251{
3252 int lines;
3253 char *endptr = NULL;
3254
3255 lines = strtol(argv[0], &endptr, 10);
3256 if (lines < 0 || lines > 512 || *endptr != '\0') {
3257 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3258 return CMD_WARNING;
3259 }
3260 vty->lines = lines;
3261
3262 return CMD_SUCCESS;
3263}
3264
3265DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3266 "terminal no length",
3267 "Set terminal line parameters\n"
3268 NO_STR "Set number of lines on a screen\n")
3269{
3270 vty->lines = -1;
3271 return CMD_SUCCESS;
3272}
3273
3274DEFUN(service_terminal_length, service_terminal_length_cmd,
3275 "service terminal-length <0-512>",
3276 "Set up miscellaneous service\n"
3277 "System wide terminal length configuration\n"
3278 "Number of lines of VTY (0 means no line control)\n")
3279{
3280 int lines;
3281 char *endptr = NULL;
3282
3283 lines = strtol(argv[0], &endptr, 10);
3284 if (lines < 0 || lines > 512 || *endptr != '\0') {
3285 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3286 return CMD_WARNING;
3287 }
3288 host.lines = lines;
3289
3290 return CMD_SUCCESS;
3291}
3292
3293DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3294 "no service terminal-length [<0-512>]",
3295 NO_STR
3296 "Set up miscellaneous service\n"
3297 "System wide terminal length configuration\n"
3298 "Number of lines of VTY (0 means no line control)\n")
3299{
3300 host.lines = -1;
3301 return CMD_SUCCESS;
3302}
3303
3304DEFUN_HIDDEN(do_echo,
3305 echo_cmd,
3306 "echo .MESSAGE",
3307 "Echo a message back to the vty\n" "The message to echo\n")
3308{
3309 char *message;
3310
3311 vty_out(vty, "%s%s",
3312 ((message =
3313 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3314 if (message)
3315 talloc_free(message);
3316 return CMD_SUCCESS;
3317}
3318
3319#if 0
3320DEFUN(config_logmsg,
3321 config_logmsg_cmd,
3322 "logmsg " LOG_LEVELS " .MESSAGE",
3323 "Send a message to enabled logging destinations\n"
3324 LOG_LEVEL_DESC "The message to send\n")
3325{
3326 int level;
3327 char *message;
3328
3329 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3330 return CMD_ERR_NO_MATCH;
3331
3332 zlog(NULL, level,
3333 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3334 if (message)
3335 talloc_free(message);
3336 return CMD_SUCCESS;
3337}
3338
3339DEFUN(show_logging,
3340 show_logging_cmd,
3341 "show logging", SHOW_STR "Show current logging configuration\n")
3342{
3343 struct zlog *zl = zlog_default;
3344
3345 vty_out(vty, "Syslog logging: ");
3346 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3347 vty_out(vty, "disabled");
3348 else
3349 vty_out(vty, "level %s, facility %s, ident %s",
3350 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3351 facility_name(zl->facility), zl->ident);
3352 vty_out(vty, "%s", VTY_NEWLINE);
3353
3354 vty_out(vty, "Stdout logging: ");
3355 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3356 vty_out(vty, "disabled");
3357 else
3358 vty_out(vty, "level %s",
3359 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3360 vty_out(vty, "%s", VTY_NEWLINE);
3361
3362 vty_out(vty, "Monitor logging: ");
3363 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3364 vty_out(vty, "disabled");
3365 else
3366 vty_out(vty, "level %s",
3367 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3368 vty_out(vty, "%s", VTY_NEWLINE);
3369
3370 vty_out(vty, "File logging: ");
3371 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3372 vty_out(vty, "disabled");
3373 else
3374 vty_out(vty, "level %s, filename %s",
3375 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3376 zl->filename);
3377 vty_out(vty, "%s", VTY_NEWLINE);
3378
3379 vty_out(vty, "Protocol name: %s%s",
3380 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3381 vty_out(vty, "Record priority: %s%s",
3382 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3383
3384 return CMD_SUCCESS;
3385}
3386
3387DEFUN(config_log_stdout,
3388 config_log_stdout_cmd,
3389 "log stdout", "Logging control\n" "Set stdout logging level\n")
3390{
3391 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3392 return CMD_SUCCESS;
3393}
3394
3395DEFUN(config_log_stdout_level,
3396 config_log_stdout_level_cmd,
3397 "log stdout " LOG_LEVELS,
3398 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3399{
3400 int level;
3401
3402 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3403 return CMD_ERR_NO_MATCH;
3404 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3405 return CMD_SUCCESS;
3406}
3407
3408DEFUN(no_config_log_stdout,
3409 no_config_log_stdout_cmd,
3410 "no log stdout [LEVEL]",
3411 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3412{
3413 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3414 return CMD_SUCCESS;
3415}
3416
3417DEFUN(config_log_monitor,
3418 config_log_monitor_cmd,
3419 "log monitor",
3420 "Logging control\n" "Set terminal line (monitor) logging level\n")
3421{
3422 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3423 return CMD_SUCCESS;
3424}
3425
3426DEFUN(config_log_monitor_level,
3427 config_log_monitor_level_cmd,
3428 "log monitor " LOG_LEVELS,
3429 "Logging control\n"
3430 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3431{
3432 int level;
3433
3434 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3435 return CMD_ERR_NO_MATCH;
3436 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3437 return CMD_SUCCESS;
3438}
3439
3440DEFUN(no_config_log_monitor,
3441 no_config_log_monitor_cmd,
3442 "no log monitor [LEVEL]",
3443 NO_STR
3444 "Logging control\n"
3445 "Disable terminal line (monitor) logging\n" "Logging level\n")
3446{
3447 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3448 return CMD_SUCCESS;
3449}
3450
3451static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3452{
3453 int ret;
3454 char *p = NULL;
3455 const char *fullpath;
3456
3457 /* Path detection. */
3458 if (!IS_DIRECTORY_SEP(*fname)) {
3459 char cwd[MAXPATHLEN + 1];
3460 cwd[MAXPATHLEN] = '\0';
3461
3462 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3463 zlog_err("config_log_file: Unable to alloc mem!");
3464 return CMD_WARNING;
3465 }
3466
3467 if ((p = _talloc_zero(tall_vcmd_ctx,
3468 strlen(cwd) + strlen(fname) + 2),
3469 "set_log_file")
3470 == NULL) {
3471 zlog_err("config_log_file: Unable to alloc mem!");
3472 return CMD_WARNING;
3473 }
3474 sprintf(p, "%s/%s", cwd, fname);
3475 fullpath = p;
3476 } else
3477 fullpath = fname;
3478
3479 ret = zlog_set_file(NULL, fullpath, loglevel);
3480
3481 if (p)
3482 talloc_free(p);
3483
3484 if (!ret) {
3485 vty_out(vty, "can't open logfile %s\n", fname);
3486 return CMD_WARNING;
3487 }
3488
3489 if (host.logfile)
3490 talloc_free(host.logfile);
3491
3492 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3493
3494 return CMD_SUCCESS;
3495}
3496
3497DEFUN(config_log_file,
3498 config_log_file_cmd,
3499 "log file FILENAME",
3500 "Logging control\n" "Logging to file\n" "Logging filename\n")
3501{
3502 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3503}
3504
3505DEFUN(config_log_file_level,
3506 config_log_file_level_cmd,
3507 "log file FILENAME " LOG_LEVELS,
3508 "Logging control\n"
3509 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3510{
3511 int level;
3512
3513 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3514 return CMD_ERR_NO_MATCH;
3515 return set_log_file(vty, argv[0], level);
3516}
3517
3518DEFUN(no_config_log_file,
3519 no_config_log_file_cmd,
3520 "no log file [FILENAME]",
3521 NO_STR
3522 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3523{
3524 zlog_reset_file(NULL);
3525
3526 if (host.logfile)
3527 talloc_free(host.logfile);
3528
3529 host.logfile = NULL;
3530
3531 return CMD_SUCCESS;
3532}
3533
3534ALIAS(no_config_log_file,
3535 no_config_log_file_level_cmd,
3536 "no log file FILENAME LEVEL",
3537 NO_STR
3538 "Logging control\n"
3539 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3540
3541 DEFUN(config_log_syslog,
3542 config_log_syslog_cmd,
3543 "log syslog", "Logging control\n" "Set syslog logging level\n")
3544{
3545 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3546 return CMD_SUCCESS;
3547}
3548
3549DEFUN(config_log_syslog_level,
3550 config_log_syslog_level_cmd,
3551 "log syslog " LOG_LEVELS,
3552 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3553{
3554 int level;
3555
3556 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3557 return CMD_ERR_NO_MATCH;
3558 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3559 return CMD_SUCCESS;
3560}
3561
3562DEFUN_DEPRECATED(config_log_syslog_facility,
3563 config_log_syslog_facility_cmd,
3564 "log syslog facility " LOG_FACILITIES,
3565 "Logging control\n"
3566 "Logging goes to syslog\n"
3567 "(Deprecated) Facility parameter for syslog messages\n"
3568 LOG_FACILITY_DESC)
3569{
3570 int facility;
3571
3572 if ((facility = facility_match(argv[0])) < 0)
3573 return CMD_ERR_NO_MATCH;
3574
3575 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3576 zlog_default->facility = facility;
3577 return CMD_SUCCESS;
3578}
3579
3580DEFUN(no_config_log_syslog,
3581 no_config_log_syslog_cmd,
3582 "no log syslog [LEVEL]",
3583 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3584{
3585 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3586 return CMD_SUCCESS;
3587}
3588
3589ALIAS(no_config_log_syslog,
3590 no_config_log_syslog_facility_cmd,
3591 "no log syslog facility " LOG_FACILITIES,
3592 NO_STR
3593 "Logging control\n"
3594 "Logging goes to syslog\n"
3595 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3596
3597 DEFUN(config_log_facility,
3598 config_log_facility_cmd,
3599 "log facility " LOG_FACILITIES,
3600 "Logging control\n"
3601 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3602{
3603 int facility;
3604
3605 if ((facility = facility_match(argv[0])) < 0)
3606 return CMD_ERR_NO_MATCH;
3607 zlog_default->facility = facility;
3608 return CMD_SUCCESS;
3609}
3610
3611DEFUN(no_config_log_facility,
3612 no_config_log_facility_cmd,
3613 "no log facility [FACILITY]",
3614 NO_STR
3615 "Logging control\n"
3616 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3617{
3618 zlog_default->facility = LOG_DAEMON;
3619 return CMD_SUCCESS;
3620}
3621
3622DEFUN_DEPRECATED(config_log_trap,
3623 config_log_trap_cmd,
3624 "log trap " LOG_LEVELS,
3625 "Logging control\n"
3626 "(Deprecated) Set logging level and default for all destinations\n"
3627 LOG_LEVEL_DESC)
3628{
3629 int new_level;
3630 int i;
3631
3632 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3633 return CMD_ERR_NO_MATCH;
3634
3635 zlog_default->default_lvl = new_level;
3636 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3637 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3638 zlog_default->maxlvl[i] = new_level;
3639 return CMD_SUCCESS;
3640}
3641
3642DEFUN_DEPRECATED(no_config_log_trap,
3643 no_config_log_trap_cmd,
3644 "no log trap [LEVEL]",
3645 NO_STR
3646 "Logging control\n"
3647 "Permit all logging information\n" "Logging level\n")
3648{
3649 zlog_default->default_lvl = LOG_DEBUG;
3650 return CMD_SUCCESS;
3651}
3652
3653DEFUN(config_log_record_priority,
3654 config_log_record_priority_cmd,
3655 "log record-priority",
3656 "Logging control\n"
3657 "Log the priority of the message within the message\n")
3658{
3659 zlog_default->record_priority = 1;
3660 return CMD_SUCCESS;
3661}
3662
3663DEFUN(no_config_log_record_priority,
3664 no_config_log_record_priority_cmd,
3665 "no log record-priority",
3666 NO_STR
3667 "Logging control\n"
3668 "Do not log the priority of the message within the message\n")
3669{
3670 zlog_default->record_priority = 0;
3671 return CMD_SUCCESS;
3672}
3673#endif
3674
3675DEFUN(banner_motd_file,
3676 banner_motd_file_cmd,
3677 "banner motd file [FILE]",
3678 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3679{
3680 if (host.motdfile)
3681 talloc_free(host.motdfile);
3682 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3683
3684 return CMD_SUCCESS;
3685}
3686
3687DEFUN(banner_motd_default,
3688 banner_motd_default_cmd,
3689 "banner motd default",
3690 "Set banner string\n" "Strings for motd\n" "Default string\n")
3691{
3692 host.motd = default_motd;
3693 return CMD_SUCCESS;
3694}
3695
3696DEFUN(no_banner_motd,
3697 no_banner_motd_cmd,
3698 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3699{
3700 host.motd = NULL;
3701 if (host.motdfile)
3702 talloc_free(host.motdfile);
3703 host.motdfile = NULL;
3704 return CMD_SUCCESS;
3705}
3706
3707/* Set config filename. Called from vty.c */
3708void host_config_set(const char *filename)
3709{
3710 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3711}
3712
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003713/*! Deprecated, now happens implicitly when calling install_node().
3714 * Users of the API may still attempt to call this function, hence
3715 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003716void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003717{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003718}
3719
3720/*! Deprecated, now happens implicitly when calling install_node().
3721 * Users of the API may still attempt to call this function, hence
3722 * leave it here as a no-op. */
3723void vty_install_default(int node)
3724{
3725}
3726
3727/*! Install common commands like 'exit' and 'list'. */
3728static void install_basic_node_commands(int node)
3729{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003730 install_element(node, &config_help_cmd);
3731 install_element(node, &config_list_cmd);
3732
3733 install_element(node, &config_write_terminal_cmd);
3734 install_element(node, &config_write_file_cmd);
3735 install_element(node, &config_write_memory_cmd);
3736 install_element(node, &config_write_cmd);
3737 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003738
3739 install_element(node, &config_exit_cmd);
3740
3741 if (node >= CONFIG_NODE) {
3742 /* It's not a top node. */
3743 install_element(node, &config_end_cmd);
3744 }
3745}
3746
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003747/*! Return true if a node is installed by install_basic_node_commands(), so
3748 * that we can avoid repeating them for each and every node during 'show
3749 * running-config' */
3750static bool vty_command_is_common(struct cmd_element *cmd)
3751{
3752 if (cmd == &config_help_cmd
3753 || cmd == &config_list_cmd
3754 || cmd == &config_write_terminal_cmd
3755 || cmd == &config_write_file_cmd
3756 || cmd == &config_write_memory_cmd
3757 || cmd == &config_write_cmd
3758 || cmd == &show_running_config_cmd
3759 || cmd == &config_exit_cmd
3760 || cmd == &config_end_cmd)
3761 return true;
3762 return false;
3763}
3764
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003765/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003766 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003767 * \param[in] vty the vty of the code
3768 * \param[in] filename where to store the file
3769 * \return 0 in case of success.
3770 *
3771 * If the filename already exists create a filename.sav
3772 * version with the current code.
3773 *
3774 */
3775int osmo_vty_write_config_file(const char *filename)
3776{
3777 char *failed_file;
3778 int rc;
3779
3780 rc = write_config_file(filename, &failed_file);
3781 talloc_free(failed_file);
3782 return rc;
3783}
3784
3785/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003786 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003787 * \return 0 in case of success.
3788 *
3789 * If the filename already exists create a filename.sav
3790 * version with the current code.
3791 *
3792 */
3793int osmo_vty_save_config_file(void)
3794{
3795 char *failed_file;
3796 int rc;
3797
3798 if (host.config == NULL)
3799 return -7;
3800
3801 rc = write_config_file(host.config, &failed_file);
3802 talloc_free(failed_file);
3803 return rc;
3804}
3805
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003806/* Initialize command interface. Install basic nodes and commands. */
3807void cmd_init(int terminal)
3808{
3809 /* Allocate initial top vector of commands. */
3810 cmdvec = vector_init(VECTOR_MIN_SIZE);
3811
3812 /* Default host value settings. */
3813 host.name = NULL;
3814 host.password = NULL;
3815 host.enable = NULL;
3816 host.logfile = NULL;
3817 host.config = NULL;
3818 host.lines = -1;
3819 host.motd = default_motd;
3820 host.motdfile = NULL;
3821
3822 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003823 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003824 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003825 install_node_bare(&auth_node, NULL);
3826 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003827 install_node(&config_node, config_write_host);
3828
3829 /* Each node's basic commands. */
3830 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003831 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003832 if (terminal) {
3833 install_element(VIEW_NODE, &config_list_cmd);
3834 install_element(VIEW_NODE, &config_exit_cmd);
3835 install_element(VIEW_NODE, &config_help_cmd);
3836 install_element(VIEW_NODE, &config_enable_cmd);
3837 install_element(VIEW_NODE, &config_terminal_length_cmd);
3838 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3839 install_element(VIEW_NODE, &echo_cmd);
3840 }
3841
3842 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003843 install_element(ENABLE_NODE, &config_disable_cmd);
3844 install_element(ENABLE_NODE, &config_terminal_cmd);
3845 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3846 }
3847 install_element (ENABLE_NODE, &show_startup_config_cmd);
3848 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003849 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003850
3851 if (terminal) {
3852 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3853 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3854 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003855 }
3856
3857 install_element(CONFIG_NODE, &hostname_cmd);
3858 install_element(CONFIG_NODE, &no_hostname_cmd);
3859
3860 if (terminal) {
3861 install_element(CONFIG_NODE, &password_cmd);
3862 install_element(CONFIG_NODE, &password_text_cmd);
3863 install_element(CONFIG_NODE, &enable_password_cmd);
3864 install_element(CONFIG_NODE, &enable_password_text_cmd);
3865 install_element(CONFIG_NODE, &no_enable_password_cmd);
3866
3867#ifdef VTY_CRYPT_PW
3868 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3869 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3870#endif
3871 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3872 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3873 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3874 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3875 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3876
3877 }
3878 srand(time(NULL));
3879}
Harald Welte7acb30c2011-08-17 17:13:48 +02003880
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003881/*! @} */