blob: 4952567dee7efe90c2e1ec0ef8d0701f32502f49 [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>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020041#include <sys/time.h>
42#include <sys/stat.h>
43
44#include <osmocom/vty/vector.h>
45#include <osmocom/vty/vty.h>
46#include <osmocom/vty/command.h>
47
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010048#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010049#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050
Ruben Undheim766f77c2018-11-18 13:02:47 +010051#ifndef MAXPATHLEN
52 #define MAXPATHLEN 4096
53#endif
54
55
Harald Weltee881b1b2011-08-17 18:52:30 +020056/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020057 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020059 *
60 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020061
Harald Welte3fb0b6f2010-05-19 19:02:52 +020062#define CONFIGFILE_MASK 022
63
64void *tall_vty_cmd_ctx;
65
66/* Command vector which includes some level of command lists. Normally
67 each daemon maintains each own cmdvec. */
68vector cmdvec;
69
70/* Host information structure. */
71struct host host;
72
73/* Standard command node structures. */
74struct cmd_node auth_node = {
75 AUTH_NODE,
76 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node view_node = {
81 VIEW_NODE,
82 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node auth_enable_node = {
87 AUTH_ENABLE_NODE,
88 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node enable_node = {
93 ENABLE_NODE,
94 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010095 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096};
97
98struct cmd_node config_node = {
99 CONFIG_NODE,
100 "%s(config)# ",
101 1
102};
103
104/* Default motd string. */
105const char *default_motd = "";
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200108 *
109 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110void print_version(int print_copyright)
111{
Harald Welte237f6242010-05-25 23:00:45 +0200112 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200114 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115}
116
117/* Utility function to concatenate argv argument into a single string
118 with inserting ' ' character between each argument. */
119char *argv_concat(const char **argv, int argc, int shift)
120{
121 int i;
122 size_t len;
123 char *str;
124 char *p;
125
126 len = 0;
127 for (i = shift; i < argc; i++)
128 len += strlen(argv[i]) + 1;
129 if (!len)
130 return NULL;
131 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
132 for (i = shift; i < argc; i++) {
133 size_t arglen;
134 memcpy(p, argv[i], (arglen = strlen(argv[i])));
135 p += arglen;
136 *p++ = ' ';
137 }
138 *(p - 1) = '\0';
139 return str;
140}
141
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200142/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
143 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
144 * in turn, this name us used for XML IDs in 'show online-help'. */
145static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
146{
147 const char *pos;
148 int dest = 0;
149
150 if (!prompt || !*prompt)
151 return "";
152
153 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
154 if (pos[0] == '%' && pos[1]) {
155 /* skip "%s"; loop pos++ does the second one. */
156 pos++;
157 continue;
158 }
159 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
160 continue;
161 name_buf[dest] = pos[0];
162 dest++;
163 }
164 name_buf[dest] = '\0';
165 return name_buf;
166}
167
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200168static void install_basic_node_commands(int node);
169
170/*! Install top node of command vector, without adding basic node commands. */
171static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172{
173 vector_set_index(cmdvec, node->node, node);
174 node->func = func;
175 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200176 if (!*node->name)
177 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200178}
179
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200180/*! Install top node of command vector. */
181void install_node(struct cmd_node *node, int (*func) (struct vty *))
182{
183 install_node_bare(node, func);
184 install_basic_node_commands(node->node);
185}
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187/* Compare two command's string. Used in sort_node (). */
188static int cmp_node(const void *p, const void *q)
189{
190 struct cmd_element *a = *(struct cmd_element **)p;
191 struct cmd_element *b = *(struct cmd_element **)q;
192
193 return strcmp(a->string, b->string);
194}
195
196static int cmp_desc(const void *p, const void *q)
197{
198 struct desc *a = *(struct desc **)p;
199 struct desc *b = *(struct desc **)q;
200
201 return strcmp(a->cmd, b->cmd);
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200205void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200206{
207 unsigned int i, j;
208 struct cmd_node *cnode;
209 vector descvec;
210 struct cmd_element *cmd_element;
211
212 for (i = 0; i < vector_active(cmdvec); i++)
213 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
214 vector cmd_vector = cnode->cmd_vector;
215 qsort(cmd_vector->index, vector_active(cmd_vector),
216 sizeof(void *), cmp_node);
217
218 for (j = 0; j < vector_active(cmd_vector); j++)
219 if ((cmd_element =
220 vector_slot(cmd_vector, j)) != NULL
221 && vector_active(cmd_element->strvec)) {
222 descvec =
223 vector_slot(cmd_element->strvec,
224 vector_active
225 (cmd_element->strvec) -
226 1);
227 qsort(descvec->index,
228 vector_active(descvec),
229 sizeof(void *), cmp_desc);
230 }
231 }
232}
233
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200234/*! Break up string in command tokens. Return leading indents.
235 * \param[in] string String to split.
236 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
237 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
238 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
239 *
240 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
241 * so that \a indent can simply return the count of leading spaces.
242 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
243 */
244int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245{
246 const char *cp, *start;
247 char *token;
248 int strlen;
249 vector strvec;
250
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200251 *strvec_p = NULL;
252 if (indent)
253 *indent = 0;
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200257
258 cp = string;
259
260 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200261 while (isspace((int)*cp) && *cp != '\0') {
262 /* if we're counting indents, we need to be strict about them */
263 if (indent && (*cp != ' ') && (*cp != '\t')) {
264 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
265 if (*cp == '\n' || *cp == '\r') {
266 cp++;
267 string = cp;
268 continue;
269 }
270 return CMD_ERR_INVALID_INDENT;
271 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 }
274
275 if (indent)
276 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277
278 /* Return if there is only white spaces */
279 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Prepare return vector. */
286 strvec = vector_init(VECTOR_MIN_SIZE);
287
288 /* Copy each command piece and set into vector. */
289 while (1) {
290 start = cp;
291 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
292 *cp != '\0')
293 cp++;
294 strlen = cp - start;
295 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
296 memcpy(token, start, strlen);
297 *(token + strlen) = '\0';
298 vector_set(strvec, token);
299
300 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
301 *cp != '\0')
302 cp++;
303
304 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200307
308 *strvec_p = strvec;
309 return CMD_SUCCESS;
310}
311
312/*! Breaking up string into each command piece. I assume given
313 character is separated by a space character. Return value is a
314 vector which includes char ** data element. */
315vector cmd_make_strvec(const char *string)
316{
317 vector strvec;
318 cmd_make_strvec2(string, NULL, &strvec);
319 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200320}
321
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200322/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200323void cmd_free_strvec(vector v)
324{
325 unsigned int i;
326 char *cp;
327
328 if (!v)
329 return;
330
331 for (i = 0; i < vector_active(v); i++)
332 if ((cp = vector_slot(v, i)) != NULL)
333 talloc_free(cp);
334
335 vector_free(v);
336}
337
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200338/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200339static char *cmd_desc_str(const char **string)
340{
341 const char *cp, *start;
342 char *token;
343 int strlen;
344
345 cp = *string;
346
347 if (cp == NULL)
348 return NULL;
349
350 /* Skip white spaces. */
351 while (isspace((int)*cp) && *cp != '\0')
352 cp++;
353
354 /* Return if there is only white spaces */
355 if (*cp == '\0')
356 return NULL;
357
358 start = cp;
359
360 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
361 cp++;
362
363 strlen = cp - start;
364 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
365 memcpy(token, start, strlen);
366 *(token + strlen) = '\0';
367
368 *string = cp;
369
370 return token;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200374static vector cmd_make_descvec(const char *string, const char *descstr)
375{
376 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100377 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378 const char *sp;
379 char *token;
380 int len;
381 const char *cp;
382 const char *dp;
383 vector allvec;
384 vector strvec = NULL;
385 struct desc *desc;
386
387 cp = string;
388 dp = descstr;
389
390 if (cp == NULL)
391 return NULL;
392
393 allvec = vector_init(VECTOR_MIN_SIZE);
394
395 while (1) {
396 while (isspace((int)*cp) && *cp != '\0')
397 cp++;
398
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
400 if (cp[0] == '[' && cp[1] == '(') {
401 optional_brace = 1;
402 cp++;
403 }
404
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200405 if (*cp == '(') {
406 multiple = 1;
407 cp++;
408 }
409 if (*cp == ')') {
410 multiple = 0;
411 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100412 if (*cp == ']')
413 cp++;
414 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 }
416 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100417 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 cp++;
419 }
420
421 while (isspace((int)*cp) && *cp != '\0')
422 cp++;
423
424 if (*cp == '(') {
425 multiple = 1;
426 cp++;
427 }
428
429 if (*cp == '\0')
430 return allvec;
431
432 sp = cp;
433
434 while (!
435 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
436 || *cp == ')' || *cp == '|') && *cp != '\0')
437 cp++;
438
439 len = cp - sp;
440
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100441 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
442 if (optional_brace) {
443 /* Place each individual multi-choice token in its own square braces */
444 token[0] = '[';
445 memcpy(token + 1, sp, len);
446 token[1 + len] = ']';
447 token[2 + len] = '\0';
448 } else {
449 memcpy(token, sp, len);
450 *(token + len) = '\0';
451 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200452
453 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
454 desc->cmd = token;
455 desc->str = cmd_desc_str(&dp);
456
457 if (multiple) {
458 if (multiple == 1) {
459 strvec = vector_init(VECTOR_MIN_SIZE);
460 vector_set(allvec, strvec);
461 }
462 multiple++;
463 } else {
464 strvec = vector_init(VECTOR_MIN_SIZE);
465 vector_set(allvec, strvec);
466 }
467 vector_set(strvec, desc);
468 }
469}
470
471/* Count mandantory string vector size. This is to determine inputed
472 command has enough command length. */
473static int cmd_cmdsize(vector strvec)
474{
475 unsigned int i;
476 int size = 0;
477 vector descvec;
478 struct desc *desc;
479
480 for (i = 0; i < vector_active(strvec); i++)
481 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100482 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483 && (desc = vector_slot(descvec, 0)) != NULL) {
484 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
485 return size;
486 else
487 size++;
488 } else
489 size++;
490 }
491 return size;
492}
493
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200494/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200495const char *cmd_prompt(enum node_type node)
496{
497 struct cmd_node *cnode;
498
499 cnode = vector_slot(cmdvec, node);
500 return cnode->prompt;
501}
502
Alexander Couzensad580ba2016-05-16 16:01:45 +0200503/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200505 * \param unsafe string
506 * \return a new talloc char *
507 */
508char *osmo_asciidoc_escape(const char *inp)
509{
510 int _strlen;
511 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200512 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200513
514 if (!inp)
515 return NULL;
516 _strlen = strlen(inp);
517
518 for (i = 0; i < _strlen; ++i) {
519 switch (inp[i]) {
520 case '|':
521 len += 2;
522 break;
523 default:
524 len += 1;
525 break;
526 }
527 }
528
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200529 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200530 if (!out)
531 return NULL;
532
533 out_ptr = out;
534
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200538 /* Prepend escape character "\": */
539 *(out_ptr++) = '\\';
540 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 default:
542 *(out_ptr++) = inp[i];
543 break;
544 }
545 }
546
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 out_ptr[0] = '\0';
548 return out;
549}
550
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100551static char *xml_escape(const char *inp)
552{
553 int _strlen;
554 char *out, *out_ptr;
555 int len = 0, i, j;
556
557 if (!inp)
558 return NULL;
559 _strlen = strlen(inp);
560
561 for (i = 0; i < _strlen; ++i) {
562 switch (inp[i]) {
563 case '"':
564 len += 6;
565 break;
566 case '\'':
567 len += 6;
568 break;
569 case '<':
570 len += 4;
571 break;
572 case '>':
573 len += 4;
574 break;
575 case '&':
576 len += 5;
577 break;
578 default:
579 len += 1;
580 break;
581 }
582 }
583
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200584 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100585 if (!out)
586 return NULL;
587
588 out_ptr = out;
589
590#define ADD(out, str) \
591 for (j = 0; j < strlen(str); ++j) \
592 *(out++) = str[j];
593
594 for (i = 0; i < _strlen; ++i) {
595 switch (inp[i]) {
596 case '"':
597 ADD(out_ptr, "&quot;");
598 break;
599 case '\'':
600 ADD(out_ptr, "&apos;");
601 break;
602 case '<':
603 ADD(out_ptr, "&lt;");
604 break;
605 case '>':
606 ADD(out_ptr, "&gt;");
607 break;
608 case '&':
609 ADD(out_ptr, "&amp;");
610 break;
611 default:
612 *(out_ptr++) = inp[i];
613 break;
614 }
615 }
616
617#undef ADD
618
619 out_ptr[0] = '\0';
620 return out;
621}
622
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200623typedef int (*print_func_t)(void *data, const char *fmt, ...);
624
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700625static const struct value_string cmd_attr_desc[] = {
626 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
627 { CMD_ATTR_HIDDEN, "This command is hidden" },
628 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700629 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700630 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700631 { 0, NULL }
632};
633
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700634/* Public attributes (to be printed in the VTY / XML reference) */
635#define CMD_ATTR_PUBLIC_MASK \
636 (CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
637
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700638/* Get a flag character for a global VTY command attribute */
639static char cmd_attr_get_flag(unsigned int attr)
640{
641 switch (attr) {
642 case CMD_ATTR_IMMEDIATE:
643 return '!';
644 case CMD_ATTR_NODE_EXIT:
645 return '@';
646 default:
647 return '.';
648 }
649}
650
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700651/* Description of attributes shared between the lib commands */
652static const char * const cmd_lib_attr_desc[32] = {
653 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
654 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200655 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
656 "This command applies on ASP restart",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700657};
658
659/* Flag letters of attributes shared between the lib commands.
660 * NOTE: uppercase letters only, the rest is reserved for applications. */
661static const char cmd_lib_attr_letters[32] = {
662 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200663 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700664};
665
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100666/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200667 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100668 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200669static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100670{
671 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700672 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100673
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200674 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700675
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700676 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700677 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700678 print_func(data, " <attributes scope='global'>%s", newline);
679
680 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
681 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700682 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700683
684 if (~cmd->attr & cmd_attr_desc[i].value)
685 continue;
686
687 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700688 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700689 xml_att_desc, newline);
690 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700691
692 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
693 if (flag != '.')
694 print_func(data, " flag='%c'", flag);
695 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700696 }
697
698 print_func(data, " </attributes>%s", newline);
699 }
700
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700701 /* Print application specific attributes and their description */
702 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700703 const char * const *desc;
704 const char *letters;
705
706 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
707 print_func(data, " <attributes scope='library'>%s", newline);
708 letters = &cmd_lib_attr_letters[0];
709 desc = &cmd_lib_attr_desc[0];
710 } else {
711 print_func(data, " <attributes scope='application'>%s", newline);
712 letters = &host.app_info->usr_attr_letters[0];
713 desc = &host.app_info->usr_attr_desc[0];
714 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700715
716 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
717 char *xml_att_desc;
718 char flag;
719
720 /* Skip attribute if *not* set */
721 if (~cmd->usrattr & (1 << i))
722 continue;
723
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700724 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700725 print_func(data, " <attribute doc='%s'", xml_att_desc);
726 talloc_free(xml_att_desc);
727
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700728 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700729 print_func(data, " flag='%c'", flag);
730 print_func(data, " />%s", newline);
731 }
732
733 print_func(data, " </attributes>%s", newline);
734 }
735
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200736 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100737
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700738 for (i = 0; i < vector_count(cmd->strvec); ++i) {
739 vector descvec = vector_slot(cmd->strvec, i);
740 int j;
741 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100742 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700743 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100744 if (desc == NULL)
745 continue;
746
747 xml_param = xml_escape(desc->cmd);
748 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200749 print_func(data, " <param name='%s' doc='%s' />%s",
750 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100751 talloc_free(xml_param);
752 talloc_free(xml_doc);
753 }
754 }
755
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200756 print_func(data, " </params>%s", newline);
757 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100758
759 talloc_free(xml_string);
760 return 0;
761}
762
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200763static bool vty_command_is_common(struct cmd_element *cmd);
764
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100765/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200766 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100767 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200768static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100769{
770 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200771 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100772
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200773 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100774
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200775 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200776 print_func(data, " <node id='_common_cmds_'>%s", newline);
777 print_func(data, " <name>Common Commands</name>%s", newline);
778 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
779 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200780 for (i = 0; i < vector_active(cmdvec); ++i) {
781 struct cmd_node *cnode;
782 cnode = vector_slot(cmdvec, i);
783 if (!cnode)
784 continue;
785 if (cnode->node != CONFIG_NODE)
786 continue;
787
788 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
789 struct cmd_element *elem;
790 elem = vector_slot(cnode->cmd_vector, j);
791 if (!vty_command_is_common(elem))
792 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200793 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200794 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200795 }
796 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200797 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200798
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100799 for (i = 0; i < vector_active(cmdvec); ++i) {
800 struct cmd_node *cnode;
801 cnode = vector_slot(cmdvec, i);
802 if (!cnode)
803 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200804 if (vector_active(cnode->cmd_vector) < 1)
805 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100806
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200807 /* De-dup node IDs: how many times has this same name been used before? Count the first
808 * occurence as _1 and omit that first suffix, so that the first occurence is called
809 * 'name', the second becomes 'name_2', then 'name_3', ... */
810 same_name_count = 1;
811 for (j = 0; j < i; ++j) {
812 struct cmd_node *cnode2;
813 cnode2 = vector_slot(cmdvec, j);
814 if (!cnode2)
815 continue;
816 if (strcmp(cnode->name, cnode2->name) == 0)
817 same_name_count ++;
818 }
819
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200820 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200821 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200822 print_func(data, "_%d", same_name_count);
823 print_func(data, "'>%s", newline);
824 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100825
826 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
827 struct cmd_element *elem;
828 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200829 if (vty_command_is_common(elem))
830 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200831 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200832 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100833 }
834
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200835 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100836 }
837
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200838 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100839
840 return 0;
841}
842
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200843static int print_func_vty(void *data, const char *format, ...)
844{
845 struct vty *vty = data;
846 va_list args;
847 int rc;
848 va_start(args, format);
849 rc = vty_out_va(vty, format, args);
850 va_end(args);
851 return rc;
852}
853
854static int vty_dump_xml_ref_to_vty(struct vty *vty)
855{
856 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
857}
858
859static int print_func_stream(void *data, const char *format, ...)
860{
861 va_list args;
862 int rc;
863 va_start(args, format);
864 rc = vfprintf((FILE*)data, format, args);
865 va_end(args);
866 return rc;
867}
868
869/*! Print the XML reference of all VTY nodes to the given stream.
870 */
871int vty_dump_xml_ref(FILE *stream)
872{
873 return vty_dump_nodes(print_func_stream, stream, "\n");
874}
875
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200876/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100877static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
878{
879 int i;
880
881 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
882 struct cmd_element *elem;
883 elem = vector_slot(cnode->cmd_vector, i);
884 if (!elem->string)
885 continue;
886 if (!strcmp(elem->string, cmdstring))
887 return 1;
888 }
889 return 0;
890}
891
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200892/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200893 * \param[in] ntype Node Type
894 * \param[cmd] element to be installed
895 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000896void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200897{
898 struct cmd_node *cnode;
899
900 cnode = vector_slot(cmdvec, ntype);
901
Harald Weltea99d45a2015-11-12 13:48:23 +0100902 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100903 /* ensure no _identical_ command has been registered at this
904 * node so far */
905 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200906
907 vector_set(cnode->cmd_vector, cmd);
908
909 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
910 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
911}
912
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700913/*! Install a library command into a node
914 * \param[in] ntype Node Type
915 * \param[in] cmd element to be installed
916 */
917void install_lib_element(int ntype, struct cmd_element *cmd)
918{
919 cmd->attr |= CMD_ATTR_LIB_COMMAND;
920 install_element(ntype, cmd);
921}
922
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200923/* Install a command into VIEW and ENABLE node */
924void install_element_ve(struct cmd_element *cmd)
925{
926 install_element(VIEW_NODE, cmd);
927 install_element(ENABLE_NODE, cmd);
928}
929
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700930/* Install a library command into VIEW and ENABLE node */
931void install_lib_element_ve(struct cmd_element *cmd)
932{
933 cmd->attr |= CMD_ATTR_LIB_COMMAND;
934 install_element_ve(cmd);
935}
936
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200937#ifdef VTY_CRYPT_PW
938static unsigned char itoa64[] =
939 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
940
941static void to64(char *s, long v, int n)
942{
943 while (--n >= 0) {
944 *s++ = itoa64[v & 0x3f];
945 v >>= 6;
946 }
947}
948
949static char *zencrypt(const char *passwd)
950{
951 char salt[6];
952 struct timeval tv;
953 char *crypt(const char *, const char *);
954
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200955 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200956
957 to64(&salt[0], random(), 3);
958 to64(&salt[3], tv.tv_usec, 3);
959 salt[5] = '\0';
960
961 return crypt(passwd, salt);
962}
963#endif
964
965/* This function write configuration of this host. */
966static int config_write_host(struct vty *vty)
967{
968 if (host.name)
969 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
970
971 if (host.encrypt) {
972 if (host.password_encrypt)
973 vty_out(vty, "password 8 %s%s", host.password_encrypt,
974 VTY_NEWLINE);
975 if (host.enable_encrypt)
976 vty_out(vty, "enable password 8 %s%s",
977 host.enable_encrypt, VTY_NEWLINE);
978 } else {
979 if (host.password)
980 vty_out(vty, "password %s%s", host.password,
981 VTY_NEWLINE);
982 if (host.enable)
983 vty_out(vty, "enable password %s%s", host.enable,
984 VTY_NEWLINE);
985 }
986
987 if (host.advanced)
988 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
989
990 if (host.encrypt)
991 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
992
993 if (host.lines >= 0)
994 vty_out(vty, "service terminal-length %d%s", host.lines,
995 VTY_NEWLINE);
996
997 if (host.motdfile)
998 vty_out(vty, "banner motd file %s%s", host.motdfile,
999 VTY_NEWLINE);
1000 else if (!host.motd)
1001 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1002
1003 return 1;
1004}
1005
1006/* Utility function for getting command vector. */
1007static vector cmd_node_vector(vector v, enum node_type ntype)
1008{
1009 struct cmd_node *cnode = vector_slot(v, ntype);
1010 return cnode->cmd_vector;
1011}
1012
1013/* Completion match types. */
1014enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001015 NO_MATCH = 0,
1016 ANY_MATCH,
1017 EXTEND_MATCH,
1018 IPV4_PREFIX_MATCH,
1019 IPV4_MATCH,
1020 IPV6_PREFIX_MATCH,
1021 IPV6_MATCH,
1022 RANGE_MATCH,
1023 VARARG_MATCH,
1024 PARTLY_MATCH,
1025 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001026};
1027
1028static enum match_type cmd_ipv4_match(const char *str)
1029{
1030 const char *sp;
1031 int dots = 0, nums = 0;
1032 char buf[4];
1033
1034 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001035 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001036
1037 for (;;) {
1038 memset(buf, 0, sizeof(buf));
1039 sp = str;
1040 while (*str != '\0') {
1041 if (*str == '.') {
1042 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001043 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001044
1045 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001046 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001047
1048 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001049 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001050
1051 dots++;
1052 break;
1053 }
1054 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001055 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001056
1057 str++;
1058 }
1059
1060 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001061 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001062
1063 strncpy(buf, sp, str - sp);
1064 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001065 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001066
1067 nums++;
1068
1069 if (*str == '\0')
1070 break;
1071
1072 str++;
1073 }
1074
1075 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001076 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001077
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001078 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001079}
1080
1081static enum match_type cmd_ipv4_prefix_match(const char *str)
1082{
1083 const char *sp;
1084 int dots = 0;
1085 char buf[4];
1086
1087 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001088 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001089
1090 for (;;) {
1091 memset(buf, 0, sizeof(buf));
1092 sp = str;
1093 while (*str != '\0' && *str != '/') {
1094 if (*str == '.') {
1095 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001096 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001097
1098 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001099 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001100
1101 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001102 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001103
1104 dots++;
1105 break;
1106 }
1107
1108 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001109 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001110
1111 str++;
1112 }
1113
1114 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001115 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116
1117 strncpy(buf, sp, str - sp);
1118 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001119 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001120
1121 if (dots == 3) {
1122 if (*str == '/') {
1123 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001124 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125
1126 str++;
1127 break;
1128 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001129 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130 }
1131
1132 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001133 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001134
1135 str++;
1136 }
1137
1138 sp = str;
1139 while (*str != '\0') {
1140 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001141 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001142
1143 str++;
1144 }
1145
1146 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001149 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001150}
1151
1152#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1153#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1154#define STATE_START 1
1155#define STATE_COLON 2
1156#define STATE_DOUBLE 3
1157#define STATE_ADDR 4
1158#define STATE_DOT 5
1159#define STATE_SLASH 6
1160#define STATE_MASK 7
1161
1162#ifdef HAVE_IPV6
1163
1164static enum match_type cmd_ipv6_match(const char *str)
1165{
1166 int state = STATE_START;
1167 int colons = 0, nums = 0, double_colon = 0;
1168 const char *sp = NULL;
1169 struct sockaddr_in6 sin6_dummy;
1170 int ret;
1171
1172 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001173 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001174
1175 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001176 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001177
1178 /* use inet_pton that has a better support,
1179 * for example inet_pton can support the automatic addresses:
1180 * ::1.2.3.4
1181 */
1182 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1183
1184 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001185 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186
1187 while (*str != '\0') {
1188 switch (state) {
1189 case STATE_START:
1190 if (*str == ':') {
1191 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001192 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001193 colons--;
1194 state = STATE_COLON;
1195 } else {
1196 sp = str;
1197 state = STATE_ADDR;
1198 }
1199
1200 continue;
1201 case STATE_COLON:
1202 colons++;
1203 if (*(str + 1) == ':')
1204 state = STATE_DOUBLE;
1205 else {
1206 sp = str + 1;
1207 state = STATE_ADDR;
1208 }
1209 break;
1210 case STATE_DOUBLE:
1211 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001212 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001213
1214 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001215 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001216 else {
1217 if (*(str + 1) != '\0')
1218 colons++;
1219 sp = str + 1;
1220 state = STATE_ADDR;
1221 }
1222
1223 double_colon++;
1224 nums++;
1225 break;
1226 case STATE_ADDR:
1227 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1228 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001229 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001230
1231 nums++;
1232 state = STATE_COLON;
1233 }
1234 if (*(str + 1) == '.')
1235 state = STATE_DOT;
1236 break;
1237 case STATE_DOT:
1238 state = STATE_ADDR;
1239 break;
1240 default:
1241 break;
1242 }
1243
1244 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001245 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001246
1247 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001248 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001249
1250 str++;
1251 }
1252
1253#if 0
1254 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001255 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001256#endif /* 0 */
1257
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001258 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259}
1260
1261static enum match_type cmd_ipv6_prefix_match(const char *str)
1262{
1263 int state = STATE_START;
1264 int colons = 0, nums = 0, double_colon = 0;
1265 int mask;
1266 const char *sp = NULL;
1267 char *endptr = NULL;
1268
1269 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001270 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001271
1272 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001273 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001274
1275 while (*str != '\0' && state != STATE_MASK) {
1276 switch (state) {
1277 case STATE_START:
1278 if (*str == ':') {
1279 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001280 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001281 colons--;
1282 state = STATE_COLON;
1283 } else {
1284 sp = str;
1285 state = STATE_ADDR;
1286 }
1287
1288 continue;
1289 case STATE_COLON:
1290 colons++;
1291 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001292 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001293 else if (*(str + 1) == ':')
1294 state = STATE_DOUBLE;
1295 else {
1296 sp = str + 1;
1297 state = STATE_ADDR;
1298 }
1299 break;
1300 case STATE_DOUBLE:
1301 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001302 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001303
1304 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001305 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001306 else {
1307 if (*(str + 1) != '\0' && *(str + 1) != '/')
1308 colons++;
1309 sp = str + 1;
1310
1311 if (*(str + 1) == '/')
1312 state = STATE_SLASH;
1313 else
1314 state = STATE_ADDR;
1315 }
1316
1317 double_colon++;
1318 nums += 1;
1319 break;
1320 case STATE_ADDR:
1321 if (*(str + 1) == ':' || *(str + 1) == '.'
1322 || *(str + 1) == '\0' || *(str + 1) == '/') {
1323 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001324 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001325
1326 for (; sp <= str; sp++)
1327 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001328 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001329
1330 nums++;
1331
1332 if (*(str + 1) == ':')
1333 state = STATE_COLON;
1334 else if (*(str + 1) == '.')
1335 state = STATE_DOT;
1336 else if (*(str + 1) == '/')
1337 state = STATE_SLASH;
1338 }
1339 break;
1340 case STATE_DOT:
1341 state = STATE_ADDR;
1342 break;
1343 case STATE_SLASH:
1344 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001345 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001346
1347 state = STATE_MASK;
1348 break;
1349 default:
1350 break;
1351 }
1352
1353 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001354 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001355
1356 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001357 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001358
1359 str++;
1360 }
1361
1362 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001363 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001364
1365 mask = strtol(str, &endptr, 10);
1366 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001367 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001368
1369 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001370 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001371
1372/* I don't know why mask < 13 makes command match partly.
1373 Forgive me to make this comments. I Want to set static default route
1374 because of lack of function to originate default in ospf6d; sorry
1375 yasu
1376 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001377 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378*/
1379
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001380 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001381}
1382
1383#endif /* HAVE_IPV6 */
1384
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001385
1386#if ULONG_MAX == 18446744073709551615UL
1387#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1388#elif ULONG_MAX == 4294967295UL
1389#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1390#else
1391#error "ULONG_MAX not defined!"
1392#endif
1393
1394#if LONG_MAX == 9223372036854775807L
1395#define DECIMAL_STRLEN_MAX_SIGNED 19
1396#elif LONG_MAX == 2147483647L
1397#define DECIMAL_STRLEN_MAX_SIGNED 10
1398#else
1399#error "LONG_MAX not defined!"
1400#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001401
1402static int cmd_range_match(const char *range, const char *str)
1403{
1404 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001405 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001406 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001407
1408 if (str == NULL)
1409 return 1;
1410
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001411 if (range[1] == '-') {
1412 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001413
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001414 val = strtol(str, &endptr, 10);
1415 if (*endptr != '\0')
1416 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001417
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001418 range += 2;
1419 p = strchr(range, '-');
1420 if (p == NULL)
1421 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001422 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001423 return 0;
1424 strncpy(buf, range, p - range);
1425 buf[p - range] = '\0';
1426 min = -strtol(buf, &endptr, 10);
1427 if (*endptr != '\0')
1428 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001429
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001430 range = p + 1;
1431 p = strchr(range, '>');
1432 if (p == NULL)
1433 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001434 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001435 return 0;
1436 strncpy(buf, range, p - range);
1437 buf[p - range] = '\0';
1438 max = strtol(buf, &endptr, 10);
1439 if (*endptr != '\0')
1440 return 0;
1441
1442 if (val < min || val > max)
1443 return 0;
1444 } else {
1445 unsigned long min, max, val;
1446
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001447 if (str[0] == '-')
1448 return 0;
1449
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001450 val = strtoul(str, &endptr, 10);
1451 if (*endptr != '\0')
1452 return 0;
1453
1454 range++;
1455 p = strchr(range, '-');
1456 if (p == NULL)
1457 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001458 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001459 return 0;
1460 strncpy(buf, range, p - range);
1461 buf[p - range] = '\0';
1462 min = strtoul(buf, &endptr, 10);
1463 if (*endptr != '\0')
1464 return 0;
1465
1466 range = p + 1;
1467 p = strchr(range, '>');
1468 if (p == NULL)
1469 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001470 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001471 return 0;
1472 strncpy(buf, range, p - range);
1473 buf[p - range] = '\0';
1474 max = strtoul(buf, &endptr, 10);
1475 if (*endptr != '\0')
1476 return 0;
1477
1478 if (val < min || val > max)
1479 return 0;
1480 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001481
1482 return 1;
1483}
1484
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001485/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001486static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001488 /* we've got "[blah]". We want to strip off the []s and redo the
1489 * match check for "blah"
1490 */
1491 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001492
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001493 if (len < 3)
1494 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001495
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001496 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001497}
1498
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001499static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001500cmd_match(const char *str, const char *command,
1501 enum match_type min, bool recur)
1502{
1503
1504 if (recur && CMD_OPTION(str))
1505 {
1506 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001507 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001508
1509 /* this would be a bug in a command, however handle it gracefully
1510 * as it we only discover it if a user tries to run it
1511 */
1512 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001513 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001514
1515 ret = cmd_match(tmp, command, min, false);
1516
1517 talloc_free(tmp);
1518
1519 return ret;
1520 }
1521 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001522 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001523 else if (CMD_RANGE(str))
1524 {
1525 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001526 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001527 }
1528#ifdef HAVE_IPV6
1529 else if (CMD_IPV6(str))
1530 {
1531 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001532 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001533 }
1534 else if (CMD_IPV6_PREFIX(str))
1535 {
1536 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001537 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001538 }
1539#endif /* HAVE_IPV6 */
1540 else if (CMD_IPV4(str))
1541 {
1542 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001543 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001544 }
1545 else if (CMD_IPV4_PREFIX(str))
1546 {
1547 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001548 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001549 }
1550 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001551 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001552 else if (strncmp(command, str, strlen(command)) == 0)
1553 {
1554 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001555 return EXACT_MATCH;
1556 else if (PARTLY_MATCH >= min)
1557 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001558 }
1559
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001560 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001561}
1562
1563/* Filter vector at the specified index and by the given command string, to
1564 * the desired matching level (thus allowing part matches), and return match
1565 * type flag.
1566 */
1567static enum match_type
1568cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001569{
1570 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001571 struct cmd_element *cmd_element;
1572 enum match_type match_type;
1573 vector descvec;
1574 struct desc *desc;
1575
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001576 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577
1578 /* If command and cmd_element string does not match set NULL to vector */
1579 for (i = 0; i < vector_active(v); i++)
1580 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001581 if (index >= vector_active(cmd_element->strvec))
1582 vector_slot(v, i) = NULL;
1583 else {
1584 unsigned int j;
1585 int matched = 0;
1586
1587 descvec =
1588 vector_slot(cmd_element->strvec, index);
1589
1590 for (j = 0; j < vector_active(descvec); j++)
1591 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001592 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001593
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001594 ret = cmd_match (desc->cmd, command, level, true);
1595
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001596 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001597 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001598
1599 if (match_type < ret)
1600 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001601 }
1602 if (!matched)
1603 vector_slot(v, i) = NULL;
1604 }
1605 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001606
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001607 if (match_type == NO_MATCH)
1608 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001609
1610 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1611 * go again and filter out commands whose argument (at this index) is
1612 * 'weaker'. E.g., if we have 2 commands:
1613 *
1614 * foo bar <1-255>
1615 * foo bar BLAH
1616 *
1617 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001618 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001619 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1620 *
1621 * If we don't do a 2nd pass and filter it out, the higher-layers will
1622 * consider this to be ambiguous.
1623 */
1624 for (i = 0; i < vector_active(v); i++)
1625 if ((cmd_element = vector_slot(v, i)) != NULL) {
1626 if (index >= vector_active(cmd_element->strvec))
1627 vector_slot(v, i) = NULL;
1628 else {
1629 unsigned int j;
1630 int matched = 0;
1631
1632 descvec =
1633 vector_slot(cmd_element->strvec, index);
1634
1635 for (j = 0; j < vector_active(descvec); j++)
1636 if ((desc = vector_slot(descvec, j))) {
1637 enum match_type ret;
1638
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001639 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001640
1641 if (ret >= match_type)
1642 matched++;
1643 }
1644 if (!matched)
1645 vector_slot(v, i) = NULL;
1646 }
1647 }
1648
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001649 return match_type;
1650}
1651
1652/* Check ambiguous match */
1653static int
1654is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1655{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001656 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001657 unsigned int i;
1658 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001659 struct cmd_element *cmd_element;
1660 const char *matched = NULL;
1661 vector descvec;
1662 struct desc *desc;
1663
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001664 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1665 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1666 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1667 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1668 * that case, the string must remain allocated until this function exits or another match comes
1669 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1670 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1671 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1672 void *cmd_deopt_ctx = NULL;
1673
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001674 for (i = 0; i < vector_active(v); i++) {
1675 cmd_element = vector_slot(v, i);
1676 if (!cmd_element)
1677 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001678
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001679 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001680
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001681 descvec = vector_slot(cmd_element->strvec, index);
1682
1683 for (j = 0; j < vector_active(descvec); j++) {
1684 desc = vector_slot(descvec, j);
1685 if (!desc)
1686 continue;
1687
1688 enum match_type mtype;
1689 const char *str = desc->cmd;
1690
1691 if (CMD_OPTION(str)) {
1692 if (!cmd_deopt_ctx)
1693 cmd_deopt_ctx =
1694 talloc_named_const(tall_vty_cmd_ctx, 0,
1695 __func__);
1696 str = cmd_deopt(cmd_deopt_ctx, str);
1697 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001698 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001699 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001700
1701 switch (type) {
1702 case EXACT_MATCH:
1703 if (!(CMD_VARIABLE (str))
1704 && strcmp(command, str) == 0)
1705 match++;
1706 break;
1707 case PARTLY_MATCH:
1708 if (!(CMD_VARIABLE (str))
1709 && strncmp(command, str, strlen (command)) == 0)
1710 {
1711 if (matched
1712 && strcmp(matched,
1713 str) != 0) {
1714 ret = 1; /* There is ambiguous match. */
1715 goto free_and_return;
1716 } else
1717 matched = str;
1718 match++;
1719 }
1720 break;
1721 case RANGE_MATCH:
1722 if (cmd_range_match
1723 (str, command)) {
1724 if (matched
1725 && strcmp(matched,
1726 str) != 0) {
1727 ret = 1;
1728 goto free_and_return;
1729 } else
1730 matched = str;
1731 match++;
1732 }
1733 break;
1734#ifdef HAVE_IPV6
1735 case IPV6_MATCH:
1736 if (CMD_IPV6(str))
1737 match++;
1738 break;
1739 case IPV6_PREFIX_MATCH:
1740 if ((mtype =
1741 cmd_ipv6_prefix_match
1742 (command)) != NO_MATCH) {
1743 if (mtype == PARTLY_MATCH) {
1744 ret = 2; /* There is incomplete match. */
1745 goto free_and_return;
1746 }
1747
1748 match++;
1749 }
1750 break;
1751#endif /* HAVE_IPV6 */
1752 case IPV4_MATCH:
1753 if (CMD_IPV4(str))
1754 match++;
1755 break;
1756 case IPV4_PREFIX_MATCH:
1757 if ((mtype =
1758 cmd_ipv4_prefix_match
1759 (command)) != NO_MATCH) {
1760 if (mtype == PARTLY_MATCH) {
1761 ret = 2; /* There is incomplete match. */
1762 goto free_and_return;
1763 }
1764
1765 match++;
1766 }
1767 break;
1768 case EXTEND_MATCH:
1769 if (CMD_VARIABLE (str))
1770 match++;
1771 break;
1772 case NO_MATCH:
1773 default:
1774 break;
1775 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001776 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001777 if (!match)
1778 vector_slot(v, i) = NULL;
1779 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001780
1781free_and_return:
1782 if (cmd_deopt_ctx)
1783 talloc_free(cmd_deopt_ctx);
1784 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001785}
1786
1787/* If src matches dst return dst string, otherwise return NULL */
1788static const char *cmd_entry_function(const char *src, const char *dst)
1789{
1790 /* Skip variable arguments. */
1791 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1792 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1793 return NULL;
1794
1795 /* In case of 'command \t', given src is NULL string. */
1796 if (src == NULL)
1797 return dst;
1798
1799 /* Matched with input string. */
1800 if (strncmp(src, dst, strlen(src)) == 0)
1801 return dst;
1802
1803 return NULL;
1804}
1805
1806/* If src matches dst return dst string, otherwise return NULL */
1807/* This version will return the dst string always if it is
1808 CMD_VARIABLE for '?' key processing */
1809static const char *cmd_entry_function_desc(const char *src, const char *dst)
1810{
1811 if (CMD_VARARG(dst))
1812 return dst;
1813
1814 if (CMD_RANGE(dst)) {
1815 if (cmd_range_match(dst, src))
1816 return dst;
1817 else
1818 return NULL;
1819 }
1820#ifdef HAVE_IPV6
1821 if (CMD_IPV6(dst)) {
1822 if (cmd_ipv6_match(src))
1823 return dst;
1824 else
1825 return NULL;
1826 }
1827
1828 if (CMD_IPV6_PREFIX(dst)) {
1829 if (cmd_ipv6_prefix_match(src))
1830 return dst;
1831 else
1832 return NULL;
1833 }
1834#endif /* HAVE_IPV6 */
1835
1836 if (CMD_IPV4(dst)) {
1837 if (cmd_ipv4_match(src))
1838 return dst;
1839 else
1840 return NULL;
1841 }
1842
1843 if (CMD_IPV4_PREFIX(dst)) {
1844 if (cmd_ipv4_prefix_match(src))
1845 return dst;
1846 else
1847 return NULL;
1848 }
1849
1850 /* Optional or variable commands always match on '?' */
1851 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1852 return dst;
1853
1854 /* In case of 'command \t', given src is NULL string. */
1855 if (src == NULL)
1856 return dst;
1857
1858 if (strncmp(src, dst, strlen(src)) == 0)
1859 return dst;
1860 else
1861 return NULL;
1862}
1863
1864/* Check same string element existence. If it isn't there return
1865 1. */
1866static int cmd_unique_string(vector v, const char *str)
1867{
1868 unsigned int i;
1869 char *match;
1870
1871 for (i = 0; i < vector_active(v); i++)
1872 if ((match = vector_slot(v, i)) != NULL)
1873 if (strcmp(match, str) == 0)
1874 return 0;
1875 return 1;
1876}
1877
1878/* Compare string to description vector. If there is same string
1879 return 1 else return 0. */
1880static int desc_unique_string(vector v, const char *str)
1881{
1882 unsigned int i;
1883 struct desc *desc;
1884
1885 for (i = 0; i < vector_active(v); i++)
1886 if ((desc = vector_slot(v, i)) != NULL)
1887 if (strcmp(desc->cmd, str) == 0)
1888 return 1;
1889 return 0;
1890}
1891
1892static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1893{
1894 if (first_word != NULL &&
1895 node != AUTH_NODE &&
1896 node != VIEW_NODE &&
1897 node != AUTH_ENABLE_NODE &&
1898 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1899 return 1;
1900 return 0;
1901}
1902
1903/* '?' describe command support. */
1904static vector
1905cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1906{
1907 unsigned int i;
1908 vector cmd_vector;
1909#define INIT_MATCHVEC_SIZE 10
1910 vector matchvec;
1911 struct cmd_element *cmd_element;
1912 unsigned int index;
1913 int ret;
1914 enum match_type match;
1915 char *command;
1916 static struct desc desc_cr = { "<cr>", "" };
1917
1918 /* Set index. */
1919 if (vector_active(vline) == 0) {
1920 *status = CMD_ERR_NO_MATCH;
1921 return NULL;
1922 } else
1923 index = vector_active(vline) - 1;
1924
1925 /* Make copy vector of current node's command vector. */
1926 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1927
1928 /* Prepare match vector */
1929 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1930
1931 /* Filter commands. */
1932 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001933 for (i = 0; i < index; i++) {
1934 command = vector_slot(vline, i);
1935 if (!command)
1936 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001937
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001938 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001939
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001940 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001941 struct cmd_element *cmd_element;
1942 vector descvec;
1943 unsigned int j, k;
1944
1945 for (j = 0; j < vector_active(cmd_vector); j++)
1946 if ((cmd_element =
1947 vector_slot(cmd_vector, j)) != NULL
1948 &&
1949 (vector_active(cmd_element->strvec))) {
1950 descvec =
1951 vector_slot(cmd_element->
1952 strvec,
1953 vector_active
1954 (cmd_element->
1955 strvec) - 1);
1956 for (k = 0;
1957 k < vector_active(descvec);
1958 k++) {
1959 struct desc *desc =
1960 vector_slot(descvec,
1961 k);
1962 vector_set(matchvec,
1963 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001964 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001965 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001966
Harald Welte80d30fe2013-02-12 11:08:57 +01001967 vector_set(matchvec, &desc_cr);
1968 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001969
Harald Welte80d30fe2013-02-12 11:08:57 +01001970 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001971 }
1972
Harald Welte80d30fe2013-02-12 11:08:57 +01001973 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1974 match)) == 1) {
1975 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001976 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001977 *status = CMD_ERR_AMBIGUOUS;
1978 return NULL;
1979 } else if (ret == 2) {
1980 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001981 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001982 *status = CMD_ERR_NO_MATCH;
1983 return NULL;
1984 }
1985 }
1986
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001987 /* Prepare match vector */
1988 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1989
1990 /* Make sure that cmd_vector is filtered based on current word */
1991 command = vector_slot(vline, index);
1992 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001993 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001994
1995 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001996 for (i = 0; i < vector_active(cmd_vector); i++) {
1997 const char *string = NULL;
1998 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001999
Harald Welte80d30fe2013-02-12 11:08:57 +01002000 cmd_element = vector_slot(cmd_vector, i);
2001 if (!cmd_element)
2002 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002003
Harald Welted17aa592013-02-12 11:11:34 +01002004 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
2005 continue;
2006
Harald Welte80d30fe2013-02-12 11:08:57 +01002007 strvec = cmd_element->strvec;
2008
2009 /* if command is NULL, index may be equal to vector_active */
2010 if (command && index >= vector_active(strvec))
2011 vector_slot(cmd_vector, i) = NULL;
2012 else {
2013 /* Check if command is completed. */
2014 if (command == NULL
2015 && index == vector_active(strvec)) {
2016 string = "<cr>";
2017 if (!desc_unique_string(matchvec, string))
2018 vector_set(matchvec, &desc_cr);
2019 } else {
2020 unsigned int j;
2021 vector descvec = vector_slot(strvec, index);
2022 struct desc *desc;
2023
2024 for (j = 0; j < vector_active(descvec); j++) {
2025 desc = vector_slot(descvec, j);
2026 if (!desc)
2027 continue;
2028 string = cmd_entry_function_desc
2029 (command, desc->cmd);
2030 if (!string)
2031 continue;
2032 /* Uniqueness check */
2033 if (!desc_unique_string(matchvec, string))
2034 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002035 }
2036 }
2037 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002038 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002039 vector_free(cmd_vector);
2040
2041 if (vector_slot(matchvec, 0) == NULL) {
2042 vector_free(matchvec);
2043 *status = CMD_ERR_NO_MATCH;
2044 } else
2045 *status = CMD_SUCCESS;
2046
2047 return matchvec;
2048}
2049
2050vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2051{
2052 vector ret;
2053
2054 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2055 enum node_type onode;
2056 vector shifted_vline;
2057 unsigned int index;
2058
2059 onode = vty->node;
2060 vty->node = ENABLE_NODE;
2061 /* We can try it on enable node, cos' the vty is authenticated */
2062
2063 shifted_vline = vector_init(vector_count(vline));
2064 /* use memcpy? */
2065 for (index = 1; index < vector_active(vline); index++) {
2066 vector_set_index(shifted_vline, index - 1,
2067 vector_lookup(vline, index));
2068 }
2069
2070 ret = cmd_describe_command_real(shifted_vline, vty, status);
2071
2072 vector_free(shifted_vline);
2073 vty->node = onode;
2074 return ret;
2075 }
2076
2077 return cmd_describe_command_real(vline, vty, status);
2078}
2079
2080/* Check LCD of matched command. */
2081static int cmd_lcd(char **matched)
2082{
2083 int i;
2084 int j;
2085 int lcd = -1;
2086 char *s1, *s2;
2087 char c1, c2;
2088
2089 if (matched[0] == NULL || matched[1] == NULL)
2090 return 0;
2091
2092 for (i = 1; matched[i] != NULL; i++) {
2093 s1 = matched[i - 1];
2094 s2 = matched[i];
2095
2096 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2097 if (c1 != c2)
2098 break;
2099
2100 if (lcd < 0)
2101 lcd = j;
2102 else {
2103 if (lcd > j)
2104 lcd = j;
2105 }
2106 }
2107 return lcd;
2108}
2109
2110/* Command line completion support. */
2111static char **cmd_complete_command_real(vector vline, struct vty *vty,
2112 int *status)
2113{
2114 unsigned int i;
2115 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2116#define INIT_MATCHVEC_SIZE 10
2117 vector matchvec;
2118 struct cmd_element *cmd_element;
2119 unsigned int index;
2120 char **match_str;
2121 struct desc *desc;
2122 vector descvec;
2123 char *command;
2124 int lcd;
2125
2126 if (vector_active(vline) == 0) {
2127 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002128 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002129 return NULL;
2130 } else
2131 index = vector_active(vline) - 1;
2132
2133 /* First, filter by preceeding command string */
2134 for (i = 0; i < index; i++)
2135 if ((command = vector_slot(vline, i))) {
2136 enum match_type match;
2137 int ret;
2138
2139 /* First try completion match, if there is exactly match return 1 */
2140 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002141 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002142
2143 /* If there is exact match then filter ambiguous match else check
2144 ambiguousness. */
2145 if ((ret =
2146 is_cmd_ambiguous(command, cmd_vector, i,
2147 match)) == 1) {
2148 vector_free(cmd_vector);
2149 *status = CMD_ERR_AMBIGUOUS;
2150 return NULL;
2151 }
2152 /*
2153 else if (ret == 2)
2154 {
2155 vector_free (cmd_vector);
2156 *status = CMD_ERR_NO_MATCH;
2157 return NULL;
2158 }
2159 */
2160 }
2161
2162 /* Prepare match vector. */
2163 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2164
2165 /* Now we got into completion */
2166 for (i = 0; i < vector_active(cmd_vector); i++)
2167 if ((cmd_element = vector_slot(cmd_vector, i))) {
2168 const char *string;
2169 vector strvec = cmd_element->strvec;
2170
2171 /* Check field length */
2172 if (index >= vector_active(strvec))
2173 vector_slot(cmd_vector, i) = NULL;
2174 else {
2175 unsigned int j;
2176
2177 descvec = vector_slot(strvec, index);
2178 for (j = 0; j < vector_active(descvec); j++)
2179 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002180 const char *cmd = desc->cmd;
2181 char *tmp = NULL;
2182
2183 if (CMD_OPTION(desc->cmd)) {
2184 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2185 cmd = tmp;
2186 }
2187 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002188 if (cmd_unique_string (matchvec, string))
2189 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002190 if (tmp)
2191 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002192 }
2193 }
2194 }
2195
2196 /* We don't need cmd_vector any more. */
2197 vector_free(cmd_vector);
2198
2199 /* No matched command */
2200 if (vector_slot(matchvec, 0) == NULL) {
2201 vector_free(matchvec);
2202
2203 /* In case of 'command \t' pattern. Do you need '?' command at
2204 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002205 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002206 *status = CMD_ERR_NOTHING_TODO;
2207 else
2208 *status = CMD_ERR_NO_MATCH;
2209 return NULL;
2210 }
2211
2212 /* Only one matched */
2213 if (vector_slot(matchvec, 1) == NULL) {
2214 match_str = (char **)matchvec->index;
2215 vector_only_wrapper_free(matchvec);
2216 *status = CMD_COMPLETE_FULL_MATCH;
2217 return match_str;
2218 }
2219 /* Make it sure last element is NULL. */
2220 vector_set(matchvec, NULL);
2221
2222 /* Check LCD of matched strings. */
2223 if (vector_slot(vline, index) != NULL) {
2224 lcd = cmd_lcd((char **)matchvec->index);
2225
2226 if (lcd) {
2227 int len = strlen(vector_slot(vline, index));
2228
2229 if (len < lcd) {
2230 char *lcdstr;
2231
2232 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2233 "complete-lcdstr");
2234 memcpy(lcdstr, matchvec->index[0], lcd);
2235 lcdstr[lcd] = '\0';
2236
2237 /* match_str = (char **) &lcdstr; */
2238
2239 /* Free matchvec. */
2240 for (i = 0; i < vector_active(matchvec); i++) {
2241 if (vector_slot(matchvec, i))
2242 talloc_free(vector_slot(matchvec, i));
2243 }
2244 vector_free(matchvec);
2245
2246 /* Make new matchvec. */
2247 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2248 vector_set(matchvec, lcdstr);
2249 match_str = (char **)matchvec->index;
2250 vector_only_wrapper_free(matchvec);
2251
2252 *status = CMD_COMPLETE_MATCH;
2253 return match_str;
2254 }
2255 }
2256 }
2257
2258 match_str = (char **)matchvec->index;
2259 vector_only_wrapper_free(matchvec);
2260 *status = CMD_COMPLETE_LIST_MATCH;
2261 return match_str;
2262}
2263
2264char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2265{
2266 char **ret;
2267
2268 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2269 enum node_type onode;
2270 vector shifted_vline;
2271 unsigned int index;
2272
2273 onode = vty->node;
2274 vty->node = ENABLE_NODE;
2275 /* We can try it on enable node, cos' the vty is authenticated */
2276
2277 shifted_vline = vector_init(vector_count(vline));
2278 /* use memcpy? */
2279 for (index = 1; index < vector_active(vline); index++) {
2280 vector_set_index(shifted_vline, index - 1,
2281 vector_lookup(vline, index));
2282 }
2283
2284 ret = cmd_complete_command_real(shifted_vline, vty, status);
2285
2286 vector_free(shifted_vline);
2287 vty->node = onode;
2288 return ret;
2289 }
2290
2291 return cmd_complete_command_real(vline, vty, status);
2292}
2293
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002294static struct vty_parent_node *vty_parent(struct vty *vty)
2295{
2296 return llist_first_entry_or_null(&vty->parent_nodes,
2297 struct vty_parent_node,
2298 entry);
2299}
2300
2301static bool vty_pop_parent(struct vty *vty)
2302{
2303 struct vty_parent_node *parent = vty_parent(vty);
2304 if (!parent)
2305 return false;
2306 llist_del(&parent->entry);
2307 vty->node = parent->node;
2308 vty->priv = parent->priv;
2309 if (vty->indent)
2310 talloc_free(vty->indent);
2311 vty->indent = parent->indent;
2312 talloc_free(parent);
2313 return true;
2314}
2315
2316static void vty_clear_parents(struct vty *vty)
2317{
2318 while (vty_pop_parent(vty));
2319}
2320
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002321/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002322/*
2323 * This function MUST eventually converge on a node when called repeatedly,
2324 * there must not be any cycles.
2325 * All 'config' nodes shall converge on CONFIG_NODE.
2326 * All other 'enable' nodes shall converge on ENABLE_NODE.
2327 * All 'view' only nodes shall converge on VIEW_NODE.
2328 * All other nodes shall converge on themselves or it must be ensured,
2329 * that the user's rights are not extended anyhow by calling this function.
2330 *
2331 * Note that these requirements also apply to all functions that are used
2332 * as go_parent_cb.
2333 * Note also that this function relies on the is_config_child callback to
2334 * recognize non-config nodes if go_parent_cb is not set.
2335 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002336int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002337{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002338 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002339 case AUTH_NODE:
2340 case VIEW_NODE:
2341 case ENABLE_NODE:
2342 case CONFIG_NODE:
2343 vty_clear_parents(vty);
2344 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002345
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002346 case AUTH_ENABLE_NODE:
2347 vty->node = VIEW_NODE;
2348 vty_clear_parents(vty);
2349 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002350
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002351 default:
2352 if (host.app_info->go_parent_cb)
2353 host.app_info->go_parent_cb(vty);
2354 vty_pop_parent(vty);
2355 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002356 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002357
2358 return vty->node;
2359}
2360
2361/* Execute command by argument vline vector. */
2362static int
2363cmd_execute_command_real(vector vline, struct vty *vty,
2364 struct cmd_element **cmd)
2365{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002366 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002367 unsigned int index;
2368 vector cmd_vector;
2369 struct cmd_element *cmd_element;
2370 struct cmd_element *matched_element;
2371 unsigned int matched_count, incomplete_count;
2372 int argc;
2373 const char *argv[CMD_ARGC_MAX];
2374 enum match_type match = 0;
2375 int varflag;
2376 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002377 int rc;
2378 /* Used for temporary storage of cmd_deopt() allocated arguments during
2379 argv[] generation */
2380 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002381
2382 /* Make copy of command elements. */
2383 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2384
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002385 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002386 if ((command = vector_slot(vline, index))) {
2387 int ret;
2388
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002389 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002390 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002391
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002392 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002393 break;
2394
2395 ret =
2396 is_cmd_ambiguous(command, cmd_vector, index, match);
2397
2398 if (ret == 1) {
2399 vector_free(cmd_vector);
2400 return CMD_ERR_AMBIGUOUS;
2401 } else if (ret == 2) {
2402 vector_free(cmd_vector);
2403 return CMD_ERR_NO_MATCH;
2404 }
2405 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002406 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002407
2408 /* Check matched count. */
2409 matched_element = NULL;
2410 matched_count = 0;
2411 incomplete_count = 0;
2412
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002413 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002414 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002415 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002416 || index >= cmd_element->cmdsize) {
2417 matched_element = cmd_element;
2418#if 0
2419 printf("DEBUG: %s\n", cmd_element->string);
2420#endif
2421 matched_count++;
2422 } else {
2423 incomplete_count++;
2424 }
2425 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002426 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002427
2428 /* Finish of using cmd_vector. */
2429 vector_free(cmd_vector);
2430
2431 /* To execute command, matched_count must be 1. */
2432 if (matched_count == 0) {
2433 if (incomplete_count)
2434 return CMD_ERR_INCOMPLETE;
2435 else
2436 return CMD_ERR_NO_MATCH;
2437 }
2438
2439 if (matched_count > 1)
2440 return CMD_ERR_AMBIGUOUS;
2441
2442 /* Argument treatment */
2443 varflag = 0;
2444 argc = 0;
2445
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002446 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2447
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002448 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002449 if (argc == CMD_ARGC_MAX) {
2450 rc = CMD_ERR_EXEED_ARGC_MAX;
2451 goto rc_free_deopt_ctx;
2452 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002453 if (varflag) {
2454 argv[argc++] = vector_slot(vline, i);
2455 continue;
2456 }
2457
2458 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002459 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002460
2461 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002462 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002463 struct desc *desc = vector_slot(descvec, 0);
2464
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002465 if (CMD_OPTION(desc->cmd)) {
2466 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2467 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2468 } else {
2469 tmp_cmd = desc->cmd;
2470 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002471
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002472 if (CMD_VARARG(tmp_cmd))
2473 varflag = 1;
2474 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002475 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002476 else if (CMD_OPTION(desc->cmd))
2477 argv[argc++] = tmp_cmd;
2478 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002479 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002480 /* multi choice argument. look up which choice
2481 the user meant (can only be one after
2482 filtering and checking for ambigous). For instance,
2483 if user typed "th" for "(two|three)" arg, we
2484 want to pass "three" in argv[]. */
2485 for (j = 0; j < vector_active(descvec); j++) {
2486 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002487 if (!desc)
2488 continue;
2489 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2490 continue;
2491 if (CMD_OPTION(desc->cmd)) {
2492 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2493 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2494 } else {
2495 tmp_cmd = desc->cmd;
2496 }
2497
2498 if(CMD_VARIABLE(tmp_cmd)) {
2499 argv[argc++] = vector_slot(vline, i);
2500 } else {
2501 argv[argc++] = tmp_cmd;
2502 }
2503 break;
2504 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002505 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002506 }
2507
2508 /* For vtysh execution. */
2509 if (cmd)
2510 *cmd = matched_element;
2511
2512 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002513 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002514 else {
2515 /* Execute matched command. */
2516 struct vty_parent_node this_node = {
2517 .node = vty->node,
2518 .priv = vty->priv,
2519 .indent = vty->indent,
2520 };
2521 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002522 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002523
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002524 /* If we have stepped down into a child node, push a parent frame.
2525 * The causality is such: we don't expect every single node entry implementation to push
2526 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2527 * a parent node. Hence if the node changed without the parent node changing, we must
2528 * have stepped into a child node. */
2529 if (vty->node != this_node.node && parent == vty_parent(vty)
2530 && vty->node > CONFIG_NODE) {
2531 /* Push the parent node. */
2532 parent = talloc_zero(vty, struct vty_parent_node);
2533 *parent = this_node;
2534 llist_add(&parent->entry, &vty->parent_nodes);
2535 }
2536 }
2537
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002538rc_free_deopt_ctx:
2539 /* Now after we called the command func, we can free temporary strings */
2540 talloc_free(cmd_deopt_ctx);
2541 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002542}
2543
2544int
2545cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2546 int vtysh)
2547{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002548 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002549 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002550
2551 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002552
2553 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2554 vector shifted_vline;
2555 unsigned int index;
2556
2557 vty->node = ENABLE_NODE;
2558 /* We can try it on enable node, cos' the vty is authenticated */
2559
2560 shifted_vline = vector_init(vector_count(vline));
2561 /* use memcpy? */
2562 for (index = 1; index < vector_active(vline); index++) {
2563 vector_set_index(shifted_vline, index - 1,
2564 vector_lookup(vline, index));
2565 }
2566
2567 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2568
2569 vector_free(shifted_vline);
2570 vty->node = onode;
2571 return ret;
2572 }
2573
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002574 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002575}
2576
2577/* Execute command by argument readline. */
2578int
2579cmd_execute_command_strict(vector vline, struct vty *vty,
2580 struct cmd_element **cmd)
2581{
2582 unsigned int i;
2583 unsigned int index;
2584 vector cmd_vector;
2585 struct cmd_element *cmd_element;
2586 struct cmd_element *matched_element;
2587 unsigned int matched_count, incomplete_count;
2588 int argc;
2589 const char *argv[CMD_ARGC_MAX];
2590 int varflag;
2591 enum match_type match = 0;
2592 char *command;
2593
2594 /* Make copy of command element */
2595 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2596
2597 for (index = 0; index < vector_active(vline); index++)
2598 if ((command = vector_slot(vline, index))) {
2599 int ret;
2600
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002601 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002602 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002603
2604 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002605 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002606 break;
2607
2608 ret =
2609 is_cmd_ambiguous(command, cmd_vector, index, match);
2610 if (ret == 1) {
2611 vector_free(cmd_vector);
2612 return CMD_ERR_AMBIGUOUS;
2613 }
2614 if (ret == 2) {
2615 vector_free(cmd_vector);
2616 return CMD_ERR_NO_MATCH;
2617 }
2618 }
2619
2620 /* Check matched count. */
2621 matched_element = NULL;
2622 matched_count = 0;
2623 incomplete_count = 0;
2624 for (i = 0; i < vector_active(cmd_vector); i++)
2625 if (vector_slot(cmd_vector, i) != NULL) {
2626 cmd_element = vector_slot(cmd_vector, i);
2627
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002628 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002629 || index >= cmd_element->cmdsize) {
2630 matched_element = cmd_element;
2631 matched_count++;
2632 } else
2633 incomplete_count++;
2634 }
2635
2636 /* Finish of using cmd_vector. */
2637 vector_free(cmd_vector);
2638
2639 /* To execute command, matched_count must be 1. */
2640 if (matched_count == 0) {
2641 if (incomplete_count)
2642 return CMD_ERR_INCOMPLETE;
2643 else
2644 return CMD_ERR_NO_MATCH;
2645 }
2646
2647 if (matched_count > 1)
2648 return CMD_ERR_AMBIGUOUS;
2649
2650 /* Argument treatment */
2651 varflag = 0;
2652 argc = 0;
2653
2654 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002655 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002656 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002657 if (varflag) {
2658 argv[argc++] = vector_slot(vline, i);
2659 continue;
2660 }
2661
2662 vector descvec = vector_slot(matched_element->strvec, i);
2663
2664 if (vector_active(descvec) == 1) {
2665 struct desc *desc = vector_slot(descvec, 0);
2666
2667 if (CMD_VARARG(desc->cmd))
2668 varflag = 1;
2669
2670 if (varflag || CMD_VARIABLE(desc->cmd)
2671 || CMD_OPTION(desc->cmd))
2672 argv[argc++] = vector_slot(vline, i);
2673 } else {
2674 argv[argc++] = vector_slot(vline, i);
2675 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002676 }
2677
2678 /* For vtysh execution. */
2679 if (cmd)
2680 *cmd = matched_element;
2681
2682 if (matched_element->daemon)
2683 return CMD_SUCCESS_DAEMON;
2684
2685 /* Now execute matched command */
2686 return (*matched_element->func) (matched_element, vty, argc, argv);
2687}
2688
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002689static inline size_t len(const char *str)
2690{
2691 return str? strlen(str) : 0;
2692}
2693
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002694/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2695 * is longer than b, a must start with exactly b, and vice versa.
2696 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2697 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002698static int indent_cmp(const char *a, const char *b)
2699{
2700 size_t al, bl;
2701 al = len(a);
2702 bl = len(b);
2703 if (al > bl) {
2704 if (bl && strncmp(a, b, bl) != 0)
2705 return EINVAL;
2706 return 1;
2707 }
2708 /* al <= bl */
2709 if (al && strncmp(a, b, al) != 0)
2710 return EINVAL;
2711 return (al < bl)? -1 : 0;
2712}
2713
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002714/* Configration make from file. */
2715int config_from_file(struct vty *vty, FILE * fp)
2716{
2717 int ret;
2718 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002719 char *indent;
2720 int cmp;
2721 struct vty_parent_node this_node;
2722 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002723
2724 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002725 indent = NULL;
2726 vline = NULL;
2727 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002728
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002729 if (ret != CMD_SUCCESS)
2730 goto return_invalid_indent;
2731
2732 /* In case of comment or empty line */
2733 if (vline == NULL) {
2734 if (indent) {
2735 talloc_free(indent);
2736 indent = NULL;
2737 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002738 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002739 }
2740
Neels Hofmeyr43063632017-09-19 23:54:01 +02002741 /* We have a nonempty line. */
2742 if (!vty->indent) {
2743 /* We have just entered a node and expecting the first child to come up; but we
2744 * may also skip right back to a parent or ancestor level. */
2745 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002746
Neels Hofmeyr43063632017-09-19 23:54:01 +02002747 /* If there is no parent, record any indentation we encounter. */
2748 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2749
2750 if (cmp == EINVAL)
2751 goto return_invalid_indent;
2752
2753 if (cmp <= 0) {
2754 /* We have gone right back to the parent level or higher, we are skipping
2755 * this child node level entirely. Pop the parent to go back to a node
2756 * that was actually there (to reinstate vty->indent) and re-use below
2757 * go-parent while-loop to find an accurate match of indent in the node
2758 * ancestry. */
2759 vty_go_parent(vty);
2760 } else {
2761 /* The indent is deeper than the just entered parent, record the new
2762 * indentation characters. */
2763 vty->indent = talloc_strdup(vty, indent);
2764 /* This *is* the new indentation. */
2765 cmp = 0;
2766 }
2767 } else {
2768 /* There is a known indentation for this node level, validate and detect node
2769 * exits. */
2770 cmp = indent_cmp(indent, vty->indent);
2771 if (cmp == EINVAL)
2772 goto return_invalid_indent;
2773 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002774
2775 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2776 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2777 while (cmp < 0) {
2778 vty_go_parent(vty);
2779 cmp = indent_cmp(indent, vty->indent);
2780 if (cmp == EINVAL)
2781 goto return_invalid_indent;
2782 }
2783
2784 /* More indent without having entered a child node level? Either the parent node's indent
2785 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2786 * or the indentation increased even though the vty command didn't enter a child. */
2787 if (cmp > 0)
2788 goto return_invalid_indent;
2789
2790 /* Remember the current node before the command possibly changes it. */
2791 this_node = (struct vty_parent_node){
2792 .node = vty->node,
2793 .priv = vty->priv,
2794 .indent = vty->indent,
2795 };
2796
2797 parent = vty_parent(vty);
2798 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002799 cmd_free_strvec(vline);
2800
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002801 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002802 if (indent) {
2803 talloc_free(indent);
2804 indent = NULL;
2805 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002806 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002807 }
2808
2809 /* If we have stepped down into a child node, push a parent frame.
2810 * The causality is such: we don't expect every single node entry implementation to push
2811 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2812 * a parent node. Hence if the node changed without the parent node changing, we must
2813 * have stepped into a child node (and now expect a deeper indent). */
2814 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2815 /* Push the parent node. */
2816 parent = talloc_zero(vty, struct vty_parent_node);
2817 *parent = this_node;
2818 llist_add(&parent->entry, &vty->parent_nodes);
2819
2820 /* The current talloc'ed vty->indent string will now be owned by this parent
2821 * struct. Indicate that we don't know what deeper indent characters the user
2822 * will choose. */
2823 vty->indent = NULL;
2824 }
2825
2826 if (indent) {
2827 talloc_free(indent);
2828 indent = NULL;
2829 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002830 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002831 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2832 while (vty_parent(vty))
2833 vty_go_parent(vty);
2834
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002835 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002836
2837return_invalid_indent:
2838 if (vline)
2839 cmd_free_strvec(vline);
2840 if (indent) {
2841 talloc_free(indent);
2842 indent = NULL;
2843 }
2844 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002845}
2846
2847/* Configration from terminal */
2848DEFUN(config_terminal,
2849 config_terminal_cmd,
2850 "configure terminal",
2851 "Configuration from vty interface\n" "Configuration terminal\n")
2852{
2853 if (vty_config_lock(vty))
2854 vty->node = CONFIG_NODE;
2855 else {
2856 vty_out(vty, "VTY configuration is locked by other VTY%s",
2857 VTY_NEWLINE);
2858 return CMD_WARNING;
2859 }
2860 return CMD_SUCCESS;
2861}
2862
2863/* Enable command */
2864DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2865{
2866 /* If enable password is NULL, change to ENABLE_NODE */
2867 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2868 vty->type == VTY_SHELL_SERV)
2869 vty->node = ENABLE_NODE;
2870 else
2871 vty->node = AUTH_ENABLE_NODE;
2872
2873 return CMD_SUCCESS;
2874}
2875
2876/* Disable command */
2877DEFUN(disable,
2878 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2879{
2880 if (vty->node == ENABLE_NODE)
2881 vty->node = VIEW_NODE;
2882 return CMD_SUCCESS;
2883}
2884
2885/* Down vty node level. */
2886gDEFUN(config_exit,
2887 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2888{
2889 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002890 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002891 case VIEW_NODE:
2892 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002893 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002894 break;
2895 case CONFIG_NODE:
2896 vty->node = ENABLE_NODE;
2897 vty_config_unlock(vty);
2898 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002899 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002900 if (vty->node > CONFIG_NODE)
2901 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002902 break;
2903 }
2904 return CMD_SUCCESS;
2905}
2906
2907/* End of configuration. */
2908 gDEFUN(config_end,
2909 config_end_cmd, "end", "End current mode and change to enable mode.")
2910{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002911 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002912 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002913
2914 /* Repeatedly call go_parent until a top node is reached. */
2915 while (vty->node > CONFIG_NODE) {
2916 if (vty->node == last_node) {
2917 /* Ensure termination, this shouldn't happen. */
2918 break;
2919 }
2920 last_node = vty->node;
2921 vty_go_parent(vty);
2922 }
2923
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002924 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002925 if (vty->node > ENABLE_NODE)
2926 vty->node = ENABLE_NODE;
2927 vty->index = NULL;
2928 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002929 }
2930 return CMD_SUCCESS;
2931}
2932
2933/* Show version. */
2934DEFUN(show_version,
2935 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2936{
Harald Welte237f6242010-05-25 23:00:45 +02002937 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2938 host.app_info->version,
2939 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2940 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002941
2942 return CMD_SUCCESS;
2943}
2944
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002945DEFUN(show_online_help,
2946 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2947{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002948 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002949 return CMD_SUCCESS;
2950}
2951
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002952/* Help display function for all node. */
2953gDEFUN(config_help,
2954 config_help_cmd, "help", "Description of the interactive help system\n")
2955{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002956 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2957 "anytime at the command line please press '?'.%s%s"
2958 "If nothing matches, the help list will be empty and you must backup%s"
2959 " until entering a '?' shows the available options.%s"
2960 "Two styles of help are provided:%s"
2961 "1. Full help is available when you are ready to enter a%s"
2962 "command argument (e.g. 'show ?') and describes each possible%s"
2963 "argument.%s"
2964 "2. Partial help is provided when an abbreviated argument is entered%s"
2965 " and you want to know what arguments match the input%s"
2966 " (e.g. 'show me?'.)%s%s",
2967 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2968 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2969 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2970 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002971 return CMD_SUCCESS;
2972}
2973
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002974enum {
2975 ATTR_TYPE_GLOBAL = (1 << 0),
2976 ATTR_TYPE_LIB = (1 << 1),
2977 ATTR_TYPE_APP = (1 << 2),
2978};
2979
2980static void print_attr_list(struct vty *vty, unsigned int attr_mask)
2981{
2982 const char *desc;
2983 unsigned int i;
2984 bool found;
2985 char flag;
2986
2987 if (attr_mask & ATTR_TYPE_GLOBAL) {
2988 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
2989
2990 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07002991 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002992 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07002993
2994 /* Skip attributes without flags */
2995 if (flag != '.')
2996 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002997 }
2998 }
2999
3000 if (attr_mask & ATTR_TYPE_LIB) {
3001 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3002
3003 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3004 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3005 continue;
3006 found = true;
3007
3008 flag = cmd_lib_attr_letters[i];
3009 if (flag == '\0')
3010 flag = '.';
3011
3012 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3013 }
3014
3015 if (!found)
3016 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3017 }
3018
3019 if (attr_mask & ATTR_TYPE_APP) {
3020 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3021
3022 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3023 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3024 continue;
3025 found = true;
3026
3027 flag = host.app_info->usr_attr_letters[i];
3028 if (flag == '\0')
3029 flag = '.';
3030
3031 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3032 }
3033
3034 if (!found)
3035 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3036 }
3037}
3038
3039gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3040 "show vty-attributes",
3041 SHOW_STR "List of VTY attributes\n")
3042{
3043 print_attr_list(vty, 0xff);
3044 return CMD_SUCCESS;
3045}
3046
3047gDEFUN(show_vty_attr, show_vty_attr_cmd,
3048 "show vty-attributes (application|library|global)",
3049 SHOW_STR "List of VTY attributes\n"
3050 "Application specific attributes only\n"
3051 "Library specific attributes only\n"
3052 "Global attributes only\n")
3053{
3054 unsigned int attr_mask = 0;
3055
3056 if (argv[0][0] == 'g') /* global */
3057 attr_mask |= ATTR_TYPE_GLOBAL;
3058 else if (argv[0][0] == 'l') /* library */
3059 attr_mask |= ATTR_TYPE_LIB;
3060 else if (argv[0][0] == 'a') /* application */
3061 attr_mask |= ATTR_TYPE_APP;
3062
3063 print_attr_list(vty, attr_mask);
3064 return CMD_SUCCESS;
3065}
3066
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003067/* Compose flag bit-mask for all commands within the given node */
3068static unsigned int node_flag_mask(const struct cmd_node *cnode)
3069{
3070 unsigned int flag_mask = 0x00;
3071 unsigned int f, i;
3072
3073 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3074 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3075 const struct cmd_element *cmd;
3076 char flag_letter;
3077
3078 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3079 continue;
3080 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
3081 continue;
3082 if (~cmd->usrattr & (1 << f))
3083 continue;
3084
3085 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3086 flag_letter = cmd_lib_attr_letters[f];
3087 else
3088 flag_letter = host.app_info->usr_attr_letters[f];
3089
3090 if (flag_letter == '\0')
3091 continue;
3092
3093 flag_mask |= (1 << f);
3094 break;
3095 }
3096 }
3097
3098 return flag_mask;
3099}
3100
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003101/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3102static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3103{
3104 static char char_mask[8 + 1];
3105 char *ptr = &char_mask[0];
3106
3107 /* Mutually exclusive global attributes */
3108 if (cmd->attr & CMD_ATTR_IMMEDIATE)
3109 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3110 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3111 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3112 else
3113 *(ptr++) = '.';
3114
3115 *ptr = '\0';
3116
3117 return char_mask;
3118}
3119
3120/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003121static const char *cmd_flag_mask(const struct cmd_element *cmd,
3122 unsigned int flag_mask)
3123{
3124 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3125 char *ptr = &char_mask[0];
3126 char flag_letter;
3127 unsigned int f;
3128
3129 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3130 if (~flag_mask & (1 << f))
3131 continue;
3132 if (~cmd->usrattr & (1 << f)) {
3133 *(ptr++) = '.';
3134 continue;
3135 }
3136
3137 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3138 flag_letter = cmd_lib_attr_letters[f];
3139 else
3140 flag_letter = host.app_info->usr_attr_letters[f];
3141
3142 *(ptr++) = flag_letter ? flag_letter : '.';
3143 }
3144
3145 *ptr = '\0';
3146
3147 return char_mask;
3148}
3149
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003150/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003151gDEFUN(config_list, config_list_cmd,
3152 "list [with-flags]",
3153 "Print command list\n"
3154 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003155{
3156 unsigned int i;
3157 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003158 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003159 struct cmd_element *cmd;
3160
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003161 if (argc > 0)
3162 flag_mask = node_flag_mask(cnode);
3163
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003164 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3165 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3166 continue;
3167 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
3168 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003169 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003170 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3171 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003172 vty_out(vty, " %s %s %s%s",
3173 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003174 cmd_flag_mask(cmd, flag_mask),
3175 cmd->string, VTY_NEWLINE);
3176 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003177 }
3178
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003179 return CMD_SUCCESS;
3180}
3181
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003182static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003183{
3184 unsigned int i;
3185 int fd;
3186 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003187 char *config_file_tmp = NULL;
3188 char *config_file_sav = NULL;
3189 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003190 struct stat st;
3191
3192 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003193
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003194 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3195 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3196 * manually instead. */
3197
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003198 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003199 config_file_sav =
3200 _talloc_zero(tall_vty_cmd_ctx,
3201 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3202 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003203 if (!config_file_sav)
3204 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003205 strcpy(config_file_sav, config_file);
3206 strcat(config_file_sav, CONF_BACKUP_EXT);
3207
3208 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003209 "config_file_tmp");
3210 if (!config_file_tmp) {
3211 talloc_free(config_file_sav);
3212 return -1;
3213 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003214 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3215
3216 /* Open file to configuration write. */
3217 fd = mkstemp(config_file_tmp);
3218 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003219 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003220 talloc_free(config_file_tmp);
3221 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003222 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003223 }
3224
3225 /* Make vty for configuration file. */
3226 file_vty = vty_new();
3227 file_vty->fd = fd;
3228 file_vty->type = VTY_FILE;
3229
3230 /* Config file header print. */
3231 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003232 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003233 //vty_time_print (file_vty, 1);
3234 vty_out(file_vty, "!\n");
3235
3236 for (i = 0; i < vector_active(cmdvec); i++)
3237 if ((node = vector_slot(cmdvec, i)) && node->func) {
3238 if ((*node->func) (file_vty))
3239 vty_out(file_vty, "!\n");
3240 }
3241 vty_close(file_vty);
3242
3243 if (unlink(config_file_sav) != 0)
3244 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003245 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003246 talloc_free(config_file_sav);
3247 talloc_free(config_file_tmp);
3248 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003249 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003250 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003251
3252 /* Only link the .sav file if the original file exists */
3253 if (stat(config_file, &st) == 0) {
3254 if (link(config_file, config_file_sav) != 0) {
3255 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3256 talloc_free(config_file_sav);
3257 talloc_free(config_file_tmp);
3258 unlink(config_file_tmp);
3259 return -3;
3260 }
3261 sync();
3262 if (unlink(config_file) != 0) {
3263 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3264 talloc_free(config_file_sav);
3265 talloc_free(config_file_tmp);
3266 unlink(config_file_tmp);
3267 return -4;
3268 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003269 }
3270 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003271 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003272 talloc_free(config_file_sav);
3273 talloc_free(config_file_tmp);
3274 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003275 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003276 }
3277 unlink(config_file_tmp);
3278 sync();
3279
3280 talloc_free(config_file_sav);
3281 talloc_free(config_file_tmp);
3282
3283 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003284 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3285 return -6;
3286 }
3287
3288 return 0;
3289}
3290
3291
3292/* Write current configuration into file. */
3293DEFUN(config_write_file,
3294 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003295 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003296 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003297 "Write to configuration file\n"
3298 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003299{
3300 char *failed_file;
3301 int rc;
3302
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003303 if (host.app_info->config_is_consistent) {
3304 rc = host.app_info->config_is_consistent(vty);
3305 if (!rc) {
3306 vty_out(vty, "Configuration is not consistent%s",
3307 VTY_NEWLINE);
3308 return CMD_WARNING;
3309 }
3310 }
3311
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003312 if (argc == 1)
3313 host_config_set(argv[0]);
3314
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003315 if (host.config == NULL) {
3316 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3317 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003318 return CMD_WARNING;
3319 }
3320
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003321 rc = write_config_file(host.config, &failed_file);
3322 switch (rc) {
3323 case -1:
3324 vty_out(vty, "Can't open configuration file %s.%s",
3325 failed_file, VTY_NEWLINE);
3326 rc = CMD_WARNING;
3327 break;
3328 case -2:
3329 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3330 failed_file, VTY_NEWLINE);
3331 rc = CMD_WARNING;
3332 break;
3333 case -3:
3334 vty_out(vty, "Can't backup old configuration file %s.%s",
3335 failed_file, VTY_NEWLINE);
3336 rc = CMD_WARNING;
3337 break;
3338 case -4:
3339 vty_out(vty, "Can't unlink configuration file %s.%s",
3340 failed_file, VTY_NEWLINE);
3341 rc = CMD_WARNING;
3342 break;
3343 case -5:
3344 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3345 VTY_NEWLINE);
3346 rc = CMD_WARNING;
3347 break;
3348 case -6:
3349 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3350 failed_file, strerror(errno), errno, VTY_NEWLINE);
3351 rc = CMD_WARNING;
3352 break;
3353 default:
3354 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3355 rc = CMD_SUCCESS;
3356 break;
3357 }
3358
3359 talloc_free(failed_file);
3360 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003361}
3362
3363ALIAS(config_write_file,
3364 config_write_cmd,
3365 "write", "Write running configuration to memory, network, or terminal\n")
3366
3367 ALIAS(config_write_file,
3368 config_write_memory_cmd,
3369 "write memory",
3370 "Write running configuration to memory, network, or terminal\n"
3371 "Write configuration to the file (same as write file)\n")
3372
3373 ALIAS(config_write_file,
3374 copy_runningconfig_startupconfig_cmd,
3375 "copy running-config startup-config",
3376 "Copy configuration\n"
3377 "Copy running config to... \n"
3378 "Copy running config to startup config (same as write file)\n")
3379
3380/* Write current configuration into the terminal. */
3381 DEFUN(config_write_terminal,
3382 config_write_terminal_cmd,
3383 "write terminal",
3384 "Write running configuration to memory, network, or terminal\n"
3385 "Write to terminal\n")
3386{
3387 unsigned int i;
3388 struct cmd_node *node;
3389
3390 if (vty->type == VTY_SHELL_SERV) {
3391 for (i = 0; i < vector_active(cmdvec); i++)
3392 if ((node = vector_slot(cmdvec, i)) && node->func
3393 && node->vtysh) {
3394 if ((*node->func) (vty))
3395 vty_out(vty, "!%s", VTY_NEWLINE);
3396 }
3397 } else {
3398 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3399 VTY_NEWLINE);
3400 vty_out(vty, "!%s", VTY_NEWLINE);
3401
3402 for (i = 0; i < vector_active(cmdvec); i++)
3403 if ((node = vector_slot(cmdvec, i)) && node->func) {
3404 if ((*node->func) (vty))
3405 vty_out(vty, "!%s", VTY_NEWLINE);
3406 }
3407 vty_out(vty, "end%s", VTY_NEWLINE);
3408 }
3409 return CMD_SUCCESS;
3410}
3411
3412/* Write current configuration into the terminal. */
3413ALIAS(config_write_terminal,
3414 show_running_config_cmd,
3415 "show running-config", SHOW_STR "running configuration\n")
3416
3417/* Write startup configuration into the terminal. */
3418 DEFUN(show_startup_config,
3419 show_startup_config_cmd,
3420 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3421{
3422 char buf[BUFSIZ];
3423 FILE *confp;
3424
3425 confp = fopen(host.config, "r");
3426 if (confp == NULL) {
3427 vty_out(vty, "Can't open configuration file [%s]%s",
3428 host.config, VTY_NEWLINE);
3429 return CMD_WARNING;
3430 }
3431
3432 while (fgets(buf, BUFSIZ, confp)) {
3433 char *cp = buf;
3434
3435 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3436 cp++;
3437 *cp = '\0';
3438
3439 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3440 }
3441
3442 fclose(confp);
3443
3444 return CMD_SUCCESS;
3445}
3446
3447/* Hostname configuration */
3448DEFUN(config_hostname,
3449 hostname_cmd,
3450 "hostname WORD",
3451 "Set system's network name\n" "This system's network name\n")
3452{
3453 if (!isalpha((int)*argv[0])) {
3454 vty_out(vty, "Please specify string starting with alphabet%s",
3455 VTY_NEWLINE);
3456 return CMD_WARNING;
3457 }
3458
3459 if (host.name)
3460 talloc_free(host.name);
3461
3462 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3463 return CMD_SUCCESS;
3464}
3465
3466DEFUN(config_no_hostname,
3467 no_hostname_cmd,
3468 "no hostname [HOSTNAME]",
3469 NO_STR "Reset system's network name\n" "Host name of this router\n")
3470{
3471 if (host.name)
3472 talloc_free(host.name);
3473 host.name = NULL;
3474 return CMD_SUCCESS;
3475}
3476
3477/* VTY interface password set. */
3478DEFUN(config_password, password_cmd,
3479 "password (8|) WORD",
3480 "Assign the terminal connection password\n"
3481 "Specifies a HIDDEN password will follow\n"
3482 "dummy string \n" "The HIDDEN line password string\n")
3483{
3484 /* Argument check. */
3485 if (argc == 0) {
3486 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3487 return CMD_WARNING;
3488 }
3489
3490 if (argc == 2) {
3491 if (*argv[0] == '8') {
3492 if (host.password)
3493 talloc_free(host.password);
3494 host.password = NULL;
3495 if (host.password_encrypt)
3496 talloc_free(host.password_encrypt);
3497 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3498 return CMD_SUCCESS;
3499 } else {
3500 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3501 return CMD_WARNING;
3502 }
3503 }
3504
3505 if (!isalnum((int)*argv[0])) {
3506 vty_out(vty,
3507 "Please specify string starting with alphanumeric%s",
3508 VTY_NEWLINE);
3509 return CMD_WARNING;
3510 }
3511
3512 if (host.password)
3513 talloc_free(host.password);
3514 host.password = NULL;
3515
3516#ifdef VTY_CRYPT_PW
3517 if (host.encrypt) {
3518 if (host.password_encrypt)
3519 talloc_free(host.password_encrypt);
3520 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3521 } else
3522#endif
3523 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3524
3525 return CMD_SUCCESS;
3526}
3527
3528ALIAS(config_password, password_text_cmd,
3529 "password LINE",
3530 "Assign the terminal connection password\n"
3531 "The UNENCRYPTED (cleartext) line password\n")
3532
3533/* VTY enable password set. */
3534 DEFUN(config_enable_password, enable_password_cmd,
3535 "enable password (8|) WORD",
3536 "Modify enable password parameters\n"
3537 "Assign the privileged level password\n"
3538 "Specifies a HIDDEN password will follow\n"
3539 "dummy string \n" "The HIDDEN 'enable' password string\n")
3540{
3541 /* Argument check. */
3542 if (argc == 0) {
3543 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3544 return CMD_WARNING;
3545 }
3546
3547 /* Crypt type is specified. */
3548 if (argc == 2) {
3549 if (*argv[0] == '8') {
3550 if (host.enable)
3551 talloc_free(host.enable);
3552 host.enable = NULL;
3553
3554 if (host.enable_encrypt)
3555 talloc_free(host.enable_encrypt);
3556 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3557
3558 return CMD_SUCCESS;
3559 } else {
3560 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3561 return CMD_WARNING;
3562 }
3563 }
3564
3565 if (!isalnum((int)*argv[0])) {
3566 vty_out(vty,
3567 "Please specify string starting with alphanumeric%s",
3568 VTY_NEWLINE);
3569 return CMD_WARNING;
3570 }
3571
3572 if (host.enable)
3573 talloc_free(host.enable);
3574 host.enable = NULL;
3575
3576 /* Plain password input. */
3577#ifdef VTY_CRYPT_PW
3578 if (host.encrypt) {
3579 if (host.enable_encrypt)
3580 talloc_free(host.enable_encrypt);
3581 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3582 } else
3583#endif
3584 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3585
3586 return CMD_SUCCESS;
3587}
3588
3589ALIAS(config_enable_password,
3590 enable_password_text_cmd,
3591 "enable password LINE",
3592 "Modify enable password parameters\n"
3593 "Assign the privileged level password\n"
3594 "The UNENCRYPTED (cleartext) 'enable' password\n")
3595
3596/* VTY enable password delete. */
3597 DEFUN(no_config_enable_password, no_enable_password_cmd,
3598 "no enable password",
3599 NO_STR
3600 "Modify enable password parameters\n"
3601 "Assign the privileged level password\n")
3602{
3603 if (host.enable)
3604 talloc_free(host.enable);
3605 host.enable = NULL;
3606
3607 if (host.enable_encrypt)
3608 talloc_free(host.enable_encrypt);
3609 host.enable_encrypt = NULL;
3610
3611 return CMD_SUCCESS;
3612}
3613
3614#ifdef VTY_CRYPT_PW
3615DEFUN(service_password_encrypt,
3616 service_password_encrypt_cmd,
3617 "service password-encryption",
3618 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3619{
3620 if (host.encrypt)
3621 return CMD_SUCCESS;
3622
3623 host.encrypt = 1;
3624
3625 if (host.password) {
3626 if (host.password_encrypt)
3627 talloc_free(host.password_encrypt);
3628 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3629 }
3630 if (host.enable) {
3631 if (host.enable_encrypt)
3632 talloc_free(host.enable_encrypt);
3633 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3634 }
3635
3636 return CMD_SUCCESS;
3637}
3638
3639DEFUN(no_service_password_encrypt,
3640 no_service_password_encrypt_cmd,
3641 "no service password-encryption",
3642 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3643{
3644 if (!host.encrypt)
3645 return CMD_SUCCESS;
3646
3647 host.encrypt = 0;
3648
3649 if (host.password_encrypt)
3650 talloc_free(host.password_encrypt);
3651 host.password_encrypt = NULL;
3652
3653 if (host.enable_encrypt)
3654 talloc_free(host.enable_encrypt);
3655 host.enable_encrypt = NULL;
3656
3657 return CMD_SUCCESS;
3658}
3659#endif
3660
3661DEFUN(config_terminal_length, config_terminal_length_cmd,
3662 "terminal length <0-512>",
3663 "Set terminal line parameters\n"
3664 "Set number of lines on a screen\n"
3665 "Number of lines on screen (0 for no pausing)\n")
3666{
3667 int lines;
3668 char *endptr = NULL;
3669
3670 lines = strtol(argv[0], &endptr, 10);
3671 if (lines < 0 || lines > 512 || *endptr != '\0') {
3672 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3673 return CMD_WARNING;
3674 }
3675 vty->lines = lines;
3676
3677 return CMD_SUCCESS;
3678}
3679
3680DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3681 "terminal no length",
3682 "Set terminal line parameters\n"
3683 NO_STR "Set number of lines on a screen\n")
3684{
3685 vty->lines = -1;
3686 return CMD_SUCCESS;
3687}
3688
3689DEFUN(service_terminal_length, service_terminal_length_cmd,
3690 "service terminal-length <0-512>",
3691 "Set up miscellaneous service\n"
3692 "System wide terminal length configuration\n"
3693 "Number of lines of VTY (0 means no line control)\n")
3694{
3695 int lines;
3696 char *endptr = NULL;
3697
3698 lines = strtol(argv[0], &endptr, 10);
3699 if (lines < 0 || lines > 512 || *endptr != '\0') {
3700 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3701 return CMD_WARNING;
3702 }
3703 host.lines = lines;
3704
3705 return CMD_SUCCESS;
3706}
3707
3708DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3709 "no service terminal-length [<0-512>]",
3710 NO_STR
3711 "Set up miscellaneous service\n"
3712 "System wide terminal length configuration\n"
3713 "Number of lines of VTY (0 means no line control)\n")
3714{
3715 host.lines = -1;
3716 return CMD_SUCCESS;
3717}
3718
3719DEFUN_HIDDEN(do_echo,
3720 echo_cmd,
3721 "echo .MESSAGE",
3722 "Echo a message back to the vty\n" "The message to echo\n")
3723{
3724 char *message;
3725
3726 vty_out(vty, "%s%s",
3727 ((message =
3728 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3729 if (message)
3730 talloc_free(message);
3731 return CMD_SUCCESS;
3732}
3733
3734#if 0
3735DEFUN(config_logmsg,
3736 config_logmsg_cmd,
3737 "logmsg " LOG_LEVELS " .MESSAGE",
3738 "Send a message to enabled logging destinations\n"
3739 LOG_LEVEL_DESC "The message to send\n")
3740{
3741 int level;
3742 char *message;
3743
3744 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3745 return CMD_ERR_NO_MATCH;
3746
3747 zlog(NULL, level,
3748 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3749 if (message)
3750 talloc_free(message);
3751 return CMD_SUCCESS;
3752}
3753
3754DEFUN(show_logging,
3755 show_logging_cmd,
3756 "show logging", SHOW_STR "Show current logging configuration\n")
3757{
3758 struct zlog *zl = zlog_default;
3759
3760 vty_out(vty, "Syslog logging: ");
3761 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3762 vty_out(vty, "disabled");
3763 else
3764 vty_out(vty, "level %s, facility %s, ident %s",
3765 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3766 facility_name(zl->facility), zl->ident);
3767 vty_out(vty, "%s", VTY_NEWLINE);
3768
3769 vty_out(vty, "Stdout logging: ");
3770 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3771 vty_out(vty, "disabled");
3772 else
3773 vty_out(vty, "level %s",
3774 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3775 vty_out(vty, "%s", VTY_NEWLINE);
3776
3777 vty_out(vty, "Monitor logging: ");
3778 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3779 vty_out(vty, "disabled");
3780 else
3781 vty_out(vty, "level %s",
3782 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3783 vty_out(vty, "%s", VTY_NEWLINE);
3784
3785 vty_out(vty, "File logging: ");
3786 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3787 vty_out(vty, "disabled");
3788 else
3789 vty_out(vty, "level %s, filename %s",
3790 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3791 zl->filename);
3792 vty_out(vty, "%s", VTY_NEWLINE);
3793
3794 vty_out(vty, "Protocol name: %s%s",
3795 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3796 vty_out(vty, "Record priority: %s%s",
3797 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3798
3799 return CMD_SUCCESS;
3800}
3801
3802DEFUN(config_log_stdout,
3803 config_log_stdout_cmd,
3804 "log stdout", "Logging control\n" "Set stdout logging level\n")
3805{
3806 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3807 return CMD_SUCCESS;
3808}
3809
3810DEFUN(config_log_stdout_level,
3811 config_log_stdout_level_cmd,
3812 "log stdout " LOG_LEVELS,
3813 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3814{
3815 int level;
3816
3817 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3818 return CMD_ERR_NO_MATCH;
3819 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3820 return CMD_SUCCESS;
3821}
3822
3823DEFUN(no_config_log_stdout,
3824 no_config_log_stdout_cmd,
3825 "no log stdout [LEVEL]",
3826 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3827{
3828 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3829 return CMD_SUCCESS;
3830}
3831
3832DEFUN(config_log_monitor,
3833 config_log_monitor_cmd,
3834 "log monitor",
3835 "Logging control\n" "Set terminal line (monitor) logging level\n")
3836{
3837 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3838 return CMD_SUCCESS;
3839}
3840
3841DEFUN(config_log_monitor_level,
3842 config_log_monitor_level_cmd,
3843 "log monitor " LOG_LEVELS,
3844 "Logging control\n"
3845 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3846{
3847 int level;
3848
3849 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3850 return CMD_ERR_NO_MATCH;
3851 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3852 return CMD_SUCCESS;
3853}
3854
3855DEFUN(no_config_log_monitor,
3856 no_config_log_monitor_cmd,
3857 "no log monitor [LEVEL]",
3858 NO_STR
3859 "Logging control\n"
3860 "Disable terminal line (monitor) logging\n" "Logging level\n")
3861{
3862 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3863 return CMD_SUCCESS;
3864}
3865
3866static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3867{
3868 int ret;
3869 char *p = NULL;
3870 const char *fullpath;
3871
3872 /* Path detection. */
3873 if (!IS_DIRECTORY_SEP(*fname)) {
3874 char cwd[MAXPATHLEN + 1];
3875 cwd[MAXPATHLEN] = '\0';
3876
3877 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3878 zlog_err("config_log_file: Unable to alloc mem!");
3879 return CMD_WARNING;
3880 }
3881
3882 if ((p = _talloc_zero(tall_vcmd_ctx,
3883 strlen(cwd) + strlen(fname) + 2),
3884 "set_log_file")
3885 == NULL) {
3886 zlog_err("config_log_file: Unable to alloc mem!");
3887 return CMD_WARNING;
3888 }
3889 sprintf(p, "%s/%s", cwd, fname);
3890 fullpath = p;
3891 } else
3892 fullpath = fname;
3893
3894 ret = zlog_set_file(NULL, fullpath, loglevel);
3895
3896 if (p)
3897 talloc_free(p);
3898
3899 if (!ret) {
3900 vty_out(vty, "can't open logfile %s\n", fname);
3901 return CMD_WARNING;
3902 }
3903
3904 if (host.logfile)
3905 talloc_free(host.logfile);
3906
3907 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3908
3909 return CMD_SUCCESS;
3910}
3911
3912DEFUN(config_log_file,
3913 config_log_file_cmd,
3914 "log file FILENAME",
3915 "Logging control\n" "Logging to file\n" "Logging filename\n")
3916{
3917 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3918}
3919
3920DEFUN(config_log_file_level,
3921 config_log_file_level_cmd,
3922 "log file FILENAME " LOG_LEVELS,
3923 "Logging control\n"
3924 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3925{
3926 int level;
3927
3928 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3929 return CMD_ERR_NO_MATCH;
3930 return set_log_file(vty, argv[0], level);
3931}
3932
3933DEFUN(no_config_log_file,
3934 no_config_log_file_cmd,
3935 "no log file [FILENAME]",
3936 NO_STR
3937 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3938{
3939 zlog_reset_file(NULL);
3940
3941 if (host.logfile)
3942 talloc_free(host.logfile);
3943
3944 host.logfile = NULL;
3945
3946 return CMD_SUCCESS;
3947}
3948
3949ALIAS(no_config_log_file,
3950 no_config_log_file_level_cmd,
3951 "no log file FILENAME LEVEL",
3952 NO_STR
3953 "Logging control\n"
3954 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3955
3956 DEFUN(config_log_syslog,
3957 config_log_syslog_cmd,
3958 "log syslog", "Logging control\n" "Set syslog logging level\n")
3959{
3960 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3961 return CMD_SUCCESS;
3962}
3963
3964DEFUN(config_log_syslog_level,
3965 config_log_syslog_level_cmd,
3966 "log syslog " LOG_LEVELS,
3967 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3968{
3969 int level;
3970
3971 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3972 return CMD_ERR_NO_MATCH;
3973 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3974 return CMD_SUCCESS;
3975}
3976
3977DEFUN_DEPRECATED(config_log_syslog_facility,
3978 config_log_syslog_facility_cmd,
3979 "log syslog facility " LOG_FACILITIES,
3980 "Logging control\n"
3981 "Logging goes to syslog\n"
3982 "(Deprecated) Facility parameter for syslog messages\n"
3983 LOG_FACILITY_DESC)
3984{
3985 int facility;
3986
3987 if ((facility = facility_match(argv[0])) < 0)
3988 return CMD_ERR_NO_MATCH;
3989
3990 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3991 zlog_default->facility = facility;
3992 return CMD_SUCCESS;
3993}
3994
3995DEFUN(no_config_log_syslog,
3996 no_config_log_syslog_cmd,
3997 "no log syslog [LEVEL]",
3998 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3999{
4000 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4001 return CMD_SUCCESS;
4002}
4003
4004ALIAS(no_config_log_syslog,
4005 no_config_log_syslog_facility_cmd,
4006 "no log syslog facility " LOG_FACILITIES,
4007 NO_STR
4008 "Logging control\n"
4009 "Logging goes to syslog\n"
4010 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4011
4012 DEFUN(config_log_facility,
4013 config_log_facility_cmd,
4014 "log facility " LOG_FACILITIES,
4015 "Logging control\n"
4016 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4017{
4018 int facility;
4019
4020 if ((facility = facility_match(argv[0])) < 0)
4021 return CMD_ERR_NO_MATCH;
4022 zlog_default->facility = facility;
4023 return CMD_SUCCESS;
4024}
4025
4026DEFUN(no_config_log_facility,
4027 no_config_log_facility_cmd,
4028 "no log facility [FACILITY]",
4029 NO_STR
4030 "Logging control\n"
4031 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4032{
4033 zlog_default->facility = LOG_DAEMON;
4034 return CMD_SUCCESS;
4035}
4036
4037DEFUN_DEPRECATED(config_log_trap,
4038 config_log_trap_cmd,
4039 "log trap " LOG_LEVELS,
4040 "Logging control\n"
4041 "(Deprecated) Set logging level and default for all destinations\n"
4042 LOG_LEVEL_DESC)
4043{
4044 int new_level;
4045 int i;
4046
4047 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4048 return CMD_ERR_NO_MATCH;
4049
4050 zlog_default->default_lvl = new_level;
4051 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4052 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4053 zlog_default->maxlvl[i] = new_level;
4054 return CMD_SUCCESS;
4055}
4056
4057DEFUN_DEPRECATED(no_config_log_trap,
4058 no_config_log_trap_cmd,
4059 "no log trap [LEVEL]",
4060 NO_STR
4061 "Logging control\n"
4062 "Permit all logging information\n" "Logging level\n")
4063{
4064 zlog_default->default_lvl = LOG_DEBUG;
4065 return CMD_SUCCESS;
4066}
4067
4068DEFUN(config_log_record_priority,
4069 config_log_record_priority_cmd,
4070 "log record-priority",
4071 "Logging control\n"
4072 "Log the priority of the message within the message\n")
4073{
4074 zlog_default->record_priority = 1;
4075 return CMD_SUCCESS;
4076}
4077
4078DEFUN(no_config_log_record_priority,
4079 no_config_log_record_priority_cmd,
4080 "no log record-priority",
4081 NO_STR
4082 "Logging control\n"
4083 "Do not log the priority of the message within the message\n")
4084{
4085 zlog_default->record_priority = 0;
4086 return CMD_SUCCESS;
4087}
4088#endif
4089
4090DEFUN(banner_motd_file,
4091 banner_motd_file_cmd,
4092 "banner motd file [FILE]",
4093 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4094{
4095 if (host.motdfile)
4096 talloc_free(host.motdfile);
4097 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4098
4099 return CMD_SUCCESS;
4100}
4101
4102DEFUN(banner_motd_default,
4103 banner_motd_default_cmd,
4104 "banner motd default",
4105 "Set banner string\n" "Strings for motd\n" "Default string\n")
4106{
4107 host.motd = default_motd;
4108 return CMD_SUCCESS;
4109}
4110
4111DEFUN(no_banner_motd,
4112 no_banner_motd_cmd,
4113 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4114{
4115 host.motd = NULL;
4116 if (host.motdfile)
4117 talloc_free(host.motdfile);
4118 host.motdfile = NULL;
4119 return CMD_SUCCESS;
4120}
4121
4122/* Set config filename. Called from vty.c */
4123void host_config_set(const char *filename)
4124{
4125 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4126}
4127
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004128/*! Deprecated, now happens implicitly when calling install_node().
4129 * Users of the API may still attempt to call this function, hence
4130 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004131void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004132{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004133}
4134
4135/*! Deprecated, now happens implicitly when calling install_node().
4136 * Users of the API may still attempt to call this function, hence
4137 * leave it here as a no-op. */
4138void vty_install_default(int node)
4139{
4140}
4141
4142/*! Install common commands like 'exit' and 'list'. */
4143static void install_basic_node_commands(int node)
4144{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004145 install_lib_element(node, &config_help_cmd);
4146 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004147
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004148 install_lib_element(node, &show_vty_attr_all_cmd);
4149 install_lib_element(node, &show_vty_attr_cmd);
4150
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004151 install_lib_element(node, &config_write_terminal_cmd);
4152 install_lib_element(node, &config_write_file_cmd);
4153 install_lib_element(node, &config_write_memory_cmd);
4154 install_lib_element(node, &config_write_cmd);
4155 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004156
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004157 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004158
4159 if (node >= CONFIG_NODE) {
4160 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004161 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004162 }
4163}
4164
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004165/*! Return true if a node is installed by install_basic_node_commands(), so
4166 * that we can avoid repeating them for each and every node during 'show
4167 * running-config' */
4168static bool vty_command_is_common(struct cmd_element *cmd)
4169{
4170 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004171 || cmd == &show_vty_attr_all_cmd
4172 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004173 || cmd == &config_list_cmd
4174 || cmd == &config_write_terminal_cmd
4175 || cmd == &config_write_file_cmd
4176 || cmd == &config_write_memory_cmd
4177 || cmd == &config_write_cmd
4178 || cmd == &show_running_config_cmd
4179 || cmd == &config_exit_cmd
4180 || cmd == &config_end_cmd)
4181 return true;
4182 return false;
4183}
4184
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004185/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004186 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004187 * \param[in] vty the vty of the code
4188 * \param[in] filename where to store the file
4189 * \return 0 in case of success.
4190 *
4191 * If the filename already exists create a filename.sav
4192 * version with the current code.
4193 *
4194 */
4195int osmo_vty_write_config_file(const char *filename)
4196{
4197 char *failed_file;
4198 int rc;
4199
4200 rc = write_config_file(filename, &failed_file);
4201 talloc_free(failed_file);
4202 return rc;
4203}
4204
4205/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004206 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004207 * \return 0 in case of success.
4208 *
4209 * If the filename already exists create a filename.sav
4210 * version with the current code.
4211 *
4212 */
4213int osmo_vty_save_config_file(void)
4214{
4215 char *failed_file;
4216 int rc;
4217
4218 if (host.config == NULL)
4219 return -7;
4220
4221 rc = write_config_file(host.config, &failed_file);
4222 talloc_free(failed_file);
4223 return rc;
4224}
4225
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004226/* Initialize command interface. Install basic nodes and commands. */
4227void cmd_init(int terminal)
4228{
4229 /* Allocate initial top vector of commands. */
4230 cmdvec = vector_init(VECTOR_MIN_SIZE);
4231
4232 /* Default host value settings. */
4233 host.name = NULL;
4234 host.password = NULL;
4235 host.enable = NULL;
4236 host.logfile = NULL;
4237 host.config = NULL;
4238 host.lines = -1;
4239 host.motd = default_motd;
4240 host.motdfile = NULL;
4241
4242 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004243 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004244 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004245 install_node_bare(&auth_node, NULL);
4246 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004247 install_node(&config_node, config_write_host);
4248
4249 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004250 install_lib_element(VIEW_NODE, &show_version_cmd);
4251 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004252 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004253 install_lib_element(VIEW_NODE, &config_list_cmd);
4254 install_lib_element(VIEW_NODE, &config_exit_cmd);
4255 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004256 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4257 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004258 install_lib_element(VIEW_NODE, &config_enable_cmd);
4259 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4260 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4261 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004262 }
4263
4264 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004265 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4266 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4267 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004268 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004269 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4270 install_lib_element(ENABLE_NODE, &show_version_cmd);
4271 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004272
4273 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004274 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4275 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4276 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004277 }
4278
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004279 install_lib_element(CONFIG_NODE, &hostname_cmd);
4280 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004281
4282 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004283 install_lib_element(CONFIG_NODE, &password_cmd);
4284 install_lib_element(CONFIG_NODE, &password_text_cmd);
4285 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4286 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4287 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004288
4289#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004290 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4291 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004292#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004293 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4294 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4295 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4296 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4297 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004298
4299 }
4300 srand(time(NULL));
4301}
Harald Welte7acb30c2011-08-17 17:13:48 +02004302
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004303/* FIXME: execute this section in the unit test instead */
4304static __attribute__((constructor)) void on_dso_load(void)
4305{
4306 unsigned int i, j;
4307
4308 /* Check total number of the library specific attributes */
4309 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4310
4311 /* Check for duplicates in the list of library specific flags */
4312 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4313 if (cmd_lib_attr_letters[i] == '\0')
4314 continue;
4315
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004316 /* Some flag characters are reserved for global attributes */
4317 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4318 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4319 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4320
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004321 /* Only upper case flag letters are allowed for libraries */
4322 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4323 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4324
4325 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4326 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4327 }
4328}
4329
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004330/*! @} */