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