blob: 454a10c32280e3a8f5be9d11afaa945bb4c2894b [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;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200518 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200519
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
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 for (i = 0; i < _strlen; ++i) {
542 switch (inp[i]) {
543 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200544 /* Prepend escape character "\": */
545 *(out_ptr++) = '\\';
546 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 default:
548 *(out_ptr++) = inp[i];
549 break;
550 }
551 }
552
Alexander Couzensad580ba2016-05-16 16:01:45 +0200553 out_ptr[0] = '\0';
554 return out;
555}
556
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100557static char *xml_escape(const char *inp)
558{
559 int _strlen;
560 char *out, *out_ptr;
561 int len = 0, i, j;
562
563 if (!inp)
564 return NULL;
565 _strlen = strlen(inp);
566
567 for (i = 0; i < _strlen; ++i) {
568 switch (inp[i]) {
569 case '"':
570 len += 6;
571 break;
572 case '\'':
573 len += 6;
574 break;
575 case '<':
576 len += 4;
577 break;
578 case '>':
579 len += 4;
580 break;
581 case '&':
582 len += 5;
583 break;
584 default:
585 len += 1;
586 break;
587 }
588 }
589
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200590 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100591 if (!out)
592 return NULL;
593
594 out_ptr = out;
595
596#define ADD(out, str) \
597 for (j = 0; j < strlen(str); ++j) \
598 *(out++) = str[j];
599
600 for (i = 0; i < _strlen; ++i) {
601 switch (inp[i]) {
602 case '"':
603 ADD(out_ptr, "&quot;");
604 break;
605 case '\'':
606 ADD(out_ptr, "&apos;");
607 break;
608 case '<':
609 ADD(out_ptr, "&lt;");
610 break;
611 case '>':
612 ADD(out_ptr, "&gt;");
613 break;
614 case '&':
615 ADD(out_ptr, "&amp;");
616 break;
617 default:
618 *(out_ptr++) = inp[i];
619 break;
620 }
621 }
622
623#undef ADD
624
625 out_ptr[0] = '\0';
626 return out;
627}
628
629/*
630 * Write one cmd_element as XML to the given VTY.
631 */
632static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
633{
634 char *xml_string = xml_escape(cmd->string);
635
636 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
637 vty_out(vty, " <params>%s", VTY_NEWLINE);
638
639 int j;
640 for (j = 0; j < vector_count(cmd->strvec); ++j) {
641 vector descvec = vector_slot(cmd->strvec, j);
642 int i;
643 for (i = 0; i < vector_active(descvec); ++i) {
644 char *xml_param, *xml_doc;
645 struct desc *desc = vector_slot(descvec, i);
646 if (desc == NULL)
647 continue;
648
649 xml_param = xml_escape(desc->cmd);
650 xml_doc = xml_escape(desc->str);
651 vty_out(vty, " <param name='%s' doc='%s' />%s",
652 xml_param, xml_doc, VTY_NEWLINE);
653 talloc_free(xml_param);
654 talloc_free(xml_doc);
655 }
656 }
657
658 vty_out(vty, " </params>%s", VTY_NEWLINE);
659 vty_out(vty, " </command>%s", VTY_NEWLINE);
660
661 talloc_free(xml_string);
662 return 0;
663}
664
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200665static bool vty_command_is_common(struct cmd_element *cmd);
666
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100667/*
668 * Dump all nodes and commands associated with a given node as XML to the VTY.
669 */
670static int vty_dump_nodes(struct vty *vty)
671{
672 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200673 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100674
675 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
676
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200677 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
678 vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
679 vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
680 vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
681 " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
682 for (i = 0; i < vector_active(cmdvec); ++i) {
683 struct cmd_node *cnode;
684 cnode = vector_slot(cmdvec, i);
685 if (!cnode)
686 continue;
687 if (cnode->node != CONFIG_NODE)
688 continue;
689
690 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
691 struct cmd_element *elem;
692 elem = vector_slot(cnode->cmd_vector, j);
693 if (!vty_command_is_common(elem))
694 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200695 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200696 vty_dump_element(elem, vty);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200697 }
698 }
699 vty_out(vty, " </node>%s", VTY_NEWLINE);
700
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100701 for (i = 0; i < vector_active(cmdvec); ++i) {
702 struct cmd_node *cnode;
703 cnode = vector_slot(cmdvec, i);
704 if (!cnode)
705 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200706 if (vector_active(cnode->cmd_vector) < 1)
707 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100708
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200709 /* De-dup node IDs: how many times has this same name been used before? Count the first
710 * occurence as _1 and omit that first suffix, so that the first occurence is called
711 * 'name', the second becomes 'name_2', then 'name_3', ... */
712 same_name_count = 1;
713 for (j = 0; j < i; ++j) {
714 struct cmd_node *cnode2;
715 cnode2 = vector_slot(cmdvec, j);
716 if (!cnode2)
717 continue;
718 if (strcmp(cnode->name, cnode2->name) == 0)
719 same_name_count ++;
720 }
721
722 vty_out(vty, " <node id='%s", cnode->name);
723 if (same_name_count > 1 || !*cnode->name)
724 vty_out(vty, "_%d", same_name_count);
725 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200726 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100727
728 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
729 struct cmd_element *elem;
730 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200731 if (vty_command_is_common(elem))
732 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200733 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200734 vty_dump_element(elem, vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100735 }
736
737 vty_out(vty, " </node>%s", VTY_NEWLINE);
738 }
739
740 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
741
742 return 0;
743}
744
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200745/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100746static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
747{
748 int i;
749
750 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
751 struct cmd_element *elem;
752 elem = vector_slot(cnode->cmd_vector, i);
753 if (!elem->string)
754 continue;
755 if (!strcmp(elem->string, cmdstring))
756 return 1;
757 }
758 return 0;
759}
760
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200761/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200762 * \param[in] ntype Node Type
763 * \param[cmd] element to be installed
764 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000765void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200766{
767 struct cmd_node *cnode;
768
769 cnode = vector_slot(cmdvec, ntype);
770
Harald Weltea99d45a2015-11-12 13:48:23 +0100771 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100772 /* ensure no _identical_ command has been registered at this
773 * node so far */
774 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200775
776 vector_set(cnode->cmd_vector, cmd);
777
778 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
779 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
780}
781
782/* Install a command into VIEW and ENABLE node */
783void install_element_ve(struct cmd_element *cmd)
784{
785 install_element(VIEW_NODE, cmd);
786 install_element(ENABLE_NODE, cmd);
787}
788
789#ifdef VTY_CRYPT_PW
790static unsigned char itoa64[] =
791 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
792
793static void to64(char *s, long v, int n)
794{
795 while (--n >= 0) {
796 *s++ = itoa64[v & 0x3f];
797 v >>= 6;
798 }
799}
800
801static char *zencrypt(const char *passwd)
802{
803 char salt[6];
804 struct timeval tv;
805 char *crypt(const char *, const char *);
806
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200807 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200808
809 to64(&salt[0], random(), 3);
810 to64(&salt[3], tv.tv_usec, 3);
811 salt[5] = '\0';
812
813 return crypt(passwd, salt);
814}
815#endif
816
817/* This function write configuration of this host. */
818static int config_write_host(struct vty *vty)
819{
820 if (host.name)
821 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
822
823 if (host.encrypt) {
824 if (host.password_encrypt)
825 vty_out(vty, "password 8 %s%s", host.password_encrypt,
826 VTY_NEWLINE);
827 if (host.enable_encrypt)
828 vty_out(vty, "enable password 8 %s%s",
829 host.enable_encrypt, VTY_NEWLINE);
830 } else {
831 if (host.password)
832 vty_out(vty, "password %s%s", host.password,
833 VTY_NEWLINE);
834 if (host.enable)
835 vty_out(vty, "enable password %s%s", host.enable,
836 VTY_NEWLINE);
837 }
838
839 if (host.advanced)
840 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
841
842 if (host.encrypt)
843 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
844
845 if (host.lines >= 0)
846 vty_out(vty, "service terminal-length %d%s", host.lines,
847 VTY_NEWLINE);
848
849 if (host.motdfile)
850 vty_out(vty, "banner motd file %s%s", host.motdfile,
851 VTY_NEWLINE);
852 else if (!host.motd)
853 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
854
855 return 1;
856}
857
858/* Utility function for getting command vector. */
859static vector cmd_node_vector(vector v, enum node_type ntype)
860{
861 struct cmd_node *cnode = vector_slot(v, ntype);
862 return cnode->cmd_vector;
863}
864
865/* Completion match types. */
866enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200867 NO_MATCH = 0,
868 ANY_MATCH,
869 EXTEND_MATCH,
870 IPV4_PREFIX_MATCH,
871 IPV4_MATCH,
872 IPV6_PREFIX_MATCH,
873 IPV6_MATCH,
874 RANGE_MATCH,
875 VARARG_MATCH,
876 PARTLY_MATCH,
877 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200878};
879
880static enum match_type cmd_ipv4_match(const char *str)
881{
882 const char *sp;
883 int dots = 0, nums = 0;
884 char buf[4];
885
886 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200887 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200888
889 for (;;) {
890 memset(buf, 0, sizeof(buf));
891 sp = str;
892 while (*str != '\0') {
893 if (*str == '.') {
894 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200895 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200896
897 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200898 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200899
900 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200901 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200902
903 dots++;
904 break;
905 }
906 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200907 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200908
909 str++;
910 }
911
912 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200913 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200914
915 strncpy(buf, sp, str - sp);
916 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200917 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200918
919 nums++;
920
921 if (*str == '\0')
922 break;
923
924 str++;
925 }
926
927 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200928 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200929
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200930 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200931}
932
933static enum match_type cmd_ipv4_prefix_match(const char *str)
934{
935 const char *sp;
936 int dots = 0;
937 char buf[4];
938
939 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200940 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200941
942 for (;;) {
943 memset(buf, 0, sizeof(buf));
944 sp = str;
945 while (*str != '\0' && *str != '/') {
946 if (*str == '.') {
947 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200948 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200949
950 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200951 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200952
953 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200954 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200955
956 dots++;
957 break;
958 }
959
960 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200961 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200962
963 str++;
964 }
965
966 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200967 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200968
969 strncpy(buf, sp, str - sp);
970 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200971 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200972
973 if (dots == 3) {
974 if (*str == '/') {
975 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200976 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200977
978 str++;
979 break;
980 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200981 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200982 }
983
984 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200985 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200986
987 str++;
988 }
989
990 sp = str;
991 while (*str != '\0') {
992 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200993 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200994
995 str++;
996 }
997
998 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200999 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001000
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001001 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001002}
1003
1004#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1005#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1006#define STATE_START 1
1007#define STATE_COLON 2
1008#define STATE_DOUBLE 3
1009#define STATE_ADDR 4
1010#define STATE_DOT 5
1011#define STATE_SLASH 6
1012#define STATE_MASK 7
1013
1014#ifdef HAVE_IPV6
1015
1016static enum match_type cmd_ipv6_match(const char *str)
1017{
1018 int state = STATE_START;
1019 int colons = 0, nums = 0, double_colon = 0;
1020 const char *sp = NULL;
1021 struct sockaddr_in6 sin6_dummy;
1022 int ret;
1023
1024 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001025 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001026
1027 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001028 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001029
1030 /* use inet_pton that has a better support,
1031 * for example inet_pton can support the automatic addresses:
1032 * ::1.2.3.4
1033 */
1034 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1035
1036 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001037 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001038
1039 while (*str != '\0') {
1040 switch (state) {
1041 case STATE_START:
1042 if (*str == ':') {
1043 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001044 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001045 colons--;
1046 state = STATE_COLON;
1047 } else {
1048 sp = str;
1049 state = STATE_ADDR;
1050 }
1051
1052 continue;
1053 case STATE_COLON:
1054 colons++;
1055 if (*(str + 1) == ':')
1056 state = STATE_DOUBLE;
1057 else {
1058 sp = str + 1;
1059 state = STATE_ADDR;
1060 }
1061 break;
1062 case STATE_DOUBLE:
1063 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001064 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001065
1066 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001067 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001068 else {
1069 if (*(str + 1) != '\0')
1070 colons++;
1071 sp = str + 1;
1072 state = STATE_ADDR;
1073 }
1074
1075 double_colon++;
1076 nums++;
1077 break;
1078 case STATE_ADDR:
1079 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1080 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001081 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001082
1083 nums++;
1084 state = STATE_COLON;
1085 }
1086 if (*(str + 1) == '.')
1087 state = STATE_DOT;
1088 break;
1089 case STATE_DOT:
1090 state = STATE_ADDR;
1091 break;
1092 default:
1093 break;
1094 }
1095
1096 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001097 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001098
1099 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001100 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001101
1102 str++;
1103 }
1104
1105#if 0
1106 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001107 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001108#endif /* 0 */
1109
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001110 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111}
1112
1113static enum match_type cmd_ipv6_prefix_match(const char *str)
1114{
1115 int state = STATE_START;
1116 int colons = 0, nums = 0, double_colon = 0;
1117 int mask;
1118 const char *sp = NULL;
1119 char *endptr = NULL;
1120
1121 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001122 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001123
1124 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126
1127 while (*str != '\0' && state != STATE_MASK) {
1128 switch (state) {
1129 case STATE_START:
1130 if (*str == ':') {
1131 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001132 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001133 colons--;
1134 state = STATE_COLON;
1135 } else {
1136 sp = str;
1137 state = STATE_ADDR;
1138 }
1139
1140 continue;
1141 case STATE_COLON:
1142 colons++;
1143 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001144 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001145 else if (*(str + 1) == ':')
1146 state = STATE_DOUBLE;
1147 else {
1148 sp = str + 1;
1149 state = STATE_ADDR;
1150 }
1151 break;
1152 case STATE_DOUBLE:
1153 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001154 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001155
1156 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001157 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001158 else {
1159 if (*(str + 1) != '\0' && *(str + 1) != '/')
1160 colons++;
1161 sp = str + 1;
1162
1163 if (*(str + 1) == '/')
1164 state = STATE_SLASH;
1165 else
1166 state = STATE_ADDR;
1167 }
1168
1169 double_colon++;
1170 nums += 1;
1171 break;
1172 case STATE_ADDR:
1173 if (*(str + 1) == ':' || *(str + 1) == '.'
1174 || *(str + 1) == '\0' || *(str + 1) == '/') {
1175 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001176 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001177
1178 for (; sp <= str; sp++)
1179 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001180 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001181
1182 nums++;
1183
1184 if (*(str + 1) == ':')
1185 state = STATE_COLON;
1186 else if (*(str + 1) == '.')
1187 state = STATE_DOT;
1188 else if (*(str + 1) == '/')
1189 state = STATE_SLASH;
1190 }
1191 break;
1192 case STATE_DOT:
1193 state = STATE_ADDR;
1194 break;
1195 case STATE_SLASH:
1196 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001197 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001198
1199 state = STATE_MASK;
1200 break;
1201 default:
1202 break;
1203 }
1204
1205 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001206 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207
1208 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001209 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001210
1211 str++;
1212 }
1213
1214 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001215 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001216
1217 mask = strtol(str, &endptr, 10);
1218 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001219 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001220
1221 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001222 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001223
1224/* I don't know why mask < 13 makes command match partly.
1225 Forgive me to make this comments. I Want to set static default route
1226 because of lack of function to originate default in ospf6d; sorry
1227 yasu
1228 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001229 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001230*/
1231
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001232 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233}
1234
1235#endif /* HAVE_IPV6 */
1236
1237#define DECIMAL_STRLEN_MAX 10
1238
1239static int cmd_range_match(const char *range, const char *str)
1240{
1241 char *p;
1242 char buf[DECIMAL_STRLEN_MAX + 1];
1243 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001244
1245 if (str == NULL)
1246 return 1;
1247
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001248 if (range[1] == '-') {
1249 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001250
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001251 val = strtol(str, &endptr, 10);
1252 if (*endptr != '\0')
1253 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001254
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001255 range += 2;
1256 p = strchr(range, '-');
1257 if (p == NULL)
1258 return 0;
1259 if (p - range > DECIMAL_STRLEN_MAX)
1260 return 0;
1261 strncpy(buf, range, p - range);
1262 buf[p - range] = '\0';
1263 min = -strtol(buf, &endptr, 10);
1264 if (*endptr != '\0')
1265 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001266
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001267 range = p + 1;
1268 p = strchr(range, '>');
1269 if (p == NULL)
1270 return 0;
1271 if (p - range > DECIMAL_STRLEN_MAX)
1272 return 0;
1273 strncpy(buf, range, p - range);
1274 buf[p - range] = '\0';
1275 max = strtol(buf, &endptr, 10);
1276 if (*endptr != '\0')
1277 return 0;
1278
1279 if (val < min || val > max)
1280 return 0;
1281 } else {
1282 unsigned long min, max, val;
1283
1284 val = strtoul(str, &endptr, 10);
1285 if (*endptr != '\0')
1286 return 0;
1287
1288 range++;
1289 p = strchr(range, '-');
1290 if (p == NULL)
1291 return 0;
1292 if (p - range > DECIMAL_STRLEN_MAX)
1293 return 0;
1294 strncpy(buf, range, p - range);
1295 buf[p - range] = '\0';
1296 min = strtoul(buf, &endptr, 10);
1297 if (*endptr != '\0')
1298 return 0;
1299
1300 range = p + 1;
1301 p = strchr(range, '>');
1302 if (p == NULL)
1303 return 0;
1304 if (p - range > DECIMAL_STRLEN_MAX)
1305 return 0;
1306 strncpy(buf, range, p - range);
1307 buf[p - range] = '\0';
1308 max = strtoul(buf, &endptr, 10);
1309 if (*endptr != '\0')
1310 return 0;
1311
1312 if (val < min || val > max)
1313 return 0;
1314 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001315
1316 return 1;
1317}
1318
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001319/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001320static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001321{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001322 /* we've got "[blah]". We want to strip off the []s and redo the
1323 * match check for "blah"
1324 */
1325 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001326
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001327 if (len < 3)
1328 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001329
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001330 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331}
1332
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001333static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001334cmd_match(const char *str, const char *command,
1335 enum match_type min, bool recur)
1336{
1337
1338 if (recur && CMD_OPTION(str))
1339 {
1340 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001341 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001342
1343 /* this would be a bug in a command, however handle it gracefully
1344 * as it we only discover it if a user tries to run it
1345 */
1346 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001347 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001348
1349 ret = cmd_match(tmp, command, min, false);
1350
1351 talloc_free(tmp);
1352
1353 return ret;
1354 }
1355 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001356 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001357 else if (CMD_RANGE(str))
1358 {
1359 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001360 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001361 }
1362#ifdef HAVE_IPV6
1363 else if (CMD_IPV6(str))
1364 {
1365 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001366 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001367 }
1368 else if (CMD_IPV6_PREFIX(str))
1369 {
1370 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001371 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001372 }
1373#endif /* HAVE_IPV6 */
1374 else if (CMD_IPV4(str))
1375 {
1376 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001377 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001378 }
1379 else if (CMD_IPV4_PREFIX(str))
1380 {
1381 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001382 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001383 }
1384 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001385 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001386 else if (strncmp(command, str, strlen(command)) == 0)
1387 {
1388 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001389 return EXACT_MATCH;
1390 else if (PARTLY_MATCH >= min)
1391 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001392 }
1393
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001394 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001395}
1396
1397/* Filter vector at the specified index and by the given command string, to
1398 * the desired matching level (thus allowing part matches), and return match
1399 * type flag.
1400 */
1401static enum match_type
1402cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001403{
1404 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001405 struct cmd_element *cmd_element;
1406 enum match_type match_type;
1407 vector descvec;
1408 struct desc *desc;
1409
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001410 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001411
1412 /* If command and cmd_element string does not match set NULL to vector */
1413 for (i = 0; i < vector_active(v); i++)
1414 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001415 if (index >= vector_active(cmd_element->strvec))
1416 vector_slot(v, i) = NULL;
1417 else {
1418 unsigned int j;
1419 int matched = 0;
1420
1421 descvec =
1422 vector_slot(cmd_element->strvec, index);
1423
1424 for (j = 0; j < vector_active(descvec); j++)
1425 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001426 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001427
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001428 ret = cmd_match (desc->cmd, command, level, true);
1429
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001430 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001431 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001432
1433 if (match_type < ret)
1434 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001435 }
1436 if (!matched)
1437 vector_slot(v, i) = NULL;
1438 }
1439 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001440
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001441 if (match_type == NO_MATCH)
1442 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001443
1444 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1445 * go again and filter out commands whose argument (at this index) is
1446 * 'weaker'. E.g., if we have 2 commands:
1447 *
1448 * foo bar <1-255>
1449 * foo bar BLAH
1450 *
1451 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001452 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001453 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1454 *
1455 * If we don't do a 2nd pass and filter it out, the higher-layers will
1456 * consider this to be ambiguous.
1457 */
1458 for (i = 0; i < vector_active(v); i++)
1459 if ((cmd_element = vector_slot(v, i)) != NULL) {
1460 if (index >= vector_active(cmd_element->strvec))
1461 vector_slot(v, i) = NULL;
1462 else {
1463 unsigned int j;
1464 int matched = 0;
1465
1466 descvec =
1467 vector_slot(cmd_element->strvec, index);
1468
1469 for (j = 0; j < vector_active(descvec); j++)
1470 if ((desc = vector_slot(descvec, j))) {
1471 enum match_type ret;
1472
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001473 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001474
1475 if (ret >= match_type)
1476 matched++;
1477 }
1478 if (!matched)
1479 vector_slot(v, i) = NULL;
1480 }
1481 }
1482
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001483 return match_type;
1484}
1485
1486/* Check ambiguous match */
1487static int
1488is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1489{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001490 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001491 unsigned int i;
1492 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001493 struct cmd_element *cmd_element;
1494 const char *matched = NULL;
1495 vector descvec;
1496 struct desc *desc;
1497
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001498 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1499 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1500 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1501 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1502 * that case, the string must remain allocated until this function exits or another match comes
1503 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1504 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1505 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1506 void *cmd_deopt_ctx = NULL;
1507
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001508 for (i = 0; i < vector_active(v); i++) {
1509 cmd_element = vector_slot(v, i);
1510 if (!cmd_element)
1511 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001512
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001513 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001514
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001515 descvec = vector_slot(cmd_element->strvec, index);
1516
1517 for (j = 0; j < vector_active(descvec); j++) {
1518 desc = vector_slot(descvec, j);
1519 if (!desc)
1520 continue;
1521
1522 enum match_type mtype;
1523 const char *str = desc->cmd;
1524
1525 if (CMD_OPTION(str)) {
1526 if (!cmd_deopt_ctx)
1527 cmd_deopt_ctx =
1528 talloc_named_const(tall_vty_cmd_ctx, 0,
1529 __func__);
1530 str = cmd_deopt(cmd_deopt_ctx, str);
1531 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001532 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001533 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001534
1535 switch (type) {
1536 case EXACT_MATCH:
1537 if (!(CMD_VARIABLE (str))
1538 && strcmp(command, str) == 0)
1539 match++;
1540 break;
1541 case PARTLY_MATCH:
1542 if (!(CMD_VARIABLE (str))
1543 && strncmp(command, str, strlen (command)) == 0)
1544 {
1545 if (matched
1546 && strcmp(matched,
1547 str) != 0) {
1548 ret = 1; /* There is ambiguous match. */
1549 goto free_and_return;
1550 } else
1551 matched = str;
1552 match++;
1553 }
1554 break;
1555 case RANGE_MATCH:
1556 if (cmd_range_match
1557 (str, command)) {
1558 if (matched
1559 && strcmp(matched,
1560 str) != 0) {
1561 ret = 1;
1562 goto free_and_return;
1563 } else
1564 matched = str;
1565 match++;
1566 }
1567 break;
1568#ifdef HAVE_IPV6
1569 case IPV6_MATCH:
1570 if (CMD_IPV6(str))
1571 match++;
1572 break;
1573 case IPV6_PREFIX_MATCH:
1574 if ((mtype =
1575 cmd_ipv6_prefix_match
1576 (command)) != NO_MATCH) {
1577 if (mtype == PARTLY_MATCH) {
1578 ret = 2; /* There is incomplete match. */
1579 goto free_and_return;
1580 }
1581
1582 match++;
1583 }
1584 break;
1585#endif /* HAVE_IPV6 */
1586 case IPV4_MATCH:
1587 if (CMD_IPV4(str))
1588 match++;
1589 break;
1590 case IPV4_PREFIX_MATCH:
1591 if ((mtype =
1592 cmd_ipv4_prefix_match
1593 (command)) != NO_MATCH) {
1594 if (mtype == PARTLY_MATCH) {
1595 ret = 2; /* There is incomplete match. */
1596 goto free_and_return;
1597 }
1598
1599 match++;
1600 }
1601 break;
1602 case EXTEND_MATCH:
1603 if (CMD_VARIABLE (str))
1604 match++;
1605 break;
1606 case NO_MATCH:
1607 default:
1608 break;
1609 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001610 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001611 if (!match)
1612 vector_slot(v, i) = NULL;
1613 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001614
1615free_and_return:
1616 if (cmd_deopt_ctx)
1617 talloc_free(cmd_deopt_ctx);
1618 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001619}
1620
1621/* If src matches dst return dst string, otherwise return NULL */
1622static const char *cmd_entry_function(const char *src, const char *dst)
1623{
1624 /* Skip variable arguments. */
1625 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1626 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1627 return NULL;
1628
1629 /* In case of 'command \t', given src is NULL string. */
1630 if (src == NULL)
1631 return dst;
1632
1633 /* Matched with input string. */
1634 if (strncmp(src, dst, strlen(src)) == 0)
1635 return dst;
1636
1637 return NULL;
1638}
1639
1640/* If src matches dst return dst string, otherwise return NULL */
1641/* This version will return the dst string always if it is
1642 CMD_VARIABLE for '?' key processing */
1643static const char *cmd_entry_function_desc(const char *src, const char *dst)
1644{
1645 if (CMD_VARARG(dst))
1646 return dst;
1647
1648 if (CMD_RANGE(dst)) {
1649 if (cmd_range_match(dst, src))
1650 return dst;
1651 else
1652 return NULL;
1653 }
1654#ifdef HAVE_IPV6
1655 if (CMD_IPV6(dst)) {
1656 if (cmd_ipv6_match(src))
1657 return dst;
1658 else
1659 return NULL;
1660 }
1661
1662 if (CMD_IPV6_PREFIX(dst)) {
1663 if (cmd_ipv6_prefix_match(src))
1664 return dst;
1665 else
1666 return NULL;
1667 }
1668#endif /* HAVE_IPV6 */
1669
1670 if (CMD_IPV4(dst)) {
1671 if (cmd_ipv4_match(src))
1672 return dst;
1673 else
1674 return NULL;
1675 }
1676
1677 if (CMD_IPV4_PREFIX(dst)) {
1678 if (cmd_ipv4_prefix_match(src))
1679 return dst;
1680 else
1681 return NULL;
1682 }
1683
1684 /* Optional or variable commands always match on '?' */
1685 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1686 return dst;
1687
1688 /* In case of 'command \t', given src is NULL string. */
1689 if (src == NULL)
1690 return dst;
1691
1692 if (strncmp(src, dst, strlen(src)) == 0)
1693 return dst;
1694 else
1695 return NULL;
1696}
1697
1698/* Check same string element existence. If it isn't there return
1699 1. */
1700static int cmd_unique_string(vector v, const char *str)
1701{
1702 unsigned int i;
1703 char *match;
1704
1705 for (i = 0; i < vector_active(v); i++)
1706 if ((match = vector_slot(v, i)) != NULL)
1707 if (strcmp(match, str) == 0)
1708 return 0;
1709 return 1;
1710}
1711
1712/* Compare string to description vector. If there is same string
1713 return 1 else return 0. */
1714static int desc_unique_string(vector v, const char *str)
1715{
1716 unsigned int i;
1717 struct desc *desc;
1718
1719 for (i = 0; i < vector_active(v); i++)
1720 if ((desc = vector_slot(v, i)) != NULL)
1721 if (strcmp(desc->cmd, str) == 0)
1722 return 1;
1723 return 0;
1724}
1725
1726static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1727{
1728 if (first_word != NULL &&
1729 node != AUTH_NODE &&
1730 node != VIEW_NODE &&
1731 node != AUTH_ENABLE_NODE &&
1732 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1733 return 1;
1734 return 0;
1735}
1736
1737/* '?' describe command support. */
1738static vector
1739cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1740{
1741 unsigned int i;
1742 vector cmd_vector;
1743#define INIT_MATCHVEC_SIZE 10
1744 vector matchvec;
1745 struct cmd_element *cmd_element;
1746 unsigned int index;
1747 int ret;
1748 enum match_type match;
1749 char *command;
1750 static struct desc desc_cr = { "<cr>", "" };
1751
1752 /* Set index. */
1753 if (vector_active(vline) == 0) {
1754 *status = CMD_ERR_NO_MATCH;
1755 return NULL;
1756 } else
1757 index = vector_active(vline) - 1;
1758
1759 /* Make copy vector of current node's command vector. */
1760 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1761
1762 /* Prepare match vector */
1763 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1764
1765 /* Filter commands. */
1766 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001767 for (i = 0; i < index; i++) {
1768 command = vector_slot(vline, i);
1769 if (!command)
1770 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001771
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001772 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001773
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001774 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001775 struct cmd_element *cmd_element;
1776 vector descvec;
1777 unsigned int j, k;
1778
1779 for (j = 0; j < vector_active(cmd_vector); j++)
1780 if ((cmd_element =
1781 vector_slot(cmd_vector, j)) != NULL
1782 &&
1783 (vector_active(cmd_element->strvec))) {
1784 descvec =
1785 vector_slot(cmd_element->
1786 strvec,
1787 vector_active
1788 (cmd_element->
1789 strvec) - 1);
1790 for (k = 0;
1791 k < vector_active(descvec);
1792 k++) {
1793 struct desc *desc =
1794 vector_slot(descvec,
1795 k);
1796 vector_set(matchvec,
1797 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001798 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001799 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001800
Harald Welte80d30fe2013-02-12 11:08:57 +01001801 vector_set(matchvec, &desc_cr);
1802 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001803
Harald Welte80d30fe2013-02-12 11:08:57 +01001804 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001805 }
1806
Harald Welte80d30fe2013-02-12 11:08:57 +01001807 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1808 match)) == 1) {
1809 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001810 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001811 *status = CMD_ERR_AMBIGUOUS;
1812 return NULL;
1813 } else if (ret == 2) {
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_NO_MATCH;
1817 return NULL;
1818 }
1819 }
1820
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001821 /* Prepare match vector */
1822 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1823
1824 /* Make sure that cmd_vector is filtered based on current word */
1825 command = vector_slot(vline, index);
1826 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001827 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001828
1829 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001830 for (i = 0; i < vector_active(cmd_vector); i++) {
1831 const char *string = NULL;
1832 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001833
Harald Welte80d30fe2013-02-12 11:08:57 +01001834 cmd_element = vector_slot(cmd_vector, i);
1835 if (!cmd_element)
1836 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001837
Harald Welted17aa592013-02-12 11:11:34 +01001838 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1839 continue;
1840
Harald Welte80d30fe2013-02-12 11:08:57 +01001841 strvec = cmd_element->strvec;
1842
1843 /* if command is NULL, index may be equal to vector_active */
1844 if (command && index >= vector_active(strvec))
1845 vector_slot(cmd_vector, i) = NULL;
1846 else {
1847 /* Check if command is completed. */
1848 if (command == NULL
1849 && index == vector_active(strvec)) {
1850 string = "<cr>";
1851 if (!desc_unique_string(matchvec, string))
1852 vector_set(matchvec, &desc_cr);
1853 } else {
1854 unsigned int j;
1855 vector descvec = vector_slot(strvec, index);
1856 struct desc *desc;
1857
1858 for (j = 0; j < vector_active(descvec); j++) {
1859 desc = vector_slot(descvec, j);
1860 if (!desc)
1861 continue;
1862 string = cmd_entry_function_desc
1863 (command, desc->cmd);
1864 if (!string)
1865 continue;
1866 /* Uniqueness check */
1867 if (!desc_unique_string(matchvec, string))
1868 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001869 }
1870 }
1871 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001872 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001873 vector_free(cmd_vector);
1874
1875 if (vector_slot(matchvec, 0) == NULL) {
1876 vector_free(matchvec);
1877 *status = CMD_ERR_NO_MATCH;
1878 } else
1879 *status = CMD_SUCCESS;
1880
1881 return matchvec;
1882}
1883
1884vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1885{
1886 vector ret;
1887
1888 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1889 enum node_type onode;
1890 vector shifted_vline;
1891 unsigned int index;
1892
1893 onode = vty->node;
1894 vty->node = ENABLE_NODE;
1895 /* We can try it on enable node, cos' the vty is authenticated */
1896
1897 shifted_vline = vector_init(vector_count(vline));
1898 /* use memcpy? */
1899 for (index = 1; index < vector_active(vline); index++) {
1900 vector_set_index(shifted_vline, index - 1,
1901 vector_lookup(vline, index));
1902 }
1903
1904 ret = cmd_describe_command_real(shifted_vline, vty, status);
1905
1906 vector_free(shifted_vline);
1907 vty->node = onode;
1908 return ret;
1909 }
1910
1911 return cmd_describe_command_real(vline, vty, status);
1912}
1913
1914/* Check LCD of matched command. */
1915static int cmd_lcd(char **matched)
1916{
1917 int i;
1918 int j;
1919 int lcd = -1;
1920 char *s1, *s2;
1921 char c1, c2;
1922
1923 if (matched[0] == NULL || matched[1] == NULL)
1924 return 0;
1925
1926 for (i = 1; matched[i] != NULL; i++) {
1927 s1 = matched[i - 1];
1928 s2 = matched[i];
1929
1930 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1931 if (c1 != c2)
1932 break;
1933
1934 if (lcd < 0)
1935 lcd = j;
1936 else {
1937 if (lcd > j)
1938 lcd = j;
1939 }
1940 }
1941 return lcd;
1942}
1943
1944/* Command line completion support. */
1945static char **cmd_complete_command_real(vector vline, struct vty *vty,
1946 int *status)
1947{
1948 unsigned int i;
1949 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1950#define INIT_MATCHVEC_SIZE 10
1951 vector matchvec;
1952 struct cmd_element *cmd_element;
1953 unsigned int index;
1954 char **match_str;
1955 struct desc *desc;
1956 vector descvec;
1957 char *command;
1958 int lcd;
1959
1960 if (vector_active(vline) == 0) {
1961 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001962 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001963 return NULL;
1964 } else
1965 index = vector_active(vline) - 1;
1966
1967 /* First, filter by preceeding command string */
1968 for (i = 0; i < index; i++)
1969 if ((command = vector_slot(vline, i))) {
1970 enum match_type match;
1971 int ret;
1972
1973 /* First try completion match, if there is exactly match return 1 */
1974 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001975 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001976
1977 /* If there is exact match then filter ambiguous match else check
1978 ambiguousness. */
1979 if ((ret =
1980 is_cmd_ambiguous(command, cmd_vector, i,
1981 match)) == 1) {
1982 vector_free(cmd_vector);
1983 *status = CMD_ERR_AMBIGUOUS;
1984 return NULL;
1985 }
1986 /*
1987 else if (ret == 2)
1988 {
1989 vector_free (cmd_vector);
1990 *status = CMD_ERR_NO_MATCH;
1991 return NULL;
1992 }
1993 */
1994 }
1995
1996 /* Prepare match vector. */
1997 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1998
1999 /* Now we got into completion */
2000 for (i = 0; i < vector_active(cmd_vector); i++)
2001 if ((cmd_element = vector_slot(cmd_vector, i))) {
2002 const char *string;
2003 vector strvec = cmd_element->strvec;
2004
2005 /* Check field length */
2006 if (index >= vector_active(strvec))
2007 vector_slot(cmd_vector, i) = NULL;
2008 else {
2009 unsigned int j;
2010
2011 descvec = vector_slot(strvec, index);
2012 for (j = 0; j < vector_active(descvec); j++)
2013 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002014 const char *cmd = desc->cmd;
2015 char *tmp = NULL;
2016
2017 if (CMD_OPTION(desc->cmd)) {
2018 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2019 cmd = tmp;
2020 }
2021 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002022 if (cmd_unique_string (matchvec, string))
2023 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002024 if (tmp)
2025 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002026 }
2027 }
2028 }
2029
2030 /* We don't need cmd_vector any more. */
2031 vector_free(cmd_vector);
2032
2033 /* No matched command */
2034 if (vector_slot(matchvec, 0) == NULL) {
2035 vector_free(matchvec);
2036
2037 /* In case of 'command \t' pattern. Do you need '?' command at
2038 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002039 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002040 *status = CMD_ERR_NOTHING_TODO;
2041 else
2042 *status = CMD_ERR_NO_MATCH;
2043 return NULL;
2044 }
2045
2046 /* Only one matched */
2047 if (vector_slot(matchvec, 1) == NULL) {
2048 match_str = (char **)matchvec->index;
2049 vector_only_wrapper_free(matchvec);
2050 *status = CMD_COMPLETE_FULL_MATCH;
2051 return match_str;
2052 }
2053 /* Make it sure last element is NULL. */
2054 vector_set(matchvec, NULL);
2055
2056 /* Check LCD of matched strings. */
2057 if (vector_slot(vline, index) != NULL) {
2058 lcd = cmd_lcd((char **)matchvec->index);
2059
2060 if (lcd) {
2061 int len = strlen(vector_slot(vline, index));
2062
2063 if (len < lcd) {
2064 char *lcdstr;
2065
2066 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2067 "complete-lcdstr");
2068 memcpy(lcdstr, matchvec->index[0], lcd);
2069 lcdstr[lcd] = '\0';
2070
2071 /* match_str = (char **) &lcdstr; */
2072
2073 /* Free matchvec. */
2074 for (i = 0; i < vector_active(matchvec); i++) {
2075 if (vector_slot(matchvec, i))
2076 talloc_free(vector_slot(matchvec, i));
2077 }
2078 vector_free(matchvec);
2079
2080 /* Make new matchvec. */
2081 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2082 vector_set(matchvec, lcdstr);
2083 match_str = (char **)matchvec->index;
2084 vector_only_wrapper_free(matchvec);
2085
2086 *status = CMD_COMPLETE_MATCH;
2087 return match_str;
2088 }
2089 }
2090 }
2091
2092 match_str = (char **)matchvec->index;
2093 vector_only_wrapper_free(matchvec);
2094 *status = CMD_COMPLETE_LIST_MATCH;
2095 return match_str;
2096}
2097
2098char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2099{
2100 char **ret;
2101
2102 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2103 enum node_type onode;
2104 vector shifted_vline;
2105 unsigned int index;
2106
2107 onode = vty->node;
2108 vty->node = ENABLE_NODE;
2109 /* We can try it on enable node, cos' the vty is authenticated */
2110
2111 shifted_vline = vector_init(vector_count(vline));
2112 /* use memcpy? */
2113 for (index = 1; index < vector_active(vline); index++) {
2114 vector_set_index(shifted_vline, index - 1,
2115 vector_lookup(vline, index));
2116 }
2117
2118 ret = cmd_complete_command_real(shifted_vline, vty, status);
2119
2120 vector_free(shifted_vline);
2121 vty->node = onode;
2122 return ret;
2123 }
2124
2125 return cmd_complete_command_real(vline, vty, status);
2126}
2127
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002128static struct vty_parent_node *vty_parent(struct vty *vty)
2129{
2130 return llist_first_entry_or_null(&vty->parent_nodes,
2131 struct vty_parent_node,
2132 entry);
2133}
2134
2135static bool vty_pop_parent(struct vty *vty)
2136{
2137 struct vty_parent_node *parent = vty_parent(vty);
2138 if (!parent)
2139 return false;
2140 llist_del(&parent->entry);
2141 vty->node = parent->node;
2142 vty->priv = parent->priv;
2143 if (vty->indent)
2144 talloc_free(vty->indent);
2145 vty->indent = parent->indent;
2146 talloc_free(parent);
2147 return true;
2148}
2149
2150static void vty_clear_parents(struct vty *vty)
2151{
2152 while (vty_pop_parent(vty));
2153}
2154
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002155/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002156/*
2157 * This function MUST eventually converge on a node when called repeatedly,
2158 * there must not be any cycles.
2159 * All 'config' nodes shall converge on CONFIG_NODE.
2160 * All other 'enable' nodes shall converge on ENABLE_NODE.
2161 * All 'view' only nodes shall converge on VIEW_NODE.
2162 * All other nodes shall converge on themselves or it must be ensured,
2163 * that the user's rights are not extended anyhow by calling this function.
2164 *
2165 * Note that these requirements also apply to all functions that are used
2166 * as go_parent_cb.
2167 * Note also that this function relies on the is_config_child callback to
2168 * recognize non-config nodes if go_parent_cb is not set.
2169 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002170int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002171{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002172 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002173 case AUTH_NODE:
2174 case VIEW_NODE:
2175 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002176 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002177 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002178 break;
2179
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002180 case AUTH_ENABLE_NODE:
2181 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002182 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002183 break;
2184
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002185 case CFG_LOG_NODE:
2186 case VTY_NODE:
2187 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002188 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002189 break;
2190
2191 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002192 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002193 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002194 vty_pop_parent(vty);
2195 }
2196 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002197 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002198 vty_clear_parents(vty);
2199 }
2200 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002201 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002202 vty_clear_parents(vty);
2203 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002204 break;
2205 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002206
2207 return vty->node;
2208}
2209
2210/* Execute command by argument vline vector. */
2211static int
2212cmd_execute_command_real(vector vline, struct vty *vty,
2213 struct cmd_element **cmd)
2214{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002215 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002216 unsigned int index;
2217 vector cmd_vector;
2218 struct cmd_element *cmd_element;
2219 struct cmd_element *matched_element;
2220 unsigned int matched_count, incomplete_count;
2221 int argc;
2222 const char *argv[CMD_ARGC_MAX];
2223 enum match_type match = 0;
2224 int varflag;
2225 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002226 int rc;
2227 /* Used for temporary storage of cmd_deopt() allocated arguments during
2228 argv[] generation */
2229 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002230
2231 /* Make copy of command elements. */
2232 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2233
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002234 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002235 if ((command = vector_slot(vline, index))) {
2236 int ret;
2237
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002238 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002239 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002240
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002241 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002242 break;
2243
2244 ret =
2245 is_cmd_ambiguous(command, cmd_vector, index, match);
2246
2247 if (ret == 1) {
2248 vector_free(cmd_vector);
2249 return CMD_ERR_AMBIGUOUS;
2250 } else if (ret == 2) {
2251 vector_free(cmd_vector);
2252 return CMD_ERR_NO_MATCH;
2253 }
2254 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002255 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002256
2257 /* Check matched count. */
2258 matched_element = NULL;
2259 matched_count = 0;
2260 incomplete_count = 0;
2261
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002262 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002263 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002264 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002265 || index >= cmd_element->cmdsize) {
2266 matched_element = cmd_element;
2267#if 0
2268 printf("DEBUG: %s\n", cmd_element->string);
2269#endif
2270 matched_count++;
2271 } else {
2272 incomplete_count++;
2273 }
2274 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002275 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002276
2277 /* Finish of using cmd_vector. */
2278 vector_free(cmd_vector);
2279
2280 /* To execute command, matched_count must be 1. */
2281 if (matched_count == 0) {
2282 if (incomplete_count)
2283 return CMD_ERR_INCOMPLETE;
2284 else
2285 return CMD_ERR_NO_MATCH;
2286 }
2287
2288 if (matched_count > 1)
2289 return CMD_ERR_AMBIGUOUS;
2290
2291 /* Argument treatment */
2292 varflag = 0;
2293 argc = 0;
2294
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002295 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2296
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002297 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002298 if (argc == CMD_ARGC_MAX) {
2299 rc = CMD_ERR_EXEED_ARGC_MAX;
2300 goto rc_free_deopt_ctx;
2301 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002302 if (varflag) {
2303 argv[argc++] = vector_slot(vline, i);
2304 continue;
2305 }
2306
2307 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002308 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002309
2310 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002311 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002312 struct desc *desc = vector_slot(descvec, 0);
2313
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002314 if (CMD_OPTION(desc->cmd)) {
2315 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2316 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2317 } else {
2318 tmp_cmd = desc->cmd;
2319 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002320
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002321 if (CMD_VARARG(tmp_cmd))
2322 varflag = 1;
2323 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002324 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002325 else if (CMD_OPTION(desc->cmd))
2326 argv[argc++] = tmp_cmd;
2327 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002328 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002329 /* multi choice argument. look up which choice
2330 the user meant (can only be one after
2331 filtering and checking for ambigous). For instance,
2332 if user typed "th" for "(two|three)" arg, we
2333 want to pass "three" in argv[]. */
2334 for (j = 0; j < vector_active(descvec); j++) {
2335 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002336 if (!desc)
2337 continue;
2338 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2339 continue;
2340 if (CMD_OPTION(desc->cmd)) {
2341 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2342 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2343 } else {
2344 tmp_cmd = desc->cmd;
2345 }
2346
2347 if(CMD_VARIABLE(tmp_cmd)) {
2348 argv[argc++] = vector_slot(vline, i);
2349 } else {
2350 argv[argc++] = tmp_cmd;
2351 }
2352 break;
2353 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002354 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002355 }
2356
2357 /* For vtysh execution. */
2358 if (cmd)
2359 *cmd = matched_element;
2360
2361 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002362 rc = CMD_SUCCESS_DAEMON;
2363 else /* Execute matched command. */
2364 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002365
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002366rc_free_deopt_ctx:
2367 /* Now after we called the command func, we can free temporary strings */
2368 talloc_free(cmd_deopt_ctx);
2369 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002370}
2371
2372int
2373cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2374 int vtysh)
2375{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002376 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002377 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002378
2379 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002380
2381 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2382 vector shifted_vline;
2383 unsigned int index;
2384
2385 vty->node = ENABLE_NODE;
2386 /* We can try it on enable node, cos' the vty is authenticated */
2387
2388 shifted_vline = vector_init(vector_count(vline));
2389 /* use memcpy? */
2390 for (index = 1; index < vector_active(vline); index++) {
2391 vector_set_index(shifted_vline, index - 1,
2392 vector_lookup(vline, index));
2393 }
2394
2395 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2396
2397 vector_free(shifted_vline);
2398 vty->node = onode;
2399 return ret;
2400 }
2401
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002402 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002403}
2404
2405/* Execute command by argument readline. */
2406int
2407cmd_execute_command_strict(vector vline, struct vty *vty,
2408 struct cmd_element **cmd)
2409{
2410 unsigned int i;
2411 unsigned int index;
2412 vector cmd_vector;
2413 struct cmd_element *cmd_element;
2414 struct cmd_element *matched_element;
2415 unsigned int matched_count, incomplete_count;
2416 int argc;
2417 const char *argv[CMD_ARGC_MAX];
2418 int varflag;
2419 enum match_type match = 0;
2420 char *command;
2421
2422 /* Make copy of command element */
2423 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2424
2425 for (index = 0; index < vector_active(vline); index++)
2426 if ((command = vector_slot(vline, index))) {
2427 int ret;
2428
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002429 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002430 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002431
2432 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002433 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002434 break;
2435
2436 ret =
2437 is_cmd_ambiguous(command, cmd_vector, index, match);
2438 if (ret == 1) {
2439 vector_free(cmd_vector);
2440 return CMD_ERR_AMBIGUOUS;
2441 }
2442 if (ret == 2) {
2443 vector_free(cmd_vector);
2444 return CMD_ERR_NO_MATCH;
2445 }
2446 }
2447
2448 /* Check matched count. */
2449 matched_element = NULL;
2450 matched_count = 0;
2451 incomplete_count = 0;
2452 for (i = 0; i < vector_active(cmd_vector); i++)
2453 if (vector_slot(cmd_vector, i) != NULL) {
2454 cmd_element = vector_slot(cmd_vector, i);
2455
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002456 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002457 || index >= cmd_element->cmdsize) {
2458 matched_element = cmd_element;
2459 matched_count++;
2460 } else
2461 incomplete_count++;
2462 }
2463
2464 /* Finish of using cmd_vector. */
2465 vector_free(cmd_vector);
2466
2467 /* To execute command, matched_count must be 1. */
2468 if (matched_count == 0) {
2469 if (incomplete_count)
2470 return CMD_ERR_INCOMPLETE;
2471 else
2472 return CMD_ERR_NO_MATCH;
2473 }
2474
2475 if (matched_count > 1)
2476 return CMD_ERR_AMBIGUOUS;
2477
2478 /* Argument treatment */
2479 varflag = 0;
2480 argc = 0;
2481
2482 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002483 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002484 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002485 if (varflag) {
2486 argv[argc++] = vector_slot(vline, i);
2487 continue;
2488 }
2489
2490 vector descvec = vector_slot(matched_element->strvec, i);
2491
2492 if (vector_active(descvec) == 1) {
2493 struct desc *desc = vector_slot(descvec, 0);
2494
2495 if (CMD_VARARG(desc->cmd))
2496 varflag = 1;
2497
2498 if (varflag || CMD_VARIABLE(desc->cmd)
2499 || CMD_OPTION(desc->cmd))
2500 argv[argc++] = vector_slot(vline, i);
2501 } else {
2502 argv[argc++] = vector_slot(vline, i);
2503 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002504 }
2505
2506 /* For vtysh execution. */
2507 if (cmd)
2508 *cmd = matched_element;
2509
2510 if (matched_element->daemon)
2511 return CMD_SUCCESS_DAEMON;
2512
2513 /* Now execute matched command */
2514 return (*matched_element->func) (matched_element, vty, argc, argv);
2515}
2516
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002517static inline size_t len(const char *str)
2518{
2519 return str? strlen(str) : 0;
2520}
2521
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002522/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2523 * is longer than b, a must start with exactly b, and vice versa.
2524 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2525 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002526static int indent_cmp(const char *a, const char *b)
2527{
2528 size_t al, bl;
2529 al = len(a);
2530 bl = len(b);
2531 if (al > bl) {
2532 if (bl && strncmp(a, b, bl) != 0)
2533 return EINVAL;
2534 return 1;
2535 }
2536 /* al <= bl */
2537 if (al && strncmp(a, b, al) != 0)
2538 return EINVAL;
2539 return (al < bl)? -1 : 0;
2540}
2541
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002542/* Configration make from file. */
2543int config_from_file(struct vty *vty, FILE * fp)
2544{
2545 int ret;
2546 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002547 char *indent;
2548 int cmp;
2549 struct vty_parent_node this_node;
2550 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002551
2552 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002553 indent = NULL;
2554 vline = NULL;
2555 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002556
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002557 if (ret != CMD_SUCCESS)
2558 goto return_invalid_indent;
2559
2560 /* In case of comment or empty line */
2561 if (vline == NULL) {
2562 if (indent) {
2563 talloc_free(indent);
2564 indent = NULL;
2565 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002566 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002567 }
2568
Neels Hofmeyr43063632017-09-19 23:54:01 +02002569 /* We have a nonempty line. */
2570 if (!vty->indent) {
2571 /* We have just entered a node and expecting the first child to come up; but we
2572 * may also skip right back to a parent or ancestor level. */
2573 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002574
Neels Hofmeyr43063632017-09-19 23:54:01 +02002575 /* If there is no parent, record any indentation we encounter. */
2576 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2577
2578 if (cmp == EINVAL)
2579 goto return_invalid_indent;
2580
2581 if (cmp <= 0) {
2582 /* We have gone right back to the parent level or higher, we are skipping
2583 * this child node level entirely. Pop the parent to go back to a node
2584 * that was actually there (to reinstate vty->indent) and re-use below
2585 * go-parent while-loop to find an accurate match of indent in the node
2586 * ancestry. */
2587 vty_go_parent(vty);
2588 } else {
2589 /* The indent is deeper than the just entered parent, record the new
2590 * indentation characters. */
2591 vty->indent = talloc_strdup(vty, indent);
2592 /* This *is* the new indentation. */
2593 cmp = 0;
2594 }
2595 } else {
2596 /* There is a known indentation for this node level, validate and detect node
2597 * exits. */
2598 cmp = indent_cmp(indent, vty->indent);
2599 if (cmp == EINVAL)
2600 goto return_invalid_indent;
2601 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002602
2603 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2604 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2605 while (cmp < 0) {
2606 vty_go_parent(vty);
2607 cmp = indent_cmp(indent, vty->indent);
2608 if (cmp == EINVAL)
2609 goto return_invalid_indent;
2610 }
2611
2612 /* More indent without having entered a child node level? Either the parent node's indent
2613 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2614 * or the indentation increased even though the vty command didn't enter a child. */
2615 if (cmp > 0)
2616 goto return_invalid_indent;
2617
2618 /* Remember the current node before the command possibly changes it. */
2619 this_node = (struct vty_parent_node){
2620 .node = vty->node,
2621 .priv = vty->priv,
2622 .indent = vty->indent,
2623 };
2624
2625 parent = vty_parent(vty);
2626 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002627 cmd_free_strvec(vline);
2628
2629 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002630 && ret != CMD_ERR_NOTHING_TODO) {
2631 if (indent) {
2632 talloc_free(indent);
2633 indent = NULL;
2634 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002635 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002636 }
2637
2638 /* If we have stepped down into a child node, push a parent frame.
2639 * The causality is such: we don't expect every single node entry implementation to push
2640 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2641 * a parent node. Hence if the node changed without the parent node changing, we must
2642 * have stepped into a child node (and now expect a deeper indent). */
2643 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2644 /* Push the parent node. */
2645 parent = talloc_zero(vty, struct vty_parent_node);
2646 *parent = this_node;
2647 llist_add(&parent->entry, &vty->parent_nodes);
2648
2649 /* The current talloc'ed vty->indent string will now be owned by this parent
2650 * struct. Indicate that we don't know what deeper indent characters the user
2651 * will choose. */
2652 vty->indent = NULL;
2653 }
2654
2655 if (indent) {
2656 talloc_free(indent);
2657 indent = NULL;
2658 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002659 }
2660 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002661
2662return_invalid_indent:
2663 if (vline)
2664 cmd_free_strvec(vline);
2665 if (indent) {
2666 talloc_free(indent);
2667 indent = NULL;
2668 }
2669 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002670}
2671
2672/* Configration from terminal */
2673DEFUN(config_terminal,
2674 config_terminal_cmd,
2675 "configure terminal",
2676 "Configuration from vty interface\n" "Configuration terminal\n")
2677{
2678 if (vty_config_lock(vty))
2679 vty->node = CONFIG_NODE;
2680 else {
2681 vty_out(vty, "VTY configuration is locked by other VTY%s",
2682 VTY_NEWLINE);
2683 return CMD_WARNING;
2684 }
2685 return CMD_SUCCESS;
2686}
2687
2688/* Enable command */
2689DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2690{
2691 /* If enable password is NULL, change to ENABLE_NODE */
2692 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2693 vty->type == VTY_SHELL_SERV)
2694 vty->node = ENABLE_NODE;
2695 else
2696 vty->node = AUTH_ENABLE_NODE;
2697
2698 return CMD_SUCCESS;
2699}
2700
2701/* Disable command */
2702DEFUN(disable,
2703 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2704{
2705 if (vty->node == ENABLE_NODE)
2706 vty->node = VIEW_NODE;
2707 return CMD_SUCCESS;
2708}
2709
2710/* Down vty node level. */
2711gDEFUN(config_exit,
2712 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2713{
2714 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002715 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002716 case VIEW_NODE:
2717 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002718 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002719 break;
2720 case CONFIG_NODE:
2721 vty->node = ENABLE_NODE;
2722 vty_config_unlock(vty);
2723 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002724 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002725 if (vty->node > CONFIG_NODE)
2726 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002727 break;
2728 }
2729 return CMD_SUCCESS;
2730}
2731
2732/* End of configuration. */
2733 gDEFUN(config_end,
2734 config_end_cmd, "end", "End current mode and change to enable mode.")
2735{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002736 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002737 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002738
2739 /* Repeatedly call go_parent until a top node is reached. */
2740 while (vty->node > CONFIG_NODE) {
2741 if (vty->node == last_node) {
2742 /* Ensure termination, this shouldn't happen. */
2743 break;
2744 }
2745 last_node = vty->node;
2746 vty_go_parent(vty);
2747 }
2748
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002749 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002750 if (vty->node > ENABLE_NODE)
2751 vty->node = ENABLE_NODE;
2752 vty->index = NULL;
2753 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002754 }
2755 return CMD_SUCCESS;
2756}
2757
2758/* Show version. */
2759DEFUN(show_version,
2760 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2761{
Harald Welte237f6242010-05-25 23:00:45 +02002762 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2763 host.app_info->version,
2764 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2765 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002766
2767 return CMD_SUCCESS;
2768}
2769
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002770DEFUN(show_online_help,
2771 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2772{
2773 vty_dump_nodes(vty);
2774 return CMD_SUCCESS;
2775}
2776
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002777/* Help display function for all node. */
2778gDEFUN(config_help,
2779 config_help_cmd, "help", "Description of the interactive help system\n")
2780{
2781 vty_out(vty,
2782 "This VTY provides advanced help features. When you need help,%s\
2783anytime at the command line please press '?'.%s\
2784%s\
2785If nothing matches, the help list will be empty and you must backup%s\
2786 until entering a '?' shows the available options.%s\
2787Two styles of help are provided:%s\
27881. Full help is available when you are ready to enter a%s\
2789command argument (e.g. 'show ?') and describes each possible%s\
2790argument.%s\
27912. Partial help is provided when an abbreviated argument is entered%s\
2792 and you want to know what arguments match the input%s\
2793 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2794 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2795 return CMD_SUCCESS;
2796}
2797
2798/* Help display function for all node. */
2799gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2800{
2801 unsigned int i;
2802 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2803 struct cmd_element *cmd;
2804
2805 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2806 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002807 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002808 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2809 return CMD_SUCCESS;
2810}
2811
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002812static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002813{
2814 unsigned int i;
2815 int fd;
2816 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002817 char *config_file_tmp = NULL;
2818 char *config_file_sav = NULL;
2819 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002820 struct stat st;
2821
2822 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002823
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002824 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2825 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2826 * manually instead. */
2827
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002828 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002829 config_file_sav =
2830 _talloc_zero(tall_vty_cmd_ctx,
2831 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2832 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002833 if (!config_file_sav)
2834 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002835 strcpy(config_file_sav, config_file);
2836 strcat(config_file_sav, CONF_BACKUP_EXT);
2837
2838 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002839 "config_file_tmp");
2840 if (!config_file_tmp) {
2841 talloc_free(config_file_sav);
2842 return -1;
2843 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002844 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2845
2846 /* Open file to configuration write. */
2847 fd = mkstemp(config_file_tmp);
2848 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002849 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002850 talloc_free(config_file_tmp);
2851 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002852 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002853 }
2854
2855 /* Make vty for configuration file. */
2856 file_vty = vty_new();
2857 file_vty->fd = fd;
2858 file_vty->type = VTY_FILE;
2859
2860 /* Config file header print. */
2861 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002862 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002863 //vty_time_print (file_vty, 1);
2864 vty_out(file_vty, "!\n");
2865
2866 for (i = 0; i < vector_active(cmdvec); i++)
2867 if ((node = vector_slot(cmdvec, i)) && node->func) {
2868 if ((*node->func) (file_vty))
2869 vty_out(file_vty, "!\n");
2870 }
2871 vty_close(file_vty);
2872
2873 if (unlink(config_file_sav) != 0)
2874 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002875 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002876 talloc_free(config_file_sav);
2877 talloc_free(config_file_tmp);
2878 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002879 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002880 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002881
2882 /* Only link the .sav file if the original file exists */
2883 if (stat(config_file, &st) == 0) {
2884 if (link(config_file, config_file_sav) != 0) {
2885 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2886 talloc_free(config_file_sav);
2887 talloc_free(config_file_tmp);
2888 unlink(config_file_tmp);
2889 return -3;
2890 }
2891 sync();
2892 if (unlink(config_file) != 0) {
2893 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2894 talloc_free(config_file_sav);
2895 talloc_free(config_file_tmp);
2896 unlink(config_file_tmp);
2897 return -4;
2898 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002899 }
2900 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002901 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002902 talloc_free(config_file_sav);
2903 talloc_free(config_file_tmp);
2904 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002905 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002906 }
2907 unlink(config_file_tmp);
2908 sync();
2909
2910 talloc_free(config_file_sav);
2911 talloc_free(config_file_tmp);
2912
2913 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002914 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2915 return -6;
2916 }
2917
2918 return 0;
2919}
2920
2921
2922/* Write current configuration into file. */
2923DEFUN(config_write_file,
2924 config_write_file_cmd,
2925 "write file",
2926 "Write running configuration to memory, network, or terminal\n"
2927 "Write to configuration file\n")
2928{
2929 char *failed_file;
2930 int rc;
2931
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002932 if (host.app_info->config_is_consistent) {
2933 rc = host.app_info->config_is_consistent(vty);
2934 if (!rc) {
2935 vty_out(vty, "Configuration is not consistent%s",
2936 VTY_NEWLINE);
2937 return CMD_WARNING;
2938 }
2939 }
2940
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002941 if (host.config == NULL) {
2942 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2943 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002944 return CMD_WARNING;
2945 }
2946
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002947 rc = write_config_file(host.config, &failed_file);
2948 switch (rc) {
2949 case -1:
2950 vty_out(vty, "Can't open configuration file %s.%s",
2951 failed_file, VTY_NEWLINE);
2952 rc = CMD_WARNING;
2953 break;
2954 case -2:
2955 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2956 failed_file, VTY_NEWLINE);
2957 rc = CMD_WARNING;
2958 break;
2959 case -3:
2960 vty_out(vty, "Can't backup old configuration file %s.%s",
2961 failed_file, VTY_NEWLINE);
2962 rc = CMD_WARNING;
2963 break;
2964 case -4:
2965 vty_out(vty, "Can't unlink configuration file %s.%s",
2966 failed_file, VTY_NEWLINE);
2967 rc = CMD_WARNING;
2968 break;
2969 case -5:
2970 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2971 VTY_NEWLINE);
2972 rc = CMD_WARNING;
2973 break;
2974 case -6:
2975 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2976 failed_file, strerror(errno), errno, VTY_NEWLINE);
2977 rc = CMD_WARNING;
2978 break;
2979 default:
2980 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2981 rc = CMD_SUCCESS;
2982 break;
2983 }
2984
2985 talloc_free(failed_file);
2986 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002987}
2988
2989ALIAS(config_write_file,
2990 config_write_cmd,
2991 "write", "Write running configuration to memory, network, or terminal\n")
2992
2993 ALIAS(config_write_file,
2994 config_write_memory_cmd,
2995 "write memory",
2996 "Write running configuration to memory, network, or terminal\n"
2997 "Write configuration to the file (same as write file)\n")
2998
2999 ALIAS(config_write_file,
3000 copy_runningconfig_startupconfig_cmd,
3001 "copy running-config startup-config",
3002 "Copy configuration\n"
3003 "Copy running config to... \n"
3004 "Copy running config to startup config (same as write file)\n")
3005
3006/* Write current configuration into the terminal. */
3007 DEFUN(config_write_terminal,
3008 config_write_terminal_cmd,
3009 "write terminal",
3010 "Write running configuration to memory, network, or terminal\n"
3011 "Write to terminal\n")
3012{
3013 unsigned int i;
3014 struct cmd_node *node;
3015
3016 if (vty->type == VTY_SHELL_SERV) {
3017 for (i = 0; i < vector_active(cmdvec); i++)
3018 if ((node = vector_slot(cmdvec, i)) && node->func
3019 && node->vtysh) {
3020 if ((*node->func) (vty))
3021 vty_out(vty, "!%s", VTY_NEWLINE);
3022 }
3023 } else {
3024 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3025 VTY_NEWLINE);
3026 vty_out(vty, "!%s", VTY_NEWLINE);
3027
3028 for (i = 0; i < vector_active(cmdvec); i++)
3029 if ((node = vector_slot(cmdvec, i)) && node->func) {
3030 if ((*node->func) (vty))
3031 vty_out(vty, "!%s", VTY_NEWLINE);
3032 }
3033 vty_out(vty, "end%s", VTY_NEWLINE);
3034 }
3035 return CMD_SUCCESS;
3036}
3037
3038/* Write current configuration into the terminal. */
3039ALIAS(config_write_terminal,
3040 show_running_config_cmd,
3041 "show running-config", SHOW_STR "running configuration\n")
3042
3043/* Write startup configuration into the terminal. */
3044 DEFUN(show_startup_config,
3045 show_startup_config_cmd,
3046 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3047{
3048 char buf[BUFSIZ];
3049 FILE *confp;
3050
3051 confp = fopen(host.config, "r");
3052 if (confp == NULL) {
3053 vty_out(vty, "Can't open configuration file [%s]%s",
3054 host.config, VTY_NEWLINE);
3055 return CMD_WARNING;
3056 }
3057
3058 while (fgets(buf, BUFSIZ, confp)) {
3059 char *cp = buf;
3060
3061 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3062 cp++;
3063 *cp = '\0';
3064
3065 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3066 }
3067
3068 fclose(confp);
3069
3070 return CMD_SUCCESS;
3071}
3072
3073/* Hostname configuration */
3074DEFUN(config_hostname,
3075 hostname_cmd,
3076 "hostname WORD",
3077 "Set system's network name\n" "This system's network name\n")
3078{
3079 if (!isalpha((int)*argv[0])) {
3080 vty_out(vty, "Please specify string starting with alphabet%s",
3081 VTY_NEWLINE);
3082 return CMD_WARNING;
3083 }
3084
3085 if (host.name)
3086 talloc_free(host.name);
3087
3088 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3089 return CMD_SUCCESS;
3090}
3091
3092DEFUN(config_no_hostname,
3093 no_hostname_cmd,
3094 "no hostname [HOSTNAME]",
3095 NO_STR "Reset system's network name\n" "Host name of this router\n")
3096{
3097 if (host.name)
3098 talloc_free(host.name);
3099 host.name = NULL;
3100 return CMD_SUCCESS;
3101}
3102
3103/* VTY interface password set. */
3104DEFUN(config_password, password_cmd,
3105 "password (8|) WORD",
3106 "Assign the terminal connection password\n"
3107 "Specifies a HIDDEN password will follow\n"
3108 "dummy string \n" "The HIDDEN line password string\n")
3109{
3110 /* Argument check. */
3111 if (argc == 0) {
3112 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3113 return CMD_WARNING;
3114 }
3115
3116 if (argc == 2) {
3117 if (*argv[0] == '8') {
3118 if (host.password)
3119 talloc_free(host.password);
3120 host.password = NULL;
3121 if (host.password_encrypt)
3122 talloc_free(host.password_encrypt);
3123 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3124 return CMD_SUCCESS;
3125 } else {
3126 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3127 return CMD_WARNING;
3128 }
3129 }
3130
3131 if (!isalnum((int)*argv[0])) {
3132 vty_out(vty,
3133 "Please specify string starting with alphanumeric%s",
3134 VTY_NEWLINE);
3135 return CMD_WARNING;
3136 }
3137
3138 if (host.password)
3139 talloc_free(host.password);
3140 host.password = NULL;
3141
3142#ifdef VTY_CRYPT_PW
3143 if (host.encrypt) {
3144 if (host.password_encrypt)
3145 talloc_free(host.password_encrypt);
3146 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3147 } else
3148#endif
3149 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3150
3151 return CMD_SUCCESS;
3152}
3153
3154ALIAS(config_password, password_text_cmd,
3155 "password LINE",
3156 "Assign the terminal connection password\n"
3157 "The UNENCRYPTED (cleartext) line password\n")
3158
3159/* VTY enable password set. */
3160 DEFUN(config_enable_password, enable_password_cmd,
3161 "enable password (8|) WORD",
3162 "Modify enable password parameters\n"
3163 "Assign the privileged level password\n"
3164 "Specifies a HIDDEN password will follow\n"
3165 "dummy string \n" "The HIDDEN 'enable' password string\n")
3166{
3167 /* Argument check. */
3168 if (argc == 0) {
3169 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3170 return CMD_WARNING;
3171 }
3172
3173 /* Crypt type is specified. */
3174 if (argc == 2) {
3175 if (*argv[0] == '8') {
3176 if (host.enable)
3177 talloc_free(host.enable);
3178 host.enable = NULL;
3179
3180 if (host.enable_encrypt)
3181 talloc_free(host.enable_encrypt);
3182 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3183
3184 return CMD_SUCCESS;
3185 } else {
3186 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3187 return CMD_WARNING;
3188 }
3189 }
3190
3191 if (!isalnum((int)*argv[0])) {
3192 vty_out(vty,
3193 "Please specify string starting with alphanumeric%s",
3194 VTY_NEWLINE);
3195 return CMD_WARNING;
3196 }
3197
3198 if (host.enable)
3199 talloc_free(host.enable);
3200 host.enable = NULL;
3201
3202 /* Plain password input. */
3203#ifdef VTY_CRYPT_PW
3204 if (host.encrypt) {
3205 if (host.enable_encrypt)
3206 talloc_free(host.enable_encrypt);
3207 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3208 } else
3209#endif
3210 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3211
3212 return CMD_SUCCESS;
3213}
3214
3215ALIAS(config_enable_password,
3216 enable_password_text_cmd,
3217 "enable password LINE",
3218 "Modify enable password parameters\n"
3219 "Assign the privileged level password\n"
3220 "The UNENCRYPTED (cleartext) 'enable' password\n")
3221
3222/* VTY enable password delete. */
3223 DEFUN(no_config_enable_password, no_enable_password_cmd,
3224 "no enable password",
3225 NO_STR
3226 "Modify enable password parameters\n"
3227 "Assign the privileged level password\n")
3228{
3229 if (host.enable)
3230 talloc_free(host.enable);
3231 host.enable = NULL;
3232
3233 if (host.enable_encrypt)
3234 talloc_free(host.enable_encrypt);
3235 host.enable_encrypt = NULL;
3236
3237 return CMD_SUCCESS;
3238}
3239
3240#ifdef VTY_CRYPT_PW
3241DEFUN(service_password_encrypt,
3242 service_password_encrypt_cmd,
3243 "service password-encryption",
3244 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3245{
3246 if (host.encrypt)
3247 return CMD_SUCCESS;
3248
3249 host.encrypt = 1;
3250
3251 if (host.password) {
3252 if (host.password_encrypt)
3253 talloc_free(host.password_encrypt);
3254 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3255 }
3256 if (host.enable) {
3257 if (host.enable_encrypt)
3258 talloc_free(host.enable_encrypt);
3259 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3260 }
3261
3262 return CMD_SUCCESS;
3263}
3264
3265DEFUN(no_service_password_encrypt,
3266 no_service_password_encrypt_cmd,
3267 "no service password-encryption",
3268 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3269{
3270 if (!host.encrypt)
3271 return CMD_SUCCESS;
3272
3273 host.encrypt = 0;
3274
3275 if (host.password_encrypt)
3276 talloc_free(host.password_encrypt);
3277 host.password_encrypt = NULL;
3278
3279 if (host.enable_encrypt)
3280 talloc_free(host.enable_encrypt);
3281 host.enable_encrypt = NULL;
3282
3283 return CMD_SUCCESS;
3284}
3285#endif
3286
3287DEFUN(config_terminal_length, config_terminal_length_cmd,
3288 "terminal length <0-512>",
3289 "Set terminal line parameters\n"
3290 "Set number of lines on a screen\n"
3291 "Number of lines on screen (0 for no pausing)\n")
3292{
3293 int lines;
3294 char *endptr = NULL;
3295
3296 lines = strtol(argv[0], &endptr, 10);
3297 if (lines < 0 || lines > 512 || *endptr != '\0') {
3298 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3299 return CMD_WARNING;
3300 }
3301 vty->lines = lines;
3302
3303 return CMD_SUCCESS;
3304}
3305
3306DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3307 "terminal no length",
3308 "Set terminal line parameters\n"
3309 NO_STR "Set number of lines on a screen\n")
3310{
3311 vty->lines = -1;
3312 return CMD_SUCCESS;
3313}
3314
3315DEFUN(service_terminal_length, service_terminal_length_cmd,
3316 "service terminal-length <0-512>",
3317 "Set up miscellaneous service\n"
3318 "System wide terminal length configuration\n"
3319 "Number of lines of VTY (0 means no line control)\n")
3320{
3321 int lines;
3322 char *endptr = NULL;
3323
3324 lines = strtol(argv[0], &endptr, 10);
3325 if (lines < 0 || lines > 512 || *endptr != '\0') {
3326 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3327 return CMD_WARNING;
3328 }
3329 host.lines = lines;
3330
3331 return CMD_SUCCESS;
3332}
3333
3334DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3335 "no service terminal-length [<0-512>]",
3336 NO_STR
3337 "Set up miscellaneous service\n"
3338 "System wide terminal length configuration\n"
3339 "Number of lines of VTY (0 means no line control)\n")
3340{
3341 host.lines = -1;
3342 return CMD_SUCCESS;
3343}
3344
3345DEFUN_HIDDEN(do_echo,
3346 echo_cmd,
3347 "echo .MESSAGE",
3348 "Echo a message back to the vty\n" "The message to echo\n")
3349{
3350 char *message;
3351
3352 vty_out(vty, "%s%s",
3353 ((message =
3354 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3355 if (message)
3356 talloc_free(message);
3357 return CMD_SUCCESS;
3358}
3359
3360#if 0
3361DEFUN(config_logmsg,
3362 config_logmsg_cmd,
3363 "logmsg " LOG_LEVELS " .MESSAGE",
3364 "Send a message to enabled logging destinations\n"
3365 LOG_LEVEL_DESC "The message to send\n")
3366{
3367 int level;
3368 char *message;
3369
3370 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3371 return CMD_ERR_NO_MATCH;
3372
3373 zlog(NULL, level,
3374 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3375 if (message)
3376 talloc_free(message);
3377 return CMD_SUCCESS;
3378}
3379
3380DEFUN(show_logging,
3381 show_logging_cmd,
3382 "show logging", SHOW_STR "Show current logging configuration\n")
3383{
3384 struct zlog *zl = zlog_default;
3385
3386 vty_out(vty, "Syslog logging: ");
3387 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3388 vty_out(vty, "disabled");
3389 else
3390 vty_out(vty, "level %s, facility %s, ident %s",
3391 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3392 facility_name(zl->facility), zl->ident);
3393 vty_out(vty, "%s", VTY_NEWLINE);
3394
3395 vty_out(vty, "Stdout logging: ");
3396 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3397 vty_out(vty, "disabled");
3398 else
3399 vty_out(vty, "level %s",
3400 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3401 vty_out(vty, "%s", VTY_NEWLINE);
3402
3403 vty_out(vty, "Monitor logging: ");
3404 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3405 vty_out(vty, "disabled");
3406 else
3407 vty_out(vty, "level %s",
3408 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3409 vty_out(vty, "%s", VTY_NEWLINE);
3410
3411 vty_out(vty, "File logging: ");
3412 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3413 vty_out(vty, "disabled");
3414 else
3415 vty_out(vty, "level %s, filename %s",
3416 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3417 zl->filename);
3418 vty_out(vty, "%s", VTY_NEWLINE);
3419
3420 vty_out(vty, "Protocol name: %s%s",
3421 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3422 vty_out(vty, "Record priority: %s%s",
3423 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3424
3425 return CMD_SUCCESS;
3426}
3427
3428DEFUN(config_log_stdout,
3429 config_log_stdout_cmd,
3430 "log stdout", "Logging control\n" "Set stdout logging level\n")
3431{
3432 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3433 return CMD_SUCCESS;
3434}
3435
3436DEFUN(config_log_stdout_level,
3437 config_log_stdout_level_cmd,
3438 "log stdout " LOG_LEVELS,
3439 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3440{
3441 int level;
3442
3443 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3444 return CMD_ERR_NO_MATCH;
3445 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3446 return CMD_SUCCESS;
3447}
3448
3449DEFUN(no_config_log_stdout,
3450 no_config_log_stdout_cmd,
3451 "no log stdout [LEVEL]",
3452 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3453{
3454 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3455 return CMD_SUCCESS;
3456}
3457
3458DEFUN(config_log_monitor,
3459 config_log_monitor_cmd,
3460 "log monitor",
3461 "Logging control\n" "Set terminal line (monitor) logging level\n")
3462{
3463 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3464 return CMD_SUCCESS;
3465}
3466
3467DEFUN(config_log_monitor_level,
3468 config_log_monitor_level_cmd,
3469 "log monitor " LOG_LEVELS,
3470 "Logging control\n"
3471 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3472{
3473 int level;
3474
3475 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3476 return CMD_ERR_NO_MATCH;
3477 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3478 return CMD_SUCCESS;
3479}
3480
3481DEFUN(no_config_log_monitor,
3482 no_config_log_monitor_cmd,
3483 "no log monitor [LEVEL]",
3484 NO_STR
3485 "Logging control\n"
3486 "Disable terminal line (monitor) logging\n" "Logging level\n")
3487{
3488 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3489 return CMD_SUCCESS;
3490}
3491
3492static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3493{
3494 int ret;
3495 char *p = NULL;
3496 const char *fullpath;
3497
3498 /* Path detection. */
3499 if (!IS_DIRECTORY_SEP(*fname)) {
3500 char cwd[MAXPATHLEN + 1];
3501 cwd[MAXPATHLEN] = '\0';
3502
3503 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3504 zlog_err("config_log_file: Unable to alloc mem!");
3505 return CMD_WARNING;
3506 }
3507
3508 if ((p = _talloc_zero(tall_vcmd_ctx,
3509 strlen(cwd) + strlen(fname) + 2),
3510 "set_log_file")
3511 == NULL) {
3512 zlog_err("config_log_file: Unable to alloc mem!");
3513 return CMD_WARNING;
3514 }
3515 sprintf(p, "%s/%s", cwd, fname);
3516 fullpath = p;
3517 } else
3518 fullpath = fname;
3519
3520 ret = zlog_set_file(NULL, fullpath, loglevel);
3521
3522 if (p)
3523 talloc_free(p);
3524
3525 if (!ret) {
3526 vty_out(vty, "can't open logfile %s\n", fname);
3527 return CMD_WARNING;
3528 }
3529
3530 if (host.logfile)
3531 talloc_free(host.logfile);
3532
3533 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3534
3535 return CMD_SUCCESS;
3536}
3537
3538DEFUN(config_log_file,
3539 config_log_file_cmd,
3540 "log file FILENAME",
3541 "Logging control\n" "Logging to file\n" "Logging filename\n")
3542{
3543 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3544}
3545
3546DEFUN(config_log_file_level,
3547 config_log_file_level_cmd,
3548 "log file FILENAME " LOG_LEVELS,
3549 "Logging control\n"
3550 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3551{
3552 int level;
3553
3554 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3555 return CMD_ERR_NO_MATCH;
3556 return set_log_file(vty, argv[0], level);
3557}
3558
3559DEFUN(no_config_log_file,
3560 no_config_log_file_cmd,
3561 "no log file [FILENAME]",
3562 NO_STR
3563 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3564{
3565 zlog_reset_file(NULL);
3566
3567 if (host.logfile)
3568 talloc_free(host.logfile);
3569
3570 host.logfile = NULL;
3571
3572 return CMD_SUCCESS;
3573}
3574
3575ALIAS(no_config_log_file,
3576 no_config_log_file_level_cmd,
3577 "no log file FILENAME LEVEL",
3578 NO_STR
3579 "Logging control\n"
3580 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3581
3582 DEFUN(config_log_syslog,
3583 config_log_syslog_cmd,
3584 "log syslog", "Logging control\n" "Set syslog logging level\n")
3585{
3586 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3587 return CMD_SUCCESS;
3588}
3589
3590DEFUN(config_log_syslog_level,
3591 config_log_syslog_level_cmd,
3592 "log syslog " LOG_LEVELS,
3593 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3594{
3595 int level;
3596
3597 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3598 return CMD_ERR_NO_MATCH;
3599 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3600 return CMD_SUCCESS;
3601}
3602
3603DEFUN_DEPRECATED(config_log_syslog_facility,
3604 config_log_syslog_facility_cmd,
3605 "log syslog facility " LOG_FACILITIES,
3606 "Logging control\n"
3607 "Logging goes to syslog\n"
3608 "(Deprecated) Facility parameter for syslog messages\n"
3609 LOG_FACILITY_DESC)
3610{
3611 int facility;
3612
3613 if ((facility = facility_match(argv[0])) < 0)
3614 return CMD_ERR_NO_MATCH;
3615
3616 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3617 zlog_default->facility = facility;
3618 return CMD_SUCCESS;
3619}
3620
3621DEFUN(no_config_log_syslog,
3622 no_config_log_syslog_cmd,
3623 "no log syslog [LEVEL]",
3624 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3625{
3626 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3627 return CMD_SUCCESS;
3628}
3629
3630ALIAS(no_config_log_syslog,
3631 no_config_log_syslog_facility_cmd,
3632 "no log syslog facility " LOG_FACILITIES,
3633 NO_STR
3634 "Logging control\n"
3635 "Logging goes to syslog\n"
3636 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3637
3638 DEFUN(config_log_facility,
3639 config_log_facility_cmd,
3640 "log facility " LOG_FACILITIES,
3641 "Logging control\n"
3642 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3643{
3644 int facility;
3645
3646 if ((facility = facility_match(argv[0])) < 0)
3647 return CMD_ERR_NO_MATCH;
3648 zlog_default->facility = facility;
3649 return CMD_SUCCESS;
3650}
3651
3652DEFUN(no_config_log_facility,
3653 no_config_log_facility_cmd,
3654 "no log facility [FACILITY]",
3655 NO_STR
3656 "Logging control\n"
3657 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3658{
3659 zlog_default->facility = LOG_DAEMON;
3660 return CMD_SUCCESS;
3661}
3662
3663DEFUN_DEPRECATED(config_log_trap,
3664 config_log_trap_cmd,
3665 "log trap " LOG_LEVELS,
3666 "Logging control\n"
3667 "(Deprecated) Set logging level and default for all destinations\n"
3668 LOG_LEVEL_DESC)
3669{
3670 int new_level;
3671 int i;
3672
3673 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3674 return CMD_ERR_NO_MATCH;
3675
3676 zlog_default->default_lvl = new_level;
3677 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3678 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3679 zlog_default->maxlvl[i] = new_level;
3680 return CMD_SUCCESS;
3681}
3682
3683DEFUN_DEPRECATED(no_config_log_trap,
3684 no_config_log_trap_cmd,
3685 "no log trap [LEVEL]",
3686 NO_STR
3687 "Logging control\n"
3688 "Permit all logging information\n" "Logging level\n")
3689{
3690 zlog_default->default_lvl = LOG_DEBUG;
3691 return CMD_SUCCESS;
3692}
3693
3694DEFUN(config_log_record_priority,
3695 config_log_record_priority_cmd,
3696 "log record-priority",
3697 "Logging control\n"
3698 "Log the priority of the message within the message\n")
3699{
3700 zlog_default->record_priority = 1;
3701 return CMD_SUCCESS;
3702}
3703
3704DEFUN(no_config_log_record_priority,
3705 no_config_log_record_priority_cmd,
3706 "no log record-priority",
3707 NO_STR
3708 "Logging control\n"
3709 "Do not log the priority of the message within the message\n")
3710{
3711 zlog_default->record_priority = 0;
3712 return CMD_SUCCESS;
3713}
3714#endif
3715
3716DEFUN(banner_motd_file,
3717 banner_motd_file_cmd,
3718 "banner motd file [FILE]",
3719 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3720{
3721 if (host.motdfile)
3722 talloc_free(host.motdfile);
3723 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3724
3725 return CMD_SUCCESS;
3726}
3727
3728DEFUN(banner_motd_default,
3729 banner_motd_default_cmd,
3730 "banner motd default",
3731 "Set banner string\n" "Strings for motd\n" "Default string\n")
3732{
3733 host.motd = default_motd;
3734 return CMD_SUCCESS;
3735}
3736
3737DEFUN(no_banner_motd,
3738 no_banner_motd_cmd,
3739 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3740{
3741 host.motd = NULL;
3742 if (host.motdfile)
3743 talloc_free(host.motdfile);
3744 host.motdfile = NULL;
3745 return CMD_SUCCESS;
3746}
3747
3748/* Set config filename. Called from vty.c */
3749void host_config_set(const char *filename)
3750{
3751 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3752}
3753
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003754/*! Deprecated, now happens implicitly when calling install_node().
3755 * Users of the API may still attempt to call this function, hence
3756 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003757void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003758{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003759}
3760
3761/*! Deprecated, now happens implicitly when calling install_node().
3762 * Users of the API may still attempt to call this function, hence
3763 * leave it here as a no-op. */
3764void vty_install_default(int node)
3765{
3766}
3767
3768/*! Install common commands like 'exit' and 'list'. */
3769static void install_basic_node_commands(int node)
3770{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003771 install_element(node, &config_help_cmd);
3772 install_element(node, &config_list_cmd);
3773
3774 install_element(node, &config_write_terminal_cmd);
3775 install_element(node, &config_write_file_cmd);
3776 install_element(node, &config_write_memory_cmd);
3777 install_element(node, &config_write_cmd);
3778 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003779
3780 install_element(node, &config_exit_cmd);
3781
3782 if (node >= CONFIG_NODE) {
3783 /* It's not a top node. */
3784 install_element(node, &config_end_cmd);
3785 }
3786}
3787
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003788/*! Return true if a node is installed by install_basic_node_commands(), so
3789 * that we can avoid repeating them for each and every node during 'show
3790 * running-config' */
3791static bool vty_command_is_common(struct cmd_element *cmd)
3792{
3793 if (cmd == &config_help_cmd
3794 || cmd == &config_list_cmd
3795 || cmd == &config_write_terminal_cmd
3796 || cmd == &config_write_file_cmd
3797 || cmd == &config_write_memory_cmd
3798 || cmd == &config_write_cmd
3799 || cmd == &show_running_config_cmd
3800 || cmd == &config_exit_cmd
3801 || cmd == &config_end_cmd)
3802 return true;
3803 return false;
3804}
3805
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003806/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003807 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003808 * \param[in] vty the vty of the code
3809 * \param[in] filename where to store the file
3810 * \return 0 in case of success.
3811 *
3812 * If the filename already exists create a filename.sav
3813 * version with the current code.
3814 *
3815 */
3816int osmo_vty_write_config_file(const char *filename)
3817{
3818 char *failed_file;
3819 int rc;
3820
3821 rc = write_config_file(filename, &failed_file);
3822 talloc_free(failed_file);
3823 return rc;
3824}
3825
3826/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003827 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003828 * \return 0 in case of success.
3829 *
3830 * If the filename already exists create a filename.sav
3831 * version with the current code.
3832 *
3833 */
3834int osmo_vty_save_config_file(void)
3835{
3836 char *failed_file;
3837 int rc;
3838
3839 if (host.config == NULL)
3840 return -7;
3841
3842 rc = write_config_file(host.config, &failed_file);
3843 talloc_free(failed_file);
3844 return rc;
3845}
3846
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003847/* Initialize command interface. Install basic nodes and commands. */
3848void cmd_init(int terminal)
3849{
3850 /* Allocate initial top vector of commands. */
3851 cmdvec = vector_init(VECTOR_MIN_SIZE);
3852
3853 /* Default host value settings. */
3854 host.name = NULL;
3855 host.password = NULL;
3856 host.enable = NULL;
3857 host.logfile = NULL;
3858 host.config = NULL;
3859 host.lines = -1;
3860 host.motd = default_motd;
3861 host.motdfile = NULL;
3862
3863 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003864 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003865 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003866 install_node_bare(&auth_node, NULL);
3867 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003868 install_node(&config_node, config_write_host);
3869
3870 /* Each node's basic commands. */
3871 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003872 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003873 if (terminal) {
3874 install_element(VIEW_NODE, &config_list_cmd);
3875 install_element(VIEW_NODE, &config_exit_cmd);
3876 install_element(VIEW_NODE, &config_help_cmd);
3877 install_element(VIEW_NODE, &config_enable_cmd);
3878 install_element(VIEW_NODE, &config_terminal_length_cmd);
3879 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3880 install_element(VIEW_NODE, &echo_cmd);
3881 }
3882
3883 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003884 install_element(ENABLE_NODE, &config_disable_cmd);
3885 install_element(ENABLE_NODE, &config_terminal_cmd);
3886 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3887 }
3888 install_element (ENABLE_NODE, &show_startup_config_cmd);
3889 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003890 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003891
3892 if (terminal) {
3893 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3894 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3895 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003896 }
3897
3898 install_element(CONFIG_NODE, &hostname_cmd);
3899 install_element(CONFIG_NODE, &no_hostname_cmd);
3900
3901 if (terminal) {
3902 install_element(CONFIG_NODE, &password_cmd);
3903 install_element(CONFIG_NODE, &password_text_cmd);
3904 install_element(CONFIG_NODE, &enable_password_cmd);
3905 install_element(CONFIG_NODE, &enable_password_text_cmd);
3906 install_element(CONFIG_NODE, &no_enable_password_cmd);
3907
3908#ifdef VTY_CRYPT_PW
3909 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3910 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3911#endif
3912 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3913 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3914 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3915 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3916 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3917
3918 }
3919 srand(time(NULL));
3920}
Harald Welte7acb30c2011-08-17 17:13:48 +02003921
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003922/*! @} */