blob: 53ebcd865c186b9a5512da8331559c586c12eee1 [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" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700627 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700628 { 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) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700642 case CMD_ATTR_HIDDEN:
643 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700644 case CMD_ATTR_IMMEDIATE:
645 return '!';
646 case CMD_ATTR_NODE_EXIT:
647 return '@';
648 default:
649 return '.';
650 }
651}
652
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700653/* Description of attributes shared between the lib commands */
654static const char * const cmd_lib_attr_desc[32] = {
655 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
656 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200657 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
658 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200659 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
660 "This command applies on IPA link establishment",
661 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
662 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700663};
664
665/* Flag letters of attributes shared between the lib commands.
666 * NOTE: uppercase letters only, the rest is reserved for applications. */
667static const char cmd_lib_attr_letters[32] = {
668 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200669 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200670 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
671 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700672};
673
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100674/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200675 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100676 */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700677static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
678 void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100679{
680 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700681 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100682
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200683 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700684
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700685 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700686 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700687 print_func(data, " <attributes scope='global'>%s", newline);
688
689 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
690 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700691 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700692
693 if (~cmd->attr & cmd_attr_desc[i].value)
694 continue;
695
696 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700697 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700698 xml_att_desc, newline);
699 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700700
701 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
702 if (flag != '.')
703 print_func(data, " flag='%c'", flag);
704 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700705 }
706
707 print_func(data, " </attributes>%s", newline);
708 }
709
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700710 /* Print application specific attributes and their description */
711 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700712 const char * const *desc;
713 const char *letters;
714
715 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
716 print_func(data, " <attributes scope='library'>%s", newline);
717 letters = &cmd_lib_attr_letters[0];
718 desc = &cmd_lib_attr_desc[0];
719 } else {
720 print_func(data, " <attributes scope='application'>%s", newline);
721 letters = &host.app_info->usr_attr_letters[0];
722 desc = &host.app_info->usr_attr_desc[0];
723 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700724
725 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
726 char *xml_att_desc;
727 char flag;
728
729 /* Skip attribute if *not* set */
730 if (~cmd->usrattr & (1 << i))
731 continue;
732
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700733 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700734 print_func(data, " <attribute doc='%s'", xml_att_desc);
735 talloc_free(xml_att_desc);
736
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700737 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700738 print_func(data, " flag='%c'", flag);
739 print_func(data, " />%s", newline);
740 }
741
742 print_func(data, " </attributes>%s", newline);
743 }
744
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200745 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100746
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700747 for (i = 0; i < vector_count(cmd->strvec); ++i) {
748 vector descvec = vector_slot(cmd->strvec, i);
749 int j;
750 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100751 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700752 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100753 if (desc == NULL)
754 continue;
755
756 xml_param = xml_escape(desc->cmd);
757 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200758 print_func(data, " <param name='%s' doc='%s' />%s",
759 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100760 talloc_free(xml_param);
761 talloc_free(xml_doc);
762 }
763 }
764
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200765 print_func(data, " </params>%s", newline);
766 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100767
768 talloc_free(xml_string);
769 return 0;
770}
771
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700772static bool vty_command_is_common(const struct cmd_element *cmd);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200773
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100774/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200775 * 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 +0100776 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200777static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100778{
779 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200780 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100781
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200782 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100783
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200784 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200785 print_func(data, " <node id='_common_cmds_'>%s", newline);
786 print_func(data, " <name>Common Commands</name>%s", newline);
787 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
788 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200789 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700790 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200791 if (!cnode)
792 continue;
793 if (cnode->node != CONFIG_NODE)
794 continue;
795
796 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700797 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200798 if (!vty_command_is_common(elem))
799 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700800 if (elem->attr & CMD_ATTR_DEPRECATED)
801 continue;
802 if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
803 continue;
804 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200805 }
806 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200807 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200808
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100809 for (i = 0; i < vector_active(cmdvec); ++i) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700810 const struct cmd_node *cnode = vector_slot(cmdvec, i);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100811 if (!cnode)
812 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200813 if (vector_active(cnode->cmd_vector) < 1)
814 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100815
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200816 /* De-dup node IDs: how many times has this same name been used before? Count the first
817 * occurence as _1 and omit that first suffix, so that the first occurence is called
818 * 'name', the second becomes 'name_2', then 'name_3', ... */
819 same_name_count = 1;
820 for (j = 0; j < i; ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700821 const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200822 if (!cnode2)
823 continue;
824 if (strcmp(cnode->name, cnode2->name) == 0)
825 same_name_count ++;
826 }
827
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200828 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200829 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200830 print_func(data, "_%d", same_name_count);
831 print_func(data, "'>%s", newline);
832 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100833
834 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
Vadim Yanitskiyefe13422020-10-21 20:36:17 +0700835 const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200836 if (vty_command_is_common(elem))
837 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700838 if (elem->attr & CMD_ATTR_DEPRECATED)
839 continue;
840 if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
841 continue;
842 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100843 }
844
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200845 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100846 }
847
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200848 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100849
850 return 0;
851}
852
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200853static int print_func_vty(void *data, const char *format, ...)
854{
855 struct vty *vty = data;
856 va_list args;
857 int rc;
858 va_start(args, format);
859 rc = vty_out_va(vty, format, args);
860 va_end(args);
861 return rc;
862}
863
864static int vty_dump_xml_ref_to_vty(struct vty *vty)
865{
866 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
867}
868
869static int print_func_stream(void *data, const char *format, ...)
870{
871 va_list args;
872 int rc;
873 va_start(args, format);
874 rc = vfprintf((FILE*)data, format, args);
875 va_end(args);
876 return rc;
877}
878
879/*! Print the XML reference of all VTY nodes to the given stream.
880 */
881int vty_dump_xml_ref(FILE *stream)
882{
883 return vty_dump_nodes(print_func_stream, stream, "\n");
884}
885
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200886/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100887static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
888{
889 int i;
890
891 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
892 struct cmd_element *elem;
893 elem = vector_slot(cnode->cmd_vector, i);
894 if (!elem->string)
895 continue;
896 if (!strcmp(elem->string, cmdstring))
897 return 1;
898 }
899 return 0;
900}
901
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200902/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200903 * \param[in] ntype Node Type
904 * \param[cmd] element to be installed
905 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000906void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200907{
908 struct cmd_node *cnode;
909
910 cnode = vector_slot(cmdvec, ntype);
911
Harald Weltea99d45a2015-11-12 13:48:23 +0100912 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100913 /* ensure no _identical_ command has been registered at this
914 * node so far */
915 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200916
917 vector_set(cnode->cmd_vector, cmd);
918
919 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
920 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
921}
922
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700923/*! Install a library command into a node
924 * \param[in] ntype Node Type
925 * \param[in] cmd element to be installed
926 */
927void install_lib_element(int ntype, struct cmd_element *cmd)
928{
929 cmd->attr |= CMD_ATTR_LIB_COMMAND;
930 install_element(ntype, cmd);
931}
932
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200933/* Install a command into VIEW and ENABLE node */
934void install_element_ve(struct cmd_element *cmd)
935{
936 install_element(VIEW_NODE, cmd);
937 install_element(ENABLE_NODE, cmd);
938}
939
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700940/* Install a library command into VIEW and ENABLE node */
941void install_lib_element_ve(struct cmd_element *cmd)
942{
943 cmd->attr |= CMD_ATTR_LIB_COMMAND;
944 install_element_ve(cmd);
945}
946
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200947#ifdef VTY_CRYPT_PW
948static unsigned char itoa64[] =
949 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
950
951static void to64(char *s, long v, int n)
952{
953 while (--n >= 0) {
954 *s++ = itoa64[v & 0x3f];
955 v >>= 6;
956 }
957}
958
959static char *zencrypt(const char *passwd)
960{
961 char salt[6];
962 struct timeval tv;
963 char *crypt(const char *, const char *);
964
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200965 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200966
967 to64(&salt[0], random(), 3);
968 to64(&salt[3], tv.tv_usec, 3);
969 salt[5] = '\0';
970
971 return crypt(passwd, salt);
972}
973#endif
974
975/* This function write configuration of this host. */
976static int config_write_host(struct vty *vty)
977{
978 if (host.name)
979 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
980
981 if (host.encrypt) {
982 if (host.password_encrypt)
983 vty_out(vty, "password 8 %s%s", host.password_encrypt,
984 VTY_NEWLINE);
985 if (host.enable_encrypt)
986 vty_out(vty, "enable password 8 %s%s",
987 host.enable_encrypt, VTY_NEWLINE);
988 } else {
989 if (host.password)
990 vty_out(vty, "password %s%s", host.password,
991 VTY_NEWLINE);
992 if (host.enable)
993 vty_out(vty, "enable password %s%s", host.enable,
994 VTY_NEWLINE);
995 }
996
997 if (host.advanced)
998 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
999
1000 if (host.encrypt)
1001 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1002
1003 if (host.lines >= 0)
1004 vty_out(vty, "service terminal-length %d%s", host.lines,
1005 VTY_NEWLINE);
1006
1007 if (host.motdfile)
1008 vty_out(vty, "banner motd file %s%s", host.motdfile,
1009 VTY_NEWLINE);
1010 else if (!host.motd)
1011 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1012
1013 return 1;
1014}
1015
1016/* Utility function for getting command vector. */
1017static vector cmd_node_vector(vector v, enum node_type ntype)
1018{
1019 struct cmd_node *cnode = vector_slot(v, ntype);
1020 return cnode->cmd_vector;
1021}
1022
1023/* Completion match types. */
1024enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001025 NO_MATCH = 0,
1026 ANY_MATCH,
1027 EXTEND_MATCH,
1028 IPV4_PREFIX_MATCH,
1029 IPV4_MATCH,
1030 IPV6_PREFIX_MATCH,
1031 IPV6_MATCH,
1032 RANGE_MATCH,
1033 VARARG_MATCH,
1034 PARTLY_MATCH,
1035 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001036};
1037
1038static enum match_type cmd_ipv4_match(const char *str)
1039{
1040 const char *sp;
1041 int dots = 0, nums = 0;
1042 char buf[4];
1043
1044 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001045 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001046
1047 for (;;) {
1048 memset(buf, 0, sizeof(buf));
1049 sp = str;
1050 while (*str != '\0') {
1051 if (*str == '.') {
1052 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001053 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001054
1055 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001056 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001057
1058 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001059 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001060
1061 dots++;
1062 break;
1063 }
1064 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001065 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001066
1067 str++;
1068 }
1069
1070 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001071 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001072
1073 strncpy(buf, sp, str - sp);
1074 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001075 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001076
1077 nums++;
1078
1079 if (*str == '\0')
1080 break;
1081
1082 str++;
1083 }
1084
1085 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001086 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001087
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001088 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001089}
1090
1091static enum match_type cmd_ipv4_prefix_match(const char *str)
1092{
1093 const char *sp;
1094 int dots = 0;
1095 char buf[4];
1096
1097 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001098 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001099
1100 for (;;) {
1101 memset(buf, 0, sizeof(buf));
1102 sp = str;
1103 while (*str != '\0' && *str != '/') {
1104 if (*str == '.') {
1105 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001106 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001107
1108 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001109 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001110
1111 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001112 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001113
1114 dots++;
1115 break;
1116 }
1117
1118 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001119 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001120
1121 str++;
1122 }
1123
1124 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126
1127 strncpy(buf, sp, str - sp);
1128 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001129 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130
1131 if (dots == 3) {
1132 if (*str == '/') {
1133 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001134 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001135
1136 str++;
1137 break;
1138 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001139 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001140 }
1141
1142 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001143 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001144
1145 str++;
1146 }
1147
1148 sp = str;
1149 while (*str != '\0') {
1150 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001151 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001152
1153 str++;
1154 }
1155
1156 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001157 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001158
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001159 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001160}
1161
1162#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1163#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1164#define STATE_START 1
1165#define STATE_COLON 2
1166#define STATE_DOUBLE 3
1167#define STATE_ADDR 4
1168#define STATE_DOT 5
1169#define STATE_SLASH 6
1170#define STATE_MASK 7
1171
1172#ifdef HAVE_IPV6
1173
1174static enum match_type cmd_ipv6_match(const char *str)
1175{
1176 int state = STATE_START;
1177 int colons = 0, nums = 0, double_colon = 0;
1178 const char *sp = NULL;
1179 struct sockaddr_in6 sin6_dummy;
1180 int ret;
1181
1182 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001183 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001184
1185 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001186 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001187
1188 /* use inet_pton that has a better support,
1189 * for example inet_pton can support the automatic addresses:
1190 * ::1.2.3.4
1191 */
1192 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1193
1194 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001195 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001196
1197 while (*str != '\0') {
1198 switch (state) {
1199 case STATE_START:
1200 if (*str == ':') {
1201 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001202 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001203 colons--;
1204 state = STATE_COLON;
1205 } else {
1206 sp = str;
1207 state = STATE_ADDR;
1208 }
1209
1210 continue;
1211 case STATE_COLON:
1212 colons++;
1213 if (*(str + 1) == ':')
1214 state = STATE_DOUBLE;
1215 else {
1216 sp = str + 1;
1217 state = STATE_ADDR;
1218 }
1219 break;
1220 case STATE_DOUBLE:
1221 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001222 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001223
1224 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226 else {
1227 if (*(str + 1) != '\0')
1228 colons++;
1229 sp = str + 1;
1230 state = STATE_ADDR;
1231 }
1232
1233 double_colon++;
1234 nums++;
1235 break;
1236 case STATE_ADDR:
1237 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1238 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001239 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001240
1241 nums++;
1242 state = STATE_COLON;
1243 }
1244 if (*(str + 1) == '.')
1245 state = STATE_DOT;
1246 break;
1247 case STATE_DOT:
1248 state = STATE_ADDR;
1249 break;
1250 default:
1251 break;
1252 }
1253
1254 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001255 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001256
1257 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001258 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259
1260 str++;
1261 }
1262
1263#if 0
1264 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001265 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001266#endif /* 0 */
1267
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001268 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001269}
1270
1271static enum match_type cmd_ipv6_prefix_match(const char *str)
1272{
1273 int state = STATE_START;
1274 int colons = 0, nums = 0, double_colon = 0;
1275 int mask;
1276 const char *sp = NULL;
1277 char *endptr = NULL;
1278
1279 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001280 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001281
1282 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001283 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001284
1285 while (*str != '\0' && state != STATE_MASK) {
1286 switch (state) {
1287 case STATE_START:
1288 if (*str == ':') {
1289 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001290 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001291 colons--;
1292 state = STATE_COLON;
1293 } else {
1294 sp = str;
1295 state = STATE_ADDR;
1296 }
1297
1298 continue;
1299 case STATE_COLON:
1300 colons++;
1301 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001302 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001303 else if (*(str + 1) == ':')
1304 state = STATE_DOUBLE;
1305 else {
1306 sp = str + 1;
1307 state = STATE_ADDR;
1308 }
1309 break;
1310 case STATE_DOUBLE:
1311 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001312 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001313
1314 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001315 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001316 else {
1317 if (*(str + 1) != '\0' && *(str + 1) != '/')
1318 colons++;
1319 sp = str + 1;
1320
1321 if (*(str + 1) == '/')
1322 state = STATE_SLASH;
1323 else
1324 state = STATE_ADDR;
1325 }
1326
1327 double_colon++;
1328 nums += 1;
1329 break;
1330 case STATE_ADDR:
1331 if (*(str + 1) == ':' || *(str + 1) == '.'
1332 || *(str + 1) == '\0' || *(str + 1) == '/') {
1333 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001334 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001335
1336 for (; sp <= str; sp++)
1337 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001338 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001339
1340 nums++;
1341
1342 if (*(str + 1) == ':')
1343 state = STATE_COLON;
1344 else if (*(str + 1) == '.')
1345 state = STATE_DOT;
1346 else if (*(str + 1) == '/')
1347 state = STATE_SLASH;
1348 }
1349 break;
1350 case STATE_DOT:
1351 state = STATE_ADDR;
1352 break;
1353 case STATE_SLASH:
1354 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001355 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001356
1357 state = STATE_MASK;
1358 break;
1359 default:
1360 break;
1361 }
1362
1363 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001364 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001365
1366 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001367 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001368
1369 str++;
1370 }
1371
1372 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001373 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001374
1375 mask = strtol(str, &endptr, 10);
1376 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001377 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378
1379 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001380 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001381
1382/* I don't know why mask < 13 makes command match partly.
1383 Forgive me to make this comments. I Want to set static default route
1384 because of lack of function to originate default in ospf6d; sorry
1385 yasu
1386 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001387 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001388*/
1389
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001390 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001391}
1392
1393#endif /* HAVE_IPV6 */
1394
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001395
1396#if ULONG_MAX == 18446744073709551615UL
1397#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1398#elif ULONG_MAX == 4294967295UL
1399#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1400#else
1401#error "ULONG_MAX not defined!"
1402#endif
1403
1404#if LONG_MAX == 9223372036854775807L
1405#define DECIMAL_STRLEN_MAX_SIGNED 19
1406#elif LONG_MAX == 2147483647L
1407#define DECIMAL_STRLEN_MAX_SIGNED 10
1408#else
1409#error "LONG_MAX not defined!"
1410#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001411
1412static int cmd_range_match(const char *range, const char *str)
1413{
1414 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001415 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001416 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001417
1418 if (str == NULL)
1419 return 1;
1420
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001421 if (range[1] == '-') {
1422 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001423
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001424 val = strtol(str, &endptr, 10);
1425 if (*endptr != '\0')
1426 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001427
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001428 range += 2;
1429 p = strchr(range, '-');
1430 if (p == NULL)
1431 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001432 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001433 return 0;
1434 strncpy(buf, range, p - range);
1435 buf[p - range] = '\0';
1436 min = -strtol(buf, &endptr, 10);
1437 if (*endptr != '\0')
1438 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001439
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001440 range = p + 1;
1441 p = strchr(range, '>');
1442 if (p == NULL)
1443 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001444 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001445 return 0;
1446 strncpy(buf, range, p - range);
1447 buf[p - range] = '\0';
1448 max = strtol(buf, &endptr, 10);
1449 if (*endptr != '\0')
1450 return 0;
1451
1452 if (val < min || val > max)
1453 return 0;
1454 } else {
1455 unsigned long min, max, val;
1456
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001457 if (str[0] == '-')
1458 return 0;
1459
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001460 val = strtoul(str, &endptr, 10);
1461 if (*endptr != '\0')
1462 return 0;
1463
1464 range++;
1465 p = strchr(range, '-');
1466 if (p == NULL)
1467 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001468 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001469 return 0;
1470 strncpy(buf, range, p - range);
1471 buf[p - range] = '\0';
1472 min = strtoul(buf, &endptr, 10);
1473 if (*endptr != '\0')
1474 return 0;
1475
1476 range = p + 1;
1477 p = strchr(range, '>');
1478 if (p == NULL)
1479 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001480 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001481 return 0;
1482 strncpy(buf, range, p - range);
1483 buf[p - range] = '\0';
1484 max = strtoul(buf, &endptr, 10);
1485 if (*endptr != '\0')
1486 return 0;
1487
1488 if (val < min || val > max)
1489 return 0;
1490 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001491
1492 return 1;
1493}
1494
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001495/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001496static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001497{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001498 /* we've got "[blah]". We want to strip off the []s and redo the
1499 * match check for "blah"
1500 */
1501 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001502
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001503 if (len < 3)
1504 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001505
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001506 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001507}
1508
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001509static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001510cmd_match(const char *str, const char *command,
1511 enum match_type min, bool recur)
1512{
1513
1514 if (recur && CMD_OPTION(str))
1515 {
1516 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001517 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001518
1519 /* this would be a bug in a command, however handle it gracefully
1520 * as it we only discover it if a user tries to run it
1521 */
1522 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001523 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001524
1525 ret = cmd_match(tmp, command, min, false);
1526
1527 talloc_free(tmp);
1528
1529 return ret;
1530 }
1531 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001532 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001533 else if (CMD_RANGE(str))
1534 {
1535 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001536 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001537 }
1538#ifdef HAVE_IPV6
1539 else if (CMD_IPV6(str))
1540 {
1541 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001542 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001543 }
1544 else if (CMD_IPV6_PREFIX(str))
1545 {
1546 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001547 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001548 }
1549#endif /* HAVE_IPV6 */
1550 else if (CMD_IPV4(str))
1551 {
1552 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001553 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001554 }
1555 else if (CMD_IPV4_PREFIX(str))
1556 {
1557 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001558 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001559 }
1560 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001561 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001562 else if (strncmp(command, str, strlen(command)) == 0)
1563 {
1564 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001565 return EXACT_MATCH;
1566 else if (PARTLY_MATCH >= min)
1567 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001568 }
1569
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001570 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001571}
1572
1573/* Filter vector at the specified index and by the given command string, to
1574 * the desired matching level (thus allowing part matches), and return match
1575 * type flag.
1576 */
1577static enum match_type
1578cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001579{
1580 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001581 struct cmd_element *cmd_element;
1582 enum match_type match_type;
1583 vector descvec;
1584 struct desc *desc;
1585
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001586 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001587
1588 /* If command and cmd_element string does not match set NULL to vector */
1589 for (i = 0; i < vector_active(v); i++)
1590 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001591 if (index >= vector_active(cmd_element->strvec))
1592 vector_slot(v, i) = NULL;
1593 else {
1594 unsigned int j;
1595 int matched = 0;
1596
1597 descvec =
1598 vector_slot(cmd_element->strvec, index);
1599
1600 for (j = 0; j < vector_active(descvec); j++)
1601 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001602 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001603
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001604 ret = cmd_match (desc->cmd, command, level, true);
1605
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001606 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001607 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001608
1609 if (match_type < ret)
1610 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001611 }
1612 if (!matched)
1613 vector_slot(v, i) = NULL;
1614 }
1615 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001616
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001617 if (match_type == NO_MATCH)
1618 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001619
1620 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1621 * go again and filter out commands whose argument (at this index) is
1622 * 'weaker'. E.g., if we have 2 commands:
1623 *
1624 * foo bar <1-255>
1625 * foo bar BLAH
1626 *
1627 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001628 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001629 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1630 *
1631 * If we don't do a 2nd pass and filter it out, the higher-layers will
1632 * consider this to be ambiguous.
1633 */
1634 for (i = 0; i < vector_active(v); i++)
1635 if ((cmd_element = vector_slot(v, i)) != NULL) {
1636 if (index >= vector_active(cmd_element->strvec))
1637 vector_slot(v, i) = NULL;
1638 else {
1639 unsigned int j;
1640 int matched = 0;
1641
1642 descvec =
1643 vector_slot(cmd_element->strvec, index);
1644
1645 for (j = 0; j < vector_active(descvec); j++)
1646 if ((desc = vector_slot(descvec, j))) {
1647 enum match_type ret;
1648
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001649 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001650
1651 if (ret >= match_type)
1652 matched++;
1653 }
1654 if (!matched)
1655 vector_slot(v, i) = NULL;
1656 }
1657 }
1658
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001659 return match_type;
1660}
1661
1662/* Check ambiguous match */
1663static int
1664is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1665{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001666 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001667 unsigned int i;
1668 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001669 struct cmd_element *cmd_element;
1670 const char *matched = NULL;
1671 vector descvec;
1672 struct desc *desc;
1673
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001674 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1675 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1676 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1677 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1678 * that case, the string must remain allocated until this function exits or another match comes
1679 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1680 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1681 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1682 void *cmd_deopt_ctx = NULL;
1683
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001684 for (i = 0; i < vector_active(v); i++) {
1685 cmd_element = vector_slot(v, i);
1686 if (!cmd_element)
1687 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001688
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001689 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001690
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001691 descvec = vector_slot(cmd_element->strvec, index);
1692
1693 for (j = 0; j < vector_active(descvec); j++) {
1694 desc = vector_slot(descvec, j);
1695 if (!desc)
1696 continue;
1697
1698 enum match_type mtype;
1699 const char *str = desc->cmd;
1700
1701 if (CMD_OPTION(str)) {
1702 if (!cmd_deopt_ctx)
1703 cmd_deopt_ctx =
1704 talloc_named_const(tall_vty_cmd_ctx, 0,
1705 __func__);
1706 str = cmd_deopt(cmd_deopt_ctx, str);
1707 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001708 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001709 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001710
1711 switch (type) {
1712 case EXACT_MATCH:
1713 if (!(CMD_VARIABLE (str))
1714 && strcmp(command, str) == 0)
1715 match++;
1716 break;
1717 case PARTLY_MATCH:
1718 if (!(CMD_VARIABLE (str))
1719 && strncmp(command, str, strlen (command)) == 0)
1720 {
1721 if (matched
1722 && strcmp(matched,
1723 str) != 0) {
1724 ret = 1; /* There is ambiguous match. */
1725 goto free_and_return;
1726 } else
1727 matched = str;
1728 match++;
1729 }
1730 break;
1731 case RANGE_MATCH:
1732 if (cmd_range_match
1733 (str, command)) {
1734 if (matched
1735 && strcmp(matched,
1736 str) != 0) {
1737 ret = 1;
1738 goto free_and_return;
1739 } else
1740 matched = str;
1741 match++;
1742 }
1743 break;
1744#ifdef HAVE_IPV6
1745 case IPV6_MATCH:
1746 if (CMD_IPV6(str))
1747 match++;
1748 break;
1749 case IPV6_PREFIX_MATCH:
1750 if ((mtype =
1751 cmd_ipv6_prefix_match
1752 (command)) != NO_MATCH) {
1753 if (mtype == PARTLY_MATCH) {
1754 ret = 2; /* There is incomplete match. */
1755 goto free_and_return;
1756 }
1757
1758 match++;
1759 }
1760 break;
1761#endif /* HAVE_IPV6 */
1762 case IPV4_MATCH:
1763 if (CMD_IPV4(str))
1764 match++;
1765 break;
1766 case IPV4_PREFIX_MATCH:
1767 if ((mtype =
1768 cmd_ipv4_prefix_match
1769 (command)) != NO_MATCH) {
1770 if (mtype == PARTLY_MATCH) {
1771 ret = 2; /* There is incomplete match. */
1772 goto free_and_return;
1773 }
1774
1775 match++;
1776 }
1777 break;
1778 case EXTEND_MATCH:
1779 if (CMD_VARIABLE (str))
1780 match++;
1781 break;
1782 case NO_MATCH:
1783 default:
1784 break;
1785 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001786 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001787 if (!match)
1788 vector_slot(v, i) = NULL;
1789 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001790
1791free_and_return:
1792 if (cmd_deopt_ctx)
1793 talloc_free(cmd_deopt_ctx);
1794 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001795}
1796
1797/* If src matches dst return dst string, otherwise return NULL */
1798static const char *cmd_entry_function(const char *src, const char *dst)
1799{
1800 /* Skip variable arguments. */
1801 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1802 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1803 return NULL;
1804
1805 /* In case of 'command \t', given src is NULL string. */
1806 if (src == NULL)
1807 return dst;
1808
1809 /* Matched with input string. */
1810 if (strncmp(src, dst, strlen(src)) == 0)
1811 return dst;
1812
1813 return NULL;
1814}
1815
1816/* If src matches dst return dst string, otherwise return NULL */
1817/* This version will return the dst string always if it is
1818 CMD_VARIABLE for '?' key processing */
1819static const char *cmd_entry_function_desc(const char *src, const char *dst)
1820{
1821 if (CMD_VARARG(dst))
1822 return dst;
1823
1824 if (CMD_RANGE(dst)) {
1825 if (cmd_range_match(dst, src))
1826 return dst;
1827 else
1828 return NULL;
1829 }
1830#ifdef HAVE_IPV6
1831 if (CMD_IPV6(dst)) {
1832 if (cmd_ipv6_match(src))
1833 return dst;
1834 else
1835 return NULL;
1836 }
1837
1838 if (CMD_IPV6_PREFIX(dst)) {
1839 if (cmd_ipv6_prefix_match(src))
1840 return dst;
1841 else
1842 return NULL;
1843 }
1844#endif /* HAVE_IPV6 */
1845
1846 if (CMD_IPV4(dst)) {
1847 if (cmd_ipv4_match(src))
1848 return dst;
1849 else
1850 return NULL;
1851 }
1852
1853 if (CMD_IPV4_PREFIX(dst)) {
1854 if (cmd_ipv4_prefix_match(src))
1855 return dst;
1856 else
1857 return NULL;
1858 }
1859
1860 /* Optional or variable commands always match on '?' */
1861 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1862 return dst;
1863
1864 /* In case of 'command \t', given src is NULL string. */
1865 if (src == NULL)
1866 return dst;
1867
1868 if (strncmp(src, dst, strlen(src)) == 0)
1869 return dst;
1870 else
1871 return NULL;
1872}
1873
1874/* Check same string element existence. If it isn't there return
1875 1. */
1876static int cmd_unique_string(vector v, const char *str)
1877{
1878 unsigned int i;
1879 char *match;
1880
1881 for (i = 0; i < vector_active(v); i++)
1882 if ((match = vector_slot(v, i)) != NULL)
1883 if (strcmp(match, str) == 0)
1884 return 0;
1885 return 1;
1886}
1887
1888/* Compare string to description vector. If there is same string
1889 return 1 else return 0. */
1890static int desc_unique_string(vector v, const char *str)
1891{
1892 unsigned int i;
1893 struct desc *desc;
1894
1895 for (i = 0; i < vector_active(v); i++)
1896 if ((desc = vector_slot(v, i)) != NULL)
1897 if (strcmp(desc->cmd, str) == 0)
1898 return 1;
1899 return 0;
1900}
1901
1902static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1903{
1904 if (first_word != NULL &&
1905 node != AUTH_NODE &&
1906 node != VIEW_NODE &&
1907 node != AUTH_ENABLE_NODE &&
1908 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1909 return 1;
1910 return 0;
1911}
1912
1913/* '?' describe command support. */
1914static vector
1915cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1916{
1917 unsigned int i;
1918 vector cmd_vector;
1919#define INIT_MATCHVEC_SIZE 10
1920 vector matchvec;
1921 struct cmd_element *cmd_element;
1922 unsigned int index;
1923 int ret;
1924 enum match_type match;
1925 char *command;
1926 static struct desc desc_cr = { "<cr>", "" };
1927
1928 /* Set index. */
1929 if (vector_active(vline) == 0) {
1930 *status = CMD_ERR_NO_MATCH;
1931 return NULL;
1932 } else
1933 index = vector_active(vline) - 1;
1934
1935 /* Make copy vector of current node's command vector. */
1936 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1937
1938 /* Prepare match vector */
1939 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1940
1941 /* Filter commands. */
1942 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001943 for (i = 0; i < index; i++) {
1944 command = vector_slot(vline, i);
1945 if (!command)
1946 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001947
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001948 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001949
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001950 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001951 struct cmd_element *cmd_element;
1952 vector descvec;
1953 unsigned int j, k;
1954
1955 for (j = 0; j < vector_active(cmd_vector); j++)
1956 if ((cmd_element =
1957 vector_slot(cmd_vector, j)) != NULL
1958 &&
1959 (vector_active(cmd_element->strvec))) {
1960 descvec =
1961 vector_slot(cmd_element->
1962 strvec,
1963 vector_active
1964 (cmd_element->
1965 strvec) - 1);
1966 for (k = 0;
1967 k < vector_active(descvec);
1968 k++) {
1969 struct desc *desc =
1970 vector_slot(descvec,
1971 k);
1972 vector_set(matchvec,
1973 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001974 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001975 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001976
Harald Welte80d30fe2013-02-12 11:08:57 +01001977 vector_set(matchvec, &desc_cr);
1978 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001979
Harald Welte80d30fe2013-02-12 11:08:57 +01001980 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001981 }
1982
Harald Welte80d30fe2013-02-12 11:08:57 +01001983 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1984 match)) == 1) {
1985 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001986 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001987 *status = CMD_ERR_AMBIGUOUS;
1988 return NULL;
1989 } else if (ret == 2) {
1990 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001991 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001992 *status = CMD_ERR_NO_MATCH;
1993 return NULL;
1994 }
1995 }
1996
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001997 /* Prepare match vector */
1998 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1999
2000 /* Make sure that cmd_vector is filtered based on current word */
2001 command = vector_slot(vline, index);
2002 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002003 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002004
2005 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002006 for (i = 0; i < vector_active(cmd_vector); i++) {
2007 const char *string = NULL;
2008 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002009
Harald Welte80d30fe2013-02-12 11:08:57 +01002010 cmd_element = vector_slot(cmd_vector, i);
2011 if (!cmd_element)
2012 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002013
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002014 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2015 continue;
2016 if (!host.expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002017 continue;
2018
Harald Welte80d30fe2013-02-12 11:08:57 +01002019 strvec = cmd_element->strvec;
2020
2021 /* if command is NULL, index may be equal to vector_active */
2022 if (command && index >= vector_active(strvec))
2023 vector_slot(cmd_vector, i) = NULL;
2024 else {
2025 /* Check if command is completed. */
2026 if (command == NULL
2027 && index == vector_active(strvec)) {
2028 string = "<cr>";
2029 if (!desc_unique_string(matchvec, string))
2030 vector_set(matchvec, &desc_cr);
2031 } else {
2032 unsigned int j;
2033 vector descvec = vector_slot(strvec, index);
2034 struct desc *desc;
2035
2036 for (j = 0; j < vector_active(descvec); j++) {
2037 desc = vector_slot(descvec, j);
2038 if (!desc)
2039 continue;
2040 string = cmd_entry_function_desc
2041 (command, desc->cmd);
2042 if (!string)
2043 continue;
2044 /* Uniqueness check */
2045 if (!desc_unique_string(matchvec, string))
2046 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002047 }
2048 }
2049 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002050 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002051 vector_free(cmd_vector);
2052
2053 if (vector_slot(matchvec, 0) == NULL) {
2054 vector_free(matchvec);
2055 *status = CMD_ERR_NO_MATCH;
2056 } else
2057 *status = CMD_SUCCESS;
2058
2059 return matchvec;
2060}
2061
2062vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2063{
2064 vector ret;
2065
2066 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2067 enum node_type onode;
2068 vector shifted_vline;
2069 unsigned int index;
2070
2071 onode = vty->node;
2072 vty->node = ENABLE_NODE;
2073 /* We can try it on enable node, cos' the vty is authenticated */
2074
2075 shifted_vline = vector_init(vector_count(vline));
2076 /* use memcpy? */
2077 for (index = 1; index < vector_active(vline); index++) {
2078 vector_set_index(shifted_vline, index - 1,
2079 vector_lookup(vline, index));
2080 }
2081
2082 ret = cmd_describe_command_real(shifted_vline, vty, status);
2083
2084 vector_free(shifted_vline);
2085 vty->node = onode;
2086 return ret;
2087 }
2088
2089 return cmd_describe_command_real(vline, vty, status);
2090}
2091
2092/* Check LCD of matched command. */
2093static int cmd_lcd(char **matched)
2094{
2095 int i;
2096 int j;
2097 int lcd = -1;
2098 char *s1, *s2;
2099 char c1, c2;
2100
2101 if (matched[0] == NULL || matched[1] == NULL)
2102 return 0;
2103
2104 for (i = 1; matched[i] != NULL; i++) {
2105 s1 = matched[i - 1];
2106 s2 = matched[i];
2107
2108 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2109 if (c1 != c2)
2110 break;
2111
2112 if (lcd < 0)
2113 lcd = j;
2114 else {
2115 if (lcd > j)
2116 lcd = j;
2117 }
2118 }
2119 return lcd;
2120}
2121
2122/* Command line completion support. */
2123static char **cmd_complete_command_real(vector vline, struct vty *vty,
2124 int *status)
2125{
2126 unsigned int i;
2127 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2128#define INIT_MATCHVEC_SIZE 10
2129 vector matchvec;
2130 struct cmd_element *cmd_element;
2131 unsigned int index;
2132 char **match_str;
2133 struct desc *desc;
2134 vector descvec;
2135 char *command;
2136 int lcd;
2137
2138 if (vector_active(vline) == 0) {
2139 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002140 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002141 return NULL;
2142 } else
2143 index = vector_active(vline) - 1;
2144
2145 /* First, filter by preceeding command string */
2146 for (i = 0; i < index; i++)
2147 if ((command = vector_slot(vline, i))) {
2148 enum match_type match;
2149 int ret;
2150
2151 /* First try completion match, if there is exactly match return 1 */
2152 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002153 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002154
2155 /* If there is exact match then filter ambiguous match else check
2156 ambiguousness. */
2157 if ((ret =
2158 is_cmd_ambiguous(command, cmd_vector, i,
2159 match)) == 1) {
2160 vector_free(cmd_vector);
2161 *status = CMD_ERR_AMBIGUOUS;
2162 return NULL;
2163 }
2164 /*
2165 else if (ret == 2)
2166 {
2167 vector_free (cmd_vector);
2168 *status = CMD_ERR_NO_MATCH;
2169 return NULL;
2170 }
2171 */
2172 }
2173
2174 /* Prepare match vector. */
2175 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2176
2177 /* Now we got into completion */
2178 for (i = 0; i < vector_active(cmd_vector); i++)
2179 if ((cmd_element = vector_slot(cmd_vector, i))) {
2180 const char *string;
2181 vector strvec = cmd_element->strvec;
2182
2183 /* Check field length */
2184 if (index >= vector_active(strvec))
2185 vector_slot(cmd_vector, i) = NULL;
2186 else {
2187 unsigned int j;
2188
2189 descvec = vector_slot(strvec, index);
2190 for (j = 0; j < vector_active(descvec); j++)
2191 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002192 const char *cmd = desc->cmd;
2193 char *tmp = NULL;
2194
2195 if (CMD_OPTION(desc->cmd)) {
2196 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2197 cmd = tmp;
2198 }
2199 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002200 if (cmd_unique_string (matchvec, string))
2201 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002202 if (tmp)
2203 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002204 }
2205 }
2206 }
2207
2208 /* We don't need cmd_vector any more. */
2209 vector_free(cmd_vector);
2210
2211 /* No matched command */
2212 if (vector_slot(matchvec, 0) == NULL) {
2213 vector_free(matchvec);
2214
2215 /* In case of 'command \t' pattern. Do you need '?' command at
2216 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002217 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002218 *status = CMD_ERR_NOTHING_TODO;
2219 else
2220 *status = CMD_ERR_NO_MATCH;
2221 return NULL;
2222 }
2223
2224 /* Only one matched */
2225 if (vector_slot(matchvec, 1) == NULL) {
2226 match_str = (char **)matchvec->index;
2227 vector_only_wrapper_free(matchvec);
2228 *status = CMD_COMPLETE_FULL_MATCH;
2229 return match_str;
2230 }
2231 /* Make it sure last element is NULL. */
2232 vector_set(matchvec, NULL);
2233
2234 /* Check LCD of matched strings. */
2235 if (vector_slot(vline, index) != NULL) {
2236 lcd = cmd_lcd((char **)matchvec->index);
2237
2238 if (lcd) {
2239 int len = strlen(vector_slot(vline, index));
2240
2241 if (len < lcd) {
2242 char *lcdstr;
2243
2244 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2245 "complete-lcdstr");
2246 memcpy(lcdstr, matchvec->index[0], lcd);
2247 lcdstr[lcd] = '\0';
2248
2249 /* match_str = (char **) &lcdstr; */
2250
2251 /* Free matchvec. */
2252 for (i = 0; i < vector_active(matchvec); i++) {
2253 if (vector_slot(matchvec, i))
2254 talloc_free(vector_slot(matchvec, i));
2255 }
2256 vector_free(matchvec);
2257
2258 /* Make new matchvec. */
2259 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2260 vector_set(matchvec, lcdstr);
2261 match_str = (char **)matchvec->index;
2262 vector_only_wrapper_free(matchvec);
2263
2264 *status = CMD_COMPLETE_MATCH;
2265 return match_str;
2266 }
2267 }
2268 }
2269
2270 match_str = (char **)matchvec->index;
2271 vector_only_wrapper_free(matchvec);
2272 *status = CMD_COMPLETE_LIST_MATCH;
2273 return match_str;
2274}
2275
2276char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2277{
2278 char **ret;
2279
2280 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2281 enum node_type onode;
2282 vector shifted_vline;
2283 unsigned int index;
2284
2285 onode = vty->node;
2286 vty->node = ENABLE_NODE;
2287 /* We can try it on enable node, cos' the vty is authenticated */
2288
2289 shifted_vline = vector_init(vector_count(vline));
2290 /* use memcpy? */
2291 for (index = 1; index < vector_active(vline); index++) {
2292 vector_set_index(shifted_vline, index - 1,
2293 vector_lookup(vline, index));
2294 }
2295
2296 ret = cmd_complete_command_real(shifted_vline, vty, status);
2297
2298 vector_free(shifted_vline);
2299 vty->node = onode;
2300 return ret;
2301 }
2302
2303 return cmd_complete_command_real(vline, vty, status);
2304}
2305
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002306static struct vty_parent_node *vty_parent(struct vty *vty)
2307{
2308 return llist_first_entry_or_null(&vty->parent_nodes,
2309 struct vty_parent_node,
2310 entry);
2311}
2312
2313static bool vty_pop_parent(struct vty *vty)
2314{
2315 struct vty_parent_node *parent = vty_parent(vty);
2316 if (!parent)
2317 return false;
2318 llist_del(&parent->entry);
2319 vty->node = parent->node;
2320 vty->priv = parent->priv;
2321 if (vty->indent)
2322 talloc_free(vty->indent);
2323 vty->indent = parent->indent;
2324 talloc_free(parent);
2325 return true;
2326}
2327
2328static void vty_clear_parents(struct vty *vty)
2329{
2330 while (vty_pop_parent(vty));
2331}
2332
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002333/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002334/*
2335 * This function MUST eventually converge on a node when called repeatedly,
2336 * there must not be any cycles.
2337 * All 'config' nodes shall converge on CONFIG_NODE.
2338 * All other 'enable' nodes shall converge on ENABLE_NODE.
2339 * All 'view' only nodes shall converge on VIEW_NODE.
2340 * All other nodes shall converge on themselves or it must be ensured,
2341 * that the user's rights are not extended anyhow by calling this function.
2342 *
2343 * Note that these requirements also apply to all functions that are used
2344 * as go_parent_cb.
2345 * Note also that this function relies on the is_config_child callback to
2346 * recognize non-config nodes if go_parent_cb is not set.
2347 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002348int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002349{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002350 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002351 case AUTH_NODE:
2352 case VIEW_NODE:
2353 case ENABLE_NODE:
2354 case CONFIG_NODE:
2355 vty_clear_parents(vty);
2356 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002357
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002358 case AUTH_ENABLE_NODE:
2359 vty->node = VIEW_NODE;
2360 vty_clear_parents(vty);
2361 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002362
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002363 default:
2364 if (host.app_info->go_parent_cb)
2365 host.app_info->go_parent_cb(vty);
2366 vty_pop_parent(vty);
2367 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002368 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002369
2370 return vty->node;
2371}
2372
2373/* Execute command by argument vline vector. */
2374static int
2375cmd_execute_command_real(vector vline, struct vty *vty,
2376 struct cmd_element **cmd)
2377{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002378 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002379 unsigned int index;
2380 vector cmd_vector;
2381 struct cmd_element *cmd_element;
2382 struct cmd_element *matched_element;
2383 unsigned int matched_count, incomplete_count;
2384 int argc;
2385 const char *argv[CMD_ARGC_MAX];
2386 enum match_type match = 0;
2387 int varflag;
2388 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002389 int rc;
2390 /* Used for temporary storage of cmd_deopt() allocated arguments during
2391 argv[] generation */
2392 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002393
2394 /* Make copy of command elements. */
2395 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2396
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002397 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002398 if ((command = vector_slot(vline, index))) {
2399 int ret;
2400
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002401 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002402 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002403
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002404 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002405 break;
2406
2407 ret =
2408 is_cmd_ambiguous(command, cmd_vector, index, match);
2409
2410 if (ret == 1) {
2411 vector_free(cmd_vector);
2412 return CMD_ERR_AMBIGUOUS;
2413 } else if (ret == 2) {
2414 vector_free(cmd_vector);
2415 return CMD_ERR_NO_MATCH;
2416 }
2417 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002418 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002419
2420 /* Check matched count. */
2421 matched_element = NULL;
2422 matched_count = 0;
2423 incomplete_count = 0;
2424
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002425 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002426 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002427 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002428 || index >= cmd_element->cmdsize) {
2429 matched_element = cmd_element;
2430#if 0
2431 printf("DEBUG: %s\n", cmd_element->string);
2432#endif
2433 matched_count++;
2434 } else {
2435 incomplete_count++;
2436 }
2437 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002438 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002439
2440 /* Finish of using cmd_vector. */
2441 vector_free(cmd_vector);
2442
2443 /* To execute command, matched_count must be 1. */
2444 if (matched_count == 0) {
2445 if (incomplete_count)
2446 return CMD_ERR_INCOMPLETE;
2447 else
2448 return CMD_ERR_NO_MATCH;
2449 }
2450
2451 if (matched_count > 1)
2452 return CMD_ERR_AMBIGUOUS;
2453
2454 /* Argument treatment */
2455 varflag = 0;
2456 argc = 0;
2457
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002458 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2459
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002460 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002461 if (argc == CMD_ARGC_MAX) {
2462 rc = CMD_ERR_EXEED_ARGC_MAX;
2463 goto rc_free_deopt_ctx;
2464 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002465 if (varflag) {
2466 argv[argc++] = vector_slot(vline, i);
2467 continue;
2468 }
2469
2470 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002471 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002472
2473 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002474 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002475 struct desc *desc = vector_slot(descvec, 0);
2476
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002477 if (CMD_OPTION(desc->cmd)) {
2478 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2479 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2480 } else {
2481 tmp_cmd = desc->cmd;
2482 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002483
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002484 if (CMD_VARARG(tmp_cmd))
2485 varflag = 1;
2486 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002487 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002488 else if (CMD_OPTION(desc->cmd))
2489 argv[argc++] = tmp_cmd;
2490 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002491 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002492 /* multi choice argument. look up which choice
2493 the user meant (can only be one after
2494 filtering and checking for ambigous). For instance,
2495 if user typed "th" for "(two|three)" arg, we
2496 want to pass "three" in argv[]. */
2497 for (j = 0; j < vector_active(descvec); j++) {
2498 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002499 if (!desc)
2500 continue;
2501 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2502 continue;
2503 if (CMD_OPTION(desc->cmd)) {
2504 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2505 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2506 } else {
2507 tmp_cmd = desc->cmd;
2508 }
2509
2510 if(CMD_VARIABLE(tmp_cmd)) {
2511 argv[argc++] = vector_slot(vline, i);
2512 } else {
2513 argv[argc++] = tmp_cmd;
2514 }
2515 break;
2516 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002517 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002518 }
2519
2520 /* For vtysh execution. */
2521 if (cmd)
2522 *cmd = matched_element;
2523
2524 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002525 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002526 else {
2527 /* Execute matched command. */
2528 struct vty_parent_node this_node = {
2529 .node = vty->node,
2530 .priv = vty->priv,
2531 .indent = vty->indent,
2532 };
2533 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002534 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002535
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002536 /* If we have stepped down into a child node, push a parent frame.
2537 * The causality is such: we don't expect every single node entry implementation to push
2538 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2539 * a parent node. Hence if the node changed without the parent node changing, we must
2540 * have stepped into a child node. */
2541 if (vty->node != this_node.node && parent == vty_parent(vty)
2542 && vty->node > CONFIG_NODE) {
2543 /* Push the parent node. */
2544 parent = talloc_zero(vty, struct vty_parent_node);
2545 *parent = this_node;
2546 llist_add(&parent->entry, &vty->parent_nodes);
2547 }
2548 }
2549
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002550rc_free_deopt_ctx:
2551 /* Now after we called the command func, we can free temporary strings */
2552 talloc_free(cmd_deopt_ctx);
2553 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002554}
2555
2556int
2557cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2558 int vtysh)
2559{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002560 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002561 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002562
2563 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002564
2565 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2566 vector shifted_vline;
2567 unsigned int index;
2568
2569 vty->node = ENABLE_NODE;
2570 /* We can try it on enable node, cos' the vty is authenticated */
2571
2572 shifted_vline = vector_init(vector_count(vline));
2573 /* use memcpy? */
2574 for (index = 1; index < vector_active(vline); index++) {
2575 vector_set_index(shifted_vline, index - 1,
2576 vector_lookup(vline, index));
2577 }
2578
2579 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2580
2581 vector_free(shifted_vline);
2582 vty->node = onode;
2583 return ret;
2584 }
2585
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002586 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002587}
2588
2589/* Execute command by argument readline. */
2590int
2591cmd_execute_command_strict(vector vline, struct vty *vty,
2592 struct cmd_element **cmd)
2593{
2594 unsigned int i;
2595 unsigned int index;
2596 vector cmd_vector;
2597 struct cmd_element *cmd_element;
2598 struct cmd_element *matched_element;
2599 unsigned int matched_count, incomplete_count;
2600 int argc;
2601 const char *argv[CMD_ARGC_MAX];
2602 int varflag;
2603 enum match_type match = 0;
2604 char *command;
2605
2606 /* Make copy of command element */
2607 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2608
2609 for (index = 0; index < vector_active(vline); index++)
2610 if ((command = vector_slot(vline, index))) {
2611 int ret;
2612
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002613 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002614 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002615
2616 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002617 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002618 break;
2619
2620 ret =
2621 is_cmd_ambiguous(command, cmd_vector, index, match);
2622 if (ret == 1) {
2623 vector_free(cmd_vector);
2624 return CMD_ERR_AMBIGUOUS;
2625 }
2626 if (ret == 2) {
2627 vector_free(cmd_vector);
2628 return CMD_ERR_NO_MATCH;
2629 }
2630 }
2631
2632 /* Check matched count. */
2633 matched_element = NULL;
2634 matched_count = 0;
2635 incomplete_count = 0;
2636 for (i = 0; i < vector_active(cmd_vector); i++)
2637 if (vector_slot(cmd_vector, i) != NULL) {
2638 cmd_element = vector_slot(cmd_vector, i);
2639
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002640 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002641 || index >= cmd_element->cmdsize) {
2642 matched_element = cmd_element;
2643 matched_count++;
2644 } else
2645 incomplete_count++;
2646 }
2647
2648 /* Finish of using cmd_vector. */
2649 vector_free(cmd_vector);
2650
2651 /* To execute command, matched_count must be 1. */
2652 if (matched_count == 0) {
2653 if (incomplete_count)
2654 return CMD_ERR_INCOMPLETE;
2655 else
2656 return CMD_ERR_NO_MATCH;
2657 }
2658
2659 if (matched_count > 1)
2660 return CMD_ERR_AMBIGUOUS;
2661
2662 /* Argument treatment */
2663 varflag = 0;
2664 argc = 0;
2665
2666 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002667 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002668 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002669 if (varflag) {
2670 argv[argc++] = vector_slot(vline, i);
2671 continue;
2672 }
2673
2674 vector descvec = vector_slot(matched_element->strvec, i);
2675
2676 if (vector_active(descvec) == 1) {
2677 struct desc *desc = vector_slot(descvec, 0);
2678
2679 if (CMD_VARARG(desc->cmd))
2680 varflag = 1;
2681
2682 if (varflag || CMD_VARIABLE(desc->cmd)
2683 || CMD_OPTION(desc->cmd))
2684 argv[argc++] = vector_slot(vline, i);
2685 } else {
2686 argv[argc++] = vector_slot(vline, i);
2687 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002688 }
2689
2690 /* For vtysh execution. */
2691 if (cmd)
2692 *cmd = matched_element;
2693
2694 if (matched_element->daemon)
2695 return CMD_SUCCESS_DAEMON;
2696
2697 /* Now execute matched command */
2698 return (*matched_element->func) (matched_element, vty, argc, argv);
2699}
2700
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002701static inline size_t len(const char *str)
2702{
2703 return str? strlen(str) : 0;
2704}
2705
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002706/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2707 * is longer than b, a must start with exactly b, and vice versa.
2708 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2709 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002710static int indent_cmp(const char *a, const char *b)
2711{
2712 size_t al, bl;
2713 al = len(a);
2714 bl = len(b);
2715 if (al > bl) {
2716 if (bl && strncmp(a, b, bl) != 0)
2717 return EINVAL;
2718 return 1;
2719 }
2720 /* al <= bl */
2721 if (al && strncmp(a, b, al) != 0)
2722 return EINVAL;
2723 return (al < bl)? -1 : 0;
2724}
2725
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002726/* Configration make from file. */
2727int config_from_file(struct vty *vty, FILE * fp)
2728{
2729 int ret;
2730 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002731 char *indent;
2732 int cmp;
2733 struct vty_parent_node this_node;
2734 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002735
2736 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002737 indent = NULL;
2738 vline = NULL;
2739 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002740
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002741 if (ret != CMD_SUCCESS)
2742 goto return_invalid_indent;
2743
2744 /* In case of comment or empty line */
2745 if (vline == NULL) {
2746 if (indent) {
2747 talloc_free(indent);
2748 indent = NULL;
2749 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002750 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002751 }
2752
Neels Hofmeyr43063632017-09-19 23:54:01 +02002753 /* We have a nonempty line. */
2754 if (!vty->indent) {
2755 /* We have just entered a node and expecting the first child to come up; but we
2756 * may also skip right back to a parent or ancestor level. */
2757 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002758
Neels Hofmeyr43063632017-09-19 23:54:01 +02002759 /* If there is no parent, record any indentation we encounter. */
2760 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2761
2762 if (cmp == EINVAL)
2763 goto return_invalid_indent;
2764
2765 if (cmp <= 0) {
2766 /* We have gone right back to the parent level or higher, we are skipping
2767 * this child node level entirely. Pop the parent to go back to a node
2768 * that was actually there (to reinstate vty->indent) and re-use below
2769 * go-parent while-loop to find an accurate match of indent in the node
2770 * ancestry. */
2771 vty_go_parent(vty);
2772 } else {
2773 /* The indent is deeper than the just entered parent, record the new
2774 * indentation characters. */
2775 vty->indent = talloc_strdup(vty, indent);
2776 /* This *is* the new indentation. */
2777 cmp = 0;
2778 }
2779 } else {
2780 /* There is a known indentation for this node level, validate and detect node
2781 * exits. */
2782 cmp = indent_cmp(indent, vty->indent);
2783 if (cmp == EINVAL)
2784 goto return_invalid_indent;
2785 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002786
2787 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2788 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2789 while (cmp < 0) {
2790 vty_go_parent(vty);
2791 cmp = indent_cmp(indent, vty->indent);
2792 if (cmp == EINVAL)
2793 goto return_invalid_indent;
2794 }
2795
2796 /* More indent without having entered a child node level? Either the parent node's indent
2797 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2798 * or the indentation increased even though the vty command didn't enter a child. */
2799 if (cmp > 0)
2800 goto return_invalid_indent;
2801
2802 /* Remember the current node before the command possibly changes it. */
2803 this_node = (struct vty_parent_node){
2804 .node = vty->node,
2805 .priv = vty->priv,
2806 .indent = vty->indent,
2807 };
2808
2809 parent = vty_parent(vty);
2810 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002811 cmd_free_strvec(vline);
2812
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002813 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002814 if (indent) {
2815 talloc_free(indent);
2816 indent = NULL;
2817 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002818 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002819 }
2820
2821 /* If we have stepped down into a child node, push a parent frame.
2822 * The causality is such: we don't expect every single node entry implementation to push
2823 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2824 * a parent node. Hence if the node changed without the parent node changing, we must
2825 * have stepped into a child node (and now expect a deeper indent). */
2826 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2827 /* Push the parent node. */
2828 parent = talloc_zero(vty, struct vty_parent_node);
2829 *parent = this_node;
2830 llist_add(&parent->entry, &vty->parent_nodes);
2831
2832 /* The current talloc'ed vty->indent string will now be owned by this parent
2833 * struct. Indicate that we don't know what deeper indent characters the user
2834 * will choose. */
2835 vty->indent = NULL;
2836 }
2837
2838 if (indent) {
2839 talloc_free(indent);
2840 indent = NULL;
2841 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002842 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002843 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2844 while (vty_parent(vty))
2845 vty_go_parent(vty);
2846
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002847 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002848
2849return_invalid_indent:
2850 if (vline)
2851 cmd_free_strvec(vline);
2852 if (indent) {
2853 talloc_free(indent);
2854 indent = NULL;
2855 }
2856 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002857}
2858
2859/* Configration from terminal */
2860DEFUN(config_terminal,
2861 config_terminal_cmd,
2862 "configure terminal",
2863 "Configuration from vty interface\n" "Configuration terminal\n")
2864{
2865 if (vty_config_lock(vty))
2866 vty->node = CONFIG_NODE;
2867 else {
2868 vty_out(vty, "VTY configuration is locked by other VTY%s",
2869 VTY_NEWLINE);
2870 return CMD_WARNING;
2871 }
2872 return CMD_SUCCESS;
2873}
2874
2875/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002876DEFUN(enable, config_enable_cmd,
2877 "enable [expert-mode]",
2878 "Turn on privileged mode command\n"
2879 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002880{
2881 /* If enable password is NULL, change to ENABLE_NODE */
2882 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2883 vty->type == VTY_SHELL_SERV)
2884 vty->node = ENABLE_NODE;
2885 else
2886 vty->node = AUTH_ENABLE_NODE;
2887
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002888 host.expert_mode = argc > 0;
2889
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002890 return CMD_SUCCESS;
2891}
2892
2893/* Disable command */
2894DEFUN(disable,
2895 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2896{
2897 if (vty->node == ENABLE_NODE)
2898 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002899
2900 host.expert_mode = false;
2901
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002902 return CMD_SUCCESS;
2903}
2904
2905/* Down vty node level. */
2906gDEFUN(config_exit,
2907 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2908{
2909 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002910 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002911 case VIEW_NODE:
2912 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002913 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002914 break;
2915 case CONFIG_NODE:
2916 vty->node = ENABLE_NODE;
2917 vty_config_unlock(vty);
2918 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002919 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002920 if (vty->node > CONFIG_NODE)
2921 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002922 break;
2923 }
2924 return CMD_SUCCESS;
2925}
2926
2927/* End of configuration. */
2928 gDEFUN(config_end,
2929 config_end_cmd, "end", "End current mode and change to enable mode.")
2930{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002931 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002932 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002933
2934 /* Repeatedly call go_parent until a top node is reached. */
2935 while (vty->node > CONFIG_NODE) {
2936 if (vty->node == last_node) {
2937 /* Ensure termination, this shouldn't happen. */
2938 break;
2939 }
2940 last_node = vty->node;
2941 vty_go_parent(vty);
2942 }
2943
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002944 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002945 if (vty->node > ENABLE_NODE)
2946 vty->node = ENABLE_NODE;
2947 vty->index = NULL;
2948 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002949 }
2950 return CMD_SUCCESS;
2951}
2952
2953/* Show version. */
2954DEFUN(show_version,
2955 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2956{
Harald Welte237f6242010-05-25 23:00:45 +02002957 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2958 host.app_info->version,
2959 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2960 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002961
2962 return CMD_SUCCESS;
2963}
2964
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002965DEFUN(show_online_help,
2966 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2967{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002968 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002969 return CMD_SUCCESS;
2970}
2971
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002972/* Help display function for all node. */
2973gDEFUN(config_help,
2974 config_help_cmd, "help", "Description of the interactive help system\n")
2975{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002976 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2977 "anytime at the command line please press '?'.%s%s"
2978 "If nothing matches, the help list will be empty and you must backup%s"
2979 " until entering a '?' shows the available options.%s"
2980 "Two styles of help are provided:%s"
2981 "1. Full help is available when you are ready to enter a%s"
2982 "command argument (e.g. 'show ?') and describes each possible%s"
2983 "argument.%s"
2984 "2. Partial help is provided when an abbreviated argument is entered%s"
2985 " and you want to know what arguments match the input%s"
2986 " (e.g. 'show me?'.)%s%s",
2987 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2988 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2989 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2990 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002991 return CMD_SUCCESS;
2992}
2993
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002994enum {
2995 ATTR_TYPE_GLOBAL = (1 << 0),
2996 ATTR_TYPE_LIB = (1 << 1),
2997 ATTR_TYPE_APP = (1 << 2),
2998};
2999
3000static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3001{
3002 const char *desc;
3003 unsigned int i;
3004 bool found;
3005 char flag;
3006
3007 if (attr_mask & ATTR_TYPE_GLOBAL) {
3008 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3009
3010 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003011 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003012 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003013
3014 /* Skip attributes without flags */
3015 if (flag != '.')
3016 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003017 }
3018 }
3019
3020 if (attr_mask & ATTR_TYPE_LIB) {
3021 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3022
3023 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3024 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3025 continue;
3026 found = true;
3027
3028 flag = cmd_lib_attr_letters[i];
3029 if (flag == '\0')
3030 flag = '.';
3031
3032 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3033 }
3034
3035 if (!found)
3036 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3037 }
3038
3039 if (attr_mask & ATTR_TYPE_APP) {
3040 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3041
3042 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3043 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3044 continue;
3045 found = true;
3046
3047 flag = host.app_info->usr_attr_letters[i];
3048 if (flag == '\0')
3049 flag = '.';
3050
3051 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3052 }
3053
3054 if (!found)
3055 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3056 }
3057}
3058
3059gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3060 "show vty-attributes",
3061 SHOW_STR "List of VTY attributes\n")
3062{
3063 print_attr_list(vty, 0xff);
3064 return CMD_SUCCESS;
3065}
3066
3067gDEFUN(show_vty_attr, show_vty_attr_cmd,
3068 "show vty-attributes (application|library|global)",
3069 SHOW_STR "List of VTY attributes\n"
3070 "Application specific attributes only\n"
3071 "Library specific attributes only\n"
3072 "Global attributes only\n")
3073{
3074 unsigned int attr_mask = 0;
3075
3076 if (argv[0][0] == 'g') /* global */
3077 attr_mask |= ATTR_TYPE_GLOBAL;
3078 else if (argv[0][0] == 'l') /* library */
3079 attr_mask |= ATTR_TYPE_LIB;
3080 else if (argv[0][0] == 'a') /* application */
3081 attr_mask |= ATTR_TYPE_APP;
3082
3083 print_attr_list(vty, attr_mask);
3084 return CMD_SUCCESS;
3085}
3086
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003087/* Compose flag bit-mask for all commands within the given node */
3088static unsigned int node_flag_mask(const struct cmd_node *cnode)
3089{
3090 unsigned int flag_mask = 0x00;
3091 unsigned int f, i;
3092
3093 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3094 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3095 const struct cmd_element *cmd;
3096 char flag_letter;
3097
3098 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3099 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003100 if (cmd->attr & CMD_ATTR_DEPRECATED)
3101 continue;
3102 if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003103 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003104 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003105 continue;
3106
3107 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3108 flag_letter = cmd_lib_attr_letters[f];
3109 else
3110 flag_letter = host.app_info->usr_attr_letters[f];
3111
3112 if (flag_letter == '\0')
3113 continue;
3114
3115 flag_mask |= (1 << f);
3116 break;
3117 }
3118 }
3119
3120 return flag_mask;
3121}
3122
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003123/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3124static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3125{
3126 static char char_mask[8 + 1];
3127 char *ptr = &char_mask[0];
3128
3129 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003130 if (cmd->attr & CMD_ATTR_HIDDEN)
3131 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3132 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003133 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3134 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3135 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3136 else
3137 *(ptr++) = '.';
3138
3139 *ptr = '\0';
3140
3141 return char_mask;
3142}
3143
3144/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003145static const char *cmd_flag_mask(const struct cmd_element *cmd,
3146 unsigned int flag_mask)
3147{
3148 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3149 char *ptr = &char_mask[0];
3150 char flag_letter;
3151 unsigned int f;
3152
3153 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003154 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003155 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003156 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003157 *(ptr++) = '.';
3158 continue;
3159 }
3160
3161 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3162 flag_letter = cmd_lib_attr_letters[f];
3163 else
3164 flag_letter = host.app_info->usr_attr_letters[f];
3165
3166 *(ptr++) = flag_letter ? flag_letter : '.';
3167 }
3168
3169 *ptr = '\0';
3170
3171 return char_mask;
3172}
3173
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003174/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003175gDEFUN(config_list, config_list_cmd,
3176 "list [with-flags]",
3177 "Print command list\n"
3178 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003179{
3180 unsigned int i;
3181 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003182 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003183 struct cmd_element *cmd;
3184
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003185 if (argc > 0)
3186 flag_mask = node_flag_mask(cnode);
3187
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003188 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3189 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3190 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003191 if (cmd->attr & CMD_ATTR_DEPRECATED)
3192 continue;
3193 if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003194 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003195 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003196 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3197 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003198 vty_out(vty, " %s %s %s%s",
3199 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003200 cmd_flag_mask(cmd, flag_mask),
3201 cmd->string, VTY_NEWLINE);
3202 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003203 }
3204
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003205 return CMD_SUCCESS;
3206}
3207
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003208static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003209{
3210 unsigned int i;
3211 int fd;
3212 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003213 char *config_file_tmp = NULL;
3214 char *config_file_sav = NULL;
3215 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003216 struct stat st;
3217
3218 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003219
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003220 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3221 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3222 * manually instead. */
3223
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003224 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003225 config_file_sav =
3226 _talloc_zero(tall_vty_cmd_ctx,
3227 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3228 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003229 if (!config_file_sav)
3230 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003231 strcpy(config_file_sav, config_file);
3232 strcat(config_file_sav, CONF_BACKUP_EXT);
3233
3234 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003235 "config_file_tmp");
3236 if (!config_file_tmp) {
3237 talloc_free(config_file_sav);
3238 return -1;
3239 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003240 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3241
3242 /* Open file to configuration write. */
3243 fd = mkstemp(config_file_tmp);
3244 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003245 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003246 talloc_free(config_file_tmp);
3247 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003248 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003249 }
3250
3251 /* Make vty for configuration file. */
3252 file_vty = vty_new();
3253 file_vty->fd = fd;
3254 file_vty->type = VTY_FILE;
3255
3256 /* Config file header print. */
3257 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003258 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003259 //vty_time_print (file_vty, 1);
3260 vty_out(file_vty, "!\n");
3261
3262 for (i = 0; i < vector_active(cmdvec); i++)
3263 if ((node = vector_slot(cmdvec, i)) && node->func) {
3264 if ((*node->func) (file_vty))
3265 vty_out(file_vty, "!\n");
3266 }
3267 vty_close(file_vty);
3268
3269 if (unlink(config_file_sav) != 0)
3270 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003271 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
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 -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003276 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003277
3278 /* Only link the .sav file if the original file exists */
3279 if (stat(config_file, &st) == 0) {
3280 if (link(config_file, config_file_sav) != 0) {
3281 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3282 talloc_free(config_file_sav);
3283 talloc_free(config_file_tmp);
3284 unlink(config_file_tmp);
3285 return -3;
3286 }
3287 sync();
3288 if (unlink(config_file) != 0) {
3289 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3290 talloc_free(config_file_sav);
3291 talloc_free(config_file_tmp);
3292 unlink(config_file_tmp);
3293 return -4;
3294 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003295 }
3296 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003297 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003298 talloc_free(config_file_sav);
3299 talloc_free(config_file_tmp);
3300 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003301 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003302 }
3303 unlink(config_file_tmp);
3304 sync();
3305
3306 talloc_free(config_file_sav);
3307 talloc_free(config_file_tmp);
3308
3309 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003310 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3311 return -6;
3312 }
3313
3314 return 0;
3315}
3316
3317
3318/* Write current configuration into file. */
3319DEFUN(config_write_file,
3320 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003321 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003322 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003323 "Write to configuration file\n"
3324 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003325{
3326 char *failed_file;
3327 int rc;
3328
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003329 if (host.app_info->config_is_consistent) {
3330 rc = host.app_info->config_is_consistent(vty);
3331 if (!rc) {
3332 vty_out(vty, "Configuration is not consistent%s",
3333 VTY_NEWLINE);
3334 return CMD_WARNING;
3335 }
3336 }
3337
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003338 if (argc == 1)
3339 host_config_set(argv[0]);
3340
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003341 if (host.config == NULL) {
3342 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3343 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003344 return CMD_WARNING;
3345 }
3346
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003347 rc = write_config_file(host.config, &failed_file);
3348 switch (rc) {
3349 case -1:
3350 vty_out(vty, "Can't open configuration file %s.%s",
3351 failed_file, VTY_NEWLINE);
3352 rc = CMD_WARNING;
3353 break;
3354 case -2:
3355 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3356 failed_file, VTY_NEWLINE);
3357 rc = CMD_WARNING;
3358 break;
3359 case -3:
3360 vty_out(vty, "Can't backup old configuration file %s.%s",
3361 failed_file, VTY_NEWLINE);
3362 rc = CMD_WARNING;
3363 break;
3364 case -4:
3365 vty_out(vty, "Can't unlink configuration file %s.%s",
3366 failed_file, VTY_NEWLINE);
3367 rc = CMD_WARNING;
3368 break;
3369 case -5:
3370 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3371 VTY_NEWLINE);
3372 rc = CMD_WARNING;
3373 break;
3374 case -6:
3375 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3376 failed_file, strerror(errno), errno, VTY_NEWLINE);
3377 rc = CMD_WARNING;
3378 break;
3379 default:
3380 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3381 rc = CMD_SUCCESS;
3382 break;
3383 }
3384
3385 talloc_free(failed_file);
3386 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003387}
3388
3389ALIAS(config_write_file,
3390 config_write_cmd,
3391 "write", "Write running configuration to memory, network, or terminal\n")
3392
3393 ALIAS(config_write_file,
3394 config_write_memory_cmd,
3395 "write memory",
3396 "Write running configuration to memory, network, or terminal\n"
3397 "Write configuration to the file (same as write file)\n")
3398
3399 ALIAS(config_write_file,
3400 copy_runningconfig_startupconfig_cmd,
3401 "copy running-config startup-config",
3402 "Copy configuration\n"
3403 "Copy running config to... \n"
3404 "Copy running config to startup config (same as write file)\n")
3405
3406/* Write current configuration into the terminal. */
3407 DEFUN(config_write_terminal,
3408 config_write_terminal_cmd,
3409 "write terminal",
3410 "Write running configuration to memory, network, or terminal\n"
3411 "Write to terminal\n")
3412{
3413 unsigned int i;
3414 struct cmd_node *node;
3415
3416 if (vty->type == VTY_SHELL_SERV) {
3417 for (i = 0; i < vector_active(cmdvec); i++)
3418 if ((node = vector_slot(cmdvec, i)) && node->func
3419 && node->vtysh) {
3420 if ((*node->func) (vty))
3421 vty_out(vty, "!%s", VTY_NEWLINE);
3422 }
3423 } else {
3424 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3425 VTY_NEWLINE);
3426 vty_out(vty, "!%s", VTY_NEWLINE);
3427
3428 for (i = 0; i < vector_active(cmdvec); i++)
3429 if ((node = vector_slot(cmdvec, i)) && node->func) {
3430 if ((*node->func) (vty))
3431 vty_out(vty, "!%s", VTY_NEWLINE);
3432 }
3433 vty_out(vty, "end%s", VTY_NEWLINE);
3434 }
3435 return CMD_SUCCESS;
3436}
3437
3438/* Write current configuration into the terminal. */
3439ALIAS(config_write_terminal,
3440 show_running_config_cmd,
3441 "show running-config", SHOW_STR "running configuration\n")
3442
3443/* Write startup configuration into the terminal. */
3444 DEFUN(show_startup_config,
3445 show_startup_config_cmd,
3446 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3447{
3448 char buf[BUFSIZ];
3449 FILE *confp;
3450
3451 confp = fopen(host.config, "r");
3452 if (confp == NULL) {
3453 vty_out(vty, "Can't open configuration file [%s]%s",
3454 host.config, VTY_NEWLINE);
3455 return CMD_WARNING;
3456 }
3457
3458 while (fgets(buf, BUFSIZ, confp)) {
3459 char *cp = buf;
3460
3461 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3462 cp++;
3463 *cp = '\0';
3464
3465 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3466 }
3467
3468 fclose(confp);
3469
3470 return CMD_SUCCESS;
3471}
3472
3473/* Hostname configuration */
3474DEFUN(config_hostname,
3475 hostname_cmd,
3476 "hostname WORD",
3477 "Set system's network name\n" "This system's network name\n")
3478{
3479 if (!isalpha((int)*argv[0])) {
3480 vty_out(vty, "Please specify string starting with alphabet%s",
3481 VTY_NEWLINE);
3482 return CMD_WARNING;
3483 }
3484
3485 if (host.name)
3486 talloc_free(host.name);
3487
3488 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3489 return CMD_SUCCESS;
3490}
3491
3492DEFUN(config_no_hostname,
3493 no_hostname_cmd,
3494 "no hostname [HOSTNAME]",
3495 NO_STR "Reset system's network name\n" "Host name of this router\n")
3496{
3497 if (host.name)
3498 talloc_free(host.name);
3499 host.name = NULL;
3500 return CMD_SUCCESS;
3501}
3502
3503/* VTY interface password set. */
3504DEFUN(config_password, password_cmd,
3505 "password (8|) WORD",
3506 "Assign the terminal connection password\n"
3507 "Specifies a HIDDEN password will follow\n"
3508 "dummy string \n" "The HIDDEN line password string\n")
3509{
3510 /* Argument check. */
3511 if (argc == 0) {
3512 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3513 return CMD_WARNING;
3514 }
3515
3516 if (argc == 2) {
3517 if (*argv[0] == '8') {
3518 if (host.password)
3519 talloc_free(host.password);
3520 host.password = NULL;
3521 if (host.password_encrypt)
3522 talloc_free(host.password_encrypt);
3523 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3524 return CMD_SUCCESS;
3525 } else {
3526 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3527 return CMD_WARNING;
3528 }
3529 }
3530
3531 if (!isalnum((int)*argv[0])) {
3532 vty_out(vty,
3533 "Please specify string starting with alphanumeric%s",
3534 VTY_NEWLINE);
3535 return CMD_WARNING;
3536 }
3537
3538 if (host.password)
3539 talloc_free(host.password);
3540 host.password = NULL;
3541
3542#ifdef VTY_CRYPT_PW
3543 if (host.encrypt) {
3544 if (host.password_encrypt)
3545 talloc_free(host.password_encrypt);
3546 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3547 } else
3548#endif
3549 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3550
3551 return CMD_SUCCESS;
3552}
3553
3554ALIAS(config_password, password_text_cmd,
3555 "password LINE",
3556 "Assign the terminal connection password\n"
3557 "The UNENCRYPTED (cleartext) line password\n")
3558
3559/* VTY enable password set. */
3560 DEFUN(config_enable_password, enable_password_cmd,
3561 "enable password (8|) WORD",
3562 "Modify enable password parameters\n"
3563 "Assign the privileged level password\n"
3564 "Specifies a HIDDEN password will follow\n"
3565 "dummy string \n" "The HIDDEN 'enable' password string\n")
3566{
3567 /* Argument check. */
3568 if (argc == 0) {
3569 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3570 return CMD_WARNING;
3571 }
3572
3573 /* Crypt type is specified. */
3574 if (argc == 2) {
3575 if (*argv[0] == '8') {
3576 if (host.enable)
3577 talloc_free(host.enable);
3578 host.enable = NULL;
3579
3580 if (host.enable_encrypt)
3581 talloc_free(host.enable_encrypt);
3582 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3583
3584 return CMD_SUCCESS;
3585 } else {
3586 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3587 return CMD_WARNING;
3588 }
3589 }
3590
3591 if (!isalnum((int)*argv[0])) {
3592 vty_out(vty,
3593 "Please specify string starting with alphanumeric%s",
3594 VTY_NEWLINE);
3595 return CMD_WARNING;
3596 }
3597
3598 if (host.enable)
3599 talloc_free(host.enable);
3600 host.enable = NULL;
3601
3602 /* Plain password input. */
3603#ifdef VTY_CRYPT_PW
3604 if (host.encrypt) {
3605 if (host.enable_encrypt)
3606 talloc_free(host.enable_encrypt);
3607 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3608 } else
3609#endif
3610 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3611
3612 return CMD_SUCCESS;
3613}
3614
3615ALIAS(config_enable_password,
3616 enable_password_text_cmd,
3617 "enable password LINE",
3618 "Modify enable password parameters\n"
3619 "Assign the privileged level password\n"
3620 "The UNENCRYPTED (cleartext) 'enable' password\n")
3621
3622/* VTY enable password delete. */
3623 DEFUN(no_config_enable_password, no_enable_password_cmd,
3624 "no enable password",
3625 NO_STR
3626 "Modify enable password parameters\n"
3627 "Assign the privileged level password\n")
3628{
3629 if (host.enable)
3630 talloc_free(host.enable);
3631 host.enable = NULL;
3632
3633 if (host.enable_encrypt)
3634 talloc_free(host.enable_encrypt);
3635 host.enable_encrypt = NULL;
3636
3637 return CMD_SUCCESS;
3638}
3639
3640#ifdef VTY_CRYPT_PW
3641DEFUN(service_password_encrypt,
3642 service_password_encrypt_cmd,
3643 "service password-encryption",
3644 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3645{
3646 if (host.encrypt)
3647 return CMD_SUCCESS;
3648
3649 host.encrypt = 1;
3650
3651 if (host.password) {
3652 if (host.password_encrypt)
3653 talloc_free(host.password_encrypt);
3654 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3655 }
3656 if (host.enable) {
3657 if (host.enable_encrypt)
3658 talloc_free(host.enable_encrypt);
3659 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3660 }
3661
3662 return CMD_SUCCESS;
3663}
3664
3665DEFUN(no_service_password_encrypt,
3666 no_service_password_encrypt_cmd,
3667 "no service password-encryption",
3668 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3669{
3670 if (!host.encrypt)
3671 return CMD_SUCCESS;
3672
3673 host.encrypt = 0;
3674
3675 if (host.password_encrypt)
3676 talloc_free(host.password_encrypt);
3677 host.password_encrypt = NULL;
3678
3679 if (host.enable_encrypt)
3680 talloc_free(host.enable_encrypt);
3681 host.enable_encrypt = NULL;
3682
3683 return CMD_SUCCESS;
3684}
3685#endif
3686
3687DEFUN(config_terminal_length, config_terminal_length_cmd,
3688 "terminal length <0-512>",
3689 "Set terminal line parameters\n"
3690 "Set number of lines on a screen\n"
3691 "Number of lines on screen (0 for no pausing)\n")
3692{
3693 int lines;
3694 char *endptr = NULL;
3695
3696 lines = strtol(argv[0], &endptr, 10);
3697 if (lines < 0 || lines > 512 || *endptr != '\0') {
3698 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3699 return CMD_WARNING;
3700 }
3701 vty->lines = lines;
3702
3703 return CMD_SUCCESS;
3704}
3705
3706DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3707 "terminal no length",
3708 "Set terminal line parameters\n"
3709 NO_STR "Set number of lines on a screen\n")
3710{
3711 vty->lines = -1;
3712 return CMD_SUCCESS;
3713}
3714
3715DEFUN(service_terminal_length, service_terminal_length_cmd,
3716 "service terminal-length <0-512>",
3717 "Set up miscellaneous service\n"
3718 "System wide terminal length configuration\n"
3719 "Number of lines of VTY (0 means no line control)\n")
3720{
3721 int lines;
3722 char *endptr = NULL;
3723
3724 lines = strtol(argv[0], &endptr, 10);
3725 if (lines < 0 || lines > 512 || *endptr != '\0') {
3726 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3727 return CMD_WARNING;
3728 }
3729 host.lines = lines;
3730
3731 return CMD_SUCCESS;
3732}
3733
3734DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3735 "no service terminal-length [<0-512>]",
3736 NO_STR
3737 "Set up miscellaneous service\n"
3738 "System wide terminal length configuration\n"
3739 "Number of lines of VTY (0 means no line control)\n")
3740{
3741 host.lines = -1;
3742 return CMD_SUCCESS;
3743}
3744
3745DEFUN_HIDDEN(do_echo,
3746 echo_cmd,
3747 "echo .MESSAGE",
3748 "Echo a message back to the vty\n" "The message to echo\n")
3749{
3750 char *message;
3751
3752 vty_out(vty, "%s%s",
3753 ((message =
3754 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3755 if (message)
3756 talloc_free(message);
3757 return CMD_SUCCESS;
3758}
3759
3760#if 0
3761DEFUN(config_logmsg,
3762 config_logmsg_cmd,
3763 "logmsg " LOG_LEVELS " .MESSAGE",
3764 "Send a message to enabled logging destinations\n"
3765 LOG_LEVEL_DESC "The message to send\n")
3766{
3767 int level;
3768 char *message;
3769
3770 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3771 return CMD_ERR_NO_MATCH;
3772
3773 zlog(NULL, level,
3774 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3775 if (message)
3776 talloc_free(message);
3777 return CMD_SUCCESS;
3778}
3779
3780DEFUN(show_logging,
3781 show_logging_cmd,
3782 "show logging", SHOW_STR "Show current logging configuration\n")
3783{
3784 struct zlog *zl = zlog_default;
3785
3786 vty_out(vty, "Syslog logging: ");
3787 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3788 vty_out(vty, "disabled");
3789 else
3790 vty_out(vty, "level %s, facility %s, ident %s",
3791 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3792 facility_name(zl->facility), zl->ident);
3793 vty_out(vty, "%s", VTY_NEWLINE);
3794
3795 vty_out(vty, "Stdout logging: ");
3796 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3797 vty_out(vty, "disabled");
3798 else
3799 vty_out(vty, "level %s",
3800 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3801 vty_out(vty, "%s", VTY_NEWLINE);
3802
3803 vty_out(vty, "Monitor logging: ");
3804 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3805 vty_out(vty, "disabled");
3806 else
3807 vty_out(vty, "level %s",
3808 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3809 vty_out(vty, "%s", VTY_NEWLINE);
3810
3811 vty_out(vty, "File logging: ");
3812 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3813 vty_out(vty, "disabled");
3814 else
3815 vty_out(vty, "level %s, filename %s",
3816 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3817 zl->filename);
3818 vty_out(vty, "%s", VTY_NEWLINE);
3819
3820 vty_out(vty, "Protocol name: %s%s",
3821 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3822 vty_out(vty, "Record priority: %s%s",
3823 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3824
3825 return CMD_SUCCESS;
3826}
3827
3828DEFUN(config_log_stdout,
3829 config_log_stdout_cmd,
3830 "log stdout", "Logging control\n" "Set stdout logging level\n")
3831{
3832 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3833 return CMD_SUCCESS;
3834}
3835
3836DEFUN(config_log_stdout_level,
3837 config_log_stdout_level_cmd,
3838 "log stdout " LOG_LEVELS,
3839 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3840{
3841 int level;
3842
3843 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3844 return CMD_ERR_NO_MATCH;
3845 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3846 return CMD_SUCCESS;
3847}
3848
3849DEFUN(no_config_log_stdout,
3850 no_config_log_stdout_cmd,
3851 "no log stdout [LEVEL]",
3852 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3853{
3854 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3855 return CMD_SUCCESS;
3856}
3857
3858DEFUN(config_log_monitor,
3859 config_log_monitor_cmd,
3860 "log monitor",
3861 "Logging control\n" "Set terminal line (monitor) logging level\n")
3862{
3863 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3864 return CMD_SUCCESS;
3865}
3866
3867DEFUN(config_log_monitor_level,
3868 config_log_monitor_level_cmd,
3869 "log monitor " LOG_LEVELS,
3870 "Logging control\n"
3871 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3872{
3873 int level;
3874
3875 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3876 return CMD_ERR_NO_MATCH;
3877 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3878 return CMD_SUCCESS;
3879}
3880
3881DEFUN(no_config_log_monitor,
3882 no_config_log_monitor_cmd,
3883 "no log monitor [LEVEL]",
3884 NO_STR
3885 "Logging control\n"
3886 "Disable terminal line (monitor) logging\n" "Logging level\n")
3887{
3888 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3889 return CMD_SUCCESS;
3890}
3891
3892static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3893{
3894 int ret;
3895 char *p = NULL;
3896 const char *fullpath;
3897
3898 /* Path detection. */
3899 if (!IS_DIRECTORY_SEP(*fname)) {
3900 char cwd[MAXPATHLEN + 1];
3901 cwd[MAXPATHLEN] = '\0';
3902
3903 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3904 zlog_err("config_log_file: Unable to alloc mem!");
3905 return CMD_WARNING;
3906 }
3907
3908 if ((p = _talloc_zero(tall_vcmd_ctx,
3909 strlen(cwd) + strlen(fname) + 2),
3910 "set_log_file")
3911 == NULL) {
3912 zlog_err("config_log_file: Unable to alloc mem!");
3913 return CMD_WARNING;
3914 }
3915 sprintf(p, "%s/%s", cwd, fname);
3916 fullpath = p;
3917 } else
3918 fullpath = fname;
3919
3920 ret = zlog_set_file(NULL, fullpath, loglevel);
3921
3922 if (p)
3923 talloc_free(p);
3924
3925 if (!ret) {
3926 vty_out(vty, "can't open logfile %s\n", fname);
3927 return CMD_WARNING;
3928 }
3929
3930 if (host.logfile)
3931 talloc_free(host.logfile);
3932
3933 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3934
3935 return CMD_SUCCESS;
3936}
3937
3938DEFUN(config_log_file,
3939 config_log_file_cmd,
3940 "log file FILENAME",
3941 "Logging control\n" "Logging to file\n" "Logging filename\n")
3942{
3943 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3944}
3945
3946DEFUN(config_log_file_level,
3947 config_log_file_level_cmd,
3948 "log file FILENAME " LOG_LEVELS,
3949 "Logging control\n"
3950 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3951{
3952 int level;
3953
3954 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3955 return CMD_ERR_NO_MATCH;
3956 return set_log_file(vty, argv[0], level);
3957}
3958
3959DEFUN(no_config_log_file,
3960 no_config_log_file_cmd,
3961 "no log file [FILENAME]",
3962 NO_STR
3963 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3964{
3965 zlog_reset_file(NULL);
3966
3967 if (host.logfile)
3968 talloc_free(host.logfile);
3969
3970 host.logfile = NULL;
3971
3972 return CMD_SUCCESS;
3973}
3974
3975ALIAS(no_config_log_file,
3976 no_config_log_file_level_cmd,
3977 "no log file FILENAME LEVEL",
3978 NO_STR
3979 "Logging control\n"
3980 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3981
3982 DEFUN(config_log_syslog,
3983 config_log_syslog_cmd,
3984 "log syslog", "Logging control\n" "Set syslog logging level\n")
3985{
3986 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3987 return CMD_SUCCESS;
3988}
3989
3990DEFUN(config_log_syslog_level,
3991 config_log_syslog_level_cmd,
3992 "log syslog " LOG_LEVELS,
3993 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3994{
3995 int level;
3996
3997 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3998 return CMD_ERR_NO_MATCH;
3999 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4000 return CMD_SUCCESS;
4001}
4002
4003DEFUN_DEPRECATED(config_log_syslog_facility,
4004 config_log_syslog_facility_cmd,
4005 "log syslog facility " LOG_FACILITIES,
4006 "Logging control\n"
4007 "Logging goes to syslog\n"
4008 "(Deprecated) Facility parameter for syslog messages\n"
4009 LOG_FACILITY_DESC)
4010{
4011 int facility;
4012
4013 if ((facility = facility_match(argv[0])) < 0)
4014 return CMD_ERR_NO_MATCH;
4015
4016 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4017 zlog_default->facility = facility;
4018 return CMD_SUCCESS;
4019}
4020
4021DEFUN(no_config_log_syslog,
4022 no_config_log_syslog_cmd,
4023 "no log syslog [LEVEL]",
4024 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4025{
4026 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4027 return CMD_SUCCESS;
4028}
4029
4030ALIAS(no_config_log_syslog,
4031 no_config_log_syslog_facility_cmd,
4032 "no log syslog facility " LOG_FACILITIES,
4033 NO_STR
4034 "Logging control\n"
4035 "Logging goes to syslog\n"
4036 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4037
4038 DEFUN(config_log_facility,
4039 config_log_facility_cmd,
4040 "log facility " LOG_FACILITIES,
4041 "Logging control\n"
4042 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4043{
4044 int facility;
4045
4046 if ((facility = facility_match(argv[0])) < 0)
4047 return CMD_ERR_NO_MATCH;
4048 zlog_default->facility = facility;
4049 return CMD_SUCCESS;
4050}
4051
4052DEFUN(no_config_log_facility,
4053 no_config_log_facility_cmd,
4054 "no log facility [FACILITY]",
4055 NO_STR
4056 "Logging control\n"
4057 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4058{
4059 zlog_default->facility = LOG_DAEMON;
4060 return CMD_SUCCESS;
4061}
4062
4063DEFUN_DEPRECATED(config_log_trap,
4064 config_log_trap_cmd,
4065 "log trap " LOG_LEVELS,
4066 "Logging control\n"
4067 "(Deprecated) Set logging level and default for all destinations\n"
4068 LOG_LEVEL_DESC)
4069{
4070 int new_level;
4071 int i;
4072
4073 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4074 return CMD_ERR_NO_MATCH;
4075
4076 zlog_default->default_lvl = new_level;
4077 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4078 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4079 zlog_default->maxlvl[i] = new_level;
4080 return CMD_SUCCESS;
4081}
4082
4083DEFUN_DEPRECATED(no_config_log_trap,
4084 no_config_log_trap_cmd,
4085 "no log trap [LEVEL]",
4086 NO_STR
4087 "Logging control\n"
4088 "Permit all logging information\n" "Logging level\n")
4089{
4090 zlog_default->default_lvl = LOG_DEBUG;
4091 return CMD_SUCCESS;
4092}
4093
4094DEFUN(config_log_record_priority,
4095 config_log_record_priority_cmd,
4096 "log record-priority",
4097 "Logging control\n"
4098 "Log the priority of the message within the message\n")
4099{
4100 zlog_default->record_priority = 1;
4101 return CMD_SUCCESS;
4102}
4103
4104DEFUN(no_config_log_record_priority,
4105 no_config_log_record_priority_cmd,
4106 "no log record-priority",
4107 NO_STR
4108 "Logging control\n"
4109 "Do not log the priority of the message within the message\n")
4110{
4111 zlog_default->record_priority = 0;
4112 return CMD_SUCCESS;
4113}
4114#endif
4115
4116DEFUN(banner_motd_file,
4117 banner_motd_file_cmd,
4118 "banner motd file [FILE]",
4119 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4120{
4121 if (host.motdfile)
4122 talloc_free(host.motdfile);
4123 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4124
4125 return CMD_SUCCESS;
4126}
4127
4128DEFUN(banner_motd_default,
4129 banner_motd_default_cmd,
4130 "banner motd default",
4131 "Set banner string\n" "Strings for motd\n" "Default string\n")
4132{
4133 host.motd = default_motd;
4134 return CMD_SUCCESS;
4135}
4136
4137DEFUN(no_banner_motd,
4138 no_banner_motd_cmd,
4139 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4140{
4141 host.motd = NULL;
4142 if (host.motdfile)
4143 talloc_free(host.motdfile);
4144 host.motdfile = NULL;
4145 return CMD_SUCCESS;
4146}
4147
4148/* Set config filename. Called from vty.c */
4149void host_config_set(const char *filename)
4150{
4151 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4152}
4153
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004154/*! Deprecated, now happens implicitly when calling install_node().
4155 * Users of the API may still attempt to call this function, hence
4156 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004157void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004158{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004159}
4160
4161/*! Deprecated, now happens implicitly when calling install_node().
4162 * Users of the API may still attempt to call this function, hence
4163 * leave it here as a no-op. */
4164void vty_install_default(int node)
4165{
4166}
4167
4168/*! Install common commands like 'exit' and 'list'. */
4169static void install_basic_node_commands(int node)
4170{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004171 install_lib_element(node, &config_help_cmd);
4172 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004173
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004174 install_lib_element(node, &show_vty_attr_all_cmd);
4175 install_lib_element(node, &show_vty_attr_cmd);
4176
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004177 install_lib_element(node, &config_write_terminal_cmd);
4178 install_lib_element(node, &config_write_file_cmd);
4179 install_lib_element(node, &config_write_memory_cmd);
4180 install_lib_element(node, &config_write_cmd);
4181 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004182
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004183 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004184
4185 if (node >= CONFIG_NODE) {
4186 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004187 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004188 }
4189}
4190
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004191/*! Return true if a node is installed by install_basic_node_commands(), so
4192 * that we can avoid repeating them for each and every node during 'show
4193 * running-config' */
Vadim Yanitskiyefe13422020-10-21 20:36:17 +07004194static bool vty_command_is_common(const struct cmd_element *cmd)
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004195{
4196 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004197 || cmd == &show_vty_attr_all_cmd
4198 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004199 || cmd == &config_list_cmd
4200 || cmd == &config_write_terminal_cmd
4201 || cmd == &config_write_file_cmd
4202 || cmd == &config_write_memory_cmd
4203 || cmd == &config_write_cmd
4204 || cmd == &show_running_config_cmd
4205 || cmd == &config_exit_cmd
4206 || cmd == &config_end_cmd)
4207 return true;
4208 return false;
4209}
4210
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004211/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004212 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004213 * \param[in] vty the vty of the code
4214 * \param[in] filename where to store the file
4215 * \return 0 in case of success.
4216 *
4217 * If the filename already exists create a filename.sav
4218 * version with the current code.
4219 *
4220 */
4221int osmo_vty_write_config_file(const char *filename)
4222{
4223 char *failed_file;
4224 int rc;
4225
4226 rc = write_config_file(filename, &failed_file);
4227 talloc_free(failed_file);
4228 return rc;
4229}
4230
4231/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004232 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004233 * \return 0 in case of success.
4234 *
4235 * If the filename already exists create a filename.sav
4236 * version with the current code.
4237 *
4238 */
4239int osmo_vty_save_config_file(void)
4240{
4241 char *failed_file;
4242 int rc;
4243
4244 if (host.config == NULL)
4245 return -7;
4246
4247 rc = write_config_file(host.config, &failed_file);
4248 talloc_free(failed_file);
4249 return rc;
4250}
4251
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004252/* Initialize command interface. Install basic nodes and commands. */
4253void cmd_init(int terminal)
4254{
4255 /* Allocate initial top vector of commands. */
4256 cmdvec = vector_init(VECTOR_MIN_SIZE);
4257
4258 /* Default host value settings. */
4259 host.name = NULL;
4260 host.password = NULL;
4261 host.enable = NULL;
4262 host.logfile = NULL;
4263 host.config = NULL;
4264 host.lines = -1;
4265 host.motd = default_motd;
4266 host.motdfile = NULL;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07004267 host.expert_mode = false;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004268
4269 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004270 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004271 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004272 install_node_bare(&auth_node, NULL);
4273 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004274 install_node(&config_node, config_write_host);
4275
4276 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004277 install_lib_element(VIEW_NODE, &show_version_cmd);
4278 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004279 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004280 install_lib_element(VIEW_NODE, &config_list_cmd);
4281 install_lib_element(VIEW_NODE, &config_exit_cmd);
4282 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004283 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4284 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004285 install_lib_element(VIEW_NODE, &config_enable_cmd);
4286 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4287 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4288 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004289 }
4290
4291 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004292 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4293 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4294 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004295 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004296 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4297 install_lib_element(ENABLE_NODE, &show_version_cmd);
4298 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004299
4300 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004301 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4302 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4303 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004304 }
4305
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004306 install_lib_element(CONFIG_NODE, &hostname_cmd);
4307 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004308
4309 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004310 install_lib_element(CONFIG_NODE, &password_cmd);
4311 install_lib_element(CONFIG_NODE, &password_text_cmd);
4312 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4313 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4314 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004315
4316#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004317 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4318 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004319#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004320 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4321 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4322 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4323 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4324 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004325
4326 }
4327 srand(time(NULL));
4328}
Harald Welte7acb30c2011-08-17 17:13:48 +02004329
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004330/* FIXME: execute this section in the unit test instead */
4331static __attribute__((constructor)) void on_dso_load(void)
4332{
4333 unsigned int i, j;
4334
4335 /* Check total number of the library specific attributes */
4336 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4337
4338 /* Check for duplicates in the list of library specific flags */
4339 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4340 if (cmd_lib_attr_letters[i] == '\0')
4341 continue;
4342
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004343 /* Some flag characters are reserved for global attributes */
4344 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4345 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4346 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4347
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004348 /* Only upper case flag letters are allowed for libraries */
4349 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4350 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4351
4352 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4353 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4354 }
4355}
4356
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004357/*! @} */