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