blob: daee5c5aa91a07267edc3d47a140b0a6b7f2b510 [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
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200203/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200204void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200205{
206 unsigned int i, j;
207 struct cmd_node *cnode;
208 vector descvec;
209 struct cmd_element *cmd_element;
210
211 for (i = 0; i < vector_active(cmdvec); i++)
212 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
213 vector cmd_vector = cnode->cmd_vector;
214 qsort(cmd_vector->index, vector_active(cmd_vector),
215 sizeof(void *), cmp_node);
216
217 for (j = 0; j < vector_active(cmd_vector); j++)
218 if ((cmd_element =
219 vector_slot(cmd_vector, j)) != NULL
220 && vector_active(cmd_element->strvec)) {
221 descvec =
222 vector_slot(cmd_element->strvec,
223 vector_active
224 (cmd_element->strvec) -
225 1);
226 qsort(descvec->index,
227 vector_active(descvec),
228 sizeof(void *), cmp_desc);
229 }
230 }
231}
232
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200233/*! Break up string in command tokens. Return leading indents.
234 * \param[in] string String to split.
235 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
236 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
237 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
238 *
239 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
240 * so that \a indent can simply return the count of leading spaces.
241 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
242 */
243int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200244{
245 const char *cp, *start;
246 char *token;
247 int strlen;
248 vector strvec;
249
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200250 *strvec_p = NULL;
251 if (indent)
252 *indent = 0;
253
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200254 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200255 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200256
257 cp = string;
258
259 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200260 while (isspace((int)*cp) && *cp != '\0') {
261 /* if we're counting indents, we need to be strict about them */
262 if (indent && (*cp != ' ') && (*cp != '\t')) {
263 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
264 if (*cp == '\n' || *cp == '\r') {
265 cp++;
266 string = cp;
267 continue;
268 }
269 return CMD_ERR_INVALID_INDENT;
270 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200271 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200272 }
273
274 if (indent)
275 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200276
277 /* Return if there is only white spaces */
278 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200279 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200280
281 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200282 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200283
284 /* Prepare return vector. */
285 strvec = vector_init(VECTOR_MIN_SIZE);
286
287 /* Copy each command piece and set into vector. */
288 while (1) {
289 start = cp;
290 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
291 *cp != '\0')
292 cp++;
293 strlen = cp - start;
294 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
295 memcpy(token, start, strlen);
296 *(token + strlen) = '\0';
297 vector_set(strvec, token);
298
299 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
300 *cp != '\0')
301 cp++;
302
303 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200304 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200305 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200306
307 *strvec_p = strvec;
308 return CMD_SUCCESS;
309}
310
311/*! Breaking up string into each command piece. I assume given
312 character is separated by a space character. Return value is a
313 vector which includes char ** data element. */
314vector cmd_make_strvec(const char *string)
315{
316 vector strvec;
317 cmd_make_strvec2(string, NULL, &strvec);
318 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200319}
320
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200321/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200322void cmd_free_strvec(vector v)
323{
324 unsigned int i;
325 char *cp;
326
327 if (!v)
328 return;
329
330 for (i = 0; i < vector_active(v); i++)
331 if ((cp = vector_slot(v, i)) != NULL)
332 talloc_free(cp);
333
334 vector_free(v);
335}
336
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200337/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200338static char *cmd_desc_str(const char **string)
339{
340 const char *cp, *start;
341 char *token;
342 int strlen;
343
344 cp = *string;
345
346 if (cp == NULL)
347 return NULL;
348
349 /* Skip white spaces. */
350 while (isspace((int)*cp) && *cp != '\0')
351 cp++;
352
353 /* Return if there is only white spaces */
354 if (*cp == '\0')
355 return NULL;
356
357 start = cp;
358
359 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
360 cp++;
361
362 strlen = cp - start;
363 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
364 memcpy(token, start, strlen);
365 *(token + strlen) = '\0';
366
367 *string = cp;
368
369 return token;
370}
371
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200372/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200373static vector cmd_make_descvec(const char *string, const char *descstr)
374{
375 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100376 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200377 const char *sp;
378 char *token;
379 int len;
380 const char *cp;
381 const char *dp;
382 vector allvec;
383 vector strvec = NULL;
384 struct desc *desc;
385
386 cp = string;
387 dp = descstr;
388
389 if (cp == NULL)
390 return NULL;
391
392 allvec = vector_init(VECTOR_MIN_SIZE);
393
394 while (1) {
395 while (isspace((int)*cp) && *cp != '\0')
396 cp++;
397
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100398 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
399 if (cp[0] == '[' && cp[1] == '(') {
400 optional_brace = 1;
401 cp++;
402 }
403
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200404 if (*cp == '(') {
405 multiple = 1;
406 cp++;
407 }
408 if (*cp == ')') {
409 multiple = 0;
410 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100411 if (*cp == ']')
412 cp++;
413 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200414 }
415 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100416 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200417 cp++;
418 }
419
420 while (isspace((int)*cp) && *cp != '\0')
421 cp++;
422
423 if (*cp == '(') {
424 multiple = 1;
425 cp++;
426 }
427
428 if (*cp == '\0')
429 return allvec;
430
431 sp = cp;
432
433 while (!
434 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
435 || *cp == ')' || *cp == '|') && *cp != '\0')
436 cp++;
437
438 len = cp - sp;
439
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100440 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
441 if (optional_brace) {
442 /* Place each individual multi-choice token in its own square braces */
443 token[0] = '[';
444 memcpy(token + 1, sp, len);
445 token[1 + len] = ']';
446 token[2 + len] = '\0';
447 } else {
448 memcpy(token, sp, len);
449 *(token + len) = '\0';
450 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200451
452 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
453 desc->cmd = token;
454 desc->str = cmd_desc_str(&dp);
455
456 if (multiple) {
457 if (multiple == 1) {
458 strvec = vector_init(VECTOR_MIN_SIZE);
459 vector_set(allvec, strvec);
460 }
461 multiple++;
462 } else {
463 strvec = vector_init(VECTOR_MIN_SIZE);
464 vector_set(allvec, strvec);
465 }
466 vector_set(strvec, desc);
467 }
468}
469
470/* Count mandantory string vector size. This is to determine inputed
471 command has enough command length. */
472static int cmd_cmdsize(vector strvec)
473{
474 unsigned int i;
475 int size = 0;
476 vector descvec;
477 struct desc *desc;
478
479 for (i = 0; i < vector_active(strvec); i++)
480 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100481 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200482 && (desc = vector_slot(descvec, 0)) != NULL) {
483 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
484 return size;
485 else
486 size++;
487 } else
488 size++;
489 }
490 return size;
491}
492
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200493/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200494const char *cmd_prompt(enum node_type node)
495{
496 struct cmd_node *cnode;
497
498 cnode = vector_slot(cmdvec, node);
499 return cnode->prompt;
500}
501
Alexander Couzensad580ba2016-05-16 16:01:45 +0200502/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200503 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200504 * \param unsafe string
505 * \return a new talloc char *
506 */
507char *osmo_asciidoc_escape(const char *inp)
508{
509 int _strlen;
510 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200511 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200512
513 if (!inp)
514 return NULL;
515 _strlen = strlen(inp);
516
517 for (i = 0; i < _strlen; ++i) {
518 switch (inp[i]) {
519 case '|':
520 len += 2;
521 break;
522 default:
523 len += 1;
524 break;
525 }
526 }
527
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200528 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200529 if (!out)
530 return NULL;
531
532 out_ptr = out;
533
Alexander Couzensad580ba2016-05-16 16:01:45 +0200534 for (i = 0; i < _strlen; ++i) {
535 switch (inp[i]) {
536 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200537 /* Prepend escape character "\": */
538 *(out_ptr++) = '\\';
539 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200540 default:
541 *(out_ptr++) = inp[i];
542 break;
543 }
544 }
545
Alexander Couzensad580ba2016-05-16 16:01:45 +0200546 out_ptr[0] = '\0';
547 return out;
548}
549
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100550static char *xml_escape(const char *inp)
551{
552 int _strlen;
553 char *out, *out_ptr;
554 int len = 0, i, j;
555
556 if (!inp)
557 return NULL;
558 _strlen = strlen(inp);
559
560 for (i = 0; i < _strlen; ++i) {
561 switch (inp[i]) {
562 case '"':
563 len += 6;
564 break;
565 case '\'':
566 len += 6;
567 break;
568 case '<':
569 len += 4;
570 break;
571 case '>':
572 len += 4;
573 break;
574 case '&':
575 len += 5;
576 break;
577 default:
578 len += 1;
579 break;
580 }
581 }
582
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200583 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100584 if (!out)
585 return NULL;
586
587 out_ptr = out;
588
589#define ADD(out, str) \
590 for (j = 0; j < strlen(str); ++j) \
591 *(out++) = str[j];
592
593 for (i = 0; i < _strlen; ++i) {
594 switch (inp[i]) {
595 case '"':
596 ADD(out_ptr, "&quot;");
597 break;
598 case '\'':
599 ADD(out_ptr, "&apos;");
600 break;
601 case '<':
602 ADD(out_ptr, "&lt;");
603 break;
604 case '>':
605 ADD(out_ptr, "&gt;");
606 break;
607 case '&':
608 ADD(out_ptr, "&amp;");
609 break;
610 default:
611 *(out_ptr++) = inp[i];
612 break;
613 }
614 }
615
616#undef ADD
617
618 out_ptr[0] = '\0';
619 return out;
620}
621
622/*
623 * Write one cmd_element as XML to the given VTY.
624 */
625static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
626{
627 char *xml_string = xml_escape(cmd->string);
628
629 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
630 vty_out(vty, " <params>%s", VTY_NEWLINE);
631
632 int j;
633 for (j = 0; j < vector_count(cmd->strvec); ++j) {
634 vector descvec = vector_slot(cmd->strvec, j);
635 int i;
636 for (i = 0; i < vector_active(descvec); ++i) {
637 char *xml_param, *xml_doc;
638 struct desc *desc = vector_slot(descvec, i);
639 if (desc == NULL)
640 continue;
641
642 xml_param = xml_escape(desc->cmd);
643 xml_doc = xml_escape(desc->str);
644 vty_out(vty, " <param name='%s' doc='%s' />%s",
645 xml_param, xml_doc, VTY_NEWLINE);
646 talloc_free(xml_param);
647 talloc_free(xml_doc);
648 }
649 }
650
651 vty_out(vty, " </params>%s", VTY_NEWLINE);
652 vty_out(vty, " </command>%s", VTY_NEWLINE);
653
654 talloc_free(xml_string);
655 return 0;
656}
657
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200658static bool vty_command_is_common(struct cmd_element *cmd);
659
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100660/*
661 * Dump all nodes and commands associated with a given node as XML to the VTY.
662 */
663static int vty_dump_nodes(struct vty *vty)
664{
665 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200666 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100667
668 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
669
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200670 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
671 vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
672 vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
673 vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
674 " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
675 for (i = 0; i < vector_active(cmdvec); ++i) {
676 struct cmd_node *cnode;
677 cnode = vector_slot(cmdvec, i);
678 if (!cnode)
679 continue;
680 if (cnode->node != CONFIG_NODE)
681 continue;
682
683 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
684 struct cmd_element *elem;
685 elem = vector_slot(cnode->cmd_vector, j);
686 if (!vty_command_is_common(elem))
687 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200688 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200689 vty_dump_element(elem, vty);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200690 }
691 }
692 vty_out(vty, " </node>%s", VTY_NEWLINE);
693
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100694 for (i = 0; i < vector_active(cmdvec); ++i) {
695 struct cmd_node *cnode;
696 cnode = vector_slot(cmdvec, i);
697 if (!cnode)
698 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200699 if (vector_active(cnode->cmd_vector) < 1)
700 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100701
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200702 /* De-dup node IDs: how many times has this same name been used before? Count the first
703 * occurence as _1 and omit that first suffix, so that the first occurence is called
704 * 'name', the second becomes 'name_2', then 'name_3', ... */
705 same_name_count = 1;
706 for (j = 0; j < i; ++j) {
707 struct cmd_node *cnode2;
708 cnode2 = vector_slot(cmdvec, j);
709 if (!cnode2)
710 continue;
711 if (strcmp(cnode->name, cnode2->name) == 0)
712 same_name_count ++;
713 }
714
715 vty_out(vty, " <node id='%s", cnode->name);
716 if (same_name_count > 1 || !*cnode->name)
717 vty_out(vty, "_%d", same_name_count);
718 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200719 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100720
721 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
722 struct cmd_element *elem;
723 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200724 if (vty_command_is_common(elem))
725 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200726 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200727 vty_dump_element(elem, vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100728 }
729
730 vty_out(vty, " </node>%s", VTY_NEWLINE);
731 }
732
733 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
734
735 return 0;
736}
737
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200738/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100739static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
740{
741 int i;
742
743 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
744 struct cmd_element *elem;
745 elem = vector_slot(cnode->cmd_vector, i);
746 if (!elem->string)
747 continue;
748 if (!strcmp(elem->string, cmdstring))
749 return 1;
750 }
751 return 0;
752}
753
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200754/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200755 * \param[in] ntype Node Type
756 * \param[cmd] element to be installed
757 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000758void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200759{
760 struct cmd_node *cnode;
761
762 cnode = vector_slot(cmdvec, ntype);
763
Harald Weltea99d45a2015-11-12 13:48:23 +0100764 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100765 /* ensure no _identical_ command has been registered at this
766 * node so far */
767 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200768
769 vector_set(cnode->cmd_vector, cmd);
770
771 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
772 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
773}
774
775/* Install a command into VIEW and ENABLE node */
776void install_element_ve(struct cmd_element *cmd)
777{
778 install_element(VIEW_NODE, cmd);
779 install_element(ENABLE_NODE, cmd);
780}
781
782#ifdef VTY_CRYPT_PW
783static unsigned char itoa64[] =
784 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
785
786static void to64(char *s, long v, int n)
787{
788 while (--n >= 0) {
789 *s++ = itoa64[v & 0x3f];
790 v >>= 6;
791 }
792}
793
794static char *zencrypt(const char *passwd)
795{
796 char salt[6];
797 struct timeval tv;
798 char *crypt(const char *, const char *);
799
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200800 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200801
802 to64(&salt[0], random(), 3);
803 to64(&salt[3], tv.tv_usec, 3);
804 salt[5] = '\0';
805
806 return crypt(passwd, salt);
807}
808#endif
809
810/* This function write configuration of this host. */
811static int config_write_host(struct vty *vty)
812{
813 if (host.name)
814 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
815
816 if (host.encrypt) {
817 if (host.password_encrypt)
818 vty_out(vty, "password 8 %s%s", host.password_encrypt,
819 VTY_NEWLINE);
820 if (host.enable_encrypt)
821 vty_out(vty, "enable password 8 %s%s",
822 host.enable_encrypt, VTY_NEWLINE);
823 } else {
824 if (host.password)
825 vty_out(vty, "password %s%s", host.password,
826 VTY_NEWLINE);
827 if (host.enable)
828 vty_out(vty, "enable password %s%s", host.enable,
829 VTY_NEWLINE);
830 }
831
832 if (host.advanced)
833 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
834
835 if (host.encrypt)
836 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
837
838 if (host.lines >= 0)
839 vty_out(vty, "service terminal-length %d%s", host.lines,
840 VTY_NEWLINE);
841
842 if (host.motdfile)
843 vty_out(vty, "banner motd file %s%s", host.motdfile,
844 VTY_NEWLINE);
845 else if (!host.motd)
846 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
847
848 return 1;
849}
850
851/* Utility function for getting command vector. */
852static vector cmd_node_vector(vector v, enum node_type ntype)
853{
854 struct cmd_node *cnode = vector_slot(v, ntype);
855 return cnode->cmd_vector;
856}
857
858/* Completion match types. */
859enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200860 NO_MATCH = 0,
861 ANY_MATCH,
862 EXTEND_MATCH,
863 IPV4_PREFIX_MATCH,
864 IPV4_MATCH,
865 IPV6_PREFIX_MATCH,
866 IPV6_MATCH,
867 RANGE_MATCH,
868 VARARG_MATCH,
869 PARTLY_MATCH,
870 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200871};
872
873static enum match_type cmd_ipv4_match(const char *str)
874{
875 const char *sp;
876 int dots = 0, nums = 0;
877 char buf[4];
878
879 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200880 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200881
882 for (;;) {
883 memset(buf, 0, sizeof(buf));
884 sp = str;
885 while (*str != '\0') {
886 if (*str == '.') {
887 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200888 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200889
890 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200891 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200892
893 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200894 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200895
896 dots++;
897 break;
898 }
899 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200900 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200901
902 str++;
903 }
904
905 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200906 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200907
908 strncpy(buf, sp, str - sp);
909 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200910 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200911
912 nums++;
913
914 if (*str == '\0')
915 break;
916
917 str++;
918 }
919
920 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200921 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200922
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200923 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200924}
925
926static enum match_type cmd_ipv4_prefix_match(const char *str)
927{
928 const char *sp;
929 int dots = 0;
930 char buf[4];
931
932 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200933 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200934
935 for (;;) {
936 memset(buf, 0, sizeof(buf));
937 sp = str;
938 while (*str != '\0' && *str != '/') {
939 if (*str == '.') {
940 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200941 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200942
943 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200944 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200945
946 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200947 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200948
949 dots++;
950 break;
951 }
952
953 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200954 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200955
956 str++;
957 }
958
959 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200960 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200961
962 strncpy(buf, sp, str - sp);
963 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200964 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200965
966 if (dots == 3) {
967 if (*str == '/') {
968 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200969 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200970
971 str++;
972 break;
973 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200974 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200975 }
976
977 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200978 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200979
980 str++;
981 }
982
983 sp = str;
984 while (*str != '\0') {
985 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200986 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200987
988 str++;
989 }
990
991 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200992 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200993
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200994 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200995}
996
997#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
998#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
999#define STATE_START 1
1000#define STATE_COLON 2
1001#define STATE_DOUBLE 3
1002#define STATE_ADDR 4
1003#define STATE_DOT 5
1004#define STATE_SLASH 6
1005#define STATE_MASK 7
1006
1007#ifdef HAVE_IPV6
1008
1009static enum match_type cmd_ipv6_match(const char *str)
1010{
1011 int state = STATE_START;
1012 int colons = 0, nums = 0, double_colon = 0;
1013 const char *sp = NULL;
1014 struct sockaddr_in6 sin6_dummy;
1015 int ret;
1016
1017 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001018 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001019
1020 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001021 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022
1023 /* use inet_pton that has a better support,
1024 * for example inet_pton can support the automatic addresses:
1025 * ::1.2.3.4
1026 */
1027 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1028
1029 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001030 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001031
1032 while (*str != '\0') {
1033 switch (state) {
1034 case STATE_START:
1035 if (*str == ':') {
1036 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001037 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001038 colons--;
1039 state = STATE_COLON;
1040 } else {
1041 sp = str;
1042 state = STATE_ADDR;
1043 }
1044
1045 continue;
1046 case STATE_COLON:
1047 colons++;
1048 if (*(str + 1) == ':')
1049 state = STATE_DOUBLE;
1050 else {
1051 sp = str + 1;
1052 state = STATE_ADDR;
1053 }
1054 break;
1055 case STATE_DOUBLE:
1056 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001057 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001058
1059 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001060 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001061 else {
1062 if (*(str + 1) != '\0')
1063 colons++;
1064 sp = str + 1;
1065 state = STATE_ADDR;
1066 }
1067
1068 double_colon++;
1069 nums++;
1070 break;
1071 case STATE_ADDR:
1072 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1073 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001074 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001075
1076 nums++;
1077 state = STATE_COLON;
1078 }
1079 if (*(str + 1) == '.')
1080 state = STATE_DOT;
1081 break;
1082 case STATE_DOT:
1083 state = STATE_ADDR;
1084 break;
1085 default:
1086 break;
1087 }
1088
1089 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001090 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001091
1092 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001093 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001094
1095 str++;
1096 }
1097
1098#if 0
1099 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001100 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001101#endif /* 0 */
1102
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001103 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001104}
1105
1106static enum match_type cmd_ipv6_prefix_match(const char *str)
1107{
1108 int state = STATE_START;
1109 int colons = 0, nums = 0, double_colon = 0;
1110 int mask;
1111 const char *sp = NULL;
1112 char *endptr = NULL;
1113
1114 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001115 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116
1117 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001118 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001119
1120 while (*str != '\0' && state != STATE_MASK) {
1121 switch (state) {
1122 case STATE_START:
1123 if (*str == ':') {
1124 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126 colons--;
1127 state = STATE_COLON;
1128 } else {
1129 sp = str;
1130 state = STATE_ADDR;
1131 }
1132
1133 continue;
1134 case STATE_COLON:
1135 colons++;
1136 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001137 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001138 else if (*(str + 1) == ':')
1139 state = STATE_DOUBLE;
1140 else {
1141 sp = str + 1;
1142 state = STATE_ADDR;
1143 }
1144 break;
1145 case STATE_DOUBLE:
1146 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151 else {
1152 if (*(str + 1) != '\0' && *(str + 1) != '/')
1153 colons++;
1154 sp = str + 1;
1155
1156 if (*(str + 1) == '/')
1157 state = STATE_SLASH;
1158 else
1159 state = STATE_ADDR;
1160 }
1161
1162 double_colon++;
1163 nums += 1;
1164 break;
1165 case STATE_ADDR:
1166 if (*(str + 1) == ':' || *(str + 1) == '.'
1167 || *(str + 1) == '\0' || *(str + 1) == '/') {
1168 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001169 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001170
1171 for (; sp <= str; sp++)
1172 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001173 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001174
1175 nums++;
1176
1177 if (*(str + 1) == ':')
1178 state = STATE_COLON;
1179 else if (*(str + 1) == '.')
1180 state = STATE_DOT;
1181 else if (*(str + 1) == '/')
1182 state = STATE_SLASH;
1183 }
1184 break;
1185 case STATE_DOT:
1186 state = STATE_ADDR;
1187 break;
1188 case STATE_SLASH:
1189 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001190 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001191
1192 state = STATE_MASK;
1193 break;
1194 default:
1195 break;
1196 }
1197
1198 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001199 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001200
1201 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001202 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001203
1204 str++;
1205 }
1206
1207 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001208 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001209
1210 mask = strtol(str, &endptr, 10);
1211 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001212 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001213
1214 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001215 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001216
1217/* I don't know why mask < 13 makes command match partly.
1218 Forgive me to make this comments. I Want to set static default route
1219 because of lack of function to originate default in ospf6d; sorry
1220 yasu
1221 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001222 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001223*/
1224
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226}
1227
1228#endif /* HAVE_IPV6 */
1229
1230#define DECIMAL_STRLEN_MAX 10
1231
1232static int cmd_range_match(const char *range, const char *str)
1233{
1234 char *p;
1235 char buf[DECIMAL_STRLEN_MAX + 1];
1236 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001237
1238 if (str == NULL)
1239 return 1;
1240
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001241 if (range[1] == '-') {
1242 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001243
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001244 val = strtol(str, &endptr, 10);
1245 if (*endptr != '\0')
1246 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001247
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001248 range += 2;
1249 p = strchr(range, '-');
1250 if (p == NULL)
1251 return 0;
1252 if (p - range > DECIMAL_STRLEN_MAX)
1253 return 0;
1254 strncpy(buf, range, p - range);
1255 buf[p - range] = '\0';
1256 min = -strtol(buf, &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 = p + 1;
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 max = strtol(buf, &endptr, 10);
1269 if (*endptr != '\0')
1270 return 0;
1271
1272 if (val < min || val > max)
1273 return 0;
1274 } else {
1275 unsigned long min, max, val;
1276
1277 val = strtoul(str, &endptr, 10);
1278 if (*endptr != '\0')
1279 return 0;
1280
1281 range++;
1282 p = strchr(range, '-');
1283 if (p == NULL)
1284 return 0;
1285 if (p - range > DECIMAL_STRLEN_MAX)
1286 return 0;
1287 strncpy(buf, range, p - range);
1288 buf[p - range] = '\0';
1289 min = strtoul(buf, &endptr, 10);
1290 if (*endptr != '\0')
1291 return 0;
1292
1293 range = p + 1;
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 max = strtoul(buf, &endptr, 10);
1302 if (*endptr != '\0')
1303 return 0;
1304
1305 if (val < min || val > max)
1306 return 0;
1307 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001308
1309 return 1;
1310}
1311
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001312/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001313static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001314{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001315 /* we've got "[blah]". We want to strip off the []s and redo the
1316 * match check for "blah"
1317 */
1318 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001319
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001320 if (len < 3)
1321 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001322
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001323 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001324}
1325
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001326static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001327cmd_match(const char *str, const char *command,
1328 enum match_type min, bool recur)
1329{
1330
1331 if (recur && CMD_OPTION(str))
1332 {
1333 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001334 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001335
1336 /* this would be a bug in a command, however handle it gracefully
1337 * as it we only discover it if a user tries to run it
1338 */
1339 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001341
1342 ret = cmd_match(tmp, command, min, false);
1343
1344 talloc_free(tmp);
1345
1346 return ret;
1347 }
1348 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001349 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001350 else if (CMD_RANGE(str))
1351 {
1352 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001353 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001354 }
1355#ifdef HAVE_IPV6
1356 else if (CMD_IPV6(str))
1357 {
1358 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001359 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001360 }
1361 else if (CMD_IPV6_PREFIX(str))
1362 {
1363 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001364 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001365 }
1366#endif /* HAVE_IPV6 */
1367 else if (CMD_IPV4(str))
1368 {
1369 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001370 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001371 }
1372 else if (CMD_IPV4_PREFIX(str))
1373 {
1374 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001375 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001376 }
1377 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001378 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001379 else if (strncmp(command, str, strlen(command)) == 0)
1380 {
1381 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001382 return EXACT_MATCH;
1383 else if (PARTLY_MATCH >= min)
1384 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001385 }
1386
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001387 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001388}
1389
1390/* Filter vector at the specified index and by the given command string, to
1391 * the desired matching level (thus allowing part matches), and return match
1392 * type flag.
1393 */
1394static enum match_type
1395cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001396{
1397 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001398 struct cmd_element *cmd_element;
1399 enum match_type match_type;
1400 vector descvec;
1401 struct desc *desc;
1402
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001403 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001404
1405 /* If command and cmd_element string does not match set NULL to vector */
1406 for (i = 0; i < vector_active(v); i++)
1407 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001408 if (index >= vector_active(cmd_element->strvec))
1409 vector_slot(v, i) = NULL;
1410 else {
1411 unsigned int j;
1412 int matched = 0;
1413
1414 descvec =
1415 vector_slot(cmd_element->strvec, index);
1416
1417 for (j = 0; j < vector_active(descvec); j++)
1418 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001419 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001420
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001421 ret = cmd_match (desc->cmd, command, level, true);
1422
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001423 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001424 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001425
1426 if (match_type < ret)
1427 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428 }
1429 if (!matched)
1430 vector_slot(v, i) = NULL;
1431 }
1432 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001433
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001434 if (match_type == NO_MATCH)
1435 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001436
1437 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1438 * go again and filter out commands whose argument (at this index) is
1439 * 'weaker'. E.g., if we have 2 commands:
1440 *
1441 * foo bar <1-255>
1442 * foo bar BLAH
1443 *
1444 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001445 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001446 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1447 *
1448 * If we don't do a 2nd pass and filter it out, the higher-layers will
1449 * consider this to be ambiguous.
1450 */
1451 for (i = 0; i < vector_active(v); i++)
1452 if ((cmd_element = vector_slot(v, i)) != NULL) {
1453 if (index >= vector_active(cmd_element->strvec))
1454 vector_slot(v, i) = NULL;
1455 else {
1456 unsigned int j;
1457 int matched = 0;
1458
1459 descvec =
1460 vector_slot(cmd_element->strvec, index);
1461
1462 for (j = 0; j < vector_active(descvec); j++)
1463 if ((desc = vector_slot(descvec, j))) {
1464 enum match_type ret;
1465
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001466 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001467
1468 if (ret >= match_type)
1469 matched++;
1470 }
1471 if (!matched)
1472 vector_slot(v, i) = NULL;
1473 }
1474 }
1475
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001476 return match_type;
1477}
1478
1479/* Check ambiguous match */
1480static int
1481is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1482{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001483 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001484 unsigned int i;
1485 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001486 struct cmd_element *cmd_element;
1487 const char *matched = NULL;
1488 vector descvec;
1489 struct desc *desc;
1490
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001491 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1492 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1493 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1494 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1495 * that case, the string must remain allocated until this function exits or another match comes
1496 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1497 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1498 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1499 void *cmd_deopt_ctx = NULL;
1500
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001501 for (i = 0; i < vector_active(v); i++) {
1502 cmd_element = vector_slot(v, i);
1503 if (!cmd_element)
1504 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001505
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001506 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001507
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001508 descvec = vector_slot(cmd_element->strvec, index);
1509
1510 for (j = 0; j < vector_active(descvec); j++) {
1511 desc = vector_slot(descvec, j);
1512 if (!desc)
1513 continue;
1514
1515 enum match_type mtype;
1516 const char *str = desc->cmd;
1517
1518 if (CMD_OPTION(str)) {
1519 if (!cmd_deopt_ctx)
1520 cmd_deopt_ctx =
1521 talloc_named_const(tall_vty_cmd_ctx, 0,
1522 __func__);
1523 str = cmd_deopt(cmd_deopt_ctx, str);
1524 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001525 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001526 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001527
1528 switch (type) {
1529 case EXACT_MATCH:
1530 if (!(CMD_VARIABLE (str))
1531 && strcmp(command, str) == 0)
1532 match++;
1533 break;
1534 case PARTLY_MATCH:
1535 if (!(CMD_VARIABLE (str))
1536 && strncmp(command, str, strlen (command)) == 0)
1537 {
1538 if (matched
1539 && strcmp(matched,
1540 str) != 0) {
1541 ret = 1; /* There is ambiguous match. */
1542 goto free_and_return;
1543 } else
1544 matched = str;
1545 match++;
1546 }
1547 break;
1548 case RANGE_MATCH:
1549 if (cmd_range_match
1550 (str, command)) {
1551 if (matched
1552 && strcmp(matched,
1553 str) != 0) {
1554 ret = 1;
1555 goto free_and_return;
1556 } else
1557 matched = str;
1558 match++;
1559 }
1560 break;
1561#ifdef HAVE_IPV6
1562 case IPV6_MATCH:
1563 if (CMD_IPV6(str))
1564 match++;
1565 break;
1566 case IPV6_PREFIX_MATCH:
1567 if ((mtype =
1568 cmd_ipv6_prefix_match
1569 (command)) != NO_MATCH) {
1570 if (mtype == PARTLY_MATCH) {
1571 ret = 2; /* There is incomplete match. */
1572 goto free_and_return;
1573 }
1574
1575 match++;
1576 }
1577 break;
1578#endif /* HAVE_IPV6 */
1579 case IPV4_MATCH:
1580 if (CMD_IPV4(str))
1581 match++;
1582 break;
1583 case IPV4_PREFIX_MATCH:
1584 if ((mtype =
1585 cmd_ipv4_prefix_match
1586 (command)) != NO_MATCH) {
1587 if (mtype == PARTLY_MATCH) {
1588 ret = 2; /* There is incomplete match. */
1589 goto free_and_return;
1590 }
1591
1592 match++;
1593 }
1594 break;
1595 case EXTEND_MATCH:
1596 if (CMD_VARIABLE (str))
1597 match++;
1598 break;
1599 case NO_MATCH:
1600 default:
1601 break;
1602 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001603 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001604 if (!match)
1605 vector_slot(v, i) = NULL;
1606 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001607
1608free_and_return:
1609 if (cmd_deopt_ctx)
1610 talloc_free(cmd_deopt_ctx);
1611 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001612}
1613
1614/* If src matches dst return dst string, otherwise return NULL */
1615static const char *cmd_entry_function(const char *src, const char *dst)
1616{
1617 /* Skip variable arguments. */
1618 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1619 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1620 return NULL;
1621
1622 /* In case of 'command \t', given src is NULL string. */
1623 if (src == NULL)
1624 return dst;
1625
1626 /* Matched with input string. */
1627 if (strncmp(src, dst, strlen(src)) == 0)
1628 return dst;
1629
1630 return NULL;
1631}
1632
1633/* If src matches dst return dst string, otherwise return NULL */
1634/* This version will return the dst string always if it is
1635 CMD_VARIABLE for '?' key processing */
1636static const char *cmd_entry_function_desc(const char *src, const char *dst)
1637{
1638 if (CMD_VARARG(dst))
1639 return dst;
1640
1641 if (CMD_RANGE(dst)) {
1642 if (cmd_range_match(dst, src))
1643 return dst;
1644 else
1645 return NULL;
1646 }
1647#ifdef HAVE_IPV6
1648 if (CMD_IPV6(dst)) {
1649 if (cmd_ipv6_match(src))
1650 return dst;
1651 else
1652 return NULL;
1653 }
1654
1655 if (CMD_IPV6_PREFIX(dst)) {
1656 if (cmd_ipv6_prefix_match(src))
1657 return dst;
1658 else
1659 return NULL;
1660 }
1661#endif /* HAVE_IPV6 */
1662
1663 if (CMD_IPV4(dst)) {
1664 if (cmd_ipv4_match(src))
1665 return dst;
1666 else
1667 return NULL;
1668 }
1669
1670 if (CMD_IPV4_PREFIX(dst)) {
1671 if (cmd_ipv4_prefix_match(src))
1672 return dst;
1673 else
1674 return NULL;
1675 }
1676
1677 /* Optional or variable commands always match on '?' */
1678 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1679 return dst;
1680
1681 /* In case of 'command \t', given src is NULL string. */
1682 if (src == NULL)
1683 return dst;
1684
1685 if (strncmp(src, dst, strlen(src)) == 0)
1686 return dst;
1687 else
1688 return NULL;
1689}
1690
1691/* Check same string element existence. If it isn't there return
1692 1. */
1693static int cmd_unique_string(vector v, const char *str)
1694{
1695 unsigned int i;
1696 char *match;
1697
1698 for (i = 0; i < vector_active(v); i++)
1699 if ((match = vector_slot(v, i)) != NULL)
1700 if (strcmp(match, str) == 0)
1701 return 0;
1702 return 1;
1703}
1704
1705/* Compare string to description vector. If there is same string
1706 return 1 else return 0. */
1707static int desc_unique_string(vector v, const char *str)
1708{
1709 unsigned int i;
1710 struct desc *desc;
1711
1712 for (i = 0; i < vector_active(v); i++)
1713 if ((desc = vector_slot(v, i)) != NULL)
1714 if (strcmp(desc->cmd, str) == 0)
1715 return 1;
1716 return 0;
1717}
1718
1719static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1720{
1721 if (first_word != NULL &&
1722 node != AUTH_NODE &&
1723 node != VIEW_NODE &&
1724 node != AUTH_ENABLE_NODE &&
1725 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1726 return 1;
1727 return 0;
1728}
1729
1730/* '?' describe command support. */
1731static vector
1732cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1733{
1734 unsigned int i;
1735 vector cmd_vector;
1736#define INIT_MATCHVEC_SIZE 10
1737 vector matchvec;
1738 struct cmd_element *cmd_element;
1739 unsigned int index;
1740 int ret;
1741 enum match_type match;
1742 char *command;
1743 static struct desc desc_cr = { "<cr>", "" };
1744
1745 /* Set index. */
1746 if (vector_active(vline) == 0) {
1747 *status = CMD_ERR_NO_MATCH;
1748 return NULL;
1749 } else
1750 index = vector_active(vline) - 1;
1751
1752 /* Make copy vector of current node's command vector. */
1753 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1754
1755 /* Prepare match vector */
1756 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1757
1758 /* Filter commands. */
1759 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001760 for (i = 0; i < index; i++) {
1761 command = vector_slot(vline, i);
1762 if (!command)
1763 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001764
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001765 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001766
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001767 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001768 struct cmd_element *cmd_element;
1769 vector descvec;
1770 unsigned int j, k;
1771
1772 for (j = 0; j < vector_active(cmd_vector); j++)
1773 if ((cmd_element =
1774 vector_slot(cmd_vector, j)) != NULL
1775 &&
1776 (vector_active(cmd_element->strvec))) {
1777 descvec =
1778 vector_slot(cmd_element->
1779 strvec,
1780 vector_active
1781 (cmd_element->
1782 strvec) - 1);
1783 for (k = 0;
1784 k < vector_active(descvec);
1785 k++) {
1786 struct desc *desc =
1787 vector_slot(descvec,
1788 k);
1789 vector_set(matchvec,
1790 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001791 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001792 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001793
Harald Welte80d30fe2013-02-12 11:08:57 +01001794 vector_set(matchvec, &desc_cr);
1795 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001796
Harald Welte80d30fe2013-02-12 11:08:57 +01001797 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001798 }
1799
Harald Welte80d30fe2013-02-12 11:08:57 +01001800 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1801 match)) == 1) {
1802 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001803 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001804 *status = CMD_ERR_AMBIGUOUS;
1805 return NULL;
1806 } else if (ret == 2) {
1807 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001808 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001809 *status = CMD_ERR_NO_MATCH;
1810 return NULL;
1811 }
1812 }
1813
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001814 /* Prepare match vector */
1815 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1816
1817 /* Make sure that cmd_vector is filtered based on current word */
1818 command = vector_slot(vline, index);
1819 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001820 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001821
1822 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001823 for (i = 0; i < vector_active(cmd_vector); i++) {
1824 const char *string = NULL;
1825 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001826
Harald Welte80d30fe2013-02-12 11:08:57 +01001827 cmd_element = vector_slot(cmd_vector, i);
1828 if (!cmd_element)
1829 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001830
Harald Welted17aa592013-02-12 11:11:34 +01001831 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1832 continue;
1833
Harald Welte80d30fe2013-02-12 11:08:57 +01001834 strvec = cmd_element->strvec;
1835
1836 /* if command is NULL, index may be equal to vector_active */
1837 if (command && index >= vector_active(strvec))
1838 vector_slot(cmd_vector, i) = NULL;
1839 else {
1840 /* Check if command is completed. */
1841 if (command == NULL
1842 && index == vector_active(strvec)) {
1843 string = "<cr>";
1844 if (!desc_unique_string(matchvec, string))
1845 vector_set(matchvec, &desc_cr);
1846 } else {
1847 unsigned int j;
1848 vector descvec = vector_slot(strvec, index);
1849 struct desc *desc;
1850
1851 for (j = 0; j < vector_active(descvec); j++) {
1852 desc = vector_slot(descvec, j);
1853 if (!desc)
1854 continue;
1855 string = cmd_entry_function_desc
1856 (command, desc->cmd);
1857 if (!string)
1858 continue;
1859 /* Uniqueness check */
1860 if (!desc_unique_string(matchvec, string))
1861 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001862 }
1863 }
1864 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001865 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001866 vector_free(cmd_vector);
1867
1868 if (vector_slot(matchvec, 0) == NULL) {
1869 vector_free(matchvec);
1870 *status = CMD_ERR_NO_MATCH;
1871 } else
1872 *status = CMD_SUCCESS;
1873
1874 return matchvec;
1875}
1876
1877vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1878{
1879 vector ret;
1880
1881 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1882 enum node_type onode;
1883 vector shifted_vline;
1884 unsigned int index;
1885
1886 onode = vty->node;
1887 vty->node = ENABLE_NODE;
1888 /* We can try it on enable node, cos' the vty is authenticated */
1889
1890 shifted_vline = vector_init(vector_count(vline));
1891 /* use memcpy? */
1892 for (index = 1; index < vector_active(vline); index++) {
1893 vector_set_index(shifted_vline, index - 1,
1894 vector_lookup(vline, index));
1895 }
1896
1897 ret = cmd_describe_command_real(shifted_vline, vty, status);
1898
1899 vector_free(shifted_vline);
1900 vty->node = onode;
1901 return ret;
1902 }
1903
1904 return cmd_describe_command_real(vline, vty, status);
1905}
1906
1907/* Check LCD of matched command. */
1908static int cmd_lcd(char **matched)
1909{
1910 int i;
1911 int j;
1912 int lcd = -1;
1913 char *s1, *s2;
1914 char c1, c2;
1915
1916 if (matched[0] == NULL || matched[1] == NULL)
1917 return 0;
1918
1919 for (i = 1; matched[i] != NULL; i++) {
1920 s1 = matched[i - 1];
1921 s2 = matched[i];
1922
1923 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1924 if (c1 != c2)
1925 break;
1926
1927 if (lcd < 0)
1928 lcd = j;
1929 else {
1930 if (lcd > j)
1931 lcd = j;
1932 }
1933 }
1934 return lcd;
1935}
1936
1937/* Command line completion support. */
1938static char **cmd_complete_command_real(vector vline, struct vty *vty,
1939 int *status)
1940{
1941 unsigned int i;
1942 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1943#define INIT_MATCHVEC_SIZE 10
1944 vector matchvec;
1945 struct cmd_element *cmd_element;
1946 unsigned int index;
1947 char **match_str;
1948 struct desc *desc;
1949 vector descvec;
1950 char *command;
1951 int lcd;
1952
1953 if (vector_active(vline) == 0) {
1954 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001955 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001956 return NULL;
1957 } else
1958 index = vector_active(vline) - 1;
1959
1960 /* First, filter by preceeding command string */
1961 for (i = 0; i < index; i++)
1962 if ((command = vector_slot(vline, i))) {
1963 enum match_type match;
1964 int ret;
1965
1966 /* First try completion match, if there is exactly match return 1 */
1967 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001968 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001969
1970 /* If there is exact match then filter ambiguous match else check
1971 ambiguousness. */
1972 if ((ret =
1973 is_cmd_ambiguous(command, cmd_vector, i,
1974 match)) == 1) {
1975 vector_free(cmd_vector);
1976 *status = CMD_ERR_AMBIGUOUS;
1977 return NULL;
1978 }
1979 /*
1980 else if (ret == 2)
1981 {
1982 vector_free (cmd_vector);
1983 *status = CMD_ERR_NO_MATCH;
1984 return NULL;
1985 }
1986 */
1987 }
1988
1989 /* Prepare match vector. */
1990 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1991
1992 /* Now we got into completion */
1993 for (i = 0; i < vector_active(cmd_vector); i++)
1994 if ((cmd_element = vector_slot(cmd_vector, i))) {
1995 const char *string;
1996 vector strvec = cmd_element->strvec;
1997
1998 /* Check field length */
1999 if (index >= vector_active(strvec))
2000 vector_slot(cmd_vector, i) = NULL;
2001 else {
2002 unsigned int j;
2003
2004 descvec = vector_slot(strvec, index);
2005 for (j = 0; j < vector_active(descvec); j++)
2006 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002007 const char *cmd = desc->cmd;
2008 char *tmp = NULL;
2009
2010 if (CMD_OPTION(desc->cmd)) {
2011 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2012 cmd = tmp;
2013 }
2014 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002015 if (cmd_unique_string (matchvec, string))
2016 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002017 if (tmp)
2018 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002019 }
2020 }
2021 }
2022
2023 /* We don't need cmd_vector any more. */
2024 vector_free(cmd_vector);
2025
2026 /* No matched command */
2027 if (vector_slot(matchvec, 0) == NULL) {
2028 vector_free(matchvec);
2029
2030 /* In case of 'command \t' pattern. Do you need '?' command at
2031 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002032 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002033 *status = CMD_ERR_NOTHING_TODO;
2034 else
2035 *status = CMD_ERR_NO_MATCH;
2036 return NULL;
2037 }
2038
2039 /* Only one matched */
2040 if (vector_slot(matchvec, 1) == NULL) {
2041 match_str = (char **)matchvec->index;
2042 vector_only_wrapper_free(matchvec);
2043 *status = CMD_COMPLETE_FULL_MATCH;
2044 return match_str;
2045 }
2046 /* Make it sure last element is NULL. */
2047 vector_set(matchvec, NULL);
2048
2049 /* Check LCD of matched strings. */
2050 if (vector_slot(vline, index) != NULL) {
2051 lcd = cmd_lcd((char **)matchvec->index);
2052
2053 if (lcd) {
2054 int len = strlen(vector_slot(vline, index));
2055
2056 if (len < lcd) {
2057 char *lcdstr;
2058
2059 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2060 "complete-lcdstr");
2061 memcpy(lcdstr, matchvec->index[0], lcd);
2062 lcdstr[lcd] = '\0';
2063
2064 /* match_str = (char **) &lcdstr; */
2065
2066 /* Free matchvec. */
2067 for (i = 0; i < vector_active(matchvec); i++) {
2068 if (vector_slot(matchvec, i))
2069 talloc_free(vector_slot(matchvec, i));
2070 }
2071 vector_free(matchvec);
2072
2073 /* Make new matchvec. */
2074 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2075 vector_set(matchvec, lcdstr);
2076 match_str = (char **)matchvec->index;
2077 vector_only_wrapper_free(matchvec);
2078
2079 *status = CMD_COMPLETE_MATCH;
2080 return match_str;
2081 }
2082 }
2083 }
2084
2085 match_str = (char **)matchvec->index;
2086 vector_only_wrapper_free(matchvec);
2087 *status = CMD_COMPLETE_LIST_MATCH;
2088 return match_str;
2089}
2090
2091char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2092{
2093 char **ret;
2094
2095 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2096 enum node_type onode;
2097 vector shifted_vline;
2098 unsigned int index;
2099
2100 onode = vty->node;
2101 vty->node = ENABLE_NODE;
2102 /* We can try it on enable node, cos' the vty is authenticated */
2103
2104 shifted_vline = vector_init(vector_count(vline));
2105 /* use memcpy? */
2106 for (index = 1; index < vector_active(vline); index++) {
2107 vector_set_index(shifted_vline, index - 1,
2108 vector_lookup(vline, index));
2109 }
2110
2111 ret = cmd_complete_command_real(shifted_vline, vty, status);
2112
2113 vector_free(shifted_vline);
2114 vty->node = onode;
2115 return ret;
2116 }
2117
2118 return cmd_complete_command_real(vline, vty, status);
2119}
2120
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002121static struct vty_parent_node *vty_parent(struct vty *vty)
2122{
2123 return llist_first_entry_or_null(&vty->parent_nodes,
2124 struct vty_parent_node,
2125 entry);
2126}
2127
2128static bool vty_pop_parent(struct vty *vty)
2129{
2130 struct vty_parent_node *parent = vty_parent(vty);
2131 if (!parent)
2132 return false;
2133 llist_del(&parent->entry);
2134 vty->node = parent->node;
2135 vty->priv = parent->priv;
2136 if (vty->indent)
2137 talloc_free(vty->indent);
2138 vty->indent = parent->indent;
2139 talloc_free(parent);
2140 return true;
2141}
2142
2143static void vty_clear_parents(struct vty *vty)
2144{
2145 while (vty_pop_parent(vty));
2146}
2147
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002148/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002149/*
2150 * This function MUST eventually converge on a node when called repeatedly,
2151 * there must not be any cycles.
2152 * All 'config' nodes shall converge on CONFIG_NODE.
2153 * All other 'enable' nodes shall converge on ENABLE_NODE.
2154 * All 'view' only nodes shall converge on VIEW_NODE.
2155 * All other nodes shall converge on themselves or it must be ensured,
2156 * that the user's rights are not extended anyhow by calling this function.
2157 *
2158 * Note that these requirements also apply to all functions that are used
2159 * as go_parent_cb.
2160 * Note also that this function relies on the is_config_child callback to
2161 * recognize non-config nodes if go_parent_cb is not set.
2162 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002163int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002164{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002165 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002166 case AUTH_NODE:
2167 case VIEW_NODE:
2168 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002169 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002170 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002171 break;
2172
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002173 case AUTH_ENABLE_NODE:
2174 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002175 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002176 break;
2177
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002178 default:
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002179 if (host.app_info->go_parent_cb)
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002180 host.app_info->go_parent_cb(vty);
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002181 vty_pop_parent(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002182 break;
2183 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002184
2185 return vty->node;
2186}
2187
2188/* Execute command by argument vline vector. */
2189static int
2190cmd_execute_command_real(vector vline, struct vty *vty,
2191 struct cmd_element **cmd)
2192{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002193 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002194 unsigned int index;
2195 vector cmd_vector;
2196 struct cmd_element *cmd_element;
2197 struct cmd_element *matched_element;
2198 unsigned int matched_count, incomplete_count;
2199 int argc;
2200 const char *argv[CMD_ARGC_MAX];
2201 enum match_type match = 0;
2202 int varflag;
2203 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002204 int rc;
2205 /* Used for temporary storage of cmd_deopt() allocated arguments during
2206 argv[] generation */
2207 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002208
2209 /* Make copy of command elements. */
2210 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2211
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002212 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002213 if ((command = vector_slot(vline, index))) {
2214 int ret;
2215
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002216 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002217 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002218
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002219 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002220 break;
2221
2222 ret =
2223 is_cmd_ambiguous(command, cmd_vector, index, match);
2224
2225 if (ret == 1) {
2226 vector_free(cmd_vector);
2227 return CMD_ERR_AMBIGUOUS;
2228 } else if (ret == 2) {
2229 vector_free(cmd_vector);
2230 return CMD_ERR_NO_MATCH;
2231 }
2232 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002233 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002234
2235 /* Check matched count. */
2236 matched_element = NULL;
2237 matched_count = 0;
2238 incomplete_count = 0;
2239
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002240 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002241 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002242 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002243 || index >= cmd_element->cmdsize) {
2244 matched_element = cmd_element;
2245#if 0
2246 printf("DEBUG: %s\n", cmd_element->string);
2247#endif
2248 matched_count++;
2249 } else {
2250 incomplete_count++;
2251 }
2252 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002253 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002254
2255 /* Finish of using cmd_vector. */
2256 vector_free(cmd_vector);
2257
2258 /* To execute command, matched_count must be 1. */
2259 if (matched_count == 0) {
2260 if (incomplete_count)
2261 return CMD_ERR_INCOMPLETE;
2262 else
2263 return CMD_ERR_NO_MATCH;
2264 }
2265
2266 if (matched_count > 1)
2267 return CMD_ERR_AMBIGUOUS;
2268
2269 /* Argument treatment */
2270 varflag = 0;
2271 argc = 0;
2272
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002273 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2274
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002275 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002276 if (argc == CMD_ARGC_MAX) {
2277 rc = CMD_ERR_EXEED_ARGC_MAX;
2278 goto rc_free_deopt_ctx;
2279 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002280 if (varflag) {
2281 argv[argc++] = vector_slot(vline, i);
2282 continue;
2283 }
2284
2285 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002286 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002287
2288 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002289 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002290 struct desc *desc = vector_slot(descvec, 0);
2291
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002292 if (CMD_OPTION(desc->cmd)) {
2293 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2294 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2295 } else {
2296 tmp_cmd = desc->cmd;
2297 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002298
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002299 if (CMD_VARARG(tmp_cmd))
2300 varflag = 1;
2301 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002302 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002303 else if (CMD_OPTION(desc->cmd))
2304 argv[argc++] = tmp_cmd;
2305 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002306 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002307 /* multi choice argument. look up which choice
2308 the user meant (can only be one after
2309 filtering and checking for ambigous). For instance,
2310 if user typed "th" for "(two|three)" arg, we
2311 want to pass "three" in argv[]. */
2312 for (j = 0; j < vector_active(descvec); j++) {
2313 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002314 if (!desc)
2315 continue;
2316 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2317 continue;
2318 if (CMD_OPTION(desc->cmd)) {
2319 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2320 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2321 } else {
2322 tmp_cmd = desc->cmd;
2323 }
2324
2325 if(CMD_VARIABLE(tmp_cmd)) {
2326 argv[argc++] = vector_slot(vline, i);
2327 } else {
2328 argv[argc++] = tmp_cmd;
2329 }
2330 break;
2331 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002332 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002333 }
2334
2335 /* For vtysh execution. */
2336 if (cmd)
2337 *cmd = matched_element;
2338
2339 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002340 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002341 else {
2342 /* Execute matched command. */
2343 struct vty_parent_node this_node = {
2344 .node = vty->node,
2345 .priv = vty->priv,
2346 .indent = vty->indent,
2347 };
2348 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002349 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002350
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002351 /* If we have stepped down into a child node, push a parent frame.
2352 * The causality is such: we don't expect every single node entry implementation to push
2353 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2354 * a parent node. Hence if the node changed without the parent node changing, we must
2355 * have stepped into a child node. */
2356 if (vty->node != this_node.node && parent == vty_parent(vty)
2357 && vty->node > CONFIG_NODE) {
2358 /* Push the parent node. */
2359 parent = talloc_zero(vty, struct vty_parent_node);
2360 *parent = this_node;
2361 llist_add(&parent->entry, &vty->parent_nodes);
2362 }
2363 }
2364
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002365rc_free_deopt_ctx:
2366 /* Now after we called the command func, we can free temporary strings */
2367 talloc_free(cmd_deopt_ctx);
2368 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002369}
2370
2371int
2372cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2373 int vtysh)
2374{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002375 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002376 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002377
2378 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002379
2380 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2381 vector shifted_vline;
2382 unsigned int index;
2383
2384 vty->node = ENABLE_NODE;
2385 /* We can try it on enable node, cos' the vty is authenticated */
2386
2387 shifted_vline = vector_init(vector_count(vline));
2388 /* use memcpy? */
2389 for (index = 1; index < vector_active(vline); index++) {
2390 vector_set_index(shifted_vline, index - 1,
2391 vector_lookup(vline, index));
2392 }
2393
2394 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2395
2396 vector_free(shifted_vline);
2397 vty->node = onode;
2398 return ret;
2399 }
2400
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002401 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002402}
2403
2404/* Execute command by argument readline. */
2405int
2406cmd_execute_command_strict(vector vline, struct vty *vty,
2407 struct cmd_element **cmd)
2408{
2409 unsigned int i;
2410 unsigned int index;
2411 vector cmd_vector;
2412 struct cmd_element *cmd_element;
2413 struct cmd_element *matched_element;
2414 unsigned int matched_count, incomplete_count;
2415 int argc;
2416 const char *argv[CMD_ARGC_MAX];
2417 int varflag;
2418 enum match_type match = 0;
2419 char *command;
2420
2421 /* Make copy of command element */
2422 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2423
2424 for (index = 0; index < vector_active(vline); index++)
2425 if ((command = vector_slot(vline, index))) {
2426 int ret;
2427
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002428 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002429 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002430
2431 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002432 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002433 break;
2434
2435 ret =
2436 is_cmd_ambiguous(command, cmd_vector, index, match);
2437 if (ret == 1) {
2438 vector_free(cmd_vector);
2439 return CMD_ERR_AMBIGUOUS;
2440 }
2441 if (ret == 2) {
2442 vector_free(cmd_vector);
2443 return CMD_ERR_NO_MATCH;
2444 }
2445 }
2446
2447 /* Check matched count. */
2448 matched_element = NULL;
2449 matched_count = 0;
2450 incomplete_count = 0;
2451 for (i = 0; i < vector_active(cmd_vector); i++)
2452 if (vector_slot(cmd_vector, i) != NULL) {
2453 cmd_element = vector_slot(cmd_vector, i);
2454
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002455 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002456 || index >= cmd_element->cmdsize) {
2457 matched_element = cmd_element;
2458 matched_count++;
2459 } else
2460 incomplete_count++;
2461 }
2462
2463 /* Finish of using cmd_vector. */
2464 vector_free(cmd_vector);
2465
2466 /* To execute command, matched_count must be 1. */
2467 if (matched_count == 0) {
2468 if (incomplete_count)
2469 return CMD_ERR_INCOMPLETE;
2470 else
2471 return CMD_ERR_NO_MATCH;
2472 }
2473
2474 if (matched_count > 1)
2475 return CMD_ERR_AMBIGUOUS;
2476
2477 /* Argument treatment */
2478 varflag = 0;
2479 argc = 0;
2480
2481 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002482 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002483 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002484 if (varflag) {
2485 argv[argc++] = vector_slot(vline, i);
2486 continue;
2487 }
2488
2489 vector descvec = vector_slot(matched_element->strvec, i);
2490
2491 if (vector_active(descvec) == 1) {
2492 struct desc *desc = vector_slot(descvec, 0);
2493
2494 if (CMD_VARARG(desc->cmd))
2495 varflag = 1;
2496
2497 if (varflag || CMD_VARIABLE(desc->cmd)
2498 || CMD_OPTION(desc->cmd))
2499 argv[argc++] = vector_slot(vline, i);
2500 } else {
2501 argv[argc++] = vector_slot(vline, i);
2502 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002503 }
2504
2505 /* For vtysh execution. */
2506 if (cmd)
2507 *cmd = matched_element;
2508
2509 if (matched_element->daemon)
2510 return CMD_SUCCESS_DAEMON;
2511
2512 /* Now execute matched command */
2513 return (*matched_element->func) (matched_element, vty, argc, argv);
2514}
2515
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002516static inline size_t len(const char *str)
2517{
2518 return str? strlen(str) : 0;
2519}
2520
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002521/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2522 * is longer than b, a must start with exactly b, and vice versa.
2523 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2524 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002525static int indent_cmp(const char *a, const char *b)
2526{
2527 size_t al, bl;
2528 al = len(a);
2529 bl = len(b);
2530 if (al > bl) {
2531 if (bl && strncmp(a, b, bl) != 0)
2532 return EINVAL;
2533 return 1;
2534 }
2535 /* al <= bl */
2536 if (al && strncmp(a, b, al) != 0)
2537 return EINVAL;
2538 return (al < bl)? -1 : 0;
2539}
2540
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002541/* Configration make from file. */
2542int config_from_file(struct vty *vty, FILE * fp)
2543{
2544 int ret;
2545 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002546 char *indent;
2547 int cmp;
2548 struct vty_parent_node this_node;
2549 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002550
2551 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002552 indent = NULL;
2553 vline = NULL;
2554 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002555
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002556 if (ret != CMD_SUCCESS)
2557 goto return_invalid_indent;
2558
2559 /* In case of comment or empty line */
2560 if (vline == NULL) {
2561 if (indent) {
2562 talloc_free(indent);
2563 indent = NULL;
2564 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002565 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002566 }
2567
Neels Hofmeyr43063632017-09-19 23:54:01 +02002568 /* We have a nonempty line. */
2569 if (!vty->indent) {
2570 /* We have just entered a node and expecting the first child to come up; but we
2571 * may also skip right back to a parent or ancestor level. */
2572 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002573
Neels Hofmeyr43063632017-09-19 23:54:01 +02002574 /* If there is no parent, record any indentation we encounter. */
2575 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2576
2577 if (cmp == EINVAL)
2578 goto return_invalid_indent;
2579
2580 if (cmp <= 0) {
2581 /* We have gone right back to the parent level or higher, we are skipping
2582 * this child node level entirely. Pop the parent to go back to a node
2583 * that was actually there (to reinstate vty->indent) and re-use below
2584 * go-parent while-loop to find an accurate match of indent in the node
2585 * ancestry. */
2586 vty_go_parent(vty);
2587 } else {
2588 /* The indent is deeper than the just entered parent, record the new
2589 * indentation characters. */
2590 vty->indent = talloc_strdup(vty, indent);
2591 /* This *is* the new indentation. */
2592 cmp = 0;
2593 }
2594 } else {
2595 /* There is a known indentation for this node level, validate and detect node
2596 * exits. */
2597 cmp = indent_cmp(indent, vty->indent);
2598 if (cmp == EINVAL)
2599 goto return_invalid_indent;
2600 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002601
2602 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2603 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2604 while (cmp < 0) {
2605 vty_go_parent(vty);
2606 cmp = indent_cmp(indent, vty->indent);
2607 if (cmp == EINVAL)
2608 goto return_invalid_indent;
2609 }
2610
2611 /* More indent without having entered a child node level? Either the parent node's indent
2612 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2613 * or the indentation increased even though the vty command didn't enter a child. */
2614 if (cmp > 0)
2615 goto return_invalid_indent;
2616
2617 /* Remember the current node before the command possibly changes it. */
2618 this_node = (struct vty_parent_node){
2619 .node = vty->node,
2620 .priv = vty->priv,
2621 .indent = vty->indent,
2622 };
2623
2624 parent = vty_parent(vty);
2625 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002626 cmd_free_strvec(vline);
2627
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002628 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002629 if (indent) {
2630 talloc_free(indent);
2631 indent = NULL;
2632 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002633 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002634 }
2635
2636 /* If we have stepped down into a child node, push a parent frame.
2637 * The causality is such: we don't expect every single node entry implementation to push
2638 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2639 * a parent node. Hence if the node changed without the parent node changing, we must
2640 * have stepped into a child node (and now expect a deeper indent). */
2641 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2642 /* Push the parent node. */
2643 parent = talloc_zero(vty, struct vty_parent_node);
2644 *parent = this_node;
2645 llist_add(&parent->entry, &vty->parent_nodes);
2646
2647 /* The current talloc'ed vty->indent string will now be owned by this parent
2648 * struct. Indicate that we don't know what deeper indent characters the user
2649 * will choose. */
2650 vty->indent = NULL;
2651 }
2652
2653 if (indent) {
2654 talloc_free(indent);
2655 indent = NULL;
2656 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002657 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002658 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2659 while (vty_parent(vty))
2660 vty_go_parent(vty);
2661
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002662 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002663
2664return_invalid_indent:
2665 if (vline)
2666 cmd_free_strvec(vline);
2667 if (indent) {
2668 talloc_free(indent);
2669 indent = NULL;
2670 }
2671 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002672}
2673
2674/* Configration from terminal */
2675DEFUN(config_terminal,
2676 config_terminal_cmd,
2677 "configure terminal",
2678 "Configuration from vty interface\n" "Configuration terminal\n")
2679{
2680 if (vty_config_lock(vty))
2681 vty->node = CONFIG_NODE;
2682 else {
2683 vty_out(vty, "VTY configuration is locked by other VTY%s",
2684 VTY_NEWLINE);
2685 return CMD_WARNING;
2686 }
2687 return CMD_SUCCESS;
2688}
2689
2690/* Enable command */
2691DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2692{
2693 /* If enable password is NULL, change to ENABLE_NODE */
2694 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2695 vty->type == VTY_SHELL_SERV)
2696 vty->node = ENABLE_NODE;
2697 else
2698 vty->node = AUTH_ENABLE_NODE;
2699
2700 return CMD_SUCCESS;
2701}
2702
2703/* Disable command */
2704DEFUN(disable,
2705 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2706{
2707 if (vty->node == ENABLE_NODE)
2708 vty->node = VIEW_NODE;
2709 return CMD_SUCCESS;
2710}
2711
2712/* Down vty node level. */
2713gDEFUN(config_exit,
2714 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2715{
2716 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002717 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002718 case VIEW_NODE:
2719 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002720 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002721 break;
2722 case CONFIG_NODE:
2723 vty->node = ENABLE_NODE;
2724 vty_config_unlock(vty);
2725 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002726 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002727 if (vty->node > CONFIG_NODE)
2728 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002729 break;
2730 }
2731 return CMD_SUCCESS;
2732}
2733
2734/* End of configuration. */
2735 gDEFUN(config_end,
2736 config_end_cmd, "end", "End current mode and change to enable mode.")
2737{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002738 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002739 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002740
2741 /* Repeatedly call go_parent until a top node is reached. */
2742 while (vty->node > CONFIG_NODE) {
2743 if (vty->node == last_node) {
2744 /* Ensure termination, this shouldn't happen. */
2745 break;
2746 }
2747 last_node = vty->node;
2748 vty_go_parent(vty);
2749 }
2750
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002751 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002752 if (vty->node > ENABLE_NODE)
2753 vty->node = ENABLE_NODE;
2754 vty->index = NULL;
2755 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002756 }
2757 return CMD_SUCCESS;
2758}
2759
2760/* Show version. */
2761DEFUN(show_version,
2762 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2763{
Harald Welte237f6242010-05-25 23:00:45 +02002764 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2765 host.app_info->version,
2766 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2767 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002768
2769 return CMD_SUCCESS;
2770}
2771
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002772DEFUN(show_online_help,
2773 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2774{
2775 vty_dump_nodes(vty);
2776 return CMD_SUCCESS;
2777}
2778
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002779/* Help display function for all node. */
2780gDEFUN(config_help,
2781 config_help_cmd, "help", "Description of the interactive help system\n")
2782{
2783 vty_out(vty,
2784 "This VTY provides advanced help features. When you need help,%s\
2785anytime at the command line please press '?'.%s\
2786%s\
2787If nothing matches, the help list will be empty and you must backup%s\
2788 until entering a '?' shows the available options.%s\
2789Two styles of help are provided:%s\
27901. Full help is available when you are ready to enter a%s\
2791command argument (e.g. 'show ?') and describes each possible%s\
2792argument.%s\
27932. Partial help is provided when an abbreviated argument is entered%s\
2794 and you want to know what arguments match the input%s\
2795 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2796 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2797 return CMD_SUCCESS;
2798}
2799
2800/* Help display function for all node. */
2801gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2802{
2803 unsigned int i;
2804 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2805 struct cmd_element *cmd;
2806
2807 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2808 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002809 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002810 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2811 return CMD_SUCCESS;
2812}
2813
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002814static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002815{
2816 unsigned int i;
2817 int fd;
2818 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002819 char *config_file_tmp = NULL;
2820 char *config_file_sav = NULL;
2821 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002822 struct stat st;
2823
2824 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002825
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002826 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2827 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2828 * manually instead. */
2829
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002830 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002831 config_file_sav =
2832 _talloc_zero(tall_vty_cmd_ctx,
2833 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2834 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002835 if (!config_file_sav)
2836 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002837 strcpy(config_file_sav, config_file);
2838 strcat(config_file_sav, CONF_BACKUP_EXT);
2839
2840 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002841 "config_file_tmp");
2842 if (!config_file_tmp) {
2843 talloc_free(config_file_sav);
2844 return -1;
2845 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002846 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2847
2848 /* Open file to configuration write. */
2849 fd = mkstemp(config_file_tmp);
2850 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002851 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002852 talloc_free(config_file_tmp);
2853 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002854 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002855 }
2856
2857 /* Make vty for configuration file. */
2858 file_vty = vty_new();
2859 file_vty->fd = fd;
2860 file_vty->type = VTY_FILE;
2861
2862 /* Config file header print. */
2863 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002864 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002865 //vty_time_print (file_vty, 1);
2866 vty_out(file_vty, "!\n");
2867
2868 for (i = 0; i < vector_active(cmdvec); i++)
2869 if ((node = vector_slot(cmdvec, i)) && node->func) {
2870 if ((*node->func) (file_vty))
2871 vty_out(file_vty, "!\n");
2872 }
2873 vty_close(file_vty);
2874
2875 if (unlink(config_file_sav) != 0)
2876 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002877 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002878 talloc_free(config_file_sav);
2879 talloc_free(config_file_tmp);
2880 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002881 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002882 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002883
2884 /* Only link the .sav file if the original file exists */
2885 if (stat(config_file, &st) == 0) {
2886 if (link(config_file, config_file_sav) != 0) {
2887 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2888 talloc_free(config_file_sav);
2889 talloc_free(config_file_tmp);
2890 unlink(config_file_tmp);
2891 return -3;
2892 }
2893 sync();
2894 if (unlink(config_file) != 0) {
2895 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2896 talloc_free(config_file_sav);
2897 talloc_free(config_file_tmp);
2898 unlink(config_file_tmp);
2899 return -4;
2900 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002901 }
2902 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002903 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002904 talloc_free(config_file_sav);
2905 talloc_free(config_file_tmp);
2906 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002907 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002908 }
2909 unlink(config_file_tmp);
2910 sync();
2911
2912 talloc_free(config_file_sav);
2913 talloc_free(config_file_tmp);
2914
2915 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002916 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2917 return -6;
2918 }
2919
2920 return 0;
2921}
2922
2923
2924/* Write current configuration into file. */
2925DEFUN(config_write_file,
2926 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002927 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002928 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002929 "Write to configuration file\n"
2930 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002931{
2932 char *failed_file;
2933 int rc;
2934
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002935 if (host.app_info->config_is_consistent) {
2936 rc = host.app_info->config_is_consistent(vty);
2937 if (!rc) {
2938 vty_out(vty, "Configuration is not consistent%s",
2939 VTY_NEWLINE);
2940 return CMD_WARNING;
2941 }
2942 }
2943
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02002944 if (argc == 1)
2945 host_config_set(argv[0]);
2946
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002947 if (host.config == NULL) {
2948 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2949 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002950 return CMD_WARNING;
2951 }
2952
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002953 rc = write_config_file(host.config, &failed_file);
2954 switch (rc) {
2955 case -1:
2956 vty_out(vty, "Can't open configuration file %s.%s",
2957 failed_file, VTY_NEWLINE);
2958 rc = CMD_WARNING;
2959 break;
2960 case -2:
2961 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2962 failed_file, VTY_NEWLINE);
2963 rc = CMD_WARNING;
2964 break;
2965 case -3:
2966 vty_out(vty, "Can't backup old configuration file %s.%s",
2967 failed_file, VTY_NEWLINE);
2968 rc = CMD_WARNING;
2969 break;
2970 case -4:
2971 vty_out(vty, "Can't unlink configuration file %s.%s",
2972 failed_file, VTY_NEWLINE);
2973 rc = CMD_WARNING;
2974 break;
2975 case -5:
2976 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2977 VTY_NEWLINE);
2978 rc = CMD_WARNING;
2979 break;
2980 case -6:
2981 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2982 failed_file, strerror(errno), errno, VTY_NEWLINE);
2983 rc = CMD_WARNING;
2984 break;
2985 default:
2986 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2987 rc = CMD_SUCCESS;
2988 break;
2989 }
2990
2991 talloc_free(failed_file);
2992 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002993}
2994
2995ALIAS(config_write_file,
2996 config_write_cmd,
2997 "write", "Write running configuration to memory, network, or terminal\n")
2998
2999 ALIAS(config_write_file,
3000 config_write_memory_cmd,
3001 "write memory",
3002 "Write running configuration to memory, network, or terminal\n"
3003 "Write configuration to the file (same as write file)\n")
3004
3005 ALIAS(config_write_file,
3006 copy_runningconfig_startupconfig_cmd,
3007 "copy running-config startup-config",
3008 "Copy configuration\n"
3009 "Copy running config to... \n"
3010 "Copy running config to startup config (same as write file)\n")
3011
3012/* Write current configuration into the terminal. */
3013 DEFUN(config_write_terminal,
3014 config_write_terminal_cmd,
3015 "write terminal",
3016 "Write running configuration to memory, network, or terminal\n"
3017 "Write to terminal\n")
3018{
3019 unsigned int i;
3020 struct cmd_node *node;
3021
3022 if (vty->type == VTY_SHELL_SERV) {
3023 for (i = 0; i < vector_active(cmdvec); i++)
3024 if ((node = vector_slot(cmdvec, i)) && node->func
3025 && node->vtysh) {
3026 if ((*node->func) (vty))
3027 vty_out(vty, "!%s", VTY_NEWLINE);
3028 }
3029 } else {
3030 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3031 VTY_NEWLINE);
3032 vty_out(vty, "!%s", VTY_NEWLINE);
3033
3034 for (i = 0; i < vector_active(cmdvec); i++)
3035 if ((node = vector_slot(cmdvec, i)) && node->func) {
3036 if ((*node->func) (vty))
3037 vty_out(vty, "!%s", VTY_NEWLINE);
3038 }
3039 vty_out(vty, "end%s", VTY_NEWLINE);
3040 }
3041 return CMD_SUCCESS;
3042}
3043
3044/* Write current configuration into the terminal. */
3045ALIAS(config_write_terminal,
3046 show_running_config_cmd,
3047 "show running-config", SHOW_STR "running configuration\n")
3048
3049/* Write startup configuration into the terminal. */
3050 DEFUN(show_startup_config,
3051 show_startup_config_cmd,
3052 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3053{
3054 char buf[BUFSIZ];
3055 FILE *confp;
3056
3057 confp = fopen(host.config, "r");
3058 if (confp == NULL) {
3059 vty_out(vty, "Can't open configuration file [%s]%s",
3060 host.config, VTY_NEWLINE);
3061 return CMD_WARNING;
3062 }
3063
3064 while (fgets(buf, BUFSIZ, confp)) {
3065 char *cp = buf;
3066
3067 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3068 cp++;
3069 *cp = '\0';
3070
3071 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3072 }
3073
3074 fclose(confp);
3075
3076 return CMD_SUCCESS;
3077}
3078
3079/* Hostname configuration */
3080DEFUN(config_hostname,
3081 hostname_cmd,
3082 "hostname WORD",
3083 "Set system's network name\n" "This system's network name\n")
3084{
3085 if (!isalpha((int)*argv[0])) {
3086 vty_out(vty, "Please specify string starting with alphabet%s",
3087 VTY_NEWLINE);
3088 return CMD_WARNING;
3089 }
3090
3091 if (host.name)
3092 talloc_free(host.name);
3093
3094 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3095 return CMD_SUCCESS;
3096}
3097
3098DEFUN(config_no_hostname,
3099 no_hostname_cmd,
3100 "no hostname [HOSTNAME]",
3101 NO_STR "Reset system's network name\n" "Host name of this router\n")
3102{
3103 if (host.name)
3104 talloc_free(host.name);
3105 host.name = NULL;
3106 return CMD_SUCCESS;
3107}
3108
3109/* VTY interface password set. */
3110DEFUN(config_password, password_cmd,
3111 "password (8|) WORD",
3112 "Assign the terminal connection password\n"
3113 "Specifies a HIDDEN password will follow\n"
3114 "dummy string \n" "The HIDDEN line password string\n")
3115{
3116 /* Argument check. */
3117 if (argc == 0) {
3118 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3119 return CMD_WARNING;
3120 }
3121
3122 if (argc == 2) {
3123 if (*argv[0] == '8') {
3124 if (host.password)
3125 talloc_free(host.password);
3126 host.password = NULL;
3127 if (host.password_encrypt)
3128 talloc_free(host.password_encrypt);
3129 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3130 return CMD_SUCCESS;
3131 } else {
3132 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3133 return CMD_WARNING;
3134 }
3135 }
3136
3137 if (!isalnum((int)*argv[0])) {
3138 vty_out(vty,
3139 "Please specify string starting with alphanumeric%s",
3140 VTY_NEWLINE);
3141 return CMD_WARNING;
3142 }
3143
3144 if (host.password)
3145 talloc_free(host.password);
3146 host.password = NULL;
3147
3148#ifdef VTY_CRYPT_PW
3149 if (host.encrypt) {
3150 if (host.password_encrypt)
3151 talloc_free(host.password_encrypt);
3152 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3153 } else
3154#endif
3155 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3156
3157 return CMD_SUCCESS;
3158}
3159
3160ALIAS(config_password, password_text_cmd,
3161 "password LINE",
3162 "Assign the terminal connection password\n"
3163 "The UNENCRYPTED (cleartext) line password\n")
3164
3165/* VTY enable password set. */
3166 DEFUN(config_enable_password, enable_password_cmd,
3167 "enable password (8|) WORD",
3168 "Modify enable password parameters\n"
3169 "Assign the privileged level password\n"
3170 "Specifies a HIDDEN password will follow\n"
3171 "dummy string \n" "The HIDDEN 'enable' password string\n")
3172{
3173 /* Argument check. */
3174 if (argc == 0) {
3175 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3176 return CMD_WARNING;
3177 }
3178
3179 /* Crypt type is specified. */
3180 if (argc == 2) {
3181 if (*argv[0] == '8') {
3182 if (host.enable)
3183 talloc_free(host.enable);
3184 host.enable = NULL;
3185
3186 if (host.enable_encrypt)
3187 talloc_free(host.enable_encrypt);
3188 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3189
3190 return CMD_SUCCESS;
3191 } else {
3192 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3193 return CMD_WARNING;
3194 }
3195 }
3196
3197 if (!isalnum((int)*argv[0])) {
3198 vty_out(vty,
3199 "Please specify string starting with alphanumeric%s",
3200 VTY_NEWLINE);
3201 return CMD_WARNING;
3202 }
3203
3204 if (host.enable)
3205 talloc_free(host.enable);
3206 host.enable = NULL;
3207
3208 /* Plain password input. */
3209#ifdef VTY_CRYPT_PW
3210 if (host.encrypt) {
3211 if (host.enable_encrypt)
3212 talloc_free(host.enable_encrypt);
3213 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3214 } else
3215#endif
3216 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3217
3218 return CMD_SUCCESS;
3219}
3220
3221ALIAS(config_enable_password,
3222 enable_password_text_cmd,
3223 "enable password LINE",
3224 "Modify enable password parameters\n"
3225 "Assign the privileged level password\n"
3226 "The UNENCRYPTED (cleartext) 'enable' password\n")
3227
3228/* VTY enable password delete. */
3229 DEFUN(no_config_enable_password, no_enable_password_cmd,
3230 "no enable password",
3231 NO_STR
3232 "Modify enable password parameters\n"
3233 "Assign the privileged level password\n")
3234{
3235 if (host.enable)
3236 talloc_free(host.enable);
3237 host.enable = NULL;
3238
3239 if (host.enable_encrypt)
3240 talloc_free(host.enable_encrypt);
3241 host.enable_encrypt = NULL;
3242
3243 return CMD_SUCCESS;
3244}
3245
3246#ifdef VTY_CRYPT_PW
3247DEFUN(service_password_encrypt,
3248 service_password_encrypt_cmd,
3249 "service password-encryption",
3250 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3251{
3252 if (host.encrypt)
3253 return CMD_SUCCESS;
3254
3255 host.encrypt = 1;
3256
3257 if (host.password) {
3258 if (host.password_encrypt)
3259 talloc_free(host.password_encrypt);
3260 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3261 }
3262 if (host.enable) {
3263 if (host.enable_encrypt)
3264 talloc_free(host.enable_encrypt);
3265 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3266 }
3267
3268 return CMD_SUCCESS;
3269}
3270
3271DEFUN(no_service_password_encrypt,
3272 no_service_password_encrypt_cmd,
3273 "no service password-encryption",
3274 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3275{
3276 if (!host.encrypt)
3277 return CMD_SUCCESS;
3278
3279 host.encrypt = 0;
3280
3281 if (host.password_encrypt)
3282 talloc_free(host.password_encrypt);
3283 host.password_encrypt = NULL;
3284
3285 if (host.enable_encrypt)
3286 talloc_free(host.enable_encrypt);
3287 host.enable_encrypt = NULL;
3288
3289 return CMD_SUCCESS;
3290}
3291#endif
3292
3293DEFUN(config_terminal_length, config_terminal_length_cmd,
3294 "terminal length <0-512>",
3295 "Set terminal line parameters\n"
3296 "Set number of lines on a screen\n"
3297 "Number of lines on screen (0 for no pausing)\n")
3298{
3299 int lines;
3300 char *endptr = NULL;
3301
3302 lines = strtol(argv[0], &endptr, 10);
3303 if (lines < 0 || lines > 512 || *endptr != '\0') {
3304 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3305 return CMD_WARNING;
3306 }
3307 vty->lines = lines;
3308
3309 return CMD_SUCCESS;
3310}
3311
3312DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3313 "terminal no length",
3314 "Set terminal line parameters\n"
3315 NO_STR "Set number of lines on a screen\n")
3316{
3317 vty->lines = -1;
3318 return CMD_SUCCESS;
3319}
3320
3321DEFUN(service_terminal_length, service_terminal_length_cmd,
3322 "service terminal-length <0-512>",
3323 "Set up miscellaneous service\n"
3324 "System wide terminal length configuration\n"
3325 "Number of lines of VTY (0 means no line control)\n")
3326{
3327 int lines;
3328 char *endptr = NULL;
3329
3330 lines = strtol(argv[0], &endptr, 10);
3331 if (lines < 0 || lines > 512 || *endptr != '\0') {
3332 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3333 return CMD_WARNING;
3334 }
3335 host.lines = lines;
3336
3337 return CMD_SUCCESS;
3338}
3339
3340DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3341 "no service terminal-length [<0-512>]",
3342 NO_STR
3343 "Set up miscellaneous service\n"
3344 "System wide terminal length configuration\n"
3345 "Number of lines of VTY (0 means no line control)\n")
3346{
3347 host.lines = -1;
3348 return CMD_SUCCESS;
3349}
3350
3351DEFUN_HIDDEN(do_echo,
3352 echo_cmd,
3353 "echo .MESSAGE",
3354 "Echo a message back to the vty\n" "The message to echo\n")
3355{
3356 char *message;
3357
3358 vty_out(vty, "%s%s",
3359 ((message =
3360 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3361 if (message)
3362 talloc_free(message);
3363 return CMD_SUCCESS;
3364}
3365
3366#if 0
3367DEFUN(config_logmsg,
3368 config_logmsg_cmd,
3369 "logmsg " LOG_LEVELS " .MESSAGE",
3370 "Send a message to enabled logging destinations\n"
3371 LOG_LEVEL_DESC "The message to send\n")
3372{
3373 int level;
3374 char *message;
3375
3376 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3377 return CMD_ERR_NO_MATCH;
3378
3379 zlog(NULL, level,
3380 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3381 if (message)
3382 talloc_free(message);
3383 return CMD_SUCCESS;
3384}
3385
3386DEFUN(show_logging,
3387 show_logging_cmd,
3388 "show logging", SHOW_STR "Show current logging configuration\n")
3389{
3390 struct zlog *zl = zlog_default;
3391
3392 vty_out(vty, "Syslog logging: ");
3393 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3394 vty_out(vty, "disabled");
3395 else
3396 vty_out(vty, "level %s, facility %s, ident %s",
3397 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3398 facility_name(zl->facility), zl->ident);
3399 vty_out(vty, "%s", VTY_NEWLINE);
3400
3401 vty_out(vty, "Stdout logging: ");
3402 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3403 vty_out(vty, "disabled");
3404 else
3405 vty_out(vty, "level %s",
3406 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3407 vty_out(vty, "%s", VTY_NEWLINE);
3408
3409 vty_out(vty, "Monitor logging: ");
3410 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3411 vty_out(vty, "disabled");
3412 else
3413 vty_out(vty, "level %s",
3414 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3415 vty_out(vty, "%s", VTY_NEWLINE);
3416
3417 vty_out(vty, "File logging: ");
3418 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3419 vty_out(vty, "disabled");
3420 else
3421 vty_out(vty, "level %s, filename %s",
3422 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3423 zl->filename);
3424 vty_out(vty, "%s", VTY_NEWLINE);
3425
3426 vty_out(vty, "Protocol name: %s%s",
3427 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3428 vty_out(vty, "Record priority: %s%s",
3429 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3430
3431 return CMD_SUCCESS;
3432}
3433
3434DEFUN(config_log_stdout,
3435 config_log_stdout_cmd,
3436 "log stdout", "Logging control\n" "Set stdout logging level\n")
3437{
3438 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3439 return CMD_SUCCESS;
3440}
3441
3442DEFUN(config_log_stdout_level,
3443 config_log_stdout_level_cmd,
3444 "log stdout " LOG_LEVELS,
3445 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3446{
3447 int level;
3448
3449 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3450 return CMD_ERR_NO_MATCH;
3451 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3452 return CMD_SUCCESS;
3453}
3454
3455DEFUN(no_config_log_stdout,
3456 no_config_log_stdout_cmd,
3457 "no log stdout [LEVEL]",
3458 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3459{
3460 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3461 return CMD_SUCCESS;
3462}
3463
3464DEFUN(config_log_monitor,
3465 config_log_monitor_cmd,
3466 "log monitor",
3467 "Logging control\n" "Set terminal line (monitor) logging level\n")
3468{
3469 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3470 return CMD_SUCCESS;
3471}
3472
3473DEFUN(config_log_monitor_level,
3474 config_log_monitor_level_cmd,
3475 "log monitor " LOG_LEVELS,
3476 "Logging control\n"
3477 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3478{
3479 int level;
3480
3481 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3482 return CMD_ERR_NO_MATCH;
3483 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3484 return CMD_SUCCESS;
3485}
3486
3487DEFUN(no_config_log_monitor,
3488 no_config_log_monitor_cmd,
3489 "no log monitor [LEVEL]",
3490 NO_STR
3491 "Logging control\n"
3492 "Disable terminal line (monitor) logging\n" "Logging level\n")
3493{
3494 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3495 return CMD_SUCCESS;
3496}
3497
3498static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3499{
3500 int ret;
3501 char *p = NULL;
3502 const char *fullpath;
3503
3504 /* Path detection. */
3505 if (!IS_DIRECTORY_SEP(*fname)) {
3506 char cwd[MAXPATHLEN + 1];
3507 cwd[MAXPATHLEN] = '\0';
3508
3509 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3510 zlog_err("config_log_file: Unable to alloc mem!");
3511 return CMD_WARNING;
3512 }
3513
3514 if ((p = _talloc_zero(tall_vcmd_ctx,
3515 strlen(cwd) + strlen(fname) + 2),
3516 "set_log_file")
3517 == NULL) {
3518 zlog_err("config_log_file: Unable to alloc mem!");
3519 return CMD_WARNING;
3520 }
3521 sprintf(p, "%s/%s", cwd, fname);
3522 fullpath = p;
3523 } else
3524 fullpath = fname;
3525
3526 ret = zlog_set_file(NULL, fullpath, loglevel);
3527
3528 if (p)
3529 talloc_free(p);
3530
3531 if (!ret) {
3532 vty_out(vty, "can't open logfile %s\n", fname);
3533 return CMD_WARNING;
3534 }
3535
3536 if (host.logfile)
3537 talloc_free(host.logfile);
3538
3539 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3540
3541 return CMD_SUCCESS;
3542}
3543
3544DEFUN(config_log_file,
3545 config_log_file_cmd,
3546 "log file FILENAME",
3547 "Logging control\n" "Logging to file\n" "Logging filename\n")
3548{
3549 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3550}
3551
3552DEFUN(config_log_file_level,
3553 config_log_file_level_cmd,
3554 "log file FILENAME " LOG_LEVELS,
3555 "Logging control\n"
3556 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3557{
3558 int level;
3559
3560 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3561 return CMD_ERR_NO_MATCH;
3562 return set_log_file(vty, argv[0], level);
3563}
3564
3565DEFUN(no_config_log_file,
3566 no_config_log_file_cmd,
3567 "no log file [FILENAME]",
3568 NO_STR
3569 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3570{
3571 zlog_reset_file(NULL);
3572
3573 if (host.logfile)
3574 talloc_free(host.logfile);
3575
3576 host.logfile = NULL;
3577
3578 return CMD_SUCCESS;
3579}
3580
3581ALIAS(no_config_log_file,
3582 no_config_log_file_level_cmd,
3583 "no log file FILENAME LEVEL",
3584 NO_STR
3585 "Logging control\n"
3586 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3587
3588 DEFUN(config_log_syslog,
3589 config_log_syslog_cmd,
3590 "log syslog", "Logging control\n" "Set syslog logging level\n")
3591{
3592 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3593 return CMD_SUCCESS;
3594}
3595
3596DEFUN(config_log_syslog_level,
3597 config_log_syslog_level_cmd,
3598 "log syslog " LOG_LEVELS,
3599 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3600{
3601 int level;
3602
3603 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3604 return CMD_ERR_NO_MATCH;
3605 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3606 return CMD_SUCCESS;
3607}
3608
3609DEFUN_DEPRECATED(config_log_syslog_facility,
3610 config_log_syslog_facility_cmd,
3611 "log syslog facility " LOG_FACILITIES,
3612 "Logging control\n"
3613 "Logging goes to syslog\n"
3614 "(Deprecated) Facility parameter for syslog messages\n"
3615 LOG_FACILITY_DESC)
3616{
3617 int facility;
3618
3619 if ((facility = facility_match(argv[0])) < 0)
3620 return CMD_ERR_NO_MATCH;
3621
3622 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3623 zlog_default->facility = facility;
3624 return CMD_SUCCESS;
3625}
3626
3627DEFUN(no_config_log_syslog,
3628 no_config_log_syslog_cmd,
3629 "no log syslog [LEVEL]",
3630 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3631{
3632 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3633 return CMD_SUCCESS;
3634}
3635
3636ALIAS(no_config_log_syslog,
3637 no_config_log_syslog_facility_cmd,
3638 "no log syslog facility " LOG_FACILITIES,
3639 NO_STR
3640 "Logging control\n"
3641 "Logging goes to syslog\n"
3642 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3643
3644 DEFUN(config_log_facility,
3645 config_log_facility_cmd,
3646 "log facility " LOG_FACILITIES,
3647 "Logging control\n"
3648 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3649{
3650 int facility;
3651
3652 if ((facility = facility_match(argv[0])) < 0)
3653 return CMD_ERR_NO_MATCH;
3654 zlog_default->facility = facility;
3655 return CMD_SUCCESS;
3656}
3657
3658DEFUN(no_config_log_facility,
3659 no_config_log_facility_cmd,
3660 "no log facility [FACILITY]",
3661 NO_STR
3662 "Logging control\n"
3663 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3664{
3665 zlog_default->facility = LOG_DAEMON;
3666 return CMD_SUCCESS;
3667}
3668
3669DEFUN_DEPRECATED(config_log_trap,
3670 config_log_trap_cmd,
3671 "log trap " LOG_LEVELS,
3672 "Logging control\n"
3673 "(Deprecated) Set logging level and default for all destinations\n"
3674 LOG_LEVEL_DESC)
3675{
3676 int new_level;
3677 int i;
3678
3679 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3680 return CMD_ERR_NO_MATCH;
3681
3682 zlog_default->default_lvl = new_level;
3683 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3684 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3685 zlog_default->maxlvl[i] = new_level;
3686 return CMD_SUCCESS;
3687}
3688
3689DEFUN_DEPRECATED(no_config_log_trap,
3690 no_config_log_trap_cmd,
3691 "no log trap [LEVEL]",
3692 NO_STR
3693 "Logging control\n"
3694 "Permit all logging information\n" "Logging level\n")
3695{
3696 zlog_default->default_lvl = LOG_DEBUG;
3697 return CMD_SUCCESS;
3698}
3699
3700DEFUN(config_log_record_priority,
3701 config_log_record_priority_cmd,
3702 "log record-priority",
3703 "Logging control\n"
3704 "Log the priority of the message within the message\n")
3705{
3706 zlog_default->record_priority = 1;
3707 return CMD_SUCCESS;
3708}
3709
3710DEFUN(no_config_log_record_priority,
3711 no_config_log_record_priority_cmd,
3712 "no log record-priority",
3713 NO_STR
3714 "Logging control\n"
3715 "Do not log the priority of the message within the message\n")
3716{
3717 zlog_default->record_priority = 0;
3718 return CMD_SUCCESS;
3719}
3720#endif
3721
3722DEFUN(banner_motd_file,
3723 banner_motd_file_cmd,
3724 "banner motd file [FILE]",
3725 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3726{
3727 if (host.motdfile)
3728 talloc_free(host.motdfile);
3729 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3730
3731 return CMD_SUCCESS;
3732}
3733
3734DEFUN(banner_motd_default,
3735 banner_motd_default_cmd,
3736 "banner motd default",
3737 "Set banner string\n" "Strings for motd\n" "Default string\n")
3738{
3739 host.motd = default_motd;
3740 return CMD_SUCCESS;
3741}
3742
3743DEFUN(no_banner_motd,
3744 no_banner_motd_cmd,
3745 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3746{
3747 host.motd = NULL;
3748 if (host.motdfile)
3749 talloc_free(host.motdfile);
3750 host.motdfile = NULL;
3751 return CMD_SUCCESS;
3752}
3753
3754/* Set config filename. Called from vty.c */
3755void host_config_set(const char *filename)
3756{
3757 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3758}
3759
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003760/*! Deprecated, now happens implicitly when calling install_node().
3761 * Users of the API may still attempt to call this function, hence
3762 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003763void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003764{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003765}
3766
3767/*! Deprecated, now happens implicitly when calling install_node().
3768 * Users of the API may still attempt to call this function, hence
3769 * leave it here as a no-op. */
3770void vty_install_default(int node)
3771{
3772}
3773
3774/*! Install common commands like 'exit' and 'list'. */
3775static void install_basic_node_commands(int node)
3776{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003777 install_element(node, &config_help_cmd);
3778 install_element(node, &config_list_cmd);
3779
3780 install_element(node, &config_write_terminal_cmd);
3781 install_element(node, &config_write_file_cmd);
3782 install_element(node, &config_write_memory_cmd);
3783 install_element(node, &config_write_cmd);
3784 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003785
3786 install_element(node, &config_exit_cmd);
3787
3788 if (node >= CONFIG_NODE) {
3789 /* It's not a top node. */
3790 install_element(node, &config_end_cmd);
3791 }
3792}
3793
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003794/*! Return true if a node is installed by install_basic_node_commands(), so
3795 * that we can avoid repeating them for each and every node during 'show
3796 * running-config' */
3797static bool vty_command_is_common(struct cmd_element *cmd)
3798{
3799 if (cmd == &config_help_cmd
3800 || cmd == &config_list_cmd
3801 || cmd == &config_write_terminal_cmd
3802 || cmd == &config_write_file_cmd
3803 || cmd == &config_write_memory_cmd
3804 || cmd == &config_write_cmd
3805 || cmd == &show_running_config_cmd
3806 || cmd == &config_exit_cmd
3807 || cmd == &config_end_cmd)
3808 return true;
3809 return false;
3810}
3811
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003812/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003813 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003814 * \param[in] vty the vty of the code
3815 * \param[in] filename where to store the file
3816 * \return 0 in case of success.
3817 *
3818 * If the filename already exists create a filename.sav
3819 * version with the current code.
3820 *
3821 */
3822int osmo_vty_write_config_file(const char *filename)
3823{
3824 char *failed_file;
3825 int rc;
3826
3827 rc = write_config_file(filename, &failed_file);
3828 talloc_free(failed_file);
3829 return rc;
3830}
3831
3832/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003833 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003834 * \return 0 in case of success.
3835 *
3836 * If the filename already exists create a filename.sav
3837 * version with the current code.
3838 *
3839 */
3840int osmo_vty_save_config_file(void)
3841{
3842 char *failed_file;
3843 int rc;
3844
3845 if (host.config == NULL)
3846 return -7;
3847
3848 rc = write_config_file(host.config, &failed_file);
3849 talloc_free(failed_file);
3850 return rc;
3851}
3852
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003853/* Initialize command interface. Install basic nodes and commands. */
3854void cmd_init(int terminal)
3855{
3856 /* Allocate initial top vector of commands. */
3857 cmdvec = vector_init(VECTOR_MIN_SIZE);
3858
3859 /* Default host value settings. */
3860 host.name = NULL;
3861 host.password = NULL;
3862 host.enable = NULL;
3863 host.logfile = NULL;
3864 host.config = NULL;
3865 host.lines = -1;
3866 host.motd = default_motd;
3867 host.motdfile = NULL;
3868
3869 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003870 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003871 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003872 install_node_bare(&auth_node, NULL);
3873 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003874 install_node(&config_node, config_write_host);
3875
3876 /* Each node's basic commands. */
3877 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003878 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003879 if (terminal) {
3880 install_element(VIEW_NODE, &config_list_cmd);
3881 install_element(VIEW_NODE, &config_exit_cmd);
3882 install_element(VIEW_NODE, &config_help_cmd);
3883 install_element(VIEW_NODE, &config_enable_cmd);
3884 install_element(VIEW_NODE, &config_terminal_length_cmd);
3885 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3886 install_element(VIEW_NODE, &echo_cmd);
3887 }
3888
3889 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003890 install_element(ENABLE_NODE, &config_disable_cmd);
3891 install_element(ENABLE_NODE, &config_terminal_cmd);
3892 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3893 }
3894 install_element (ENABLE_NODE, &show_startup_config_cmd);
3895 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003896 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003897
3898 if (terminal) {
3899 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3900 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3901 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003902 }
3903
3904 install_element(CONFIG_NODE, &hostname_cmd);
3905 install_element(CONFIG_NODE, &no_hostname_cmd);
3906
3907 if (terminal) {
3908 install_element(CONFIG_NODE, &password_cmd);
3909 install_element(CONFIG_NODE, &password_text_cmd);
3910 install_element(CONFIG_NODE, &enable_password_cmd);
3911 install_element(CONFIG_NODE, &enable_password_text_cmd);
3912 install_element(CONFIG_NODE, &no_enable_password_cmd);
3913
3914#ifdef VTY_CRYPT_PW
3915 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3916 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3917#endif
3918 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3919 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3920 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3921 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3922 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3923
3924 }
3925 srand(time(NULL));
3926}
Harald Welte7acb30c2011-08-17 17:13:48 +02003927
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003928/*! @} */