blob: 6a9d18af5867f2907f30ecaf55bc43bd7165f5cf [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
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002634 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002635 if (indent) {
2636 talloc_free(indent);
2637 indent = NULL;
2638 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002639 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002640 }
2641
2642 /* If we have stepped down into a child node, push a parent frame.
2643 * The causality is such: we don't expect every single node entry implementation to push
2644 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2645 * a parent node. Hence if the node changed without the parent node changing, we must
2646 * have stepped into a child node (and now expect a deeper indent). */
2647 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2648 /* Push the parent node. */
2649 parent = talloc_zero(vty, struct vty_parent_node);
2650 *parent = this_node;
2651 llist_add(&parent->entry, &vty->parent_nodes);
2652
2653 /* The current talloc'ed vty->indent string will now be owned by this parent
2654 * struct. Indicate that we don't know what deeper indent characters the user
2655 * will choose. */
2656 vty->indent = NULL;
2657 }
2658
2659 if (indent) {
2660 talloc_free(indent);
2661 indent = NULL;
2662 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002663 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002664 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2665 while (vty_parent(vty))
2666 vty_go_parent(vty);
2667
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002668 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002669
2670return_invalid_indent:
2671 if (vline)
2672 cmd_free_strvec(vline);
2673 if (indent) {
2674 talloc_free(indent);
2675 indent = NULL;
2676 }
2677 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002678}
2679
2680/* Configration from terminal */
2681DEFUN(config_terminal,
2682 config_terminal_cmd,
2683 "configure terminal",
2684 "Configuration from vty interface\n" "Configuration terminal\n")
2685{
2686 if (vty_config_lock(vty))
2687 vty->node = CONFIG_NODE;
2688 else {
2689 vty_out(vty, "VTY configuration is locked by other VTY%s",
2690 VTY_NEWLINE);
2691 return CMD_WARNING;
2692 }
2693 return CMD_SUCCESS;
2694}
2695
2696/* Enable command */
2697DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2698{
2699 /* If enable password is NULL, change to ENABLE_NODE */
2700 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2701 vty->type == VTY_SHELL_SERV)
2702 vty->node = ENABLE_NODE;
2703 else
2704 vty->node = AUTH_ENABLE_NODE;
2705
2706 return CMD_SUCCESS;
2707}
2708
2709/* Disable command */
2710DEFUN(disable,
2711 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2712{
2713 if (vty->node == ENABLE_NODE)
2714 vty->node = VIEW_NODE;
2715 return CMD_SUCCESS;
2716}
2717
2718/* Down vty node level. */
2719gDEFUN(config_exit,
2720 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2721{
2722 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002723 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002724 case VIEW_NODE:
2725 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002726 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002727 break;
2728 case CONFIG_NODE:
2729 vty->node = ENABLE_NODE;
2730 vty_config_unlock(vty);
2731 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002732 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002733 if (vty->node > CONFIG_NODE)
2734 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002735 break;
2736 }
2737 return CMD_SUCCESS;
2738}
2739
2740/* End of configuration. */
2741 gDEFUN(config_end,
2742 config_end_cmd, "end", "End current mode and change to enable mode.")
2743{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002744 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002745 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002746
2747 /* Repeatedly call go_parent until a top node is reached. */
2748 while (vty->node > CONFIG_NODE) {
2749 if (vty->node == last_node) {
2750 /* Ensure termination, this shouldn't happen. */
2751 break;
2752 }
2753 last_node = vty->node;
2754 vty_go_parent(vty);
2755 }
2756
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002757 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002758 if (vty->node > ENABLE_NODE)
2759 vty->node = ENABLE_NODE;
2760 vty->index = NULL;
2761 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002762 }
2763 return CMD_SUCCESS;
2764}
2765
2766/* Show version. */
2767DEFUN(show_version,
2768 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2769{
Harald Welte237f6242010-05-25 23:00:45 +02002770 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2771 host.app_info->version,
2772 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2773 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002774
2775 return CMD_SUCCESS;
2776}
2777
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002778DEFUN(show_online_help,
2779 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2780{
2781 vty_dump_nodes(vty);
2782 return CMD_SUCCESS;
2783}
2784
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002785/* Help display function for all node. */
2786gDEFUN(config_help,
2787 config_help_cmd, "help", "Description of the interactive help system\n")
2788{
2789 vty_out(vty,
2790 "This VTY provides advanced help features. When you need help,%s\
2791anytime at the command line please press '?'.%s\
2792%s\
2793If nothing matches, the help list will be empty and you must backup%s\
2794 until entering a '?' shows the available options.%s\
2795Two styles of help are provided:%s\
27961. Full help is available when you are ready to enter a%s\
2797command argument (e.g. 'show ?') and describes each possible%s\
2798argument.%s\
27992. Partial help is provided when an abbreviated argument is entered%s\
2800 and you want to know what arguments match the input%s\
2801 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2802 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2803 return CMD_SUCCESS;
2804}
2805
2806/* Help display function for all node. */
2807gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2808{
2809 unsigned int i;
2810 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2811 struct cmd_element *cmd;
2812
2813 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2814 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002815 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002816 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2817 return CMD_SUCCESS;
2818}
2819
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002820static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002821{
2822 unsigned int i;
2823 int fd;
2824 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002825 char *config_file_tmp = NULL;
2826 char *config_file_sav = NULL;
2827 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002828 struct stat st;
2829
2830 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002831
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002832 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2833 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2834 * manually instead. */
2835
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002836 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002837 config_file_sav =
2838 _talloc_zero(tall_vty_cmd_ctx,
2839 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2840 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002841 if (!config_file_sav)
2842 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002843 strcpy(config_file_sav, config_file);
2844 strcat(config_file_sav, CONF_BACKUP_EXT);
2845
2846 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002847 "config_file_tmp");
2848 if (!config_file_tmp) {
2849 talloc_free(config_file_sav);
2850 return -1;
2851 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002852 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2853
2854 /* Open file to configuration write. */
2855 fd = mkstemp(config_file_tmp);
2856 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002857 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002858 talloc_free(config_file_tmp);
2859 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002860 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002861 }
2862
2863 /* Make vty for configuration file. */
2864 file_vty = vty_new();
2865 file_vty->fd = fd;
2866 file_vty->type = VTY_FILE;
2867
2868 /* Config file header print. */
2869 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002870 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002871 //vty_time_print (file_vty, 1);
2872 vty_out(file_vty, "!\n");
2873
2874 for (i = 0; i < vector_active(cmdvec); i++)
2875 if ((node = vector_slot(cmdvec, i)) && node->func) {
2876 if ((*node->func) (file_vty))
2877 vty_out(file_vty, "!\n");
2878 }
2879 vty_close(file_vty);
2880
2881 if (unlink(config_file_sav) != 0)
2882 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002883 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002884 talloc_free(config_file_sav);
2885 talloc_free(config_file_tmp);
2886 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002887 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002888 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002889
2890 /* Only link the .sav file if the original file exists */
2891 if (stat(config_file, &st) == 0) {
2892 if (link(config_file, config_file_sav) != 0) {
2893 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2894 talloc_free(config_file_sav);
2895 talloc_free(config_file_tmp);
2896 unlink(config_file_tmp);
2897 return -3;
2898 }
2899 sync();
2900 if (unlink(config_file) != 0) {
2901 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2902 talloc_free(config_file_sav);
2903 talloc_free(config_file_tmp);
2904 unlink(config_file_tmp);
2905 return -4;
2906 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002907 }
2908 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002909 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002910 talloc_free(config_file_sav);
2911 talloc_free(config_file_tmp);
2912 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002913 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002914 }
2915 unlink(config_file_tmp);
2916 sync();
2917
2918 talloc_free(config_file_sav);
2919 talloc_free(config_file_tmp);
2920
2921 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002922 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2923 return -6;
2924 }
2925
2926 return 0;
2927}
2928
2929
2930/* Write current configuration into file. */
2931DEFUN(config_write_file,
2932 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002933 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002934 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002935 "Write to configuration file\n"
2936 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002937{
2938 char *failed_file;
2939 int rc;
2940
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002941 if (host.app_info->config_is_consistent) {
2942 rc = host.app_info->config_is_consistent(vty);
2943 if (!rc) {
2944 vty_out(vty, "Configuration is not consistent%s",
2945 VTY_NEWLINE);
2946 return CMD_WARNING;
2947 }
2948 }
2949
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002950 if (argc == 1)
2951 host_config_set(argv[0]);
2952
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002953 if (host.config == NULL) {
2954 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2955 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002956 return CMD_WARNING;
2957 }
2958
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002959 rc = write_config_file(host.config, &failed_file);
2960 switch (rc) {
2961 case -1:
2962 vty_out(vty, "Can't open configuration file %s.%s",
2963 failed_file, VTY_NEWLINE);
2964 rc = CMD_WARNING;
2965 break;
2966 case -2:
2967 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2968 failed_file, VTY_NEWLINE);
2969 rc = CMD_WARNING;
2970 break;
2971 case -3:
2972 vty_out(vty, "Can't backup old configuration file %s.%s",
2973 failed_file, VTY_NEWLINE);
2974 rc = CMD_WARNING;
2975 break;
2976 case -4:
2977 vty_out(vty, "Can't unlink configuration file %s.%s",
2978 failed_file, VTY_NEWLINE);
2979 rc = CMD_WARNING;
2980 break;
2981 case -5:
2982 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2983 VTY_NEWLINE);
2984 rc = CMD_WARNING;
2985 break;
2986 case -6:
2987 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2988 failed_file, strerror(errno), errno, VTY_NEWLINE);
2989 rc = CMD_WARNING;
2990 break;
2991 default:
2992 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2993 rc = CMD_SUCCESS;
2994 break;
2995 }
2996
2997 talloc_free(failed_file);
2998 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002999}
3000
3001ALIAS(config_write_file,
3002 config_write_cmd,
3003 "write", "Write running configuration to memory, network, or terminal\n")
3004
3005 ALIAS(config_write_file,
3006 config_write_memory_cmd,
3007 "write memory",
3008 "Write running configuration to memory, network, or terminal\n"
3009 "Write configuration to the file (same as write file)\n")
3010
3011 ALIAS(config_write_file,
3012 copy_runningconfig_startupconfig_cmd,
3013 "copy running-config startup-config",
3014 "Copy configuration\n"
3015 "Copy running config to... \n"
3016 "Copy running config to startup config (same as write file)\n")
3017
3018/* Write current configuration into the terminal. */
3019 DEFUN(config_write_terminal,
3020 config_write_terminal_cmd,
3021 "write terminal",
3022 "Write running configuration to memory, network, or terminal\n"
3023 "Write to terminal\n")
3024{
3025 unsigned int i;
3026 struct cmd_node *node;
3027
3028 if (vty->type == VTY_SHELL_SERV) {
3029 for (i = 0; i < vector_active(cmdvec); i++)
3030 if ((node = vector_slot(cmdvec, i)) && node->func
3031 && node->vtysh) {
3032 if ((*node->func) (vty))
3033 vty_out(vty, "!%s", VTY_NEWLINE);
3034 }
3035 } else {
3036 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3037 VTY_NEWLINE);
3038 vty_out(vty, "!%s", VTY_NEWLINE);
3039
3040 for (i = 0; i < vector_active(cmdvec); i++)
3041 if ((node = vector_slot(cmdvec, i)) && node->func) {
3042 if ((*node->func) (vty))
3043 vty_out(vty, "!%s", VTY_NEWLINE);
3044 }
3045 vty_out(vty, "end%s", VTY_NEWLINE);
3046 }
3047 return CMD_SUCCESS;
3048}
3049
3050/* Write current configuration into the terminal. */
3051ALIAS(config_write_terminal,
3052 show_running_config_cmd,
3053 "show running-config", SHOW_STR "running configuration\n")
3054
3055/* Write startup configuration into the terminal. */
3056 DEFUN(show_startup_config,
3057 show_startup_config_cmd,
3058 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3059{
3060 char buf[BUFSIZ];
3061 FILE *confp;
3062
3063 confp = fopen(host.config, "r");
3064 if (confp == NULL) {
3065 vty_out(vty, "Can't open configuration file [%s]%s",
3066 host.config, VTY_NEWLINE);
3067 return CMD_WARNING;
3068 }
3069
3070 while (fgets(buf, BUFSIZ, confp)) {
3071 char *cp = buf;
3072
3073 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3074 cp++;
3075 *cp = '\0';
3076
3077 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3078 }
3079
3080 fclose(confp);
3081
3082 return CMD_SUCCESS;
3083}
3084
3085/* Hostname configuration */
3086DEFUN(config_hostname,
3087 hostname_cmd,
3088 "hostname WORD",
3089 "Set system's network name\n" "This system's network name\n")
3090{
3091 if (!isalpha((int)*argv[0])) {
3092 vty_out(vty, "Please specify string starting with alphabet%s",
3093 VTY_NEWLINE);
3094 return CMD_WARNING;
3095 }
3096
3097 if (host.name)
3098 talloc_free(host.name);
3099
3100 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3101 return CMD_SUCCESS;
3102}
3103
3104DEFUN(config_no_hostname,
3105 no_hostname_cmd,
3106 "no hostname [HOSTNAME]",
3107 NO_STR "Reset system's network name\n" "Host name of this router\n")
3108{
3109 if (host.name)
3110 talloc_free(host.name);
3111 host.name = NULL;
3112 return CMD_SUCCESS;
3113}
3114
3115/* VTY interface password set. */
3116DEFUN(config_password, password_cmd,
3117 "password (8|) WORD",
3118 "Assign the terminal connection password\n"
3119 "Specifies a HIDDEN password will follow\n"
3120 "dummy string \n" "The HIDDEN line password string\n")
3121{
3122 /* Argument check. */
3123 if (argc == 0) {
3124 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3125 return CMD_WARNING;
3126 }
3127
3128 if (argc == 2) {
3129 if (*argv[0] == '8') {
3130 if (host.password)
3131 talloc_free(host.password);
3132 host.password = NULL;
3133 if (host.password_encrypt)
3134 talloc_free(host.password_encrypt);
3135 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3136 return CMD_SUCCESS;
3137 } else {
3138 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3139 return CMD_WARNING;
3140 }
3141 }
3142
3143 if (!isalnum((int)*argv[0])) {
3144 vty_out(vty,
3145 "Please specify string starting with alphanumeric%s",
3146 VTY_NEWLINE);
3147 return CMD_WARNING;
3148 }
3149
3150 if (host.password)
3151 talloc_free(host.password);
3152 host.password = NULL;
3153
3154#ifdef VTY_CRYPT_PW
3155 if (host.encrypt) {
3156 if (host.password_encrypt)
3157 talloc_free(host.password_encrypt);
3158 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3159 } else
3160#endif
3161 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3162
3163 return CMD_SUCCESS;
3164}
3165
3166ALIAS(config_password, password_text_cmd,
3167 "password LINE",
3168 "Assign the terminal connection password\n"
3169 "The UNENCRYPTED (cleartext) line password\n")
3170
3171/* VTY enable password set. */
3172 DEFUN(config_enable_password, enable_password_cmd,
3173 "enable password (8|) WORD",
3174 "Modify enable password parameters\n"
3175 "Assign the privileged level password\n"
3176 "Specifies a HIDDEN password will follow\n"
3177 "dummy string \n" "The HIDDEN 'enable' password string\n")
3178{
3179 /* Argument check. */
3180 if (argc == 0) {
3181 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3182 return CMD_WARNING;
3183 }
3184
3185 /* Crypt type is specified. */
3186 if (argc == 2) {
3187 if (*argv[0] == '8') {
3188 if (host.enable)
3189 talloc_free(host.enable);
3190 host.enable = NULL;
3191
3192 if (host.enable_encrypt)
3193 talloc_free(host.enable_encrypt);
3194 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3195
3196 return CMD_SUCCESS;
3197 } else {
3198 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3199 return CMD_WARNING;
3200 }
3201 }
3202
3203 if (!isalnum((int)*argv[0])) {
3204 vty_out(vty,
3205 "Please specify string starting with alphanumeric%s",
3206 VTY_NEWLINE);
3207 return CMD_WARNING;
3208 }
3209
3210 if (host.enable)
3211 talloc_free(host.enable);
3212 host.enable = NULL;
3213
3214 /* Plain password input. */
3215#ifdef VTY_CRYPT_PW
3216 if (host.encrypt) {
3217 if (host.enable_encrypt)
3218 talloc_free(host.enable_encrypt);
3219 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3220 } else
3221#endif
3222 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3223
3224 return CMD_SUCCESS;
3225}
3226
3227ALIAS(config_enable_password,
3228 enable_password_text_cmd,
3229 "enable password LINE",
3230 "Modify enable password parameters\n"
3231 "Assign the privileged level password\n"
3232 "The UNENCRYPTED (cleartext) 'enable' password\n")
3233
3234/* VTY enable password delete. */
3235 DEFUN(no_config_enable_password, no_enable_password_cmd,
3236 "no enable password",
3237 NO_STR
3238 "Modify enable password parameters\n"
3239 "Assign the privileged level password\n")
3240{
3241 if (host.enable)
3242 talloc_free(host.enable);
3243 host.enable = NULL;
3244
3245 if (host.enable_encrypt)
3246 talloc_free(host.enable_encrypt);
3247 host.enable_encrypt = NULL;
3248
3249 return CMD_SUCCESS;
3250}
3251
3252#ifdef VTY_CRYPT_PW
3253DEFUN(service_password_encrypt,
3254 service_password_encrypt_cmd,
3255 "service password-encryption",
3256 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3257{
3258 if (host.encrypt)
3259 return CMD_SUCCESS;
3260
3261 host.encrypt = 1;
3262
3263 if (host.password) {
3264 if (host.password_encrypt)
3265 talloc_free(host.password_encrypt);
3266 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3267 }
3268 if (host.enable) {
3269 if (host.enable_encrypt)
3270 talloc_free(host.enable_encrypt);
3271 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3272 }
3273
3274 return CMD_SUCCESS;
3275}
3276
3277DEFUN(no_service_password_encrypt,
3278 no_service_password_encrypt_cmd,
3279 "no service password-encryption",
3280 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3281{
3282 if (!host.encrypt)
3283 return CMD_SUCCESS;
3284
3285 host.encrypt = 0;
3286
3287 if (host.password_encrypt)
3288 talloc_free(host.password_encrypt);
3289 host.password_encrypt = NULL;
3290
3291 if (host.enable_encrypt)
3292 talloc_free(host.enable_encrypt);
3293 host.enable_encrypt = NULL;
3294
3295 return CMD_SUCCESS;
3296}
3297#endif
3298
3299DEFUN(config_terminal_length, config_terminal_length_cmd,
3300 "terminal length <0-512>",
3301 "Set terminal line parameters\n"
3302 "Set number of lines on a screen\n"
3303 "Number of lines on screen (0 for no pausing)\n")
3304{
3305 int lines;
3306 char *endptr = NULL;
3307
3308 lines = strtol(argv[0], &endptr, 10);
3309 if (lines < 0 || lines > 512 || *endptr != '\0') {
3310 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3311 return CMD_WARNING;
3312 }
3313 vty->lines = lines;
3314
3315 return CMD_SUCCESS;
3316}
3317
3318DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3319 "terminal no length",
3320 "Set terminal line parameters\n"
3321 NO_STR "Set number of lines on a screen\n")
3322{
3323 vty->lines = -1;
3324 return CMD_SUCCESS;
3325}
3326
3327DEFUN(service_terminal_length, service_terminal_length_cmd,
3328 "service terminal-length <0-512>",
3329 "Set up miscellaneous service\n"
3330 "System wide terminal length configuration\n"
3331 "Number of lines of VTY (0 means no line control)\n")
3332{
3333 int lines;
3334 char *endptr = NULL;
3335
3336 lines = strtol(argv[0], &endptr, 10);
3337 if (lines < 0 || lines > 512 || *endptr != '\0') {
3338 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3339 return CMD_WARNING;
3340 }
3341 host.lines = lines;
3342
3343 return CMD_SUCCESS;
3344}
3345
3346DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3347 "no service terminal-length [<0-512>]",
3348 NO_STR
3349 "Set up miscellaneous service\n"
3350 "System wide terminal length configuration\n"
3351 "Number of lines of VTY (0 means no line control)\n")
3352{
3353 host.lines = -1;
3354 return CMD_SUCCESS;
3355}
3356
3357DEFUN_HIDDEN(do_echo,
3358 echo_cmd,
3359 "echo .MESSAGE",
3360 "Echo a message back to the vty\n" "The message to echo\n")
3361{
3362 char *message;
3363
3364 vty_out(vty, "%s%s",
3365 ((message =
3366 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3367 if (message)
3368 talloc_free(message);
3369 return CMD_SUCCESS;
3370}
3371
3372#if 0
3373DEFUN(config_logmsg,
3374 config_logmsg_cmd,
3375 "logmsg " LOG_LEVELS " .MESSAGE",
3376 "Send a message to enabled logging destinations\n"
3377 LOG_LEVEL_DESC "The message to send\n")
3378{
3379 int level;
3380 char *message;
3381
3382 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3383 return CMD_ERR_NO_MATCH;
3384
3385 zlog(NULL, level,
3386 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3387 if (message)
3388 talloc_free(message);
3389 return CMD_SUCCESS;
3390}
3391
3392DEFUN(show_logging,
3393 show_logging_cmd,
3394 "show logging", SHOW_STR "Show current logging configuration\n")
3395{
3396 struct zlog *zl = zlog_default;
3397
3398 vty_out(vty, "Syslog logging: ");
3399 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3400 vty_out(vty, "disabled");
3401 else
3402 vty_out(vty, "level %s, facility %s, ident %s",
3403 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3404 facility_name(zl->facility), zl->ident);
3405 vty_out(vty, "%s", VTY_NEWLINE);
3406
3407 vty_out(vty, "Stdout logging: ");
3408 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3409 vty_out(vty, "disabled");
3410 else
3411 vty_out(vty, "level %s",
3412 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3413 vty_out(vty, "%s", VTY_NEWLINE);
3414
3415 vty_out(vty, "Monitor logging: ");
3416 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3417 vty_out(vty, "disabled");
3418 else
3419 vty_out(vty, "level %s",
3420 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3421 vty_out(vty, "%s", VTY_NEWLINE);
3422
3423 vty_out(vty, "File logging: ");
3424 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3425 vty_out(vty, "disabled");
3426 else
3427 vty_out(vty, "level %s, filename %s",
3428 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3429 zl->filename);
3430 vty_out(vty, "%s", VTY_NEWLINE);
3431
3432 vty_out(vty, "Protocol name: %s%s",
3433 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3434 vty_out(vty, "Record priority: %s%s",
3435 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3436
3437 return CMD_SUCCESS;
3438}
3439
3440DEFUN(config_log_stdout,
3441 config_log_stdout_cmd,
3442 "log stdout", "Logging control\n" "Set stdout logging level\n")
3443{
3444 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3445 return CMD_SUCCESS;
3446}
3447
3448DEFUN(config_log_stdout_level,
3449 config_log_stdout_level_cmd,
3450 "log stdout " LOG_LEVELS,
3451 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3452{
3453 int level;
3454
3455 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3456 return CMD_ERR_NO_MATCH;
3457 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3458 return CMD_SUCCESS;
3459}
3460
3461DEFUN(no_config_log_stdout,
3462 no_config_log_stdout_cmd,
3463 "no log stdout [LEVEL]",
3464 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3465{
3466 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3467 return CMD_SUCCESS;
3468}
3469
3470DEFUN(config_log_monitor,
3471 config_log_monitor_cmd,
3472 "log monitor",
3473 "Logging control\n" "Set terminal line (monitor) logging level\n")
3474{
3475 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3476 return CMD_SUCCESS;
3477}
3478
3479DEFUN(config_log_monitor_level,
3480 config_log_monitor_level_cmd,
3481 "log monitor " LOG_LEVELS,
3482 "Logging control\n"
3483 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3484{
3485 int level;
3486
3487 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3488 return CMD_ERR_NO_MATCH;
3489 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3490 return CMD_SUCCESS;
3491}
3492
3493DEFUN(no_config_log_monitor,
3494 no_config_log_monitor_cmd,
3495 "no log monitor [LEVEL]",
3496 NO_STR
3497 "Logging control\n"
3498 "Disable terminal line (monitor) logging\n" "Logging level\n")
3499{
3500 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3501 return CMD_SUCCESS;
3502}
3503
3504static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3505{
3506 int ret;
3507 char *p = NULL;
3508 const char *fullpath;
3509
3510 /* Path detection. */
3511 if (!IS_DIRECTORY_SEP(*fname)) {
3512 char cwd[MAXPATHLEN + 1];
3513 cwd[MAXPATHLEN] = '\0';
3514
3515 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3516 zlog_err("config_log_file: Unable to alloc mem!");
3517 return CMD_WARNING;
3518 }
3519
3520 if ((p = _talloc_zero(tall_vcmd_ctx,
3521 strlen(cwd) + strlen(fname) + 2),
3522 "set_log_file")
3523 == NULL) {
3524 zlog_err("config_log_file: Unable to alloc mem!");
3525 return CMD_WARNING;
3526 }
3527 sprintf(p, "%s/%s", cwd, fname);
3528 fullpath = p;
3529 } else
3530 fullpath = fname;
3531
3532 ret = zlog_set_file(NULL, fullpath, loglevel);
3533
3534 if (p)
3535 talloc_free(p);
3536
3537 if (!ret) {
3538 vty_out(vty, "can't open logfile %s\n", fname);
3539 return CMD_WARNING;
3540 }
3541
3542 if (host.logfile)
3543 talloc_free(host.logfile);
3544
3545 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3546
3547 return CMD_SUCCESS;
3548}
3549
3550DEFUN(config_log_file,
3551 config_log_file_cmd,
3552 "log file FILENAME",
3553 "Logging control\n" "Logging to file\n" "Logging filename\n")
3554{
3555 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3556}
3557
3558DEFUN(config_log_file_level,
3559 config_log_file_level_cmd,
3560 "log file FILENAME " LOG_LEVELS,
3561 "Logging control\n"
3562 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3563{
3564 int level;
3565
3566 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3567 return CMD_ERR_NO_MATCH;
3568 return set_log_file(vty, argv[0], level);
3569}
3570
3571DEFUN(no_config_log_file,
3572 no_config_log_file_cmd,
3573 "no log file [FILENAME]",
3574 NO_STR
3575 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3576{
3577 zlog_reset_file(NULL);
3578
3579 if (host.logfile)
3580 talloc_free(host.logfile);
3581
3582 host.logfile = NULL;
3583
3584 return CMD_SUCCESS;
3585}
3586
3587ALIAS(no_config_log_file,
3588 no_config_log_file_level_cmd,
3589 "no log file FILENAME LEVEL",
3590 NO_STR
3591 "Logging control\n"
3592 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3593
3594 DEFUN(config_log_syslog,
3595 config_log_syslog_cmd,
3596 "log syslog", "Logging control\n" "Set syslog logging level\n")
3597{
3598 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3599 return CMD_SUCCESS;
3600}
3601
3602DEFUN(config_log_syslog_level,
3603 config_log_syslog_level_cmd,
3604 "log syslog " LOG_LEVELS,
3605 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3606{
3607 int level;
3608
3609 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3610 return CMD_ERR_NO_MATCH;
3611 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3612 return CMD_SUCCESS;
3613}
3614
3615DEFUN_DEPRECATED(config_log_syslog_facility,
3616 config_log_syslog_facility_cmd,
3617 "log syslog facility " LOG_FACILITIES,
3618 "Logging control\n"
3619 "Logging goes to syslog\n"
3620 "(Deprecated) Facility parameter for syslog messages\n"
3621 LOG_FACILITY_DESC)
3622{
3623 int facility;
3624
3625 if ((facility = facility_match(argv[0])) < 0)
3626 return CMD_ERR_NO_MATCH;
3627
3628 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3629 zlog_default->facility = facility;
3630 return CMD_SUCCESS;
3631}
3632
3633DEFUN(no_config_log_syslog,
3634 no_config_log_syslog_cmd,
3635 "no log syslog [LEVEL]",
3636 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3637{
3638 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3639 return CMD_SUCCESS;
3640}
3641
3642ALIAS(no_config_log_syslog,
3643 no_config_log_syslog_facility_cmd,
3644 "no log syslog facility " LOG_FACILITIES,
3645 NO_STR
3646 "Logging control\n"
3647 "Logging goes to syslog\n"
3648 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3649
3650 DEFUN(config_log_facility,
3651 config_log_facility_cmd,
3652 "log facility " LOG_FACILITIES,
3653 "Logging control\n"
3654 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3655{
3656 int facility;
3657
3658 if ((facility = facility_match(argv[0])) < 0)
3659 return CMD_ERR_NO_MATCH;
3660 zlog_default->facility = facility;
3661 return CMD_SUCCESS;
3662}
3663
3664DEFUN(no_config_log_facility,
3665 no_config_log_facility_cmd,
3666 "no log facility [FACILITY]",
3667 NO_STR
3668 "Logging control\n"
3669 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3670{
3671 zlog_default->facility = LOG_DAEMON;
3672 return CMD_SUCCESS;
3673}
3674
3675DEFUN_DEPRECATED(config_log_trap,
3676 config_log_trap_cmd,
3677 "log trap " LOG_LEVELS,
3678 "Logging control\n"
3679 "(Deprecated) Set logging level and default for all destinations\n"
3680 LOG_LEVEL_DESC)
3681{
3682 int new_level;
3683 int i;
3684
3685 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3686 return CMD_ERR_NO_MATCH;
3687
3688 zlog_default->default_lvl = new_level;
3689 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3690 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3691 zlog_default->maxlvl[i] = new_level;
3692 return CMD_SUCCESS;
3693}
3694
3695DEFUN_DEPRECATED(no_config_log_trap,
3696 no_config_log_trap_cmd,
3697 "no log trap [LEVEL]",
3698 NO_STR
3699 "Logging control\n"
3700 "Permit all logging information\n" "Logging level\n")
3701{
3702 zlog_default->default_lvl = LOG_DEBUG;
3703 return CMD_SUCCESS;
3704}
3705
3706DEFUN(config_log_record_priority,
3707 config_log_record_priority_cmd,
3708 "log record-priority",
3709 "Logging control\n"
3710 "Log the priority of the message within the message\n")
3711{
3712 zlog_default->record_priority = 1;
3713 return CMD_SUCCESS;
3714}
3715
3716DEFUN(no_config_log_record_priority,
3717 no_config_log_record_priority_cmd,
3718 "no log record-priority",
3719 NO_STR
3720 "Logging control\n"
3721 "Do not log the priority of the message within the message\n")
3722{
3723 zlog_default->record_priority = 0;
3724 return CMD_SUCCESS;
3725}
3726#endif
3727
3728DEFUN(banner_motd_file,
3729 banner_motd_file_cmd,
3730 "banner motd file [FILE]",
3731 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3732{
3733 if (host.motdfile)
3734 talloc_free(host.motdfile);
3735 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3736
3737 return CMD_SUCCESS;
3738}
3739
3740DEFUN(banner_motd_default,
3741 banner_motd_default_cmd,
3742 "banner motd default",
3743 "Set banner string\n" "Strings for motd\n" "Default string\n")
3744{
3745 host.motd = default_motd;
3746 return CMD_SUCCESS;
3747}
3748
3749DEFUN(no_banner_motd,
3750 no_banner_motd_cmd,
3751 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3752{
3753 host.motd = NULL;
3754 if (host.motdfile)
3755 talloc_free(host.motdfile);
3756 host.motdfile = NULL;
3757 return CMD_SUCCESS;
3758}
3759
3760/* Set config filename. Called from vty.c */
3761void host_config_set(const char *filename)
3762{
3763 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3764}
3765
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003766/*! Deprecated, now happens implicitly when calling install_node().
3767 * Users of the API may still attempt to call this function, hence
3768 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003769void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003770{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003771}
3772
3773/*! Deprecated, now happens implicitly when calling install_node().
3774 * Users of the API may still attempt to call this function, hence
3775 * leave it here as a no-op. */
3776void vty_install_default(int node)
3777{
3778}
3779
3780/*! Install common commands like 'exit' and 'list'. */
3781static void install_basic_node_commands(int node)
3782{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003783 install_element(node, &config_help_cmd);
3784 install_element(node, &config_list_cmd);
3785
3786 install_element(node, &config_write_terminal_cmd);
3787 install_element(node, &config_write_file_cmd);
3788 install_element(node, &config_write_memory_cmd);
3789 install_element(node, &config_write_cmd);
3790 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003791
3792 install_element(node, &config_exit_cmd);
3793
3794 if (node >= CONFIG_NODE) {
3795 /* It's not a top node. */
3796 install_element(node, &config_end_cmd);
3797 }
3798}
3799
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003800/*! Return true if a node is installed by install_basic_node_commands(), so
3801 * that we can avoid repeating them for each and every node during 'show
3802 * running-config' */
3803static bool vty_command_is_common(struct cmd_element *cmd)
3804{
3805 if (cmd == &config_help_cmd
3806 || cmd == &config_list_cmd
3807 || cmd == &config_write_terminal_cmd
3808 || cmd == &config_write_file_cmd
3809 || cmd == &config_write_memory_cmd
3810 || cmd == &config_write_cmd
3811 || cmd == &show_running_config_cmd
3812 || cmd == &config_exit_cmd
3813 || cmd == &config_end_cmd)
3814 return true;
3815 return false;
3816}
3817
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003818/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003819 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003820 * \param[in] vty the vty of the code
3821 * \param[in] filename where to store the file
3822 * \return 0 in case of success.
3823 *
3824 * If the filename already exists create a filename.sav
3825 * version with the current code.
3826 *
3827 */
3828int osmo_vty_write_config_file(const char *filename)
3829{
3830 char *failed_file;
3831 int rc;
3832
3833 rc = write_config_file(filename, &failed_file);
3834 talloc_free(failed_file);
3835 return rc;
3836}
3837
3838/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003839 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003840 * \return 0 in case of success.
3841 *
3842 * If the filename already exists create a filename.sav
3843 * version with the current code.
3844 *
3845 */
3846int osmo_vty_save_config_file(void)
3847{
3848 char *failed_file;
3849 int rc;
3850
3851 if (host.config == NULL)
3852 return -7;
3853
3854 rc = write_config_file(host.config, &failed_file);
3855 talloc_free(failed_file);
3856 return rc;
3857}
3858
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003859/* Initialize command interface. Install basic nodes and commands. */
3860void cmd_init(int terminal)
3861{
3862 /* Allocate initial top vector of commands. */
3863 cmdvec = vector_init(VECTOR_MIN_SIZE);
3864
3865 /* Default host value settings. */
3866 host.name = NULL;
3867 host.password = NULL;
3868 host.enable = NULL;
3869 host.logfile = NULL;
3870 host.config = NULL;
3871 host.lines = -1;
3872 host.motd = default_motd;
3873 host.motdfile = NULL;
3874
3875 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003876 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003877 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003878 install_node_bare(&auth_node, NULL);
3879 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003880 install_node(&config_node, config_write_host);
3881
3882 /* Each node's basic commands. */
3883 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003884 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003885 if (terminal) {
3886 install_element(VIEW_NODE, &config_list_cmd);
3887 install_element(VIEW_NODE, &config_exit_cmd);
3888 install_element(VIEW_NODE, &config_help_cmd);
3889 install_element(VIEW_NODE, &config_enable_cmd);
3890 install_element(VIEW_NODE, &config_terminal_length_cmd);
3891 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3892 install_element(VIEW_NODE, &echo_cmd);
3893 }
3894
3895 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003896 install_element(ENABLE_NODE, &config_disable_cmd);
3897 install_element(ENABLE_NODE, &config_terminal_cmd);
3898 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3899 }
3900 install_element (ENABLE_NODE, &show_startup_config_cmd);
3901 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003902 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003903
3904 if (terminal) {
3905 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3906 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3907 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003908 }
3909
3910 install_element(CONFIG_NODE, &hostname_cmd);
3911 install_element(CONFIG_NODE, &no_hostname_cmd);
3912
3913 if (terminal) {
3914 install_element(CONFIG_NODE, &password_cmd);
3915 install_element(CONFIG_NODE, &password_text_cmd);
3916 install_element(CONFIG_NODE, &enable_password_cmd);
3917 install_element(CONFIG_NODE, &enable_password_text_cmd);
3918 install_element(CONFIG_NODE, &no_enable_password_cmd);
3919
3920#ifdef VTY_CRYPT_PW
3921 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3922 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3923#endif
3924 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3925 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3926 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3927 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3928 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3929
3930 }
3931 srand(time(NULL));
3932}
Harald Welte7acb30c2011-08-17 17:13:48 +02003933
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003934/*! @} */