blob: d64902bae5c4e0db59bce4a1593b128a328bee89 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020041#include <sys/time.h>
42#include <sys/stat.h>
43
44#include <osmocom/vty/vector.h>
45#include <osmocom/vty/vty.h>
46#include <osmocom/vty/command.h>
47
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010048#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010049#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050
Ruben Undheim766f77c2018-11-18 13:02:47 +010051#ifndef MAXPATHLEN
52 #define MAXPATHLEN 4096
53#endif
54
55
Harald Weltee881b1b2011-08-17 18:52:30 +020056/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020057 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020059 *
60 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020061
Harald Welte3fb0b6f2010-05-19 19:02:52 +020062#define CONFIGFILE_MASK 022
63
64void *tall_vty_cmd_ctx;
65
66/* Command vector which includes some level of command lists. Normally
67 each daemon maintains each own cmdvec. */
68vector cmdvec;
69
70/* Host information structure. */
71struct host host;
72
73/* Standard command node structures. */
74struct cmd_node auth_node = {
75 AUTH_NODE,
76 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node view_node = {
81 VIEW_NODE,
82 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node auth_enable_node = {
87 AUTH_ENABLE_NODE,
88 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node enable_node = {
93 ENABLE_NODE,
94 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010095 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096};
97
98struct cmd_node config_node = {
99 CONFIG_NODE,
100 "%s(config)# ",
101 1
102};
103
104/* Default motd string. */
105const char *default_motd = "";
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200108 *
109 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110void print_version(int print_copyright)
111{
Harald Welte237f6242010-05-25 23:00:45 +0200112 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200114 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115}
116
117/* Utility function to concatenate argv argument into a single string
118 with inserting ' ' character between each argument. */
119char *argv_concat(const char **argv, int argc, int shift)
120{
121 int i;
122 size_t len;
123 char *str;
124 char *p;
125
126 len = 0;
127 for (i = shift; i < argc; i++)
128 len += strlen(argv[i]) + 1;
129 if (!len)
130 return NULL;
131 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
132 for (i = shift; i < argc; i++) {
133 size_t arglen;
134 memcpy(p, argv[i], (arglen = strlen(argv[i])));
135 p += arglen;
136 *p++ = ' ';
137 }
138 *(p - 1) = '\0';
139 return str;
140}
141
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200142/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
143 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
144 * in turn, this name us used for XML IDs in 'show online-help'. */
145static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
146{
147 const char *pos;
148 int dest = 0;
149
150 if (!prompt || !*prompt)
151 return "";
152
153 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
154 if (pos[0] == '%' && pos[1]) {
155 /* skip "%s"; loop pos++ does the second one. */
156 pos++;
157 continue;
158 }
159 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
160 continue;
161 name_buf[dest] = pos[0];
162 dest++;
163 }
164 name_buf[dest] = '\0';
165 return name_buf;
166}
167
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200168static void install_basic_node_commands(int node);
169
170/*! Install top node of command vector, without adding basic node commands. */
171static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172{
173 vector_set_index(cmdvec, node->node, node);
174 node->func = func;
175 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200176 if (!*node->name)
177 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200178}
179
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200180/*! Install top node of command vector. */
181void install_node(struct cmd_node *node, int (*func) (struct vty *))
182{
183 install_node_bare(node, func);
184 install_basic_node_commands(node->node);
185}
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187/* Compare two command's string. Used in sort_node (). */
188static int cmp_node(const void *p, const void *q)
189{
190 struct cmd_element *a = *(struct cmd_element **)p;
191 struct cmd_element *b = *(struct cmd_element **)q;
192
193 return strcmp(a->string, b->string);
194}
195
196static int cmp_desc(const void *p, const void *q)
197{
198 struct desc *a = *(struct desc **)p;
199 struct desc *b = *(struct desc **)q;
200
201 return strcmp(a->cmd, b->cmd);
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200205void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200206{
207 unsigned int i, j;
208 struct cmd_node *cnode;
209 vector descvec;
210 struct cmd_element *cmd_element;
211
212 for (i = 0; i < vector_active(cmdvec); i++)
213 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
214 vector cmd_vector = cnode->cmd_vector;
215 qsort(cmd_vector->index, vector_active(cmd_vector),
216 sizeof(void *), cmp_node);
217
218 for (j = 0; j < vector_active(cmd_vector); j++)
219 if ((cmd_element =
220 vector_slot(cmd_vector, j)) != NULL
221 && vector_active(cmd_element->strvec)) {
222 descvec =
223 vector_slot(cmd_element->strvec,
224 vector_active
225 (cmd_element->strvec) -
226 1);
227 qsort(descvec->index,
228 vector_active(descvec),
229 sizeof(void *), cmp_desc);
230 }
231 }
232}
233
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200234/*! Break up string in command tokens. Return leading indents.
235 * \param[in] string String to split.
236 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
237 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
238 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
239 *
240 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
241 * so that \a indent can simply return the count of leading spaces.
242 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
243 */
244int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245{
246 const char *cp, *start;
247 char *token;
248 int strlen;
249 vector strvec;
250
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200251 *strvec_p = NULL;
252 if (indent)
253 *indent = 0;
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200257
258 cp = string;
259
260 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200261 while (isspace((int)*cp) && *cp != '\0') {
262 /* if we're counting indents, we need to be strict about them */
263 if (indent && (*cp != ' ') && (*cp != '\t')) {
264 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
265 if (*cp == '\n' || *cp == '\r') {
266 cp++;
267 string = cp;
268 continue;
269 }
270 return CMD_ERR_INVALID_INDENT;
271 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 }
274
275 if (indent)
276 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277
278 /* Return if there is only white spaces */
279 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Prepare return vector. */
286 strvec = vector_init(VECTOR_MIN_SIZE);
287
288 /* Copy each command piece and set into vector. */
289 while (1) {
290 start = cp;
291 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
292 *cp != '\0')
293 cp++;
294 strlen = cp - start;
295 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
296 memcpy(token, start, strlen);
297 *(token + strlen) = '\0';
298 vector_set(strvec, token);
299
300 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
301 *cp != '\0')
302 cp++;
303
304 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200307
308 *strvec_p = strvec;
309 return CMD_SUCCESS;
310}
311
312/*! Breaking up string into each command piece. I assume given
313 character is separated by a space character. Return value is a
314 vector which includes char ** data element. */
315vector cmd_make_strvec(const char *string)
316{
317 vector strvec;
318 cmd_make_strvec2(string, NULL, &strvec);
319 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200320}
321
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200322/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200323void cmd_free_strvec(vector v)
324{
325 unsigned int i;
326 char *cp;
327
328 if (!v)
329 return;
330
331 for (i = 0; i < vector_active(v); i++)
332 if ((cp = vector_slot(v, i)) != NULL)
333 talloc_free(cp);
334
335 vector_free(v);
336}
337
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200338/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200339static char *cmd_desc_str(const char **string)
340{
341 const char *cp, *start;
342 char *token;
343 int strlen;
344
345 cp = *string;
346
347 if (cp == NULL)
348 return NULL;
349
350 /* Skip white spaces. */
351 while (isspace((int)*cp) && *cp != '\0')
352 cp++;
353
354 /* Return if there is only white spaces */
355 if (*cp == '\0')
356 return NULL;
357
358 start = cp;
359
360 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
361 cp++;
362
363 strlen = cp - start;
364 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
365 memcpy(token, start, strlen);
366 *(token + strlen) = '\0';
367
368 *string = cp;
369
370 return token;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200374static vector cmd_make_descvec(const char *string, const char *descstr)
375{
376 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100377 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378 const char *sp;
379 char *token;
380 int len;
381 const char *cp;
382 const char *dp;
383 vector allvec;
384 vector strvec = NULL;
385 struct desc *desc;
386
387 cp = string;
388 dp = descstr;
389
390 if (cp == NULL)
391 return NULL;
392
393 allvec = vector_init(VECTOR_MIN_SIZE);
394
395 while (1) {
396 while (isspace((int)*cp) && *cp != '\0')
397 cp++;
398
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
400 if (cp[0] == '[' && cp[1] == '(') {
401 optional_brace = 1;
402 cp++;
403 }
404
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200405 if (*cp == '(') {
406 multiple = 1;
407 cp++;
408 }
409 if (*cp == ')') {
410 multiple = 0;
411 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100412 if (*cp == ']')
413 cp++;
414 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 }
416 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100417 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 cp++;
419 }
420
421 while (isspace((int)*cp) && *cp != '\0')
422 cp++;
423
424 if (*cp == '(') {
425 multiple = 1;
426 cp++;
427 }
428
429 if (*cp == '\0')
430 return allvec;
431
432 sp = cp;
433
434 while (!
435 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
436 || *cp == ')' || *cp == '|') && *cp != '\0')
437 cp++;
438
439 len = cp - sp;
440
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100441 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
442 if (optional_brace) {
443 /* Place each individual multi-choice token in its own square braces */
444 token[0] = '[';
445 memcpy(token + 1, sp, len);
446 token[1 + len] = ']';
447 token[2 + len] = '\0';
448 } else {
449 memcpy(token, sp, len);
450 *(token + len) = '\0';
451 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200452
453 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
454 desc->cmd = token;
455 desc->str = cmd_desc_str(&dp);
456
457 if (multiple) {
458 if (multiple == 1) {
459 strvec = vector_init(VECTOR_MIN_SIZE);
460 vector_set(allvec, strvec);
461 }
462 multiple++;
463 } else {
464 strvec = vector_init(VECTOR_MIN_SIZE);
465 vector_set(allvec, strvec);
466 }
467 vector_set(strvec, desc);
468 }
469}
470
471/* Count mandantory string vector size. This is to determine inputed
472 command has enough command length. */
473static int cmd_cmdsize(vector strvec)
474{
475 unsigned int i;
476 int size = 0;
477 vector descvec;
478 struct desc *desc;
479
480 for (i = 0; i < vector_active(strvec); i++)
481 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100482 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483 && (desc = vector_slot(descvec, 0)) != NULL) {
484 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
485 return size;
486 else
487 size++;
488 } else
489 size++;
490 }
491 return size;
492}
493
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200494/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200495const char *cmd_prompt(enum node_type node)
496{
497 struct cmd_node *cnode;
498
499 cnode = vector_slot(cmdvec, node);
500 return cnode->prompt;
501}
502
Alexander Couzensad580ba2016-05-16 16:01:45 +0200503/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200505 * \param unsafe string
506 * \return a new talloc char *
507 */
508char *osmo_asciidoc_escape(const char *inp)
509{
510 int _strlen;
511 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200512 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200513
514 if (!inp)
515 return NULL;
516 _strlen = strlen(inp);
517
518 for (i = 0; i < _strlen; ++i) {
519 switch (inp[i]) {
520 case '|':
521 len += 2;
522 break;
523 default:
524 len += 1;
525 break;
526 }
527 }
528
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200529 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200530 if (!out)
531 return NULL;
532
533 out_ptr = out;
534
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200538 /* Prepend escape character "\": */
539 *(out_ptr++) = '\\';
540 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 default:
542 *(out_ptr++) = inp[i];
543 break;
544 }
545 }
546
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 out_ptr[0] = '\0';
548 return out;
549}
550
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100551static char *xml_escape(const char *inp)
552{
553 int _strlen;
554 char *out, *out_ptr;
555 int len = 0, i, j;
556
557 if (!inp)
558 return NULL;
559 _strlen = strlen(inp);
560
561 for (i = 0; i < _strlen; ++i) {
562 switch (inp[i]) {
563 case '"':
564 len += 6;
565 break;
566 case '\'':
567 len += 6;
568 break;
569 case '<':
570 len += 4;
571 break;
572 case '>':
573 len += 4;
574 break;
575 case '&':
576 len += 5;
577 break;
578 default:
579 len += 1;
580 break;
581 }
582 }
583
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200584 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100585 if (!out)
586 return NULL;
587
588 out_ptr = out;
589
590#define ADD(out, str) \
591 for (j = 0; j < strlen(str); ++j) \
592 *(out++) = str[j];
593
594 for (i = 0; i < _strlen; ++i) {
595 switch (inp[i]) {
596 case '"':
597 ADD(out_ptr, "&quot;");
598 break;
599 case '\'':
600 ADD(out_ptr, "&apos;");
601 break;
602 case '<':
603 ADD(out_ptr, "&lt;");
604 break;
605 case '>':
606 ADD(out_ptr, "&gt;");
607 break;
608 case '&':
609 ADD(out_ptr, "&amp;");
610 break;
611 default:
612 *(out_ptr++) = inp[i];
613 break;
614 }
615 }
616
617#undef ADD
618
619 out_ptr[0] = '\0';
620 return out;
621}
622
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200623typedef int (*print_func_t)(void *data, const char *fmt, ...);
624
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700625static const struct value_string cmd_attr_desc[] = {
626 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
627 { CMD_ATTR_HIDDEN, "This command is hidden" },
628 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700629 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700630 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700631 { 0, NULL }
632};
633
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700634/* Get a flag character for a global VTY command attribute */
635static char cmd_attr_get_flag(unsigned int attr)
636{
637 switch (attr) {
638 case CMD_ATTR_IMMEDIATE:
639 return '!';
640 case CMD_ATTR_NODE_EXIT:
641 return '@';
642 default:
643 return '.';
644 }
645}
646
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700647/* Description of attributes shared between the lib commands */
648static const char * const cmd_lib_attr_desc[32] = {
649 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
650 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200651 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
652 "This command applies on ASP restart",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700653};
654
655/* Flag letters of attributes shared between the lib commands.
656 * NOTE: uppercase letters only, the rest is reserved for applications. */
657static const char cmd_lib_attr_letters[32] = {
658 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200659 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700660};
661
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100662/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200663 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100664 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200665static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100666{
667 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700668 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100669
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200670 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700671
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700672 /* Print global attributes and their description */
673 if (cmd->attr != 0x00) { /* ... if at least one flag is set */
674 print_func(data, " <attributes scope='global'>%s", newline);
675
676 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
677 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700678 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700679
680 if (~cmd->attr & cmd_attr_desc[i].value)
681 continue;
682
683 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700684 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700685 xml_att_desc, newline);
686 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700687
688 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
689 if (flag != '.')
690 print_func(data, " flag='%c'", flag);
691 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700692 }
693
694 print_func(data, " </attributes>%s", newline);
695 }
696
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700697 /* Print application specific attributes and their description */
698 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700699 const char * const *desc;
700 const char *letters;
701
702 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
703 print_func(data, " <attributes scope='library'>%s", newline);
704 letters = &cmd_lib_attr_letters[0];
705 desc = &cmd_lib_attr_desc[0];
706 } else {
707 print_func(data, " <attributes scope='application'>%s", newline);
708 letters = &host.app_info->usr_attr_letters[0];
709 desc = &host.app_info->usr_attr_desc[0];
710 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700711
712 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
713 char *xml_att_desc;
714 char flag;
715
716 /* Skip attribute if *not* set */
717 if (~cmd->usrattr & (1 << i))
718 continue;
719
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700720 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700721 print_func(data, " <attribute doc='%s'", xml_att_desc);
722 talloc_free(xml_att_desc);
723
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700724 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700725 print_func(data, " flag='%c'", flag);
726 print_func(data, " />%s", newline);
727 }
728
729 print_func(data, " </attributes>%s", newline);
730 }
731
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200732 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100733
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700734 for (i = 0; i < vector_count(cmd->strvec); ++i) {
735 vector descvec = vector_slot(cmd->strvec, i);
736 int j;
737 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100738 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700739 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100740 if (desc == NULL)
741 continue;
742
743 xml_param = xml_escape(desc->cmd);
744 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200745 print_func(data, " <param name='%s' doc='%s' />%s",
746 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100747 talloc_free(xml_param);
748 talloc_free(xml_doc);
749 }
750 }
751
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200752 print_func(data, " </params>%s", newline);
753 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100754
755 talloc_free(xml_string);
756 return 0;
757}
758
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200759static bool vty_command_is_common(struct cmd_element *cmd);
760
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100761/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200762 * 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 +0100763 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200764static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100765{
766 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200767 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100768
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200769 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100770
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200771 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200772 print_func(data, " <node id='_common_cmds_'>%s", newline);
773 print_func(data, " <name>Common Commands</name>%s", newline);
774 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
775 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200776 for (i = 0; i < vector_active(cmdvec); ++i) {
777 struct cmd_node *cnode;
778 cnode = vector_slot(cmdvec, i);
779 if (!cnode)
780 continue;
781 if (cnode->node != CONFIG_NODE)
782 continue;
783
784 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
785 struct cmd_element *elem;
786 elem = vector_slot(cnode->cmd_vector, j);
787 if (!vty_command_is_common(elem))
788 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200789 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200790 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200791 }
792 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200793 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200794
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100795 for (i = 0; i < vector_active(cmdvec); ++i) {
796 struct cmd_node *cnode;
797 cnode = vector_slot(cmdvec, i);
798 if (!cnode)
799 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200800 if (vector_active(cnode->cmd_vector) < 1)
801 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100802
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200803 /* De-dup node IDs: how many times has this same name been used before? Count the first
804 * occurence as _1 and omit that first suffix, so that the first occurence is called
805 * 'name', the second becomes 'name_2', then 'name_3', ... */
806 same_name_count = 1;
807 for (j = 0; j < i; ++j) {
808 struct cmd_node *cnode2;
809 cnode2 = vector_slot(cmdvec, j);
810 if (!cnode2)
811 continue;
812 if (strcmp(cnode->name, cnode2->name) == 0)
813 same_name_count ++;
814 }
815
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200816 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200817 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200818 print_func(data, "_%d", same_name_count);
819 print_func(data, "'>%s", newline);
820 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100821
822 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
823 struct cmd_element *elem;
824 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200825 if (vty_command_is_common(elem))
826 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200827 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200828 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100829 }
830
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200831 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100832 }
833
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200834 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100835
836 return 0;
837}
838
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200839static int print_func_vty(void *data, const char *format, ...)
840{
841 struct vty *vty = data;
842 va_list args;
843 int rc;
844 va_start(args, format);
845 rc = vty_out_va(vty, format, args);
846 va_end(args);
847 return rc;
848}
849
850static int vty_dump_xml_ref_to_vty(struct vty *vty)
851{
852 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
853}
854
855static int print_func_stream(void *data, const char *format, ...)
856{
857 va_list args;
858 int rc;
859 va_start(args, format);
860 rc = vfprintf((FILE*)data, format, args);
861 va_end(args);
862 return rc;
863}
864
865/*! Print the XML reference of all VTY nodes to the given stream.
866 */
867int vty_dump_xml_ref(FILE *stream)
868{
869 return vty_dump_nodes(print_func_stream, stream, "\n");
870}
871
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200872/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100873static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
874{
875 int i;
876
877 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
878 struct cmd_element *elem;
879 elem = vector_slot(cnode->cmd_vector, i);
880 if (!elem->string)
881 continue;
882 if (!strcmp(elem->string, cmdstring))
883 return 1;
884 }
885 return 0;
886}
887
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200888/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200889 * \param[in] ntype Node Type
890 * \param[cmd] element to be installed
891 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000892void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200893{
894 struct cmd_node *cnode;
895
896 cnode = vector_slot(cmdvec, ntype);
897
Harald Weltea99d45a2015-11-12 13:48:23 +0100898 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100899 /* ensure no _identical_ command has been registered at this
900 * node so far */
901 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200902
903 vector_set(cnode->cmd_vector, cmd);
904
905 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
906 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
907}
908
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700909/*! Install a library command into a node
910 * \param[in] ntype Node Type
911 * \param[in] cmd element to be installed
912 */
913void install_lib_element(int ntype, struct cmd_element *cmd)
914{
915 cmd->attr |= CMD_ATTR_LIB_COMMAND;
916 install_element(ntype, cmd);
917}
918
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200919/* Install a command into VIEW and ENABLE node */
920void install_element_ve(struct cmd_element *cmd)
921{
922 install_element(VIEW_NODE, cmd);
923 install_element(ENABLE_NODE, cmd);
924}
925
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700926/* Install a library command into VIEW and ENABLE node */
927void install_lib_element_ve(struct cmd_element *cmd)
928{
929 cmd->attr |= CMD_ATTR_LIB_COMMAND;
930 install_element_ve(cmd);
931}
932
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200933#ifdef VTY_CRYPT_PW
934static unsigned char itoa64[] =
935 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
936
937static void to64(char *s, long v, int n)
938{
939 while (--n >= 0) {
940 *s++ = itoa64[v & 0x3f];
941 v >>= 6;
942 }
943}
944
945static char *zencrypt(const char *passwd)
946{
947 char salt[6];
948 struct timeval tv;
949 char *crypt(const char *, const char *);
950
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200951 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200952
953 to64(&salt[0], random(), 3);
954 to64(&salt[3], tv.tv_usec, 3);
955 salt[5] = '\0';
956
957 return crypt(passwd, salt);
958}
959#endif
960
961/* This function write configuration of this host. */
962static int config_write_host(struct vty *vty)
963{
964 if (host.name)
965 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
966
967 if (host.encrypt) {
968 if (host.password_encrypt)
969 vty_out(vty, "password 8 %s%s", host.password_encrypt,
970 VTY_NEWLINE);
971 if (host.enable_encrypt)
972 vty_out(vty, "enable password 8 %s%s",
973 host.enable_encrypt, VTY_NEWLINE);
974 } else {
975 if (host.password)
976 vty_out(vty, "password %s%s", host.password,
977 VTY_NEWLINE);
978 if (host.enable)
979 vty_out(vty, "enable password %s%s", host.enable,
980 VTY_NEWLINE);
981 }
982
983 if (host.advanced)
984 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
985
986 if (host.encrypt)
987 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
988
989 if (host.lines >= 0)
990 vty_out(vty, "service terminal-length %d%s", host.lines,
991 VTY_NEWLINE);
992
993 if (host.motdfile)
994 vty_out(vty, "banner motd file %s%s", host.motdfile,
995 VTY_NEWLINE);
996 else if (!host.motd)
997 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
998
999 return 1;
1000}
1001
1002/* Utility function for getting command vector. */
1003static vector cmd_node_vector(vector v, enum node_type ntype)
1004{
1005 struct cmd_node *cnode = vector_slot(v, ntype);
1006 return cnode->cmd_vector;
1007}
1008
1009/* Completion match types. */
1010enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001011 NO_MATCH = 0,
1012 ANY_MATCH,
1013 EXTEND_MATCH,
1014 IPV4_PREFIX_MATCH,
1015 IPV4_MATCH,
1016 IPV6_PREFIX_MATCH,
1017 IPV6_MATCH,
1018 RANGE_MATCH,
1019 VARARG_MATCH,
1020 PARTLY_MATCH,
1021 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022};
1023
1024static enum match_type cmd_ipv4_match(const char *str)
1025{
1026 const char *sp;
1027 int dots = 0, nums = 0;
1028 char buf[4];
1029
1030 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001031 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001032
1033 for (;;) {
1034 memset(buf, 0, sizeof(buf));
1035 sp = str;
1036 while (*str != '\0') {
1037 if (*str == '.') {
1038 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001039 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001040
1041 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001042 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001043
1044 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001045 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001046
1047 dots++;
1048 break;
1049 }
1050 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001051 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001052
1053 str++;
1054 }
1055
1056 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001057 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001058
1059 strncpy(buf, sp, str - sp);
1060 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001061 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001062
1063 nums++;
1064
1065 if (*str == '\0')
1066 break;
1067
1068 str++;
1069 }
1070
1071 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001072 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001073
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001074 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001075}
1076
1077static enum match_type cmd_ipv4_prefix_match(const char *str)
1078{
1079 const char *sp;
1080 int dots = 0;
1081 char buf[4];
1082
1083 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001084 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001085
1086 for (;;) {
1087 memset(buf, 0, sizeof(buf));
1088 sp = str;
1089 while (*str != '\0' && *str != '/') {
1090 if (*str == '.') {
1091 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001092 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001093
1094 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001095 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001096
1097 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001098 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001099
1100 dots++;
1101 break;
1102 }
1103
1104 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001105 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001106
1107 str++;
1108 }
1109
1110 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001111 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001112
1113 strncpy(buf, sp, str - sp);
1114 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001115 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116
1117 if (dots == 3) {
1118 if (*str == '/') {
1119 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001120 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001121
1122 str++;
1123 break;
1124 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126 }
1127
1128 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001129 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130
1131 str++;
1132 }
1133
1134 sp = str;
1135 while (*str != '\0') {
1136 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001137 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001138
1139 str++;
1140 }
1141
1142 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001143 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001144
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001145 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001146}
1147
1148#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1149#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1150#define STATE_START 1
1151#define STATE_COLON 2
1152#define STATE_DOUBLE 3
1153#define STATE_ADDR 4
1154#define STATE_DOT 5
1155#define STATE_SLASH 6
1156#define STATE_MASK 7
1157
1158#ifdef HAVE_IPV6
1159
1160static enum match_type cmd_ipv6_match(const char *str)
1161{
1162 int state = STATE_START;
1163 int colons = 0, nums = 0, double_colon = 0;
1164 const char *sp = NULL;
1165 struct sockaddr_in6 sin6_dummy;
1166 int ret;
1167
1168 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001169 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001170
1171 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001172 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001173
1174 /* use inet_pton that has a better support,
1175 * for example inet_pton can support the automatic addresses:
1176 * ::1.2.3.4
1177 */
1178 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1179
1180 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001181 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001182
1183 while (*str != '\0') {
1184 switch (state) {
1185 case STATE_START:
1186 if (*str == ':') {
1187 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001188 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001189 colons--;
1190 state = STATE_COLON;
1191 } else {
1192 sp = str;
1193 state = STATE_ADDR;
1194 }
1195
1196 continue;
1197 case STATE_COLON:
1198 colons++;
1199 if (*(str + 1) == ':')
1200 state = STATE_DOUBLE;
1201 else {
1202 sp = str + 1;
1203 state = STATE_ADDR;
1204 }
1205 break;
1206 case STATE_DOUBLE:
1207 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001208 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001209
1210 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001211 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001212 else {
1213 if (*(str + 1) != '\0')
1214 colons++;
1215 sp = str + 1;
1216 state = STATE_ADDR;
1217 }
1218
1219 double_colon++;
1220 nums++;
1221 break;
1222 case STATE_ADDR:
1223 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1224 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226
1227 nums++;
1228 state = STATE_COLON;
1229 }
1230 if (*(str + 1) == '.')
1231 state = STATE_DOT;
1232 break;
1233 case STATE_DOT:
1234 state = STATE_ADDR;
1235 break;
1236 default:
1237 break;
1238 }
1239
1240 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001241 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001242
1243 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001244 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001245
1246 str++;
1247 }
1248
1249#if 0
1250 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001251 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001252#endif /* 0 */
1253
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001254 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001255}
1256
1257static enum match_type cmd_ipv6_prefix_match(const char *str)
1258{
1259 int state = STATE_START;
1260 int colons = 0, nums = 0, double_colon = 0;
1261 int mask;
1262 const char *sp = NULL;
1263 char *endptr = NULL;
1264
1265 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001266 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001267
1268 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001269 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001270
1271 while (*str != '\0' && state != STATE_MASK) {
1272 switch (state) {
1273 case STATE_START:
1274 if (*str == ':') {
1275 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001276 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001277 colons--;
1278 state = STATE_COLON;
1279 } else {
1280 sp = str;
1281 state = STATE_ADDR;
1282 }
1283
1284 continue;
1285 case STATE_COLON:
1286 colons++;
1287 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001288 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001289 else if (*(str + 1) == ':')
1290 state = STATE_DOUBLE;
1291 else {
1292 sp = str + 1;
1293 state = STATE_ADDR;
1294 }
1295 break;
1296 case STATE_DOUBLE:
1297 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001298 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001299
1300 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001301 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302 else {
1303 if (*(str + 1) != '\0' && *(str + 1) != '/')
1304 colons++;
1305 sp = str + 1;
1306
1307 if (*(str + 1) == '/')
1308 state = STATE_SLASH;
1309 else
1310 state = STATE_ADDR;
1311 }
1312
1313 double_colon++;
1314 nums += 1;
1315 break;
1316 case STATE_ADDR:
1317 if (*(str + 1) == ':' || *(str + 1) == '.'
1318 || *(str + 1) == '\0' || *(str + 1) == '/') {
1319 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001320 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001321
1322 for (; sp <= str; sp++)
1323 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001324 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001325
1326 nums++;
1327
1328 if (*(str + 1) == ':')
1329 state = STATE_COLON;
1330 else if (*(str + 1) == '.')
1331 state = STATE_DOT;
1332 else if (*(str + 1) == '/')
1333 state = STATE_SLASH;
1334 }
1335 break;
1336 case STATE_DOT:
1337 state = STATE_ADDR;
1338 break;
1339 case STATE_SLASH:
1340 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001341 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001342
1343 state = STATE_MASK;
1344 break;
1345 default:
1346 break;
1347 }
1348
1349 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001350 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001351
1352 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001353 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001354
1355 str++;
1356 }
1357
1358 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001359 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001360
1361 mask = strtol(str, &endptr, 10);
1362 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001363 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001364
1365 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001366 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001367
1368/* I don't know why mask < 13 makes command match partly.
1369 Forgive me to make this comments. I Want to set static default route
1370 because of lack of function to originate default in ospf6d; sorry
1371 yasu
1372 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001373 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001374*/
1375
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001376 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001377}
1378
1379#endif /* HAVE_IPV6 */
1380
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001381
1382#if ULONG_MAX == 18446744073709551615UL
1383#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1384#elif ULONG_MAX == 4294967295UL
1385#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1386#else
1387#error "ULONG_MAX not defined!"
1388#endif
1389
1390#if LONG_MAX == 9223372036854775807L
1391#define DECIMAL_STRLEN_MAX_SIGNED 19
1392#elif LONG_MAX == 2147483647L
1393#define DECIMAL_STRLEN_MAX_SIGNED 10
1394#else
1395#error "LONG_MAX not defined!"
1396#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001397
1398static int cmd_range_match(const char *range, const char *str)
1399{
1400 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001401 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001402 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001403
1404 if (str == NULL)
1405 return 1;
1406
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001407 if (range[1] == '-') {
1408 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001409
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001410 val = strtol(str, &endptr, 10);
1411 if (*endptr != '\0')
1412 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001413
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001414 range += 2;
1415 p = strchr(range, '-');
1416 if (p == NULL)
1417 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001418 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001419 return 0;
1420 strncpy(buf, range, p - range);
1421 buf[p - range] = '\0';
1422 min = -strtol(buf, &endptr, 10);
1423 if (*endptr != '\0')
1424 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001425
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001426 range = p + 1;
1427 p = strchr(range, '>');
1428 if (p == NULL)
1429 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001430 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001431 return 0;
1432 strncpy(buf, range, p - range);
1433 buf[p - range] = '\0';
1434 max = strtol(buf, &endptr, 10);
1435 if (*endptr != '\0')
1436 return 0;
1437
1438 if (val < min || val > max)
1439 return 0;
1440 } else {
1441 unsigned long min, max, val;
1442
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001443 if (str[0] == '-')
1444 return 0;
1445
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001446 val = strtoul(str, &endptr, 10);
1447 if (*endptr != '\0')
1448 return 0;
1449
1450 range++;
1451 p = strchr(range, '-');
1452 if (p == NULL)
1453 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001454 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001455 return 0;
1456 strncpy(buf, range, p - range);
1457 buf[p - range] = '\0';
1458 min = strtoul(buf, &endptr, 10);
1459 if (*endptr != '\0')
1460 return 0;
1461
1462 range = p + 1;
1463 p = strchr(range, '>');
1464 if (p == NULL)
1465 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001466 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001467 return 0;
1468 strncpy(buf, range, p - range);
1469 buf[p - range] = '\0';
1470 max = strtoul(buf, &endptr, 10);
1471 if (*endptr != '\0')
1472 return 0;
1473
1474 if (val < min || val > max)
1475 return 0;
1476 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001477
1478 return 1;
1479}
1480
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001481/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001482static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001483{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001484 /* we've got "[blah]". We want to strip off the []s and redo the
1485 * match check for "blah"
1486 */
1487 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001488
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001489 if (len < 3)
1490 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001491
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001492 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001493}
1494
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001495static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001496cmd_match(const char *str, const char *command,
1497 enum match_type min, bool recur)
1498{
1499
1500 if (recur && CMD_OPTION(str))
1501 {
1502 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001503 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001504
1505 /* this would be a bug in a command, however handle it gracefully
1506 * as it we only discover it if a user tries to run it
1507 */
1508 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001509 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001510
1511 ret = cmd_match(tmp, command, min, false);
1512
1513 talloc_free(tmp);
1514
1515 return ret;
1516 }
1517 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001518 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001519 else if (CMD_RANGE(str))
1520 {
1521 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001522 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001523 }
1524#ifdef HAVE_IPV6
1525 else if (CMD_IPV6(str))
1526 {
1527 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001528 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001529 }
1530 else if (CMD_IPV6_PREFIX(str))
1531 {
1532 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001533 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001534 }
1535#endif /* HAVE_IPV6 */
1536 else if (CMD_IPV4(str))
1537 {
1538 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001539 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001540 }
1541 else if (CMD_IPV4_PREFIX(str))
1542 {
1543 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001544 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001545 }
1546 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001547 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001548 else if (strncmp(command, str, strlen(command)) == 0)
1549 {
1550 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001551 return EXACT_MATCH;
1552 else if (PARTLY_MATCH >= min)
1553 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001554 }
1555
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001556 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001557}
1558
1559/* Filter vector at the specified index and by the given command string, to
1560 * the desired matching level (thus allowing part matches), and return match
1561 * type flag.
1562 */
1563static enum match_type
1564cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001565{
1566 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001567 struct cmd_element *cmd_element;
1568 enum match_type match_type;
1569 vector descvec;
1570 struct desc *desc;
1571
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001572 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001573
1574 /* If command and cmd_element string does not match set NULL to vector */
1575 for (i = 0; i < vector_active(v); i++)
1576 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577 if (index >= vector_active(cmd_element->strvec))
1578 vector_slot(v, i) = NULL;
1579 else {
1580 unsigned int j;
1581 int matched = 0;
1582
1583 descvec =
1584 vector_slot(cmd_element->strvec, index);
1585
1586 for (j = 0; j < vector_active(descvec); j++)
1587 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001588 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001589
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001590 ret = cmd_match (desc->cmd, command, level, true);
1591
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001592 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001593 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001594
1595 if (match_type < ret)
1596 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001597 }
1598 if (!matched)
1599 vector_slot(v, i) = NULL;
1600 }
1601 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001602
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001603 if (match_type == NO_MATCH)
1604 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001605
1606 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1607 * go again and filter out commands whose argument (at this index) is
1608 * 'weaker'. E.g., if we have 2 commands:
1609 *
1610 * foo bar <1-255>
1611 * foo bar BLAH
1612 *
1613 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001614 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001615 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1616 *
1617 * If we don't do a 2nd pass and filter it out, the higher-layers will
1618 * consider this to be ambiguous.
1619 */
1620 for (i = 0; i < vector_active(v); i++)
1621 if ((cmd_element = vector_slot(v, i)) != NULL) {
1622 if (index >= vector_active(cmd_element->strvec))
1623 vector_slot(v, i) = NULL;
1624 else {
1625 unsigned int j;
1626 int matched = 0;
1627
1628 descvec =
1629 vector_slot(cmd_element->strvec, index);
1630
1631 for (j = 0; j < vector_active(descvec); j++)
1632 if ((desc = vector_slot(descvec, j))) {
1633 enum match_type ret;
1634
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001635 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001636
1637 if (ret >= match_type)
1638 matched++;
1639 }
1640 if (!matched)
1641 vector_slot(v, i) = NULL;
1642 }
1643 }
1644
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001645 return match_type;
1646}
1647
1648/* Check ambiguous match */
1649static int
1650is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1651{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001652 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001653 unsigned int i;
1654 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001655 struct cmd_element *cmd_element;
1656 const char *matched = NULL;
1657 vector descvec;
1658 struct desc *desc;
1659
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001660 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1661 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1662 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1663 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1664 * that case, the string must remain allocated until this function exits or another match comes
1665 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1666 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1667 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1668 void *cmd_deopt_ctx = NULL;
1669
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001670 for (i = 0; i < vector_active(v); i++) {
1671 cmd_element = vector_slot(v, i);
1672 if (!cmd_element)
1673 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001674
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001675 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001676
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001677 descvec = vector_slot(cmd_element->strvec, index);
1678
1679 for (j = 0; j < vector_active(descvec); j++) {
1680 desc = vector_slot(descvec, j);
1681 if (!desc)
1682 continue;
1683
1684 enum match_type mtype;
1685 const char *str = desc->cmd;
1686
1687 if (CMD_OPTION(str)) {
1688 if (!cmd_deopt_ctx)
1689 cmd_deopt_ctx =
1690 talloc_named_const(tall_vty_cmd_ctx, 0,
1691 __func__);
1692 str = cmd_deopt(cmd_deopt_ctx, str);
1693 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001694 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001695 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001696
1697 switch (type) {
1698 case EXACT_MATCH:
1699 if (!(CMD_VARIABLE (str))
1700 && strcmp(command, str) == 0)
1701 match++;
1702 break;
1703 case PARTLY_MATCH:
1704 if (!(CMD_VARIABLE (str))
1705 && strncmp(command, str, strlen (command)) == 0)
1706 {
1707 if (matched
1708 && strcmp(matched,
1709 str) != 0) {
1710 ret = 1; /* There is ambiguous match. */
1711 goto free_and_return;
1712 } else
1713 matched = str;
1714 match++;
1715 }
1716 break;
1717 case RANGE_MATCH:
1718 if (cmd_range_match
1719 (str, command)) {
1720 if (matched
1721 && strcmp(matched,
1722 str) != 0) {
1723 ret = 1;
1724 goto free_and_return;
1725 } else
1726 matched = str;
1727 match++;
1728 }
1729 break;
1730#ifdef HAVE_IPV6
1731 case IPV6_MATCH:
1732 if (CMD_IPV6(str))
1733 match++;
1734 break;
1735 case IPV6_PREFIX_MATCH:
1736 if ((mtype =
1737 cmd_ipv6_prefix_match
1738 (command)) != NO_MATCH) {
1739 if (mtype == PARTLY_MATCH) {
1740 ret = 2; /* There is incomplete match. */
1741 goto free_and_return;
1742 }
1743
1744 match++;
1745 }
1746 break;
1747#endif /* HAVE_IPV6 */
1748 case IPV4_MATCH:
1749 if (CMD_IPV4(str))
1750 match++;
1751 break;
1752 case IPV4_PREFIX_MATCH:
1753 if ((mtype =
1754 cmd_ipv4_prefix_match
1755 (command)) != NO_MATCH) {
1756 if (mtype == PARTLY_MATCH) {
1757 ret = 2; /* There is incomplete match. */
1758 goto free_and_return;
1759 }
1760
1761 match++;
1762 }
1763 break;
1764 case EXTEND_MATCH:
1765 if (CMD_VARIABLE (str))
1766 match++;
1767 break;
1768 case NO_MATCH:
1769 default:
1770 break;
1771 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001772 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001773 if (!match)
1774 vector_slot(v, i) = NULL;
1775 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001776
1777free_and_return:
1778 if (cmd_deopt_ctx)
1779 talloc_free(cmd_deopt_ctx);
1780 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001781}
1782
1783/* If src matches dst return dst string, otherwise return NULL */
1784static const char *cmd_entry_function(const char *src, const char *dst)
1785{
1786 /* Skip variable arguments. */
1787 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1788 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1789 return NULL;
1790
1791 /* In case of 'command \t', given src is NULL string. */
1792 if (src == NULL)
1793 return dst;
1794
1795 /* Matched with input string. */
1796 if (strncmp(src, dst, strlen(src)) == 0)
1797 return dst;
1798
1799 return NULL;
1800}
1801
1802/* If src matches dst return dst string, otherwise return NULL */
1803/* This version will return the dst string always if it is
1804 CMD_VARIABLE for '?' key processing */
1805static const char *cmd_entry_function_desc(const char *src, const char *dst)
1806{
1807 if (CMD_VARARG(dst))
1808 return dst;
1809
1810 if (CMD_RANGE(dst)) {
1811 if (cmd_range_match(dst, src))
1812 return dst;
1813 else
1814 return NULL;
1815 }
1816#ifdef HAVE_IPV6
1817 if (CMD_IPV6(dst)) {
1818 if (cmd_ipv6_match(src))
1819 return dst;
1820 else
1821 return NULL;
1822 }
1823
1824 if (CMD_IPV6_PREFIX(dst)) {
1825 if (cmd_ipv6_prefix_match(src))
1826 return dst;
1827 else
1828 return NULL;
1829 }
1830#endif /* HAVE_IPV6 */
1831
1832 if (CMD_IPV4(dst)) {
1833 if (cmd_ipv4_match(src))
1834 return dst;
1835 else
1836 return NULL;
1837 }
1838
1839 if (CMD_IPV4_PREFIX(dst)) {
1840 if (cmd_ipv4_prefix_match(src))
1841 return dst;
1842 else
1843 return NULL;
1844 }
1845
1846 /* Optional or variable commands always match on '?' */
1847 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1848 return dst;
1849
1850 /* In case of 'command \t', given src is NULL string. */
1851 if (src == NULL)
1852 return dst;
1853
1854 if (strncmp(src, dst, strlen(src)) == 0)
1855 return dst;
1856 else
1857 return NULL;
1858}
1859
1860/* Check same string element existence. If it isn't there return
1861 1. */
1862static int cmd_unique_string(vector v, const char *str)
1863{
1864 unsigned int i;
1865 char *match;
1866
1867 for (i = 0; i < vector_active(v); i++)
1868 if ((match = vector_slot(v, i)) != NULL)
1869 if (strcmp(match, str) == 0)
1870 return 0;
1871 return 1;
1872}
1873
1874/* Compare string to description vector. If there is same string
1875 return 1 else return 0. */
1876static int desc_unique_string(vector v, const char *str)
1877{
1878 unsigned int i;
1879 struct desc *desc;
1880
1881 for (i = 0; i < vector_active(v); i++)
1882 if ((desc = vector_slot(v, i)) != NULL)
1883 if (strcmp(desc->cmd, str) == 0)
1884 return 1;
1885 return 0;
1886}
1887
1888static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1889{
1890 if (first_word != NULL &&
1891 node != AUTH_NODE &&
1892 node != VIEW_NODE &&
1893 node != AUTH_ENABLE_NODE &&
1894 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1895 return 1;
1896 return 0;
1897}
1898
1899/* '?' describe command support. */
1900static vector
1901cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1902{
1903 unsigned int i;
1904 vector cmd_vector;
1905#define INIT_MATCHVEC_SIZE 10
1906 vector matchvec;
1907 struct cmd_element *cmd_element;
1908 unsigned int index;
1909 int ret;
1910 enum match_type match;
1911 char *command;
1912 static struct desc desc_cr = { "<cr>", "" };
1913
1914 /* Set index. */
1915 if (vector_active(vline) == 0) {
1916 *status = CMD_ERR_NO_MATCH;
1917 return NULL;
1918 } else
1919 index = vector_active(vline) - 1;
1920
1921 /* Make copy vector of current node's command vector. */
1922 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1923
1924 /* Prepare match vector */
1925 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1926
1927 /* Filter commands. */
1928 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001929 for (i = 0; i < index; i++) {
1930 command = vector_slot(vline, i);
1931 if (!command)
1932 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001933
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001934 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001935
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001936 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001937 struct cmd_element *cmd_element;
1938 vector descvec;
1939 unsigned int j, k;
1940
1941 for (j = 0; j < vector_active(cmd_vector); j++)
1942 if ((cmd_element =
1943 vector_slot(cmd_vector, j)) != NULL
1944 &&
1945 (vector_active(cmd_element->strvec))) {
1946 descvec =
1947 vector_slot(cmd_element->
1948 strvec,
1949 vector_active
1950 (cmd_element->
1951 strvec) - 1);
1952 for (k = 0;
1953 k < vector_active(descvec);
1954 k++) {
1955 struct desc *desc =
1956 vector_slot(descvec,
1957 k);
1958 vector_set(matchvec,
1959 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001960 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001961 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001962
Harald Welte80d30fe2013-02-12 11:08:57 +01001963 vector_set(matchvec, &desc_cr);
1964 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001965
Harald Welte80d30fe2013-02-12 11:08:57 +01001966 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001967 }
1968
Harald Welte80d30fe2013-02-12 11:08:57 +01001969 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1970 match)) == 1) {
1971 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001972 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001973 *status = CMD_ERR_AMBIGUOUS;
1974 return NULL;
1975 } else if (ret == 2) {
1976 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001977 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001978 *status = CMD_ERR_NO_MATCH;
1979 return NULL;
1980 }
1981 }
1982
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001983 /* Prepare match vector */
1984 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1985
1986 /* Make sure that cmd_vector is filtered based on current word */
1987 command = vector_slot(vline, index);
1988 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001989 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001990
1991 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001992 for (i = 0; i < vector_active(cmd_vector); i++) {
1993 const char *string = NULL;
1994 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001995
Harald Welte80d30fe2013-02-12 11:08:57 +01001996 cmd_element = vector_slot(cmd_vector, i);
1997 if (!cmd_element)
1998 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001999
Harald Welted17aa592013-02-12 11:11:34 +01002000 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
2001 continue;
2002
Harald Welte80d30fe2013-02-12 11:08:57 +01002003 strvec = cmd_element->strvec;
2004
2005 /* if command is NULL, index may be equal to vector_active */
2006 if (command && index >= vector_active(strvec))
2007 vector_slot(cmd_vector, i) = NULL;
2008 else {
2009 /* Check if command is completed. */
2010 if (command == NULL
2011 && index == vector_active(strvec)) {
2012 string = "<cr>";
2013 if (!desc_unique_string(matchvec, string))
2014 vector_set(matchvec, &desc_cr);
2015 } else {
2016 unsigned int j;
2017 vector descvec = vector_slot(strvec, index);
2018 struct desc *desc;
2019
2020 for (j = 0; j < vector_active(descvec); j++) {
2021 desc = vector_slot(descvec, j);
2022 if (!desc)
2023 continue;
2024 string = cmd_entry_function_desc
2025 (command, desc->cmd);
2026 if (!string)
2027 continue;
2028 /* Uniqueness check */
2029 if (!desc_unique_string(matchvec, string))
2030 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002031 }
2032 }
2033 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002034 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002035 vector_free(cmd_vector);
2036
2037 if (vector_slot(matchvec, 0) == NULL) {
2038 vector_free(matchvec);
2039 *status = CMD_ERR_NO_MATCH;
2040 } else
2041 *status = CMD_SUCCESS;
2042
2043 return matchvec;
2044}
2045
2046vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2047{
2048 vector ret;
2049
2050 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2051 enum node_type onode;
2052 vector shifted_vline;
2053 unsigned int index;
2054
2055 onode = vty->node;
2056 vty->node = ENABLE_NODE;
2057 /* We can try it on enable node, cos' the vty is authenticated */
2058
2059 shifted_vline = vector_init(vector_count(vline));
2060 /* use memcpy? */
2061 for (index = 1; index < vector_active(vline); index++) {
2062 vector_set_index(shifted_vline, index - 1,
2063 vector_lookup(vline, index));
2064 }
2065
2066 ret = cmd_describe_command_real(shifted_vline, vty, status);
2067
2068 vector_free(shifted_vline);
2069 vty->node = onode;
2070 return ret;
2071 }
2072
2073 return cmd_describe_command_real(vline, vty, status);
2074}
2075
2076/* Check LCD of matched command. */
2077static int cmd_lcd(char **matched)
2078{
2079 int i;
2080 int j;
2081 int lcd = -1;
2082 char *s1, *s2;
2083 char c1, c2;
2084
2085 if (matched[0] == NULL || matched[1] == NULL)
2086 return 0;
2087
2088 for (i = 1; matched[i] != NULL; i++) {
2089 s1 = matched[i - 1];
2090 s2 = matched[i];
2091
2092 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2093 if (c1 != c2)
2094 break;
2095
2096 if (lcd < 0)
2097 lcd = j;
2098 else {
2099 if (lcd > j)
2100 lcd = j;
2101 }
2102 }
2103 return lcd;
2104}
2105
2106/* Command line completion support. */
2107static char **cmd_complete_command_real(vector vline, struct vty *vty,
2108 int *status)
2109{
2110 unsigned int i;
2111 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2112#define INIT_MATCHVEC_SIZE 10
2113 vector matchvec;
2114 struct cmd_element *cmd_element;
2115 unsigned int index;
2116 char **match_str;
2117 struct desc *desc;
2118 vector descvec;
2119 char *command;
2120 int lcd;
2121
2122 if (vector_active(vline) == 0) {
2123 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002124 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002125 return NULL;
2126 } else
2127 index = vector_active(vline) - 1;
2128
2129 /* First, filter by preceeding command string */
2130 for (i = 0; i < index; i++)
2131 if ((command = vector_slot(vline, i))) {
2132 enum match_type match;
2133 int ret;
2134
2135 /* First try completion match, if there is exactly match return 1 */
2136 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002137 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002138
2139 /* If there is exact match then filter ambiguous match else check
2140 ambiguousness. */
2141 if ((ret =
2142 is_cmd_ambiguous(command, cmd_vector, i,
2143 match)) == 1) {
2144 vector_free(cmd_vector);
2145 *status = CMD_ERR_AMBIGUOUS;
2146 return NULL;
2147 }
2148 /*
2149 else if (ret == 2)
2150 {
2151 vector_free (cmd_vector);
2152 *status = CMD_ERR_NO_MATCH;
2153 return NULL;
2154 }
2155 */
2156 }
2157
2158 /* Prepare match vector. */
2159 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2160
2161 /* Now we got into completion */
2162 for (i = 0; i < vector_active(cmd_vector); i++)
2163 if ((cmd_element = vector_slot(cmd_vector, i))) {
2164 const char *string;
2165 vector strvec = cmd_element->strvec;
2166
2167 /* Check field length */
2168 if (index >= vector_active(strvec))
2169 vector_slot(cmd_vector, i) = NULL;
2170 else {
2171 unsigned int j;
2172
2173 descvec = vector_slot(strvec, index);
2174 for (j = 0; j < vector_active(descvec); j++)
2175 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002176 const char *cmd = desc->cmd;
2177 char *tmp = NULL;
2178
2179 if (CMD_OPTION(desc->cmd)) {
2180 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2181 cmd = tmp;
2182 }
2183 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002184 if (cmd_unique_string (matchvec, string))
2185 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002186 if (tmp)
2187 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002188 }
2189 }
2190 }
2191
2192 /* We don't need cmd_vector any more. */
2193 vector_free(cmd_vector);
2194
2195 /* No matched command */
2196 if (vector_slot(matchvec, 0) == NULL) {
2197 vector_free(matchvec);
2198
2199 /* In case of 'command \t' pattern. Do you need '?' command at
2200 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002201 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002202 *status = CMD_ERR_NOTHING_TODO;
2203 else
2204 *status = CMD_ERR_NO_MATCH;
2205 return NULL;
2206 }
2207
2208 /* Only one matched */
2209 if (vector_slot(matchvec, 1) == NULL) {
2210 match_str = (char **)matchvec->index;
2211 vector_only_wrapper_free(matchvec);
2212 *status = CMD_COMPLETE_FULL_MATCH;
2213 return match_str;
2214 }
2215 /* Make it sure last element is NULL. */
2216 vector_set(matchvec, NULL);
2217
2218 /* Check LCD of matched strings. */
2219 if (vector_slot(vline, index) != NULL) {
2220 lcd = cmd_lcd((char **)matchvec->index);
2221
2222 if (lcd) {
2223 int len = strlen(vector_slot(vline, index));
2224
2225 if (len < lcd) {
2226 char *lcdstr;
2227
2228 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2229 "complete-lcdstr");
2230 memcpy(lcdstr, matchvec->index[0], lcd);
2231 lcdstr[lcd] = '\0';
2232
2233 /* match_str = (char **) &lcdstr; */
2234
2235 /* Free matchvec. */
2236 for (i = 0; i < vector_active(matchvec); i++) {
2237 if (vector_slot(matchvec, i))
2238 talloc_free(vector_slot(matchvec, i));
2239 }
2240 vector_free(matchvec);
2241
2242 /* Make new matchvec. */
2243 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2244 vector_set(matchvec, lcdstr);
2245 match_str = (char **)matchvec->index;
2246 vector_only_wrapper_free(matchvec);
2247
2248 *status = CMD_COMPLETE_MATCH;
2249 return match_str;
2250 }
2251 }
2252 }
2253
2254 match_str = (char **)matchvec->index;
2255 vector_only_wrapper_free(matchvec);
2256 *status = CMD_COMPLETE_LIST_MATCH;
2257 return match_str;
2258}
2259
2260char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2261{
2262 char **ret;
2263
2264 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2265 enum node_type onode;
2266 vector shifted_vline;
2267 unsigned int index;
2268
2269 onode = vty->node;
2270 vty->node = ENABLE_NODE;
2271 /* We can try it on enable node, cos' the vty is authenticated */
2272
2273 shifted_vline = vector_init(vector_count(vline));
2274 /* use memcpy? */
2275 for (index = 1; index < vector_active(vline); index++) {
2276 vector_set_index(shifted_vline, index - 1,
2277 vector_lookup(vline, index));
2278 }
2279
2280 ret = cmd_complete_command_real(shifted_vline, vty, status);
2281
2282 vector_free(shifted_vline);
2283 vty->node = onode;
2284 return ret;
2285 }
2286
2287 return cmd_complete_command_real(vline, vty, status);
2288}
2289
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002290static struct vty_parent_node *vty_parent(struct vty *vty)
2291{
2292 return llist_first_entry_or_null(&vty->parent_nodes,
2293 struct vty_parent_node,
2294 entry);
2295}
2296
2297static bool vty_pop_parent(struct vty *vty)
2298{
2299 struct vty_parent_node *parent = vty_parent(vty);
2300 if (!parent)
2301 return false;
2302 llist_del(&parent->entry);
2303 vty->node = parent->node;
2304 vty->priv = parent->priv;
2305 if (vty->indent)
2306 talloc_free(vty->indent);
2307 vty->indent = parent->indent;
2308 talloc_free(parent);
2309 return true;
2310}
2311
2312static void vty_clear_parents(struct vty *vty)
2313{
2314 while (vty_pop_parent(vty));
2315}
2316
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002317/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002318/*
2319 * This function MUST eventually converge on a node when called repeatedly,
2320 * there must not be any cycles.
2321 * All 'config' nodes shall converge on CONFIG_NODE.
2322 * All other 'enable' nodes shall converge on ENABLE_NODE.
2323 * All 'view' only nodes shall converge on VIEW_NODE.
2324 * All other nodes shall converge on themselves or it must be ensured,
2325 * that the user's rights are not extended anyhow by calling this function.
2326 *
2327 * Note that these requirements also apply to all functions that are used
2328 * as go_parent_cb.
2329 * Note also that this function relies on the is_config_child callback to
2330 * recognize non-config nodes if go_parent_cb is not set.
2331 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002332int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002333{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002334 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002335 case AUTH_NODE:
2336 case VIEW_NODE:
2337 case ENABLE_NODE:
2338 case CONFIG_NODE:
2339 vty_clear_parents(vty);
2340 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002341
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002342 case AUTH_ENABLE_NODE:
2343 vty->node = VIEW_NODE;
2344 vty_clear_parents(vty);
2345 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002346
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002347 default:
2348 if (host.app_info->go_parent_cb)
2349 host.app_info->go_parent_cb(vty);
2350 vty_pop_parent(vty);
2351 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002352 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002353
2354 return vty->node;
2355}
2356
2357/* Execute command by argument vline vector. */
2358static int
2359cmd_execute_command_real(vector vline, struct vty *vty,
2360 struct cmd_element **cmd)
2361{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002362 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002363 unsigned int index;
2364 vector cmd_vector;
2365 struct cmd_element *cmd_element;
2366 struct cmd_element *matched_element;
2367 unsigned int matched_count, incomplete_count;
2368 int argc;
2369 const char *argv[CMD_ARGC_MAX];
2370 enum match_type match = 0;
2371 int varflag;
2372 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002373 int rc;
2374 /* Used for temporary storage of cmd_deopt() allocated arguments during
2375 argv[] generation */
2376 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002377
2378 /* Make copy of command elements. */
2379 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2380
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002381 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002382 if ((command = vector_slot(vline, index))) {
2383 int ret;
2384
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002385 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002386 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002387
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002388 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002389 break;
2390
2391 ret =
2392 is_cmd_ambiguous(command, cmd_vector, index, match);
2393
2394 if (ret == 1) {
2395 vector_free(cmd_vector);
2396 return CMD_ERR_AMBIGUOUS;
2397 } else if (ret == 2) {
2398 vector_free(cmd_vector);
2399 return CMD_ERR_NO_MATCH;
2400 }
2401 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002402 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002403
2404 /* Check matched count. */
2405 matched_element = NULL;
2406 matched_count = 0;
2407 incomplete_count = 0;
2408
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002409 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002410 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002411 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002412 || index >= cmd_element->cmdsize) {
2413 matched_element = cmd_element;
2414#if 0
2415 printf("DEBUG: %s\n", cmd_element->string);
2416#endif
2417 matched_count++;
2418 } else {
2419 incomplete_count++;
2420 }
2421 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002422 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002423
2424 /* Finish of using cmd_vector. */
2425 vector_free(cmd_vector);
2426
2427 /* To execute command, matched_count must be 1. */
2428 if (matched_count == 0) {
2429 if (incomplete_count)
2430 return CMD_ERR_INCOMPLETE;
2431 else
2432 return CMD_ERR_NO_MATCH;
2433 }
2434
2435 if (matched_count > 1)
2436 return CMD_ERR_AMBIGUOUS;
2437
2438 /* Argument treatment */
2439 varflag = 0;
2440 argc = 0;
2441
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002442 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2443
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002444 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002445 if (argc == CMD_ARGC_MAX) {
2446 rc = CMD_ERR_EXEED_ARGC_MAX;
2447 goto rc_free_deopt_ctx;
2448 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002449 if (varflag) {
2450 argv[argc++] = vector_slot(vline, i);
2451 continue;
2452 }
2453
2454 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002455 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002456
2457 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002458 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002459 struct desc *desc = vector_slot(descvec, 0);
2460
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002461 if (CMD_OPTION(desc->cmd)) {
2462 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2463 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2464 } else {
2465 tmp_cmd = desc->cmd;
2466 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002467
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002468 if (CMD_VARARG(tmp_cmd))
2469 varflag = 1;
2470 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002471 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002472 else if (CMD_OPTION(desc->cmd))
2473 argv[argc++] = tmp_cmd;
2474 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002475 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002476 /* multi choice argument. look up which choice
2477 the user meant (can only be one after
2478 filtering and checking for ambigous). For instance,
2479 if user typed "th" for "(two|three)" arg, we
2480 want to pass "three" in argv[]. */
2481 for (j = 0; j < vector_active(descvec); j++) {
2482 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002483 if (!desc)
2484 continue;
2485 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2486 continue;
2487 if (CMD_OPTION(desc->cmd)) {
2488 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2489 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2490 } else {
2491 tmp_cmd = desc->cmd;
2492 }
2493
2494 if(CMD_VARIABLE(tmp_cmd)) {
2495 argv[argc++] = vector_slot(vline, i);
2496 } else {
2497 argv[argc++] = tmp_cmd;
2498 }
2499 break;
2500 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002501 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002502 }
2503
2504 /* For vtysh execution. */
2505 if (cmd)
2506 *cmd = matched_element;
2507
2508 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002509 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002510 else {
2511 /* Execute matched command. */
2512 struct vty_parent_node this_node = {
2513 .node = vty->node,
2514 .priv = vty->priv,
2515 .indent = vty->indent,
2516 };
2517 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002518 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002519
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002520 /* If we have stepped down into a child node, push a parent frame.
2521 * The causality is such: we don't expect every single node entry implementation to push
2522 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2523 * a parent node. Hence if the node changed without the parent node changing, we must
2524 * have stepped into a child node. */
2525 if (vty->node != this_node.node && parent == vty_parent(vty)
2526 && vty->node > CONFIG_NODE) {
2527 /* Push the parent node. */
2528 parent = talloc_zero(vty, struct vty_parent_node);
2529 *parent = this_node;
2530 llist_add(&parent->entry, &vty->parent_nodes);
2531 }
2532 }
2533
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002534rc_free_deopt_ctx:
2535 /* Now after we called the command func, we can free temporary strings */
2536 talloc_free(cmd_deopt_ctx);
2537 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002538}
2539
2540int
2541cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2542 int vtysh)
2543{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002544 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002545 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002546
2547 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002548
2549 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2550 vector shifted_vline;
2551 unsigned int index;
2552
2553 vty->node = ENABLE_NODE;
2554 /* We can try it on enable node, cos' the vty is authenticated */
2555
2556 shifted_vline = vector_init(vector_count(vline));
2557 /* use memcpy? */
2558 for (index = 1; index < vector_active(vline); index++) {
2559 vector_set_index(shifted_vline, index - 1,
2560 vector_lookup(vline, index));
2561 }
2562
2563 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2564
2565 vector_free(shifted_vline);
2566 vty->node = onode;
2567 return ret;
2568 }
2569
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002570 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002571}
2572
2573/* Execute command by argument readline. */
2574int
2575cmd_execute_command_strict(vector vline, struct vty *vty,
2576 struct cmd_element **cmd)
2577{
2578 unsigned int i;
2579 unsigned int index;
2580 vector cmd_vector;
2581 struct cmd_element *cmd_element;
2582 struct cmd_element *matched_element;
2583 unsigned int matched_count, incomplete_count;
2584 int argc;
2585 const char *argv[CMD_ARGC_MAX];
2586 int varflag;
2587 enum match_type match = 0;
2588 char *command;
2589
2590 /* Make copy of command element */
2591 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2592
2593 for (index = 0; index < vector_active(vline); index++)
2594 if ((command = vector_slot(vline, index))) {
2595 int ret;
2596
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002597 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002598 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002599
2600 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002601 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002602 break;
2603
2604 ret =
2605 is_cmd_ambiguous(command, cmd_vector, index, match);
2606 if (ret == 1) {
2607 vector_free(cmd_vector);
2608 return CMD_ERR_AMBIGUOUS;
2609 }
2610 if (ret == 2) {
2611 vector_free(cmd_vector);
2612 return CMD_ERR_NO_MATCH;
2613 }
2614 }
2615
2616 /* Check matched count. */
2617 matched_element = NULL;
2618 matched_count = 0;
2619 incomplete_count = 0;
2620 for (i = 0; i < vector_active(cmd_vector); i++)
2621 if (vector_slot(cmd_vector, i) != NULL) {
2622 cmd_element = vector_slot(cmd_vector, i);
2623
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002624 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002625 || index >= cmd_element->cmdsize) {
2626 matched_element = cmd_element;
2627 matched_count++;
2628 } else
2629 incomplete_count++;
2630 }
2631
2632 /* Finish of using cmd_vector. */
2633 vector_free(cmd_vector);
2634
2635 /* To execute command, matched_count must be 1. */
2636 if (matched_count == 0) {
2637 if (incomplete_count)
2638 return CMD_ERR_INCOMPLETE;
2639 else
2640 return CMD_ERR_NO_MATCH;
2641 }
2642
2643 if (matched_count > 1)
2644 return CMD_ERR_AMBIGUOUS;
2645
2646 /* Argument treatment */
2647 varflag = 0;
2648 argc = 0;
2649
2650 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002651 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002652 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002653 if (varflag) {
2654 argv[argc++] = vector_slot(vline, i);
2655 continue;
2656 }
2657
2658 vector descvec = vector_slot(matched_element->strvec, i);
2659
2660 if (vector_active(descvec) == 1) {
2661 struct desc *desc = vector_slot(descvec, 0);
2662
2663 if (CMD_VARARG(desc->cmd))
2664 varflag = 1;
2665
2666 if (varflag || CMD_VARIABLE(desc->cmd)
2667 || CMD_OPTION(desc->cmd))
2668 argv[argc++] = vector_slot(vline, i);
2669 } else {
2670 argv[argc++] = vector_slot(vline, i);
2671 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002672 }
2673
2674 /* For vtysh execution. */
2675 if (cmd)
2676 *cmd = matched_element;
2677
2678 if (matched_element->daemon)
2679 return CMD_SUCCESS_DAEMON;
2680
2681 /* Now execute matched command */
2682 return (*matched_element->func) (matched_element, vty, argc, argv);
2683}
2684
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002685static inline size_t len(const char *str)
2686{
2687 return str? strlen(str) : 0;
2688}
2689
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002690/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2691 * is longer than b, a must start with exactly b, and vice versa.
2692 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2693 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002694static int indent_cmp(const char *a, const char *b)
2695{
2696 size_t al, bl;
2697 al = len(a);
2698 bl = len(b);
2699 if (al > bl) {
2700 if (bl && strncmp(a, b, bl) != 0)
2701 return EINVAL;
2702 return 1;
2703 }
2704 /* al <= bl */
2705 if (al && strncmp(a, b, al) != 0)
2706 return EINVAL;
2707 return (al < bl)? -1 : 0;
2708}
2709
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002710/* Configration make from file. */
2711int config_from_file(struct vty *vty, FILE * fp)
2712{
2713 int ret;
2714 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002715 char *indent;
2716 int cmp;
2717 struct vty_parent_node this_node;
2718 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002719
2720 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002721 indent = NULL;
2722 vline = NULL;
2723 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002724
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002725 if (ret != CMD_SUCCESS)
2726 goto return_invalid_indent;
2727
2728 /* In case of comment or empty line */
2729 if (vline == NULL) {
2730 if (indent) {
2731 talloc_free(indent);
2732 indent = NULL;
2733 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002734 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002735 }
2736
Neels Hofmeyr43063632017-09-19 23:54:01 +02002737 /* We have a nonempty line. */
2738 if (!vty->indent) {
2739 /* We have just entered a node and expecting the first child to come up; but we
2740 * may also skip right back to a parent or ancestor level. */
2741 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002742
Neels Hofmeyr43063632017-09-19 23:54:01 +02002743 /* If there is no parent, record any indentation we encounter. */
2744 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2745
2746 if (cmp == EINVAL)
2747 goto return_invalid_indent;
2748
2749 if (cmp <= 0) {
2750 /* We have gone right back to the parent level or higher, we are skipping
2751 * this child node level entirely. Pop the parent to go back to a node
2752 * that was actually there (to reinstate vty->indent) and re-use below
2753 * go-parent while-loop to find an accurate match of indent in the node
2754 * ancestry. */
2755 vty_go_parent(vty);
2756 } else {
2757 /* The indent is deeper than the just entered parent, record the new
2758 * indentation characters. */
2759 vty->indent = talloc_strdup(vty, indent);
2760 /* This *is* the new indentation. */
2761 cmp = 0;
2762 }
2763 } else {
2764 /* There is a known indentation for this node level, validate and detect node
2765 * exits. */
2766 cmp = indent_cmp(indent, vty->indent);
2767 if (cmp == EINVAL)
2768 goto return_invalid_indent;
2769 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002770
2771 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2772 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2773 while (cmp < 0) {
2774 vty_go_parent(vty);
2775 cmp = indent_cmp(indent, vty->indent);
2776 if (cmp == EINVAL)
2777 goto return_invalid_indent;
2778 }
2779
2780 /* More indent without having entered a child node level? Either the parent node's indent
2781 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2782 * or the indentation increased even though the vty command didn't enter a child. */
2783 if (cmp > 0)
2784 goto return_invalid_indent;
2785
2786 /* Remember the current node before the command possibly changes it. */
2787 this_node = (struct vty_parent_node){
2788 .node = vty->node,
2789 .priv = vty->priv,
2790 .indent = vty->indent,
2791 };
2792
2793 parent = vty_parent(vty);
2794 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002795 cmd_free_strvec(vline);
2796
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002797 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002798 if (indent) {
2799 talloc_free(indent);
2800 indent = NULL;
2801 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002802 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002803 }
2804
2805 /* If we have stepped down into a child node, push a parent frame.
2806 * The causality is such: we don't expect every single node entry implementation to push
2807 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2808 * a parent node. Hence if the node changed without the parent node changing, we must
2809 * have stepped into a child node (and now expect a deeper indent). */
2810 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2811 /* Push the parent node. */
2812 parent = talloc_zero(vty, struct vty_parent_node);
2813 *parent = this_node;
2814 llist_add(&parent->entry, &vty->parent_nodes);
2815
2816 /* The current talloc'ed vty->indent string will now be owned by this parent
2817 * struct. Indicate that we don't know what deeper indent characters the user
2818 * will choose. */
2819 vty->indent = NULL;
2820 }
2821
2822 if (indent) {
2823 talloc_free(indent);
2824 indent = NULL;
2825 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002826 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002827 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2828 while (vty_parent(vty))
2829 vty_go_parent(vty);
2830
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002831 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002832
2833return_invalid_indent:
2834 if (vline)
2835 cmd_free_strvec(vline);
2836 if (indent) {
2837 talloc_free(indent);
2838 indent = NULL;
2839 }
2840 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002841}
2842
2843/* Configration from terminal */
2844DEFUN(config_terminal,
2845 config_terminal_cmd,
2846 "configure terminal",
2847 "Configuration from vty interface\n" "Configuration terminal\n")
2848{
2849 if (vty_config_lock(vty))
2850 vty->node = CONFIG_NODE;
2851 else {
2852 vty_out(vty, "VTY configuration is locked by other VTY%s",
2853 VTY_NEWLINE);
2854 return CMD_WARNING;
2855 }
2856 return CMD_SUCCESS;
2857}
2858
2859/* Enable command */
2860DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2861{
2862 /* If enable password is NULL, change to ENABLE_NODE */
2863 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2864 vty->type == VTY_SHELL_SERV)
2865 vty->node = ENABLE_NODE;
2866 else
2867 vty->node = AUTH_ENABLE_NODE;
2868
2869 return CMD_SUCCESS;
2870}
2871
2872/* Disable command */
2873DEFUN(disable,
2874 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2875{
2876 if (vty->node == ENABLE_NODE)
2877 vty->node = VIEW_NODE;
2878 return CMD_SUCCESS;
2879}
2880
2881/* Down vty node level. */
2882gDEFUN(config_exit,
2883 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2884{
2885 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002886 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002887 case VIEW_NODE:
2888 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002889 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002890 break;
2891 case CONFIG_NODE:
2892 vty->node = ENABLE_NODE;
2893 vty_config_unlock(vty);
2894 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002895 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002896 if (vty->node > CONFIG_NODE)
2897 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002898 break;
2899 }
2900 return CMD_SUCCESS;
2901}
2902
2903/* End of configuration. */
2904 gDEFUN(config_end,
2905 config_end_cmd, "end", "End current mode and change to enable mode.")
2906{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002907 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002908 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002909
2910 /* Repeatedly call go_parent until a top node is reached. */
2911 while (vty->node > CONFIG_NODE) {
2912 if (vty->node == last_node) {
2913 /* Ensure termination, this shouldn't happen. */
2914 break;
2915 }
2916 last_node = vty->node;
2917 vty_go_parent(vty);
2918 }
2919
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002920 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002921 if (vty->node > ENABLE_NODE)
2922 vty->node = ENABLE_NODE;
2923 vty->index = NULL;
2924 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002925 }
2926 return CMD_SUCCESS;
2927}
2928
2929/* Show version. */
2930DEFUN(show_version,
2931 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2932{
Harald Welte237f6242010-05-25 23:00:45 +02002933 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2934 host.app_info->version,
2935 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2936 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002937
2938 return CMD_SUCCESS;
2939}
2940
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002941DEFUN(show_online_help,
2942 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2943{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002944 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002945 return CMD_SUCCESS;
2946}
2947
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002948/* Help display function for all node. */
2949gDEFUN(config_help,
2950 config_help_cmd, "help", "Description of the interactive help system\n")
2951{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002952 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2953 "anytime at the command line please press '?'.%s%s"
2954 "If nothing matches, the help list will be empty and you must backup%s"
2955 " until entering a '?' shows the available options.%s"
2956 "Two styles of help are provided:%s"
2957 "1. Full help is available when you are ready to enter a%s"
2958 "command argument (e.g. 'show ?') and describes each possible%s"
2959 "argument.%s"
2960 "2. Partial help is provided when an abbreviated argument is entered%s"
2961 " and you want to know what arguments match the input%s"
2962 " (e.g. 'show me?'.)%s%s",
2963 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2964 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2965 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2966 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002967 return CMD_SUCCESS;
2968}
2969
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002970enum {
2971 ATTR_TYPE_GLOBAL = (1 << 0),
2972 ATTR_TYPE_LIB = (1 << 1),
2973 ATTR_TYPE_APP = (1 << 2),
2974};
2975
2976static void print_attr_list(struct vty *vty, unsigned int attr_mask)
2977{
2978 const char *desc;
2979 unsigned int i;
2980 bool found;
2981 char flag;
2982
2983 if (attr_mask & ATTR_TYPE_GLOBAL) {
2984 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
2985
2986 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07002987 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002988 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07002989
2990 /* Skip attributes without flags */
2991 if (flag != '.')
2992 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002993 }
2994 }
2995
2996 if (attr_mask & ATTR_TYPE_LIB) {
2997 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
2998
2999 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3000 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3001 continue;
3002 found = true;
3003
3004 flag = cmd_lib_attr_letters[i];
3005 if (flag == '\0')
3006 flag = '.';
3007
3008 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3009 }
3010
3011 if (!found)
3012 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3013 }
3014
3015 if (attr_mask & ATTR_TYPE_APP) {
3016 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3017
3018 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3019 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3020 continue;
3021 found = true;
3022
3023 flag = host.app_info->usr_attr_letters[i];
3024 if (flag == '\0')
3025 flag = '.';
3026
3027 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3028 }
3029
3030 if (!found)
3031 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3032 }
3033}
3034
3035gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3036 "show vty-attributes",
3037 SHOW_STR "List of VTY attributes\n")
3038{
3039 print_attr_list(vty, 0xff);
3040 return CMD_SUCCESS;
3041}
3042
3043gDEFUN(show_vty_attr, show_vty_attr_cmd,
3044 "show vty-attributes (application|library|global)",
3045 SHOW_STR "List of VTY attributes\n"
3046 "Application specific attributes only\n"
3047 "Library specific attributes only\n"
3048 "Global attributes only\n")
3049{
3050 unsigned int attr_mask = 0;
3051
3052 if (argv[0][0] == 'g') /* global */
3053 attr_mask |= ATTR_TYPE_GLOBAL;
3054 else if (argv[0][0] == 'l') /* library */
3055 attr_mask |= ATTR_TYPE_LIB;
3056 else if (argv[0][0] == 'a') /* application */
3057 attr_mask |= ATTR_TYPE_APP;
3058
3059 print_attr_list(vty, attr_mask);
3060 return CMD_SUCCESS;
3061}
3062
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003063/* Compose flag bit-mask for all commands within the given node */
3064static unsigned int node_flag_mask(const struct cmd_node *cnode)
3065{
3066 unsigned int flag_mask = 0x00;
3067 unsigned int f, i;
3068
3069 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3070 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3071 const struct cmd_element *cmd;
3072 char flag_letter;
3073
3074 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3075 continue;
3076 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
3077 continue;
3078 if (~cmd->usrattr & (1 << f))
3079 continue;
3080
3081 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3082 flag_letter = cmd_lib_attr_letters[f];
3083 else
3084 flag_letter = host.app_info->usr_attr_letters[f];
3085
3086 if (flag_letter == '\0')
3087 continue;
3088
3089 flag_mask |= (1 << f);
3090 break;
3091 }
3092 }
3093
3094 return flag_mask;
3095}
3096
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003097/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3098static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3099{
3100 static char char_mask[8 + 1];
3101 char *ptr = &char_mask[0];
3102
3103 /* Mutually exclusive global attributes */
3104 if (cmd->attr & CMD_ATTR_IMMEDIATE)
3105 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3106 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3107 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3108 else
3109 *(ptr++) = '.';
3110
3111 *ptr = '\0';
3112
3113 return char_mask;
3114}
3115
3116/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003117static const char *cmd_flag_mask(const struct cmd_element *cmd,
3118 unsigned int flag_mask)
3119{
3120 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3121 char *ptr = &char_mask[0];
3122 char flag_letter;
3123 unsigned int f;
3124
3125 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3126 if (~flag_mask & (1 << f))
3127 continue;
3128 if (~cmd->usrattr & (1 << f)) {
3129 *(ptr++) = '.';
3130 continue;
3131 }
3132
3133 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3134 flag_letter = cmd_lib_attr_letters[f];
3135 else
3136 flag_letter = host.app_info->usr_attr_letters[f];
3137
3138 *(ptr++) = flag_letter ? flag_letter : '.';
3139 }
3140
3141 *ptr = '\0';
3142
3143 return char_mask;
3144}
3145
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003146/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003147gDEFUN(config_list, config_list_cmd,
3148 "list [with-flags]",
3149 "Print command list\n"
3150 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003151{
3152 unsigned int i;
3153 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003154 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003155 struct cmd_element *cmd;
3156
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003157 if (argc > 0)
3158 flag_mask = node_flag_mask(cnode);
3159
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003160 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3161 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3162 continue;
3163 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
3164 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003165 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003166 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3167 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003168 vty_out(vty, " %s %s %s%s",
3169 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003170 cmd_flag_mask(cmd, flag_mask),
3171 cmd->string, VTY_NEWLINE);
3172 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003173 }
3174
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003175 return CMD_SUCCESS;
3176}
3177
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003178static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003179{
3180 unsigned int i;
3181 int fd;
3182 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003183 char *config_file_tmp = NULL;
3184 char *config_file_sav = NULL;
3185 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003186 struct stat st;
3187
3188 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003189
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003190 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3191 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3192 * manually instead. */
3193
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003194 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003195 config_file_sav =
3196 _talloc_zero(tall_vty_cmd_ctx,
3197 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3198 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003199 if (!config_file_sav)
3200 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003201 strcpy(config_file_sav, config_file);
3202 strcat(config_file_sav, CONF_BACKUP_EXT);
3203
3204 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003205 "config_file_tmp");
3206 if (!config_file_tmp) {
3207 talloc_free(config_file_sav);
3208 return -1;
3209 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003210 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3211
3212 /* Open file to configuration write. */
3213 fd = mkstemp(config_file_tmp);
3214 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003215 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003216 talloc_free(config_file_tmp);
3217 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003218 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003219 }
3220
3221 /* Make vty for configuration file. */
3222 file_vty = vty_new();
3223 file_vty->fd = fd;
3224 file_vty->type = VTY_FILE;
3225
3226 /* Config file header print. */
3227 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003228 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003229 //vty_time_print (file_vty, 1);
3230 vty_out(file_vty, "!\n");
3231
3232 for (i = 0; i < vector_active(cmdvec); i++)
3233 if ((node = vector_slot(cmdvec, i)) && node->func) {
3234 if ((*node->func) (file_vty))
3235 vty_out(file_vty, "!\n");
3236 }
3237 vty_close(file_vty);
3238
3239 if (unlink(config_file_sav) != 0)
3240 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003241 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003242 talloc_free(config_file_sav);
3243 talloc_free(config_file_tmp);
3244 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003245 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003246 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003247
3248 /* Only link the .sav file if the original file exists */
3249 if (stat(config_file, &st) == 0) {
3250 if (link(config_file, config_file_sav) != 0) {
3251 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3252 talloc_free(config_file_sav);
3253 talloc_free(config_file_tmp);
3254 unlink(config_file_tmp);
3255 return -3;
3256 }
3257 sync();
3258 if (unlink(config_file) != 0) {
3259 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3260 talloc_free(config_file_sav);
3261 talloc_free(config_file_tmp);
3262 unlink(config_file_tmp);
3263 return -4;
3264 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003265 }
3266 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003267 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003268 talloc_free(config_file_sav);
3269 talloc_free(config_file_tmp);
3270 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003271 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003272 }
3273 unlink(config_file_tmp);
3274 sync();
3275
3276 talloc_free(config_file_sav);
3277 talloc_free(config_file_tmp);
3278
3279 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003280 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3281 return -6;
3282 }
3283
3284 return 0;
3285}
3286
3287
3288/* Write current configuration into file. */
3289DEFUN(config_write_file,
3290 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003291 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003292 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003293 "Write to configuration file\n"
3294 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003295{
3296 char *failed_file;
3297 int rc;
3298
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003299 if (host.app_info->config_is_consistent) {
3300 rc = host.app_info->config_is_consistent(vty);
3301 if (!rc) {
3302 vty_out(vty, "Configuration is not consistent%s",
3303 VTY_NEWLINE);
3304 return CMD_WARNING;
3305 }
3306 }
3307
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003308 if (argc == 1)
3309 host_config_set(argv[0]);
3310
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003311 if (host.config == NULL) {
3312 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3313 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003314 return CMD_WARNING;
3315 }
3316
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003317 rc = write_config_file(host.config, &failed_file);
3318 switch (rc) {
3319 case -1:
3320 vty_out(vty, "Can't open configuration file %s.%s",
3321 failed_file, VTY_NEWLINE);
3322 rc = CMD_WARNING;
3323 break;
3324 case -2:
3325 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3326 failed_file, VTY_NEWLINE);
3327 rc = CMD_WARNING;
3328 break;
3329 case -3:
3330 vty_out(vty, "Can't backup old configuration file %s.%s",
3331 failed_file, VTY_NEWLINE);
3332 rc = CMD_WARNING;
3333 break;
3334 case -4:
3335 vty_out(vty, "Can't unlink configuration file %s.%s",
3336 failed_file, VTY_NEWLINE);
3337 rc = CMD_WARNING;
3338 break;
3339 case -5:
3340 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3341 VTY_NEWLINE);
3342 rc = CMD_WARNING;
3343 break;
3344 case -6:
3345 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3346 failed_file, strerror(errno), errno, VTY_NEWLINE);
3347 rc = CMD_WARNING;
3348 break;
3349 default:
3350 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3351 rc = CMD_SUCCESS;
3352 break;
3353 }
3354
3355 talloc_free(failed_file);
3356 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003357}
3358
3359ALIAS(config_write_file,
3360 config_write_cmd,
3361 "write", "Write running configuration to memory, network, or terminal\n")
3362
3363 ALIAS(config_write_file,
3364 config_write_memory_cmd,
3365 "write memory",
3366 "Write running configuration to memory, network, or terminal\n"
3367 "Write configuration to the file (same as write file)\n")
3368
3369 ALIAS(config_write_file,
3370 copy_runningconfig_startupconfig_cmd,
3371 "copy running-config startup-config",
3372 "Copy configuration\n"
3373 "Copy running config to... \n"
3374 "Copy running config to startup config (same as write file)\n")
3375
3376/* Write current configuration into the terminal. */
3377 DEFUN(config_write_terminal,
3378 config_write_terminal_cmd,
3379 "write terminal",
3380 "Write running configuration to memory, network, or terminal\n"
3381 "Write to terminal\n")
3382{
3383 unsigned int i;
3384 struct cmd_node *node;
3385
3386 if (vty->type == VTY_SHELL_SERV) {
3387 for (i = 0; i < vector_active(cmdvec); i++)
3388 if ((node = vector_slot(cmdvec, i)) && node->func
3389 && node->vtysh) {
3390 if ((*node->func) (vty))
3391 vty_out(vty, "!%s", VTY_NEWLINE);
3392 }
3393 } else {
3394 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3395 VTY_NEWLINE);
3396 vty_out(vty, "!%s", VTY_NEWLINE);
3397
3398 for (i = 0; i < vector_active(cmdvec); i++)
3399 if ((node = vector_slot(cmdvec, i)) && node->func) {
3400 if ((*node->func) (vty))
3401 vty_out(vty, "!%s", VTY_NEWLINE);
3402 }
3403 vty_out(vty, "end%s", VTY_NEWLINE);
3404 }
3405 return CMD_SUCCESS;
3406}
3407
3408/* Write current configuration into the terminal. */
3409ALIAS(config_write_terminal,
3410 show_running_config_cmd,
3411 "show running-config", SHOW_STR "running configuration\n")
3412
3413/* Write startup configuration into the terminal. */
3414 DEFUN(show_startup_config,
3415 show_startup_config_cmd,
3416 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3417{
3418 char buf[BUFSIZ];
3419 FILE *confp;
3420
3421 confp = fopen(host.config, "r");
3422 if (confp == NULL) {
3423 vty_out(vty, "Can't open configuration file [%s]%s",
3424 host.config, VTY_NEWLINE);
3425 return CMD_WARNING;
3426 }
3427
3428 while (fgets(buf, BUFSIZ, confp)) {
3429 char *cp = buf;
3430
3431 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3432 cp++;
3433 *cp = '\0';
3434
3435 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3436 }
3437
3438 fclose(confp);
3439
3440 return CMD_SUCCESS;
3441}
3442
3443/* Hostname configuration */
3444DEFUN(config_hostname,
3445 hostname_cmd,
3446 "hostname WORD",
3447 "Set system's network name\n" "This system's network name\n")
3448{
3449 if (!isalpha((int)*argv[0])) {
3450 vty_out(vty, "Please specify string starting with alphabet%s",
3451 VTY_NEWLINE);
3452 return CMD_WARNING;
3453 }
3454
3455 if (host.name)
3456 talloc_free(host.name);
3457
3458 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3459 return CMD_SUCCESS;
3460}
3461
3462DEFUN(config_no_hostname,
3463 no_hostname_cmd,
3464 "no hostname [HOSTNAME]",
3465 NO_STR "Reset system's network name\n" "Host name of this router\n")
3466{
3467 if (host.name)
3468 talloc_free(host.name);
3469 host.name = NULL;
3470 return CMD_SUCCESS;
3471}
3472
3473/* VTY interface password set. */
3474DEFUN(config_password, password_cmd,
3475 "password (8|) WORD",
3476 "Assign the terminal connection password\n"
3477 "Specifies a HIDDEN password will follow\n"
3478 "dummy string \n" "The HIDDEN line password string\n")
3479{
3480 /* Argument check. */
3481 if (argc == 0) {
3482 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3483 return CMD_WARNING;
3484 }
3485
3486 if (argc == 2) {
3487 if (*argv[0] == '8') {
3488 if (host.password)
3489 talloc_free(host.password);
3490 host.password = NULL;
3491 if (host.password_encrypt)
3492 talloc_free(host.password_encrypt);
3493 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3494 return CMD_SUCCESS;
3495 } else {
3496 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3497 return CMD_WARNING;
3498 }
3499 }
3500
3501 if (!isalnum((int)*argv[0])) {
3502 vty_out(vty,
3503 "Please specify string starting with alphanumeric%s",
3504 VTY_NEWLINE);
3505 return CMD_WARNING;
3506 }
3507
3508 if (host.password)
3509 talloc_free(host.password);
3510 host.password = NULL;
3511
3512#ifdef VTY_CRYPT_PW
3513 if (host.encrypt) {
3514 if (host.password_encrypt)
3515 talloc_free(host.password_encrypt);
3516 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3517 } else
3518#endif
3519 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3520
3521 return CMD_SUCCESS;
3522}
3523
3524ALIAS(config_password, password_text_cmd,
3525 "password LINE",
3526 "Assign the terminal connection password\n"
3527 "The UNENCRYPTED (cleartext) line password\n")
3528
3529/* VTY enable password set. */
3530 DEFUN(config_enable_password, enable_password_cmd,
3531 "enable password (8|) WORD",
3532 "Modify enable password parameters\n"
3533 "Assign the privileged level password\n"
3534 "Specifies a HIDDEN password will follow\n"
3535 "dummy string \n" "The HIDDEN 'enable' password string\n")
3536{
3537 /* Argument check. */
3538 if (argc == 0) {
3539 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3540 return CMD_WARNING;
3541 }
3542
3543 /* Crypt type is specified. */
3544 if (argc == 2) {
3545 if (*argv[0] == '8') {
3546 if (host.enable)
3547 talloc_free(host.enable);
3548 host.enable = NULL;
3549
3550 if (host.enable_encrypt)
3551 talloc_free(host.enable_encrypt);
3552 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3553
3554 return CMD_SUCCESS;
3555 } else {
3556 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3557 return CMD_WARNING;
3558 }
3559 }
3560
3561 if (!isalnum((int)*argv[0])) {
3562 vty_out(vty,
3563 "Please specify string starting with alphanumeric%s",
3564 VTY_NEWLINE);
3565 return CMD_WARNING;
3566 }
3567
3568 if (host.enable)
3569 talloc_free(host.enable);
3570 host.enable = NULL;
3571
3572 /* Plain password input. */
3573#ifdef VTY_CRYPT_PW
3574 if (host.encrypt) {
3575 if (host.enable_encrypt)
3576 talloc_free(host.enable_encrypt);
3577 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3578 } else
3579#endif
3580 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3581
3582 return CMD_SUCCESS;
3583}
3584
3585ALIAS(config_enable_password,
3586 enable_password_text_cmd,
3587 "enable password LINE",
3588 "Modify enable password parameters\n"
3589 "Assign the privileged level password\n"
3590 "The UNENCRYPTED (cleartext) 'enable' password\n")
3591
3592/* VTY enable password delete. */
3593 DEFUN(no_config_enable_password, no_enable_password_cmd,
3594 "no enable password",
3595 NO_STR
3596 "Modify enable password parameters\n"
3597 "Assign the privileged level password\n")
3598{
3599 if (host.enable)
3600 talloc_free(host.enable);
3601 host.enable = NULL;
3602
3603 if (host.enable_encrypt)
3604 talloc_free(host.enable_encrypt);
3605 host.enable_encrypt = NULL;
3606
3607 return CMD_SUCCESS;
3608}
3609
3610#ifdef VTY_CRYPT_PW
3611DEFUN(service_password_encrypt,
3612 service_password_encrypt_cmd,
3613 "service password-encryption",
3614 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3615{
3616 if (host.encrypt)
3617 return CMD_SUCCESS;
3618
3619 host.encrypt = 1;
3620
3621 if (host.password) {
3622 if (host.password_encrypt)
3623 talloc_free(host.password_encrypt);
3624 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3625 }
3626 if (host.enable) {
3627 if (host.enable_encrypt)
3628 talloc_free(host.enable_encrypt);
3629 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3630 }
3631
3632 return CMD_SUCCESS;
3633}
3634
3635DEFUN(no_service_password_encrypt,
3636 no_service_password_encrypt_cmd,
3637 "no service password-encryption",
3638 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3639{
3640 if (!host.encrypt)
3641 return CMD_SUCCESS;
3642
3643 host.encrypt = 0;
3644
3645 if (host.password_encrypt)
3646 talloc_free(host.password_encrypt);
3647 host.password_encrypt = NULL;
3648
3649 if (host.enable_encrypt)
3650 talloc_free(host.enable_encrypt);
3651 host.enable_encrypt = NULL;
3652
3653 return CMD_SUCCESS;
3654}
3655#endif
3656
3657DEFUN(config_terminal_length, config_terminal_length_cmd,
3658 "terminal length <0-512>",
3659 "Set terminal line parameters\n"
3660 "Set number of lines on a screen\n"
3661 "Number of lines on screen (0 for no pausing)\n")
3662{
3663 int lines;
3664 char *endptr = NULL;
3665
3666 lines = strtol(argv[0], &endptr, 10);
3667 if (lines < 0 || lines > 512 || *endptr != '\0') {
3668 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3669 return CMD_WARNING;
3670 }
3671 vty->lines = lines;
3672
3673 return CMD_SUCCESS;
3674}
3675
3676DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3677 "terminal no length",
3678 "Set terminal line parameters\n"
3679 NO_STR "Set number of lines on a screen\n")
3680{
3681 vty->lines = -1;
3682 return CMD_SUCCESS;
3683}
3684
3685DEFUN(service_terminal_length, service_terminal_length_cmd,
3686 "service terminal-length <0-512>",
3687 "Set up miscellaneous service\n"
3688 "System wide terminal length configuration\n"
3689 "Number of lines of VTY (0 means no line control)\n")
3690{
3691 int lines;
3692 char *endptr = NULL;
3693
3694 lines = strtol(argv[0], &endptr, 10);
3695 if (lines < 0 || lines > 512 || *endptr != '\0') {
3696 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3697 return CMD_WARNING;
3698 }
3699 host.lines = lines;
3700
3701 return CMD_SUCCESS;
3702}
3703
3704DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3705 "no service terminal-length [<0-512>]",
3706 NO_STR
3707 "Set up miscellaneous service\n"
3708 "System wide terminal length configuration\n"
3709 "Number of lines of VTY (0 means no line control)\n")
3710{
3711 host.lines = -1;
3712 return CMD_SUCCESS;
3713}
3714
3715DEFUN_HIDDEN(do_echo,
3716 echo_cmd,
3717 "echo .MESSAGE",
3718 "Echo a message back to the vty\n" "The message to echo\n")
3719{
3720 char *message;
3721
3722 vty_out(vty, "%s%s",
3723 ((message =
3724 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3725 if (message)
3726 talloc_free(message);
3727 return CMD_SUCCESS;
3728}
3729
3730#if 0
3731DEFUN(config_logmsg,
3732 config_logmsg_cmd,
3733 "logmsg " LOG_LEVELS " .MESSAGE",
3734 "Send a message to enabled logging destinations\n"
3735 LOG_LEVEL_DESC "The message to send\n")
3736{
3737 int level;
3738 char *message;
3739
3740 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3741 return CMD_ERR_NO_MATCH;
3742
3743 zlog(NULL, level,
3744 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3745 if (message)
3746 talloc_free(message);
3747 return CMD_SUCCESS;
3748}
3749
3750DEFUN(show_logging,
3751 show_logging_cmd,
3752 "show logging", SHOW_STR "Show current logging configuration\n")
3753{
3754 struct zlog *zl = zlog_default;
3755
3756 vty_out(vty, "Syslog logging: ");
3757 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3758 vty_out(vty, "disabled");
3759 else
3760 vty_out(vty, "level %s, facility %s, ident %s",
3761 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3762 facility_name(zl->facility), zl->ident);
3763 vty_out(vty, "%s", VTY_NEWLINE);
3764
3765 vty_out(vty, "Stdout logging: ");
3766 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3767 vty_out(vty, "disabled");
3768 else
3769 vty_out(vty, "level %s",
3770 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3771 vty_out(vty, "%s", VTY_NEWLINE);
3772
3773 vty_out(vty, "Monitor logging: ");
3774 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3775 vty_out(vty, "disabled");
3776 else
3777 vty_out(vty, "level %s",
3778 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3779 vty_out(vty, "%s", VTY_NEWLINE);
3780
3781 vty_out(vty, "File logging: ");
3782 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3783 vty_out(vty, "disabled");
3784 else
3785 vty_out(vty, "level %s, filename %s",
3786 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3787 zl->filename);
3788 vty_out(vty, "%s", VTY_NEWLINE);
3789
3790 vty_out(vty, "Protocol name: %s%s",
3791 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3792 vty_out(vty, "Record priority: %s%s",
3793 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3794
3795 return CMD_SUCCESS;
3796}
3797
3798DEFUN(config_log_stdout,
3799 config_log_stdout_cmd,
3800 "log stdout", "Logging control\n" "Set stdout logging level\n")
3801{
3802 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3803 return CMD_SUCCESS;
3804}
3805
3806DEFUN(config_log_stdout_level,
3807 config_log_stdout_level_cmd,
3808 "log stdout " LOG_LEVELS,
3809 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3810{
3811 int level;
3812
3813 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3814 return CMD_ERR_NO_MATCH;
3815 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3816 return CMD_SUCCESS;
3817}
3818
3819DEFUN(no_config_log_stdout,
3820 no_config_log_stdout_cmd,
3821 "no log stdout [LEVEL]",
3822 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3823{
3824 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3825 return CMD_SUCCESS;
3826}
3827
3828DEFUN(config_log_monitor,
3829 config_log_monitor_cmd,
3830 "log monitor",
3831 "Logging control\n" "Set terminal line (monitor) logging level\n")
3832{
3833 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3834 return CMD_SUCCESS;
3835}
3836
3837DEFUN(config_log_monitor_level,
3838 config_log_monitor_level_cmd,
3839 "log monitor " LOG_LEVELS,
3840 "Logging control\n"
3841 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3842{
3843 int level;
3844
3845 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3846 return CMD_ERR_NO_MATCH;
3847 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3848 return CMD_SUCCESS;
3849}
3850
3851DEFUN(no_config_log_monitor,
3852 no_config_log_monitor_cmd,
3853 "no log monitor [LEVEL]",
3854 NO_STR
3855 "Logging control\n"
3856 "Disable terminal line (monitor) logging\n" "Logging level\n")
3857{
3858 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3859 return CMD_SUCCESS;
3860}
3861
3862static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3863{
3864 int ret;
3865 char *p = NULL;
3866 const char *fullpath;
3867
3868 /* Path detection. */
3869 if (!IS_DIRECTORY_SEP(*fname)) {
3870 char cwd[MAXPATHLEN + 1];
3871 cwd[MAXPATHLEN] = '\0';
3872
3873 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3874 zlog_err("config_log_file: Unable to alloc mem!");
3875 return CMD_WARNING;
3876 }
3877
3878 if ((p = _talloc_zero(tall_vcmd_ctx,
3879 strlen(cwd) + strlen(fname) + 2),
3880 "set_log_file")
3881 == NULL) {
3882 zlog_err("config_log_file: Unable to alloc mem!");
3883 return CMD_WARNING;
3884 }
3885 sprintf(p, "%s/%s", cwd, fname);
3886 fullpath = p;
3887 } else
3888 fullpath = fname;
3889
3890 ret = zlog_set_file(NULL, fullpath, loglevel);
3891
3892 if (p)
3893 talloc_free(p);
3894
3895 if (!ret) {
3896 vty_out(vty, "can't open logfile %s\n", fname);
3897 return CMD_WARNING;
3898 }
3899
3900 if (host.logfile)
3901 talloc_free(host.logfile);
3902
3903 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3904
3905 return CMD_SUCCESS;
3906}
3907
3908DEFUN(config_log_file,
3909 config_log_file_cmd,
3910 "log file FILENAME",
3911 "Logging control\n" "Logging to file\n" "Logging filename\n")
3912{
3913 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3914}
3915
3916DEFUN(config_log_file_level,
3917 config_log_file_level_cmd,
3918 "log file FILENAME " LOG_LEVELS,
3919 "Logging control\n"
3920 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3921{
3922 int level;
3923
3924 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3925 return CMD_ERR_NO_MATCH;
3926 return set_log_file(vty, argv[0], level);
3927}
3928
3929DEFUN(no_config_log_file,
3930 no_config_log_file_cmd,
3931 "no log file [FILENAME]",
3932 NO_STR
3933 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3934{
3935 zlog_reset_file(NULL);
3936
3937 if (host.logfile)
3938 talloc_free(host.logfile);
3939
3940 host.logfile = NULL;
3941
3942 return CMD_SUCCESS;
3943}
3944
3945ALIAS(no_config_log_file,
3946 no_config_log_file_level_cmd,
3947 "no log file FILENAME LEVEL",
3948 NO_STR
3949 "Logging control\n"
3950 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3951
3952 DEFUN(config_log_syslog,
3953 config_log_syslog_cmd,
3954 "log syslog", "Logging control\n" "Set syslog logging level\n")
3955{
3956 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3957 return CMD_SUCCESS;
3958}
3959
3960DEFUN(config_log_syslog_level,
3961 config_log_syslog_level_cmd,
3962 "log syslog " LOG_LEVELS,
3963 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3964{
3965 int level;
3966
3967 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3968 return CMD_ERR_NO_MATCH;
3969 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3970 return CMD_SUCCESS;
3971}
3972
3973DEFUN_DEPRECATED(config_log_syslog_facility,
3974 config_log_syslog_facility_cmd,
3975 "log syslog facility " LOG_FACILITIES,
3976 "Logging control\n"
3977 "Logging goes to syslog\n"
3978 "(Deprecated) Facility parameter for syslog messages\n"
3979 LOG_FACILITY_DESC)
3980{
3981 int facility;
3982
3983 if ((facility = facility_match(argv[0])) < 0)
3984 return CMD_ERR_NO_MATCH;
3985
3986 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3987 zlog_default->facility = facility;
3988 return CMD_SUCCESS;
3989}
3990
3991DEFUN(no_config_log_syslog,
3992 no_config_log_syslog_cmd,
3993 "no log syslog [LEVEL]",
3994 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3995{
3996 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3997 return CMD_SUCCESS;
3998}
3999
4000ALIAS(no_config_log_syslog,
4001 no_config_log_syslog_facility_cmd,
4002 "no log syslog facility " LOG_FACILITIES,
4003 NO_STR
4004 "Logging control\n"
4005 "Logging goes to syslog\n"
4006 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4007
4008 DEFUN(config_log_facility,
4009 config_log_facility_cmd,
4010 "log facility " LOG_FACILITIES,
4011 "Logging control\n"
4012 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4013{
4014 int facility;
4015
4016 if ((facility = facility_match(argv[0])) < 0)
4017 return CMD_ERR_NO_MATCH;
4018 zlog_default->facility = facility;
4019 return CMD_SUCCESS;
4020}
4021
4022DEFUN(no_config_log_facility,
4023 no_config_log_facility_cmd,
4024 "no log facility [FACILITY]",
4025 NO_STR
4026 "Logging control\n"
4027 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4028{
4029 zlog_default->facility = LOG_DAEMON;
4030 return CMD_SUCCESS;
4031}
4032
4033DEFUN_DEPRECATED(config_log_trap,
4034 config_log_trap_cmd,
4035 "log trap " LOG_LEVELS,
4036 "Logging control\n"
4037 "(Deprecated) Set logging level and default for all destinations\n"
4038 LOG_LEVEL_DESC)
4039{
4040 int new_level;
4041 int i;
4042
4043 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4044 return CMD_ERR_NO_MATCH;
4045
4046 zlog_default->default_lvl = new_level;
4047 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4048 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4049 zlog_default->maxlvl[i] = new_level;
4050 return CMD_SUCCESS;
4051}
4052
4053DEFUN_DEPRECATED(no_config_log_trap,
4054 no_config_log_trap_cmd,
4055 "no log trap [LEVEL]",
4056 NO_STR
4057 "Logging control\n"
4058 "Permit all logging information\n" "Logging level\n")
4059{
4060 zlog_default->default_lvl = LOG_DEBUG;
4061 return CMD_SUCCESS;
4062}
4063
4064DEFUN(config_log_record_priority,
4065 config_log_record_priority_cmd,
4066 "log record-priority",
4067 "Logging control\n"
4068 "Log the priority of the message within the message\n")
4069{
4070 zlog_default->record_priority = 1;
4071 return CMD_SUCCESS;
4072}
4073
4074DEFUN(no_config_log_record_priority,
4075 no_config_log_record_priority_cmd,
4076 "no log record-priority",
4077 NO_STR
4078 "Logging control\n"
4079 "Do not log the priority of the message within the message\n")
4080{
4081 zlog_default->record_priority = 0;
4082 return CMD_SUCCESS;
4083}
4084#endif
4085
4086DEFUN(banner_motd_file,
4087 banner_motd_file_cmd,
4088 "banner motd file [FILE]",
4089 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4090{
4091 if (host.motdfile)
4092 talloc_free(host.motdfile);
4093 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4094
4095 return CMD_SUCCESS;
4096}
4097
4098DEFUN(banner_motd_default,
4099 banner_motd_default_cmd,
4100 "banner motd default",
4101 "Set banner string\n" "Strings for motd\n" "Default string\n")
4102{
4103 host.motd = default_motd;
4104 return CMD_SUCCESS;
4105}
4106
4107DEFUN(no_banner_motd,
4108 no_banner_motd_cmd,
4109 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4110{
4111 host.motd = NULL;
4112 if (host.motdfile)
4113 talloc_free(host.motdfile);
4114 host.motdfile = NULL;
4115 return CMD_SUCCESS;
4116}
4117
4118/* Set config filename. Called from vty.c */
4119void host_config_set(const char *filename)
4120{
4121 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4122}
4123
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004124/*! Deprecated, now happens implicitly when calling install_node().
4125 * Users of the API may still attempt to call this function, hence
4126 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004127void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004128{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004129}
4130
4131/*! Deprecated, now happens implicitly when calling install_node().
4132 * Users of the API may still attempt to call this function, hence
4133 * leave it here as a no-op. */
4134void vty_install_default(int node)
4135{
4136}
4137
4138/*! Install common commands like 'exit' and 'list'. */
4139static void install_basic_node_commands(int node)
4140{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004141 install_lib_element(node, &config_help_cmd);
4142 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004143
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004144 install_lib_element(node, &show_vty_attr_all_cmd);
4145 install_lib_element(node, &show_vty_attr_cmd);
4146
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004147 install_lib_element(node, &config_write_terminal_cmd);
4148 install_lib_element(node, &config_write_file_cmd);
4149 install_lib_element(node, &config_write_memory_cmd);
4150 install_lib_element(node, &config_write_cmd);
4151 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004152
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004153 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004154
4155 if (node >= CONFIG_NODE) {
4156 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004157 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004158 }
4159}
4160
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004161/*! Return true if a node is installed by install_basic_node_commands(), so
4162 * that we can avoid repeating them for each and every node during 'show
4163 * running-config' */
4164static bool vty_command_is_common(struct cmd_element *cmd)
4165{
4166 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004167 || cmd == &show_vty_attr_all_cmd
4168 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004169 || cmd == &config_list_cmd
4170 || cmd == &config_write_terminal_cmd
4171 || cmd == &config_write_file_cmd
4172 || cmd == &config_write_memory_cmd
4173 || cmd == &config_write_cmd
4174 || cmd == &show_running_config_cmd
4175 || cmd == &config_exit_cmd
4176 || cmd == &config_end_cmd)
4177 return true;
4178 return false;
4179}
4180
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004181/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004182 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004183 * \param[in] vty the vty of the code
4184 * \param[in] filename where to store the file
4185 * \return 0 in case of success.
4186 *
4187 * If the filename already exists create a filename.sav
4188 * version with the current code.
4189 *
4190 */
4191int osmo_vty_write_config_file(const char *filename)
4192{
4193 char *failed_file;
4194 int rc;
4195
4196 rc = write_config_file(filename, &failed_file);
4197 talloc_free(failed_file);
4198 return rc;
4199}
4200
4201/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004202 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004203 * \return 0 in case of success.
4204 *
4205 * If the filename already exists create a filename.sav
4206 * version with the current code.
4207 *
4208 */
4209int osmo_vty_save_config_file(void)
4210{
4211 char *failed_file;
4212 int rc;
4213
4214 if (host.config == NULL)
4215 return -7;
4216
4217 rc = write_config_file(host.config, &failed_file);
4218 talloc_free(failed_file);
4219 return rc;
4220}
4221
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004222/* Initialize command interface. Install basic nodes and commands. */
4223void cmd_init(int terminal)
4224{
4225 /* Allocate initial top vector of commands. */
4226 cmdvec = vector_init(VECTOR_MIN_SIZE);
4227
4228 /* Default host value settings. */
4229 host.name = NULL;
4230 host.password = NULL;
4231 host.enable = NULL;
4232 host.logfile = NULL;
4233 host.config = NULL;
4234 host.lines = -1;
4235 host.motd = default_motd;
4236 host.motdfile = NULL;
4237
4238 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004239 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004240 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004241 install_node_bare(&auth_node, NULL);
4242 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004243 install_node(&config_node, config_write_host);
4244
4245 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004246 install_lib_element(VIEW_NODE, &show_version_cmd);
4247 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004248 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004249 install_lib_element(VIEW_NODE, &config_list_cmd);
4250 install_lib_element(VIEW_NODE, &config_exit_cmd);
4251 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004252 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4253 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004254 install_lib_element(VIEW_NODE, &config_enable_cmd);
4255 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4256 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4257 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004258 }
4259
4260 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004261 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4262 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4263 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004264 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004265 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4266 install_lib_element(ENABLE_NODE, &show_version_cmd);
4267 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004268
4269 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004270 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4271 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4272 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004273 }
4274
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004275 install_lib_element(CONFIG_NODE, &hostname_cmd);
4276 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004277
4278 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004279 install_lib_element(CONFIG_NODE, &password_cmd);
4280 install_lib_element(CONFIG_NODE, &password_text_cmd);
4281 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4282 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4283 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004284
4285#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004286 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4287 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004288#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004289 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4290 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4291 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4292 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4293 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004294
4295 }
4296 srand(time(NULL));
4297}
Harald Welte7acb30c2011-08-17 17:13:48 +02004298
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004299/* FIXME: execute this section in the unit test instead */
4300static __attribute__((constructor)) void on_dso_load(void)
4301{
4302 unsigned int i, j;
4303
4304 /* Check total number of the library specific attributes */
4305 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4306
4307 /* Check for duplicates in the list of library specific flags */
4308 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4309 if (cmd_lib_attr_letters[i] == '\0')
4310 continue;
4311
4312 /* Only upper case flag letters are allowed for libraries */
4313 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4314 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4315
4316 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4317 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4318 }
4319}
4320
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004321/*! @} */