blob: 3c91bfd39185b9a69af611cf3a5f009dd8557bcd [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{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002220 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002221 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;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002231 int rc;
2232 /* Used for temporary storage of cmd_deopt() allocated arguments during
2233 argv[] generation */
2234 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002235
2236 /* Make copy of command elements. */
2237 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2238
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002239 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002240 if ((command = vector_slot(vline, index))) {
2241 int ret;
2242
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002243 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002244 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002245
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002246 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002247 break;
2248
2249 ret =
2250 is_cmd_ambiguous(command, cmd_vector, index, match);
2251
2252 if (ret == 1) {
2253 vector_free(cmd_vector);
2254 return CMD_ERR_AMBIGUOUS;
2255 } else if (ret == 2) {
2256 vector_free(cmd_vector);
2257 return CMD_ERR_NO_MATCH;
2258 }
2259 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002260 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002261
2262 /* Check matched count. */
2263 matched_element = NULL;
2264 matched_count = 0;
2265 incomplete_count = 0;
2266
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002267 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002268 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002269 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002270 || index >= cmd_element->cmdsize) {
2271 matched_element = cmd_element;
2272#if 0
2273 printf("DEBUG: %s\n", cmd_element->string);
2274#endif
2275 matched_count++;
2276 } else {
2277 incomplete_count++;
2278 }
2279 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002280 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002281
2282 /* Finish of using cmd_vector. */
2283 vector_free(cmd_vector);
2284
2285 /* To execute command, matched_count must be 1. */
2286 if (matched_count == 0) {
2287 if (incomplete_count)
2288 return CMD_ERR_INCOMPLETE;
2289 else
2290 return CMD_ERR_NO_MATCH;
2291 }
2292
2293 if (matched_count > 1)
2294 return CMD_ERR_AMBIGUOUS;
2295
2296 /* Argument treatment */
2297 varflag = 0;
2298 argc = 0;
2299
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002300 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2301
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002302 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002303 if (argc == CMD_ARGC_MAX) {
2304 rc = CMD_ERR_EXEED_ARGC_MAX;
2305 goto rc_free_deopt_ctx;
2306 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002307 if (varflag) {
2308 argv[argc++] = vector_slot(vline, i);
2309 continue;
2310 }
2311
2312 vector descvec = vector_slot(matched_element->strvec, i);
2313
2314 if (vector_active(descvec) == 1) {
2315 struct desc *desc = vector_slot(descvec, 0);
2316
2317 if (CMD_VARARG(desc->cmd))
2318 varflag = 1;
2319
2320 if (varflag || CMD_VARIABLE(desc->cmd)
2321 || CMD_OPTION(desc->cmd))
2322 argv[argc++] = vector_slot(vline, i);
2323 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002324 /* multi choice argument. look up which choice
2325 the user meant (can only be one after
2326 filtering and checking for ambigous). For instance,
2327 if user typed "th" for "(two|three)" arg, we
2328 want to pass "three" in argv[]. */
2329 for (j = 0; j < vector_active(descvec); j++) {
2330 struct desc *desc = vector_slot(descvec, j);
2331 const char *tmp_cmd;
2332 if (!desc)
2333 continue;
2334 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2335 continue;
2336 if (CMD_OPTION(desc->cmd)) {
2337 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2338 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2339 } else {
2340 tmp_cmd = desc->cmd;
2341 }
2342
2343 if(CMD_VARIABLE(tmp_cmd)) {
2344 argv[argc++] = vector_slot(vline, i);
2345 } else {
2346 argv[argc++] = tmp_cmd;
2347 }
2348 break;
2349 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002350 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002351 }
2352
2353 /* For vtysh execution. */
2354 if (cmd)
2355 *cmd = matched_element;
2356
2357 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002358 rc = CMD_SUCCESS_DAEMON;
2359 else /* Execute matched command. */
2360 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002361
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002362rc_free_deopt_ctx:
2363 /* Now after we called the command func, we can free temporary strings */
2364 talloc_free(cmd_deopt_ctx);
2365 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002366}
2367
2368int
2369cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2370 int vtysh)
2371{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002372 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002373 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002374
2375 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002376
2377 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2378 vector shifted_vline;
2379 unsigned int index;
2380
2381 vty->node = ENABLE_NODE;
2382 /* We can try it on enable node, cos' the vty is authenticated */
2383
2384 shifted_vline = vector_init(vector_count(vline));
2385 /* use memcpy? */
2386 for (index = 1; index < vector_active(vline); index++) {
2387 vector_set_index(shifted_vline, index - 1,
2388 vector_lookup(vline, index));
2389 }
2390
2391 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2392
2393 vector_free(shifted_vline);
2394 vty->node = onode;
2395 return ret;
2396 }
2397
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002398 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002399}
2400
2401/* Execute command by argument readline. */
2402int
2403cmd_execute_command_strict(vector vline, struct vty *vty,
2404 struct cmd_element **cmd)
2405{
2406 unsigned int i;
2407 unsigned int index;
2408 vector cmd_vector;
2409 struct cmd_element *cmd_element;
2410 struct cmd_element *matched_element;
2411 unsigned int matched_count, incomplete_count;
2412 int argc;
2413 const char *argv[CMD_ARGC_MAX];
2414 int varflag;
2415 enum match_type match = 0;
2416 char *command;
2417
2418 /* Make copy of command element */
2419 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2420
2421 for (index = 0; index < vector_active(vline); index++)
2422 if ((command = vector_slot(vline, index))) {
2423 int ret;
2424
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002425 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002426 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002427
2428 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002429 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002430 break;
2431
2432 ret =
2433 is_cmd_ambiguous(command, cmd_vector, index, match);
2434 if (ret == 1) {
2435 vector_free(cmd_vector);
2436 return CMD_ERR_AMBIGUOUS;
2437 }
2438 if (ret == 2) {
2439 vector_free(cmd_vector);
2440 return CMD_ERR_NO_MATCH;
2441 }
2442 }
2443
2444 /* Check matched count. */
2445 matched_element = NULL;
2446 matched_count = 0;
2447 incomplete_count = 0;
2448 for (i = 0; i < vector_active(cmd_vector); i++)
2449 if (vector_slot(cmd_vector, i) != NULL) {
2450 cmd_element = vector_slot(cmd_vector, i);
2451
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002452 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002453 || index >= cmd_element->cmdsize) {
2454 matched_element = cmd_element;
2455 matched_count++;
2456 } else
2457 incomplete_count++;
2458 }
2459
2460 /* Finish of using cmd_vector. */
2461 vector_free(cmd_vector);
2462
2463 /* To execute command, matched_count must be 1. */
2464 if (matched_count == 0) {
2465 if (incomplete_count)
2466 return CMD_ERR_INCOMPLETE;
2467 else
2468 return CMD_ERR_NO_MATCH;
2469 }
2470
2471 if (matched_count > 1)
2472 return CMD_ERR_AMBIGUOUS;
2473
2474 /* Argument treatment */
2475 varflag = 0;
2476 argc = 0;
2477
2478 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002479 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002480 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002481 if (varflag) {
2482 argv[argc++] = vector_slot(vline, i);
2483 continue;
2484 }
2485
2486 vector descvec = vector_slot(matched_element->strvec, i);
2487
2488 if (vector_active(descvec) == 1) {
2489 struct desc *desc = vector_slot(descvec, 0);
2490
2491 if (CMD_VARARG(desc->cmd))
2492 varflag = 1;
2493
2494 if (varflag || CMD_VARIABLE(desc->cmd)
2495 || CMD_OPTION(desc->cmd))
2496 argv[argc++] = vector_slot(vline, i);
2497 } else {
2498 argv[argc++] = vector_slot(vline, i);
2499 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002500 }
2501
2502 /* For vtysh execution. */
2503 if (cmd)
2504 *cmd = matched_element;
2505
2506 if (matched_element->daemon)
2507 return CMD_SUCCESS_DAEMON;
2508
2509 /* Now execute matched command */
2510 return (*matched_element->func) (matched_element, vty, argc, argv);
2511}
2512
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002513static inline size_t len(const char *str)
2514{
2515 return str? strlen(str) : 0;
2516}
2517
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002518/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2519 * is longer than b, a must start with exactly b, and vice versa.
2520 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2521 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002522static int indent_cmp(const char *a, const char *b)
2523{
2524 size_t al, bl;
2525 al = len(a);
2526 bl = len(b);
2527 if (al > bl) {
2528 if (bl && strncmp(a, b, bl) != 0)
2529 return EINVAL;
2530 return 1;
2531 }
2532 /* al <= bl */
2533 if (al && strncmp(a, b, al) != 0)
2534 return EINVAL;
2535 return (al < bl)? -1 : 0;
2536}
2537
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002538/* Configration make from file. */
2539int config_from_file(struct vty *vty, FILE * fp)
2540{
2541 int ret;
2542 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002543 char *indent;
2544 int cmp;
2545 struct vty_parent_node this_node;
2546 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002547
2548 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002549 indent = NULL;
2550 vline = NULL;
2551 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002552
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002553 if (ret != CMD_SUCCESS)
2554 goto return_invalid_indent;
2555
2556 /* In case of comment or empty line */
2557 if (vline == NULL) {
2558 if (indent) {
2559 talloc_free(indent);
2560 indent = NULL;
2561 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002562 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002563 }
2564
Neels Hofmeyr43063632017-09-19 23:54:01 +02002565 /* We have a nonempty line. */
2566 if (!vty->indent) {
2567 /* We have just entered a node and expecting the first child to come up; but we
2568 * may also skip right back to a parent or ancestor level. */
2569 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002570
Neels Hofmeyr43063632017-09-19 23:54:01 +02002571 /* If there is no parent, record any indentation we encounter. */
2572 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2573
2574 if (cmp == EINVAL)
2575 goto return_invalid_indent;
2576
2577 if (cmp <= 0) {
2578 /* We have gone right back to the parent level or higher, we are skipping
2579 * this child node level entirely. Pop the parent to go back to a node
2580 * that was actually there (to reinstate vty->indent) and re-use below
2581 * go-parent while-loop to find an accurate match of indent in the node
2582 * ancestry. */
2583 vty_go_parent(vty);
2584 } else {
2585 /* The indent is deeper than the just entered parent, record the new
2586 * indentation characters. */
2587 vty->indent = talloc_strdup(vty, indent);
2588 /* This *is* the new indentation. */
2589 cmp = 0;
2590 }
2591 } else {
2592 /* There is a known indentation for this node level, validate and detect node
2593 * exits. */
2594 cmp = indent_cmp(indent, vty->indent);
2595 if (cmp == EINVAL)
2596 goto return_invalid_indent;
2597 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002598
2599 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2600 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2601 while (cmp < 0) {
2602 vty_go_parent(vty);
2603 cmp = indent_cmp(indent, vty->indent);
2604 if (cmp == EINVAL)
2605 goto return_invalid_indent;
2606 }
2607
2608 /* More indent without having entered a child node level? Either the parent node's indent
2609 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2610 * or the indentation increased even though the vty command didn't enter a child. */
2611 if (cmp > 0)
2612 goto return_invalid_indent;
2613
2614 /* Remember the current node before the command possibly changes it. */
2615 this_node = (struct vty_parent_node){
2616 .node = vty->node,
2617 .priv = vty->priv,
2618 .indent = vty->indent,
2619 };
2620
2621 parent = vty_parent(vty);
2622 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002623 cmd_free_strvec(vline);
2624
2625 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002626 && ret != CMD_ERR_NOTHING_TODO) {
2627 if (indent) {
2628 talloc_free(indent);
2629 indent = NULL;
2630 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002631 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002632 }
2633
2634 /* If we have stepped down into a child node, push a parent frame.
2635 * The causality is such: we don't expect every single node entry implementation to push
2636 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2637 * a parent node. Hence if the node changed without the parent node changing, we must
2638 * have stepped into a child node (and now expect a deeper indent). */
2639 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2640 /* Push the parent node. */
2641 parent = talloc_zero(vty, struct vty_parent_node);
2642 *parent = this_node;
2643 llist_add(&parent->entry, &vty->parent_nodes);
2644
2645 /* The current talloc'ed vty->indent string will now be owned by this parent
2646 * struct. Indicate that we don't know what deeper indent characters the user
2647 * will choose. */
2648 vty->indent = NULL;
2649 }
2650
2651 if (indent) {
2652 talloc_free(indent);
2653 indent = NULL;
2654 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002655 }
2656 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002657
2658return_invalid_indent:
2659 if (vline)
2660 cmd_free_strvec(vline);
2661 if (indent) {
2662 talloc_free(indent);
2663 indent = NULL;
2664 }
2665 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002666}
2667
2668/* Configration from terminal */
2669DEFUN(config_terminal,
2670 config_terminal_cmd,
2671 "configure terminal",
2672 "Configuration from vty interface\n" "Configuration terminal\n")
2673{
2674 if (vty_config_lock(vty))
2675 vty->node = CONFIG_NODE;
2676 else {
2677 vty_out(vty, "VTY configuration is locked by other VTY%s",
2678 VTY_NEWLINE);
2679 return CMD_WARNING;
2680 }
2681 return CMD_SUCCESS;
2682}
2683
2684/* Enable command */
2685DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2686{
2687 /* If enable password is NULL, change to ENABLE_NODE */
2688 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2689 vty->type == VTY_SHELL_SERV)
2690 vty->node = ENABLE_NODE;
2691 else
2692 vty->node = AUTH_ENABLE_NODE;
2693
2694 return CMD_SUCCESS;
2695}
2696
2697/* Disable command */
2698DEFUN(disable,
2699 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2700{
2701 if (vty->node == ENABLE_NODE)
2702 vty->node = VIEW_NODE;
2703 return CMD_SUCCESS;
2704}
2705
2706/* Down vty node level. */
2707gDEFUN(config_exit,
2708 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2709{
2710 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002711 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002712 case VIEW_NODE:
2713 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002714 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002715 break;
2716 case CONFIG_NODE:
2717 vty->node = ENABLE_NODE;
2718 vty_config_unlock(vty);
2719 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002720 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002721 if (vty->node > CONFIG_NODE)
2722 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002723 break;
2724 }
2725 return CMD_SUCCESS;
2726}
2727
2728/* End of configuration. */
2729 gDEFUN(config_end,
2730 config_end_cmd, "end", "End current mode and change to enable mode.")
2731{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002732 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002733 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002734
2735 /* Repeatedly call go_parent until a top node is reached. */
2736 while (vty->node > CONFIG_NODE) {
2737 if (vty->node == last_node) {
2738 /* Ensure termination, this shouldn't happen. */
2739 break;
2740 }
2741 last_node = vty->node;
2742 vty_go_parent(vty);
2743 }
2744
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002745 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002746 if (vty->node > ENABLE_NODE)
2747 vty->node = ENABLE_NODE;
2748 vty->index = NULL;
2749 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002750 }
2751 return CMD_SUCCESS;
2752}
2753
2754/* Show version. */
2755DEFUN(show_version,
2756 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2757{
Harald Welte237f6242010-05-25 23:00:45 +02002758 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2759 host.app_info->version,
2760 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2761 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002762
2763 return CMD_SUCCESS;
2764}
2765
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002766DEFUN(show_online_help,
2767 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2768{
2769 vty_dump_nodes(vty);
2770 return CMD_SUCCESS;
2771}
2772
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002773/* Help display function for all node. */
2774gDEFUN(config_help,
2775 config_help_cmd, "help", "Description of the interactive help system\n")
2776{
2777 vty_out(vty,
2778 "This VTY provides advanced help features. When you need help,%s\
2779anytime at the command line please press '?'.%s\
2780%s\
2781If nothing matches, the help list will be empty and you must backup%s\
2782 until entering a '?' shows the available options.%s\
2783Two styles of help are provided:%s\
27841. Full help is available when you are ready to enter a%s\
2785command argument (e.g. 'show ?') and describes each possible%s\
2786argument.%s\
27872. Partial help is provided when an abbreviated argument is entered%s\
2788 and you want to know what arguments match the input%s\
2789 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2790 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2791 return CMD_SUCCESS;
2792}
2793
2794/* Help display function for all node. */
2795gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2796{
2797 unsigned int i;
2798 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2799 struct cmd_element *cmd;
2800
2801 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2802 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002803 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002804 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2805 return CMD_SUCCESS;
2806}
2807
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002808static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002809{
2810 unsigned int i;
2811 int fd;
2812 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002813 char *config_file_tmp = NULL;
2814 char *config_file_sav = NULL;
2815 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002816 struct stat st;
2817
2818 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002819
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002820 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2821 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2822 * manually instead. */
2823
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002824 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002825 config_file_sav =
2826 _talloc_zero(tall_vty_cmd_ctx,
2827 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2828 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002829 if (!config_file_sav)
2830 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002831 strcpy(config_file_sav, config_file);
2832 strcat(config_file_sav, CONF_BACKUP_EXT);
2833
2834 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002835 "config_file_tmp");
2836 if (!config_file_tmp) {
2837 talloc_free(config_file_sav);
2838 return -1;
2839 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002840 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2841
2842 /* Open file to configuration write. */
2843 fd = mkstemp(config_file_tmp);
2844 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002845 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002846 talloc_free(config_file_tmp);
2847 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002848 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002849 }
2850
2851 /* Make vty for configuration file. */
2852 file_vty = vty_new();
2853 file_vty->fd = fd;
2854 file_vty->type = VTY_FILE;
2855
2856 /* Config file header print. */
2857 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002858 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002859 //vty_time_print (file_vty, 1);
2860 vty_out(file_vty, "!\n");
2861
2862 for (i = 0; i < vector_active(cmdvec); i++)
2863 if ((node = vector_slot(cmdvec, i)) && node->func) {
2864 if ((*node->func) (file_vty))
2865 vty_out(file_vty, "!\n");
2866 }
2867 vty_close(file_vty);
2868
2869 if (unlink(config_file_sav) != 0)
2870 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002871 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002872 talloc_free(config_file_sav);
2873 talloc_free(config_file_tmp);
2874 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002875 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002876 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002877
2878 /* Only link the .sav file if the original file exists */
2879 if (stat(config_file, &st) == 0) {
2880 if (link(config_file, config_file_sav) != 0) {
2881 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2882 talloc_free(config_file_sav);
2883 talloc_free(config_file_tmp);
2884 unlink(config_file_tmp);
2885 return -3;
2886 }
2887 sync();
2888 if (unlink(config_file) != 0) {
2889 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2890 talloc_free(config_file_sav);
2891 talloc_free(config_file_tmp);
2892 unlink(config_file_tmp);
2893 return -4;
2894 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002895 }
2896 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002897 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002898 talloc_free(config_file_sav);
2899 talloc_free(config_file_tmp);
2900 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002901 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002902 }
2903 unlink(config_file_tmp);
2904 sync();
2905
2906 talloc_free(config_file_sav);
2907 talloc_free(config_file_tmp);
2908
2909 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002910 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2911 return -6;
2912 }
2913
2914 return 0;
2915}
2916
2917
2918/* Write current configuration into file. */
2919DEFUN(config_write_file,
2920 config_write_file_cmd,
2921 "write file",
2922 "Write running configuration to memory, network, or terminal\n"
2923 "Write to configuration file\n")
2924{
2925 char *failed_file;
2926 int rc;
2927
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002928 if (host.app_info->config_is_consistent) {
2929 rc = host.app_info->config_is_consistent(vty);
2930 if (!rc) {
2931 vty_out(vty, "Configuration is not consistent%s",
2932 VTY_NEWLINE);
2933 return CMD_WARNING;
2934 }
2935 }
2936
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002937 if (host.config == NULL) {
2938 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2939 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002940 return CMD_WARNING;
2941 }
2942
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002943 rc = write_config_file(host.config, &failed_file);
2944 switch (rc) {
2945 case -1:
2946 vty_out(vty, "Can't open configuration file %s.%s",
2947 failed_file, VTY_NEWLINE);
2948 rc = CMD_WARNING;
2949 break;
2950 case -2:
2951 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2952 failed_file, VTY_NEWLINE);
2953 rc = CMD_WARNING;
2954 break;
2955 case -3:
2956 vty_out(vty, "Can't backup old configuration file %s.%s",
2957 failed_file, VTY_NEWLINE);
2958 rc = CMD_WARNING;
2959 break;
2960 case -4:
2961 vty_out(vty, "Can't unlink configuration file %s.%s",
2962 failed_file, VTY_NEWLINE);
2963 rc = CMD_WARNING;
2964 break;
2965 case -5:
2966 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2967 VTY_NEWLINE);
2968 rc = CMD_WARNING;
2969 break;
2970 case -6:
2971 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2972 failed_file, strerror(errno), errno, VTY_NEWLINE);
2973 rc = CMD_WARNING;
2974 break;
2975 default:
2976 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2977 rc = CMD_SUCCESS;
2978 break;
2979 }
2980
2981 talloc_free(failed_file);
2982 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002983}
2984
2985ALIAS(config_write_file,
2986 config_write_cmd,
2987 "write", "Write running configuration to memory, network, or terminal\n")
2988
2989 ALIAS(config_write_file,
2990 config_write_memory_cmd,
2991 "write memory",
2992 "Write running configuration to memory, network, or terminal\n"
2993 "Write configuration to the file (same as write file)\n")
2994
2995 ALIAS(config_write_file,
2996 copy_runningconfig_startupconfig_cmd,
2997 "copy running-config startup-config",
2998 "Copy configuration\n"
2999 "Copy running config to... \n"
3000 "Copy running config to startup config (same as write file)\n")
3001
3002/* Write current configuration into the terminal. */
3003 DEFUN(config_write_terminal,
3004 config_write_terminal_cmd,
3005 "write terminal",
3006 "Write running configuration to memory, network, or terminal\n"
3007 "Write to terminal\n")
3008{
3009 unsigned int i;
3010 struct cmd_node *node;
3011
3012 if (vty->type == VTY_SHELL_SERV) {
3013 for (i = 0; i < vector_active(cmdvec); i++)
3014 if ((node = vector_slot(cmdvec, i)) && node->func
3015 && node->vtysh) {
3016 if ((*node->func) (vty))
3017 vty_out(vty, "!%s", VTY_NEWLINE);
3018 }
3019 } else {
3020 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3021 VTY_NEWLINE);
3022 vty_out(vty, "!%s", VTY_NEWLINE);
3023
3024 for (i = 0; i < vector_active(cmdvec); i++)
3025 if ((node = vector_slot(cmdvec, i)) && node->func) {
3026 if ((*node->func) (vty))
3027 vty_out(vty, "!%s", VTY_NEWLINE);
3028 }
3029 vty_out(vty, "end%s", VTY_NEWLINE);
3030 }
3031 return CMD_SUCCESS;
3032}
3033
3034/* Write current configuration into the terminal. */
3035ALIAS(config_write_terminal,
3036 show_running_config_cmd,
3037 "show running-config", SHOW_STR "running configuration\n")
3038
3039/* Write startup configuration into the terminal. */
3040 DEFUN(show_startup_config,
3041 show_startup_config_cmd,
3042 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3043{
3044 char buf[BUFSIZ];
3045 FILE *confp;
3046
3047 confp = fopen(host.config, "r");
3048 if (confp == NULL) {
3049 vty_out(vty, "Can't open configuration file [%s]%s",
3050 host.config, VTY_NEWLINE);
3051 return CMD_WARNING;
3052 }
3053
3054 while (fgets(buf, BUFSIZ, confp)) {
3055 char *cp = buf;
3056
3057 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3058 cp++;
3059 *cp = '\0';
3060
3061 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3062 }
3063
3064 fclose(confp);
3065
3066 return CMD_SUCCESS;
3067}
3068
3069/* Hostname configuration */
3070DEFUN(config_hostname,
3071 hostname_cmd,
3072 "hostname WORD",
3073 "Set system's network name\n" "This system's network name\n")
3074{
3075 if (!isalpha((int)*argv[0])) {
3076 vty_out(vty, "Please specify string starting with alphabet%s",
3077 VTY_NEWLINE);
3078 return CMD_WARNING;
3079 }
3080
3081 if (host.name)
3082 talloc_free(host.name);
3083
3084 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3085 return CMD_SUCCESS;
3086}
3087
3088DEFUN(config_no_hostname,
3089 no_hostname_cmd,
3090 "no hostname [HOSTNAME]",
3091 NO_STR "Reset system's network name\n" "Host name of this router\n")
3092{
3093 if (host.name)
3094 talloc_free(host.name);
3095 host.name = NULL;
3096 return CMD_SUCCESS;
3097}
3098
3099/* VTY interface password set. */
3100DEFUN(config_password, password_cmd,
3101 "password (8|) WORD",
3102 "Assign the terminal connection password\n"
3103 "Specifies a HIDDEN password will follow\n"
3104 "dummy string \n" "The HIDDEN line password string\n")
3105{
3106 /* Argument check. */
3107 if (argc == 0) {
3108 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3109 return CMD_WARNING;
3110 }
3111
3112 if (argc == 2) {
3113 if (*argv[0] == '8') {
3114 if (host.password)
3115 talloc_free(host.password);
3116 host.password = NULL;
3117 if (host.password_encrypt)
3118 talloc_free(host.password_encrypt);
3119 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3120 return CMD_SUCCESS;
3121 } else {
3122 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3123 return CMD_WARNING;
3124 }
3125 }
3126
3127 if (!isalnum((int)*argv[0])) {
3128 vty_out(vty,
3129 "Please specify string starting with alphanumeric%s",
3130 VTY_NEWLINE);
3131 return CMD_WARNING;
3132 }
3133
3134 if (host.password)
3135 talloc_free(host.password);
3136 host.password = NULL;
3137
3138#ifdef VTY_CRYPT_PW
3139 if (host.encrypt) {
3140 if (host.password_encrypt)
3141 talloc_free(host.password_encrypt);
3142 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3143 } else
3144#endif
3145 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3146
3147 return CMD_SUCCESS;
3148}
3149
3150ALIAS(config_password, password_text_cmd,
3151 "password LINE",
3152 "Assign the terminal connection password\n"
3153 "The UNENCRYPTED (cleartext) line password\n")
3154
3155/* VTY enable password set. */
3156 DEFUN(config_enable_password, enable_password_cmd,
3157 "enable password (8|) WORD",
3158 "Modify enable password parameters\n"
3159 "Assign the privileged level password\n"
3160 "Specifies a HIDDEN password will follow\n"
3161 "dummy string \n" "The HIDDEN 'enable' password string\n")
3162{
3163 /* Argument check. */
3164 if (argc == 0) {
3165 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3166 return CMD_WARNING;
3167 }
3168
3169 /* Crypt type is specified. */
3170 if (argc == 2) {
3171 if (*argv[0] == '8') {
3172 if (host.enable)
3173 talloc_free(host.enable);
3174 host.enable = NULL;
3175
3176 if (host.enable_encrypt)
3177 talloc_free(host.enable_encrypt);
3178 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3179
3180 return CMD_SUCCESS;
3181 } else {
3182 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3183 return CMD_WARNING;
3184 }
3185 }
3186
3187 if (!isalnum((int)*argv[0])) {
3188 vty_out(vty,
3189 "Please specify string starting with alphanumeric%s",
3190 VTY_NEWLINE);
3191 return CMD_WARNING;
3192 }
3193
3194 if (host.enable)
3195 talloc_free(host.enable);
3196 host.enable = NULL;
3197
3198 /* Plain password input. */
3199#ifdef VTY_CRYPT_PW
3200 if (host.encrypt) {
3201 if (host.enable_encrypt)
3202 talloc_free(host.enable_encrypt);
3203 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3204 } else
3205#endif
3206 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3207
3208 return CMD_SUCCESS;
3209}
3210
3211ALIAS(config_enable_password,
3212 enable_password_text_cmd,
3213 "enable password LINE",
3214 "Modify enable password parameters\n"
3215 "Assign the privileged level password\n"
3216 "The UNENCRYPTED (cleartext) 'enable' password\n")
3217
3218/* VTY enable password delete. */
3219 DEFUN(no_config_enable_password, no_enable_password_cmd,
3220 "no enable password",
3221 NO_STR
3222 "Modify enable password parameters\n"
3223 "Assign the privileged level password\n")
3224{
3225 if (host.enable)
3226 talloc_free(host.enable);
3227 host.enable = NULL;
3228
3229 if (host.enable_encrypt)
3230 talloc_free(host.enable_encrypt);
3231 host.enable_encrypt = NULL;
3232
3233 return CMD_SUCCESS;
3234}
3235
3236#ifdef VTY_CRYPT_PW
3237DEFUN(service_password_encrypt,
3238 service_password_encrypt_cmd,
3239 "service password-encryption",
3240 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3241{
3242 if (host.encrypt)
3243 return CMD_SUCCESS;
3244
3245 host.encrypt = 1;
3246
3247 if (host.password) {
3248 if (host.password_encrypt)
3249 talloc_free(host.password_encrypt);
3250 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3251 }
3252 if (host.enable) {
3253 if (host.enable_encrypt)
3254 talloc_free(host.enable_encrypt);
3255 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3256 }
3257
3258 return CMD_SUCCESS;
3259}
3260
3261DEFUN(no_service_password_encrypt,
3262 no_service_password_encrypt_cmd,
3263 "no service password-encryption",
3264 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3265{
3266 if (!host.encrypt)
3267 return CMD_SUCCESS;
3268
3269 host.encrypt = 0;
3270
3271 if (host.password_encrypt)
3272 talloc_free(host.password_encrypt);
3273 host.password_encrypt = NULL;
3274
3275 if (host.enable_encrypt)
3276 talloc_free(host.enable_encrypt);
3277 host.enable_encrypt = NULL;
3278
3279 return CMD_SUCCESS;
3280}
3281#endif
3282
3283DEFUN(config_terminal_length, config_terminal_length_cmd,
3284 "terminal length <0-512>",
3285 "Set terminal line parameters\n"
3286 "Set number of lines on a screen\n"
3287 "Number of lines on screen (0 for no pausing)\n")
3288{
3289 int lines;
3290 char *endptr = NULL;
3291
3292 lines = strtol(argv[0], &endptr, 10);
3293 if (lines < 0 || lines > 512 || *endptr != '\0') {
3294 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3295 return CMD_WARNING;
3296 }
3297 vty->lines = lines;
3298
3299 return CMD_SUCCESS;
3300}
3301
3302DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3303 "terminal no length",
3304 "Set terminal line parameters\n"
3305 NO_STR "Set number of lines on a screen\n")
3306{
3307 vty->lines = -1;
3308 return CMD_SUCCESS;
3309}
3310
3311DEFUN(service_terminal_length, service_terminal_length_cmd,
3312 "service terminal-length <0-512>",
3313 "Set up miscellaneous service\n"
3314 "System wide terminal length configuration\n"
3315 "Number of lines of VTY (0 means no line control)\n")
3316{
3317 int lines;
3318 char *endptr = NULL;
3319
3320 lines = strtol(argv[0], &endptr, 10);
3321 if (lines < 0 || lines > 512 || *endptr != '\0') {
3322 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3323 return CMD_WARNING;
3324 }
3325 host.lines = lines;
3326
3327 return CMD_SUCCESS;
3328}
3329
3330DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3331 "no service terminal-length [<0-512>]",
3332 NO_STR
3333 "Set up miscellaneous service\n"
3334 "System wide terminal length configuration\n"
3335 "Number of lines of VTY (0 means no line control)\n")
3336{
3337 host.lines = -1;
3338 return CMD_SUCCESS;
3339}
3340
3341DEFUN_HIDDEN(do_echo,
3342 echo_cmd,
3343 "echo .MESSAGE",
3344 "Echo a message back to the vty\n" "The message to echo\n")
3345{
3346 char *message;
3347
3348 vty_out(vty, "%s%s",
3349 ((message =
3350 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3351 if (message)
3352 talloc_free(message);
3353 return CMD_SUCCESS;
3354}
3355
3356#if 0
3357DEFUN(config_logmsg,
3358 config_logmsg_cmd,
3359 "logmsg " LOG_LEVELS " .MESSAGE",
3360 "Send a message to enabled logging destinations\n"
3361 LOG_LEVEL_DESC "The message to send\n")
3362{
3363 int level;
3364 char *message;
3365
3366 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3367 return CMD_ERR_NO_MATCH;
3368
3369 zlog(NULL, level,
3370 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3371 if (message)
3372 talloc_free(message);
3373 return CMD_SUCCESS;
3374}
3375
3376DEFUN(show_logging,
3377 show_logging_cmd,
3378 "show logging", SHOW_STR "Show current logging configuration\n")
3379{
3380 struct zlog *zl = zlog_default;
3381
3382 vty_out(vty, "Syslog logging: ");
3383 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3384 vty_out(vty, "disabled");
3385 else
3386 vty_out(vty, "level %s, facility %s, ident %s",
3387 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3388 facility_name(zl->facility), zl->ident);
3389 vty_out(vty, "%s", VTY_NEWLINE);
3390
3391 vty_out(vty, "Stdout logging: ");
3392 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3393 vty_out(vty, "disabled");
3394 else
3395 vty_out(vty, "level %s",
3396 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3397 vty_out(vty, "%s", VTY_NEWLINE);
3398
3399 vty_out(vty, "Monitor logging: ");
3400 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3401 vty_out(vty, "disabled");
3402 else
3403 vty_out(vty, "level %s",
3404 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3405 vty_out(vty, "%s", VTY_NEWLINE);
3406
3407 vty_out(vty, "File logging: ");
3408 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3409 vty_out(vty, "disabled");
3410 else
3411 vty_out(vty, "level %s, filename %s",
3412 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3413 zl->filename);
3414 vty_out(vty, "%s", VTY_NEWLINE);
3415
3416 vty_out(vty, "Protocol name: %s%s",
3417 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3418 vty_out(vty, "Record priority: %s%s",
3419 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3420
3421 return CMD_SUCCESS;
3422}
3423
3424DEFUN(config_log_stdout,
3425 config_log_stdout_cmd,
3426 "log stdout", "Logging control\n" "Set stdout logging level\n")
3427{
3428 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3429 return CMD_SUCCESS;
3430}
3431
3432DEFUN(config_log_stdout_level,
3433 config_log_stdout_level_cmd,
3434 "log stdout " LOG_LEVELS,
3435 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3436{
3437 int level;
3438
3439 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3440 return CMD_ERR_NO_MATCH;
3441 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3442 return CMD_SUCCESS;
3443}
3444
3445DEFUN(no_config_log_stdout,
3446 no_config_log_stdout_cmd,
3447 "no log stdout [LEVEL]",
3448 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3449{
3450 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3451 return CMD_SUCCESS;
3452}
3453
3454DEFUN(config_log_monitor,
3455 config_log_monitor_cmd,
3456 "log monitor",
3457 "Logging control\n" "Set terminal line (monitor) logging level\n")
3458{
3459 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3460 return CMD_SUCCESS;
3461}
3462
3463DEFUN(config_log_monitor_level,
3464 config_log_monitor_level_cmd,
3465 "log monitor " LOG_LEVELS,
3466 "Logging control\n"
3467 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3468{
3469 int level;
3470
3471 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3472 return CMD_ERR_NO_MATCH;
3473 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3474 return CMD_SUCCESS;
3475}
3476
3477DEFUN(no_config_log_monitor,
3478 no_config_log_monitor_cmd,
3479 "no log monitor [LEVEL]",
3480 NO_STR
3481 "Logging control\n"
3482 "Disable terminal line (monitor) logging\n" "Logging level\n")
3483{
3484 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3485 return CMD_SUCCESS;
3486}
3487
3488static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3489{
3490 int ret;
3491 char *p = NULL;
3492 const char *fullpath;
3493
3494 /* Path detection. */
3495 if (!IS_DIRECTORY_SEP(*fname)) {
3496 char cwd[MAXPATHLEN + 1];
3497 cwd[MAXPATHLEN] = '\0';
3498
3499 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3500 zlog_err("config_log_file: Unable to alloc mem!");
3501 return CMD_WARNING;
3502 }
3503
3504 if ((p = _talloc_zero(tall_vcmd_ctx,
3505 strlen(cwd) + strlen(fname) + 2),
3506 "set_log_file")
3507 == NULL) {
3508 zlog_err("config_log_file: Unable to alloc mem!");
3509 return CMD_WARNING;
3510 }
3511 sprintf(p, "%s/%s", cwd, fname);
3512 fullpath = p;
3513 } else
3514 fullpath = fname;
3515
3516 ret = zlog_set_file(NULL, fullpath, loglevel);
3517
3518 if (p)
3519 talloc_free(p);
3520
3521 if (!ret) {
3522 vty_out(vty, "can't open logfile %s\n", fname);
3523 return CMD_WARNING;
3524 }
3525
3526 if (host.logfile)
3527 talloc_free(host.logfile);
3528
3529 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3530
3531 return CMD_SUCCESS;
3532}
3533
3534DEFUN(config_log_file,
3535 config_log_file_cmd,
3536 "log file FILENAME",
3537 "Logging control\n" "Logging to file\n" "Logging filename\n")
3538{
3539 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3540}
3541
3542DEFUN(config_log_file_level,
3543 config_log_file_level_cmd,
3544 "log file FILENAME " LOG_LEVELS,
3545 "Logging control\n"
3546 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3547{
3548 int level;
3549
3550 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3551 return CMD_ERR_NO_MATCH;
3552 return set_log_file(vty, argv[0], level);
3553}
3554
3555DEFUN(no_config_log_file,
3556 no_config_log_file_cmd,
3557 "no log file [FILENAME]",
3558 NO_STR
3559 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3560{
3561 zlog_reset_file(NULL);
3562
3563 if (host.logfile)
3564 talloc_free(host.logfile);
3565
3566 host.logfile = NULL;
3567
3568 return CMD_SUCCESS;
3569}
3570
3571ALIAS(no_config_log_file,
3572 no_config_log_file_level_cmd,
3573 "no log file FILENAME LEVEL",
3574 NO_STR
3575 "Logging control\n"
3576 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3577
3578 DEFUN(config_log_syslog,
3579 config_log_syslog_cmd,
3580 "log syslog", "Logging control\n" "Set syslog logging level\n")
3581{
3582 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3583 return CMD_SUCCESS;
3584}
3585
3586DEFUN(config_log_syslog_level,
3587 config_log_syslog_level_cmd,
3588 "log syslog " LOG_LEVELS,
3589 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3590{
3591 int level;
3592
3593 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3594 return CMD_ERR_NO_MATCH;
3595 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3596 return CMD_SUCCESS;
3597}
3598
3599DEFUN_DEPRECATED(config_log_syslog_facility,
3600 config_log_syslog_facility_cmd,
3601 "log syslog facility " LOG_FACILITIES,
3602 "Logging control\n"
3603 "Logging goes to syslog\n"
3604 "(Deprecated) Facility parameter for syslog messages\n"
3605 LOG_FACILITY_DESC)
3606{
3607 int facility;
3608
3609 if ((facility = facility_match(argv[0])) < 0)
3610 return CMD_ERR_NO_MATCH;
3611
3612 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3613 zlog_default->facility = facility;
3614 return CMD_SUCCESS;
3615}
3616
3617DEFUN(no_config_log_syslog,
3618 no_config_log_syslog_cmd,
3619 "no log syslog [LEVEL]",
3620 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3621{
3622 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3623 return CMD_SUCCESS;
3624}
3625
3626ALIAS(no_config_log_syslog,
3627 no_config_log_syslog_facility_cmd,
3628 "no log syslog facility " LOG_FACILITIES,
3629 NO_STR
3630 "Logging control\n"
3631 "Logging goes to syslog\n"
3632 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3633
3634 DEFUN(config_log_facility,
3635 config_log_facility_cmd,
3636 "log facility " LOG_FACILITIES,
3637 "Logging control\n"
3638 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3639{
3640 int facility;
3641
3642 if ((facility = facility_match(argv[0])) < 0)
3643 return CMD_ERR_NO_MATCH;
3644 zlog_default->facility = facility;
3645 return CMD_SUCCESS;
3646}
3647
3648DEFUN(no_config_log_facility,
3649 no_config_log_facility_cmd,
3650 "no log facility [FACILITY]",
3651 NO_STR
3652 "Logging control\n"
3653 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3654{
3655 zlog_default->facility = LOG_DAEMON;
3656 return CMD_SUCCESS;
3657}
3658
3659DEFUN_DEPRECATED(config_log_trap,
3660 config_log_trap_cmd,
3661 "log trap " LOG_LEVELS,
3662 "Logging control\n"
3663 "(Deprecated) Set logging level and default for all destinations\n"
3664 LOG_LEVEL_DESC)
3665{
3666 int new_level;
3667 int i;
3668
3669 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3670 return CMD_ERR_NO_MATCH;
3671
3672 zlog_default->default_lvl = new_level;
3673 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3674 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3675 zlog_default->maxlvl[i] = new_level;
3676 return CMD_SUCCESS;
3677}
3678
3679DEFUN_DEPRECATED(no_config_log_trap,
3680 no_config_log_trap_cmd,
3681 "no log trap [LEVEL]",
3682 NO_STR
3683 "Logging control\n"
3684 "Permit all logging information\n" "Logging level\n")
3685{
3686 zlog_default->default_lvl = LOG_DEBUG;
3687 return CMD_SUCCESS;
3688}
3689
3690DEFUN(config_log_record_priority,
3691 config_log_record_priority_cmd,
3692 "log record-priority",
3693 "Logging control\n"
3694 "Log the priority of the message within the message\n")
3695{
3696 zlog_default->record_priority = 1;
3697 return CMD_SUCCESS;
3698}
3699
3700DEFUN(no_config_log_record_priority,
3701 no_config_log_record_priority_cmd,
3702 "no log record-priority",
3703 NO_STR
3704 "Logging control\n"
3705 "Do not log the priority of the message within the message\n")
3706{
3707 zlog_default->record_priority = 0;
3708 return CMD_SUCCESS;
3709}
3710#endif
3711
3712DEFUN(banner_motd_file,
3713 banner_motd_file_cmd,
3714 "banner motd file [FILE]",
3715 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3716{
3717 if (host.motdfile)
3718 talloc_free(host.motdfile);
3719 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3720
3721 return CMD_SUCCESS;
3722}
3723
3724DEFUN(banner_motd_default,
3725 banner_motd_default_cmd,
3726 "banner motd default",
3727 "Set banner string\n" "Strings for motd\n" "Default string\n")
3728{
3729 host.motd = default_motd;
3730 return CMD_SUCCESS;
3731}
3732
3733DEFUN(no_banner_motd,
3734 no_banner_motd_cmd,
3735 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3736{
3737 host.motd = NULL;
3738 if (host.motdfile)
3739 talloc_free(host.motdfile);
3740 host.motdfile = NULL;
3741 return CMD_SUCCESS;
3742}
3743
3744/* Set config filename. Called from vty.c */
3745void host_config_set(const char *filename)
3746{
3747 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3748}
3749
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003750/*! Deprecated, now happens implicitly when calling install_node().
3751 * Users of the API may still attempt to call this function, hence
3752 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003753void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003754{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003755}
3756
3757/*! Deprecated, now happens implicitly when calling install_node().
3758 * Users of the API may still attempt to call this function, hence
3759 * leave it here as a no-op. */
3760void vty_install_default(int node)
3761{
3762}
3763
3764/*! Install common commands like 'exit' and 'list'. */
3765static void install_basic_node_commands(int node)
3766{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003767 install_element(node, &config_help_cmd);
3768 install_element(node, &config_list_cmd);
3769
3770 install_element(node, &config_write_terminal_cmd);
3771 install_element(node, &config_write_file_cmd);
3772 install_element(node, &config_write_memory_cmd);
3773 install_element(node, &config_write_cmd);
3774 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003775
3776 install_element(node, &config_exit_cmd);
3777
3778 if (node >= CONFIG_NODE) {
3779 /* It's not a top node. */
3780 install_element(node, &config_end_cmd);
3781 }
3782}
3783
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003784/*! Return true if a node is installed by install_basic_node_commands(), so
3785 * that we can avoid repeating them for each and every node during 'show
3786 * running-config' */
3787static bool vty_command_is_common(struct cmd_element *cmd)
3788{
3789 if (cmd == &config_help_cmd
3790 || cmd == &config_list_cmd
3791 || cmd == &config_write_terminal_cmd
3792 || cmd == &config_write_file_cmd
3793 || cmd == &config_write_memory_cmd
3794 || cmd == &config_write_cmd
3795 || cmd == &show_running_config_cmd
3796 || cmd == &config_exit_cmd
3797 || cmd == &config_end_cmd)
3798 return true;
3799 return false;
3800}
3801
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003802/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003803 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003804 * \param[in] vty the vty of the code
3805 * \param[in] filename where to store the file
3806 * \return 0 in case of success.
3807 *
3808 * If the filename already exists create a filename.sav
3809 * version with the current code.
3810 *
3811 */
3812int osmo_vty_write_config_file(const char *filename)
3813{
3814 char *failed_file;
3815 int rc;
3816
3817 rc = write_config_file(filename, &failed_file);
3818 talloc_free(failed_file);
3819 return rc;
3820}
3821
3822/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003823 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003824 * \return 0 in case of success.
3825 *
3826 * If the filename already exists create a filename.sav
3827 * version with the current code.
3828 *
3829 */
3830int osmo_vty_save_config_file(void)
3831{
3832 char *failed_file;
3833 int rc;
3834
3835 if (host.config == NULL)
3836 return -7;
3837
3838 rc = write_config_file(host.config, &failed_file);
3839 talloc_free(failed_file);
3840 return rc;
3841}
3842
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003843/* Initialize command interface. Install basic nodes and commands. */
3844void cmd_init(int terminal)
3845{
3846 /* Allocate initial top vector of commands. */
3847 cmdvec = vector_init(VECTOR_MIN_SIZE);
3848
3849 /* Default host value settings. */
3850 host.name = NULL;
3851 host.password = NULL;
3852 host.enable = NULL;
3853 host.logfile = NULL;
3854 host.config = NULL;
3855 host.lines = -1;
3856 host.motd = default_motd;
3857 host.motdfile = NULL;
3858
3859 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003860 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003861 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003862 install_node_bare(&auth_node, NULL);
3863 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003864 install_node(&config_node, config_write_host);
3865
3866 /* Each node's basic commands. */
3867 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003868 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003869 if (terminal) {
3870 install_element(VIEW_NODE, &config_list_cmd);
3871 install_element(VIEW_NODE, &config_exit_cmd);
3872 install_element(VIEW_NODE, &config_help_cmd);
3873 install_element(VIEW_NODE, &config_enable_cmd);
3874 install_element(VIEW_NODE, &config_terminal_length_cmd);
3875 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3876 install_element(VIEW_NODE, &echo_cmd);
3877 }
3878
3879 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003880 install_element(ENABLE_NODE, &config_disable_cmd);
3881 install_element(ENABLE_NODE, &config_terminal_cmd);
3882 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3883 }
3884 install_element (ENABLE_NODE, &show_startup_config_cmd);
3885 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003886 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003887
3888 if (terminal) {
3889 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3890 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3891 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003892 }
3893
3894 install_element(CONFIG_NODE, &hostname_cmd);
3895 install_element(CONFIG_NODE, &no_hostname_cmd);
3896
3897 if (terminal) {
3898 install_element(CONFIG_NODE, &password_cmd);
3899 install_element(CONFIG_NODE, &password_text_cmd);
3900 install_element(CONFIG_NODE, &enable_password_cmd);
3901 install_element(CONFIG_NODE, &enable_password_text_cmd);
3902 install_element(CONFIG_NODE, &no_enable_password_cmd);
3903
3904#ifdef VTY_CRYPT_PW
3905 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3906 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3907#endif
3908 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3909 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3910 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3911 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3912 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3913
3914 }
3915 srand(time(NULL));
3916}
Harald Welte7acb30c2011-08-17 17:13:48 +02003917
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003918/*! @} */