blob: 7752c919f28f5726df0db366df3b9f2134ca744b [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 Yanitskiyc0745eb2020-10-04 18:37:13 +0700634/* Description of attributes shared between the lib commands */
635static const char * const cmd_lib_attr_desc[32] = {
636 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
637 * "Brief but meaningful description", */
638};
639
640/* Flag letters of attributes shared between the lib commands.
641 * NOTE: uppercase letters only, the rest is reserved for applications. */
642static const char cmd_lib_attr_letters[32] = {
643 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
644};
645
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100646/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200647 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100648 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200649static 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 +0100650{
651 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700652 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100653
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200654 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700655
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700656 /* Print global attributes and their description */
657 if (cmd->attr != 0x00) { /* ... if at least one flag is set */
658 print_func(data, " <attributes scope='global'>%s", newline);
659
660 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
661 char *xml_att_desc;
662
663 if (~cmd->attr & cmd_attr_desc[i].value)
664 continue;
665
666 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
667 print_func(data, " <attribute doc='%s' />%s",
668 xml_att_desc, newline);
669 talloc_free(xml_att_desc);
670 }
671
672 print_func(data, " </attributes>%s", newline);
673 }
674
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700675 /* Print application specific attributes and their description */
676 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700677 const char * const *desc;
678 const char *letters;
679
680 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
681 print_func(data, " <attributes scope='library'>%s", newline);
682 letters = &cmd_lib_attr_letters[0];
683 desc = &cmd_lib_attr_desc[0];
684 } else {
685 print_func(data, " <attributes scope='application'>%s", newline);
686 letters = &host.app_info->usr_attr_letters[0];
687 desc = &host.app_info->usr_attr_desc[0];
688 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700689
690 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
691 char *xml_att_desc;
692 char flag;
693
694 /* Skip attribute if *not* set */
695 if (~cmd->usrattr & (1 << i))
696 continue;
697
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700698 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700699 print_func(data, " <attribute doc='%s'", xml_att_desc);
700 talloc_free(xml_att_desc);
701
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700702 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700703 print_func(data, " flag='%c'", flag);
704 print_func(data, " />%s", newline);
705 }
706
707 print_func(data, " </attributes>%s", newline);
708 }
709
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200710 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100711
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700712 for (i = 0; i < vector_count(cmd->strvec); ++i) {
713 vector descvec = vector_slot(cmd->strvec, i);
714 int j;
715 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100716 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700717 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100718 if (desc == NULL)
719 continue;
720
721 xml_param = xml_escape(desc->cmd);
722 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200723 print_func(data, " <param name='%s' doc='%s' />%s",
724 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100725 talloc_free(xml_param);
726 talloc_free(xml_doc);
727 }
728 }
729
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200730 print_func(data, " </params>%s", newline);
731 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100732
733 talloc_free(xml_string);
734 return 0;
735}
736
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200737static bool vty_command_is_common(struct cmd_element *cmd);
738
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100739/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200740 * 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 +0100741 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200742static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100743{
744 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200745 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100746
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200747 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100748
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200749 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200750 print_func(data, " <node id='_common_cmds_'>%s", newline);
751 print_func(data, " <name>Common Commands</name>%s", newline);
752 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
753 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200754 for (i = 0; i < vector_active(cmdvec); ++i) {
755 struct cmd_node *cnode;
756 cnode = vector_slot(cmdvec, i);
757 if (!cnode)
758 continue;
759 if (cnode->node != CONFIG_NODE)
760 continue;
761
762 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
763 struct cmd_element *elem;
764 elem = vector_slot(cnode->cmd_vector, j);
765 if (!vty_command_is_common(elem))
766 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200767 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200768 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200769 }
770 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200771 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200772
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100773 for (i = 0; i < vector_active(cmdvec); ++i) {
774 struct cmd_node *cnode;
775 cnode = vector_slot(cmdvec, i);
776 if (!cnode)
777 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200778 if (vector_active(cnode->cmd_vector) < 1)
779 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100780
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200781 /* De-dup node IDs: how many times has this same name been used before? Count the first
782 * occurence as _1 and omit that first suffix, so that the first occurence is called
783 * 'name', the second becomes 'name_2', then 'name_3', ... */
784 same_name_count = 1;
785 for (j = 0; j < i; ++j) {
786 struct cmd_node *cnode2;
787 cnode2 = vector_slot(cmdvec, j);
788 if (!cnode2)
789 continue;
790 if (strcmp(cnode->name, cnode2->name) == 0)
791 same_name_count ++;
792 }
793
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200794 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200795 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200796 print_func(data, "_%d", same_name_count);
797 print_func(data, "'>%s", newline);
798 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100799
800 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
801 struct cmd_element *elem;
802 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200803 if (vty_command_is_common(elem))
804 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200805 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200806 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100807 }
808
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200809 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100810 }
811
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200812 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100813
814 return 0;
815}
816
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200817static int print_func_vty(void *data, const char *format, ...)
818{
819 struct vty *vty = data;
820 va_list args;
821 int rc;
822 va_start(args, format);
823 rc = vty_out_va(vty, format, args);
824 va_end(args);
825 return rc;
826}
827
828static int vty_dump_xml_ref_to_vty(struct vty *vty)
829{
830 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
831}
832
833static int print_func_stream(void *data, const char *format, ...)
834{
835 va_list args;
836 int rc;
837 va_start(args, format);
838 rc = vfprintf((FILE*)data, format, args);
839 va_end(args);
840 return rc;
841}
842
843/*! Print the XML reference of all VTY nodes to the given stream.
844 */
845int vty_dump_xml_ref(FILE *stream)
846{
847 return vty_dump_nodes(print_func_stream, stream, "\n");
848}
849
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200850/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100851static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
852{
853 int i;
854
855 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
856 struct cmd_element *elem;
857 elem = vector_slot(cnode->cmd_vector, i);
858 if (!elem->string)
859 continue;
860 if (!strcmp(elem->string, cmdstring))
861 return 1;
862 }
863 return 0;
864}
865
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200866/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200867 * \param[in] ntype Node Type
868 * \param[cmd] element to be installed
869 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000870void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200871{
872 struct cmd_node *cnode;
873
874 cnode = vector_slot(cmdvec, ntype);
875
Harald Weltea99d45a2015-11-12 13:48:23 +0100876 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100877 /* ensure no _identical_ command has been registered at this
878 * node so far */
879 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200880
881 vector_set(cnode->cmd_vector, cmd);
882
883 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
884 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
885}
886
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700887/*! Install a library command into a node
888 * \param[in] ntype Node Type
889 * \param[in] cmd element to be installed
890 */
891void install_lib_element(int ntype, struct cmd_element *cmd)
892{
893 cmd->attr |= CMD_ATTR_LIB_COMMAND;
894 install_element(ntype, cmd);
895}
896
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200897/* Install a command into VIEW and ENABLE node */
898void install_element_ve(struct cmd_element *cmd)
899{
900 install_element(VIEW_NODE, cmd);
901 install_element(ENABLE_NODE, cmd);
902}
903
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700904/* Install a library command into VIEW and ENABLE node */
905void install_lib_element_ve(struct cmd_element *cmd)
906{
907 cmd->attr |= CMD_ATTR_LIB_COMMAND;
908 install_element_ve(cmd);
909}
910
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200911#ifdef VTY_CRYPT_PW
912static unsigned char itoa64[] =
913 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
914
915static void to64(char *s, long v, int n)
916{
917 while (--n >= 0) {
918 *s++ = itoa64[v & 0x3f];
919 v >>= 6;
920 }
921}
922
923static char *zencrypt(const char *passwd)
924{
925 char salt[6];
926 struct timeval tv;
927 char *crypt(const char *, const char *);
928
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200929 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200930
931 to64(&salt[0], random(), 3);
932 to64(&salt[3], tv.tv_usec, 3);
933 salt[5] = '\0';
934
935 return crypt(passwd, salt);
936}
937#endif
938
939/* This function write configuration of this host. */
940static int config_write_host(struct vty *vty)
941{
942 if (host.name)
943 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
944
945 if (host.encrypt) {
946 if (host.password_encrypt)
947 vty_out(vty, "password 8 %s%s", host.password_encrypt,
948 VTY_NEWLINE);
949 if (host.enable_encrypt)
950 vty_out(vty, "enable password 8 %s%s",
951 host.enable_encrypt, VTY_NEWLINE);
952 } else {
953 if (host.password)
954 vty_out(vty, "password %s%s", host.password,
955 VTY_NEWLINE);
956 if (host.enable)
957 vty_out(vty, "enable password %s%s", host.enable,
958 VTY_NEWLINE);
959 }
960
961 if (host.advanced)
962 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
963
964 if (host.encrypt)
965 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
966
967 if (host.lines >= 0)
968 vty_out(vty, "service terminal-length %d%s", host.lines,
969 VTY_NEWLINE);
970
971 if (host.motdfile)
972 vty_out(vty, "banner motd file %s%s", host.motdfile,
973 VTY_NEWLINE);
974 else if (!host.motd)
975 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
976
977 return 1;
978}
979
980/* Utility function for getting command vector. */
981static vector cmd_node_vector(vector v, enum node_type ntype)
982{
983 struct cmd_node *cnode = vector_slot(v, ntype);
984 return cnode->cmd_vector;
985}
986
987/* Completion match types. */
988enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200989 NO_MATCH = 0,
990 ANY_MATCH,
991 EXTEND_MATCH,
992 IPV4_PREFIX_MATCH,
993 IPV4_MATCH,
994 IPV6_PREFIX_MATCH,
995 IPV6_MATCH,
996 RANGE_MATCH,
997 VARARG_MATCH,
998 PARTLY_MATCH,
999 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001000};
1001
1002static enum match_type cmd_ipv4_match(const char *str)
1003{
1004 const char *sp;
1005 int dots = 0, nums = 0;
1006 char buf[4];
1007
1008 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001009 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001010
1011 for (;;) {
1012 memset(buf, 0, sizeof(buf));
1013 sp = str;
1014 while (*str != '\0') {
1015 if (*str == '.') {
1016 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001017 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001018
1019 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001020 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001021
1022 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001023 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001024
1025 dots++;
1026 break;
1027 }
1028 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001029 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001030
1031 str++;
1032 }
1033
1034 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001035 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001036
1037 strncpy(buf, sp, str - sp);
1038 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001039 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001040
1041 nums++;
1042
1043 if (*str == '\0')
1044 break;
1045
1046 str++;
1047 }
1048
1049 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001050 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001051
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001052 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001053}
1054
1055static enum match_type cmd_ipv4_prefix_match(const char *str)
1056{
1057 const char *sp;
1058 int dots = 0;
1059 char buf[4];
1060
1061 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001062 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001063
1064 for (;;) {
1065 memset(buf, 0, sizeof(buf));
1066 sp = str;
1067 while (*str != '\0' && *str != '/') {
1068 if (*str == '.') {
1069 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001070 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001071
1072 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001073 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001074
1075 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001076 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001077
1078 dots++;
1079 break;
1080 }
1081
1082 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001083 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001084
1085 str++;
1086 }
1087
1088 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001089 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001090
1091 strncpy(buf, sp, str - sp);
1092 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001093 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001094
1095 if (dots == 3) {
1096 if (*str == '/') {
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 str++;
1101 break;
1102 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001103 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001104 }
1105
1106 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001107 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001108
1109 str++;
1110 }
1111
1112 sp = str;
1113 while (*str != '\0') {
1114 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001115 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116
1117 str++;
1118 }
1119
1120 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001121 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001122
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001123 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001124}
1125
1126#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1127#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1128#define STATE_START 1
1129#define STATE_COLON 2
1130#define STATE_DOUBLE 3
1131#define STATE_ADDR 4
1132#define STATE_DOT 5
1133#define STATE_SLASH 6
1134#define STATE_MASK 7
1135
1136#ifdef HAVE_IPV6
1137
1138static enum match_type cmd_ipv6_match(const char *str)
1139{
1140 int state = STATE_START;
1141 int colons = 0, nums = 0, double_colon = 0;
1142 const char *sp = NULL;
1143 struct sockaddr_in6 sin6_dummy;
1144 int ret;
1145
1146 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 /* use inet_pton that has a better support,
1153 * for example inet_pton can support the automatic addresses:
1154 * ::1.2.3.4
1155 */
1156 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1157
1158 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001159 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001160
1161 while (*str != '\0') {
1162 switch (state) {
1163 case STATE_START:
1164 if (*str == ':') {
1165 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001166 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001167 colons--;
1168 state = STATE_COLON;
1169 } else {
1170 sp = str;
1171 state = STATE_ADDR;
1172 }
1173
1174 continue;
1175 case STATE_COLON:
1176 colons++;
1177 if (*(str + 1) == ':')
1178 state = STATE_DOUBLE;
1179 else {
1180 sp = str + 1;
1181 state = STATE_ADDR;
1182 }
1183 break;
1184 case STATE_DOUBLE:
1185 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001186 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001187
1188 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001189 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190 else {
1191 if (*(str + 1) != '\0')
1192 colons++;
1193 sp = str + 1;
1194 state = STATE_ADDR;
1195 }
1196
1197 double_colon++;
1198 nums++;
1199 break;
1200 case STATE_ADDR:
1201 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1202 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001203 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001204
1205 nums++;
1206 state = STATE_COLON;
1207 }
1208 if (*(str + 1) == '.')
1209 state = STATE_DOT;
1210 break;
1211 case STATE_DOT:
1212 state = STATE_ADDR;
1213 break;
1214 default:
1215 break;
1216 }
1217
1218 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001219 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001220
1221 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001222 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001223
1224 str++;
1225 }
1226
1227#if 0
1228 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001229 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001230#endif /* 0 */
1231
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001232 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233}
1234
1235static enum match_type cmd_ipv6_prefix_match(const char *str)
1236{
1237 int state = STATE_START;
1238 int colons = 0, nums = 0, double_colon = 0;
1239 int mask;
1240 const char *sp = NULL;
1241 char *endptr = NULL;
1242
1243 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001244 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001245
1246 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001247 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001248
1249 while (*str != '\0' && state != STATE_MASK) {
1250 switch (state) {
1251 case STATE_START:
1252 if (*str == ':') {
1253 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001254 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001255 colons--;
1256 state = STATE_COLON;
1257 } else {
1258 sp = str;
1259 state = STATE_ADDR;
1260 }
1261
1262 continue;
1263 case STATE_COLON:
1264 colons++;
1265 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001266 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001267 else if (*(str + 1) == ':')
1268 state = STATE_DOUBLE;
1269 else {
1270 sp = str + 1;
1271 state = STATE_ADDR;
1272 }
1273 break;
1274 case STATE_DOUBLE:
1275 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001276 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001277
1278 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001279 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001280 else {
1281 if (*(str + 1) != '\0' && *(str + 1) != '/')
1282 colons++;
1283 sp = str + 1;
1284
1285 if (*(str + 1) == '/')
1286 state = STATE_SLASH;
1287 else
1288 state = STATE_ADDR;
1289 }
1290
1291 double_colon++;
1292 nums += 1;
1293 break;
1294 case STATE_ADDR:
1295 if (*(str + 1) == ':' || *(str + 1) == '.'
1296 || *(str + 1) == '\0' || *(str + 1) == '/') {
1297 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001298 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001299
1300 for (; sp <= str; sp++)
1301 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001302 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001303
1304 nums++;
1305
1306 if (*(str + 1) == ':')
1307 state = STATE_COLON;
1308 else if (*(str + 1) == '.')
1309 state = STATE_DOT;
1310 else if (*(str + 1) == '/')
1311 state = STATE_SLASH;
1312 }
1313 break;
1314 case STATE_DOT:
1315 state = STATE_ADDR;
1316 break;
1317 case STATE_SLASH:
1318 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001319 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001320
1321 state = STATE_MASK;
1322 break;
1323 default:
1324 break;
1325 }
1326
1327 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001328 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001329
1330 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001331 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001332
1333 str++;
1334 }
1335
1336 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001337 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001338
1339 mask = strtol(str, &endptr, 10);
1340 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001341 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001342
1343 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001344 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001345
1346/* I don't know why mask < 13 makes command match partly.
1347 Forgive me to make this comments. I Want to set static default route
1348 because of lack of function to originate default in ospf6d; sorry
1349 yasu
1350 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001351 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001352*/
1353
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001354 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001355}
1356
1357#endif /* HAVE_IPV6 */
1358
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001359
1360#if ULONG_MAX == 18446744073709551615UL
1361#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1362#elif ULONG_MAX == 4294967295UL
1363#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1364#else
1365#error "ULONG_MAX not defined!"
1366#endif
1367
1368#if LONG_MAX == 9223372036854775807L
1369#define DECIMAL_STRLEN_MAX_SIGNED 19
1370#elif LONG_MAX == 2147483647L
1371#define DECIMAL_STRLEN_MAX_SIGNED 10
1372#else
1373#error "LONG_MAX not defined!"
1374#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001375
1376static int cmd_range_match(const char *range, const char *str)
1377{
1378 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001379 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001380 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001381
1382 if (str == NULL)
1383 return 1;
1384
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001385 if (range[1] == '-') {
1386 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001387
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001388 val = strtol(str, &endptr, 10);
1389 if (*endptr != '\0')
1390 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001391
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001392 range += 2;
1393 p = strchr(range, '-');
1394 if (p == NULL)
1395 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001396 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001397 return 0;
1398 strncpy(buf, range, p - range);
1399 buf[p - range] = '\0';
1400 min = -strtol(buf, &endptr, 10);
1401 if (*endptr != '\0')
1402 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001403
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001404 range = p + 1;
1405 p = strchr(range, '>');
1406 if (p == NULL)
1407 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001408 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001409 return 0;
1410 strncpy(buf, range, p - range);
1411 buf[p - range] = '\0';
1412 max = strtol(buf, &endptr, 10);
1413 if (*endptr != '\0')
1414 return 0;
1415
1416 if (val < min || val > max)
1417 return 0;
1418 } else {
1419 unsigned long min, max, val;
1420
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001421 if (str[0] == '-')
1422 return 0;
1423
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001424 val = strtoul(str, &endptr, 10);
1425 if (*endptr != '\0')
1426 return 0;
1427
1428 range++;
1429 p = strchr(range, '-');
1430 if (p == NULL)
1431 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001432 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001433 return 0;
1434 strncpy(buf, range, p - range);
1435 buf[p - range] = '\0';
1436 min = strtoul(buf, &endptr, 10);
1437 if (*endptr != '\0')
1438 return 0;
1439
1440 range = p + 1;
1441 p = strchr(range, '>');
1442 if (p == NULL)
1443 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001444 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001445 return 0;
1446 strncpy(buf, range, p - range);
1447 buf[p - range] = '\0';
1448 max = strtoul(buf, &endptr, 10);
1449 if (*endptr != '\0')
1450 return 0;
1451
1452 if (val < min || val > max)
1453 return 0;
1454 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001455
1456 return 1;
1457}
1458
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001459/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001460static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001461{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001462 /* we've got "[blah]". We want to strip off the []s and redo the
1463 * match check for "blah"
1464 */
1465 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001466
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001467 if (len < 3)
1468 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001469
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001470 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001471}
1472
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001473static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001474cmd_match(const char *str, const char *command,
1475 enum match_type min, bool recur)
1476{
1477
1478 if (recur && CMD_OPTION(str))
1479 {
1480 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001481 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001482
1483 /* this would be a bug in a command, however handle it gracefully
1484 * as it we only discover it if a user tries to run it
1485 */
1486 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001487 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001488
1489 ret = cmd_match(tmp, command, min, false);
1490
1491 talloc_free(tmp);
1492
1493 return ret;
1494 }
1495 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001496 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001497 else if (CMD_RANGE(str))
1498 {
1499 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001500 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001501 }
1502#ifdef HAVE_IPV6
1503 else if (CMD_IPV6(str))
1504 {
1505 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001506 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001507 }
1508 else if (CMD_IPV6_PREFIX(str))
1509 {
1510 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001511 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001512 }
1513#endif /* HAVE_IPV6 */
1514 else if (CMD_IPV4(str))
1515 {
1516 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001517 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001518 }
1519 else if (CMD_IPV4_PREFIX(str))
1520 {
1521 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001522 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001523 }
1524 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001525 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001526 else if (strncmp(command, str, strlen(command)) == 0)
1527 {
1528 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001529 return EXACT_MATCH;
1530 else if (PARTLY_MATCH >= min)
1531 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001532 }
1533
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001534 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001535}
1536
1537/* Filter vector at the specified index and by the given command string, to
1538 * the desired matching level (thus allowing part matches), and return match
1539 * type flag.
1540 */
1541static enum match_type
1542cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001543{
1544 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001545 struct cmd_element *cmd_element;
1546 enum match_type match_type;
1547 vector descvec;
1548 struct desc *desc;
1549
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001550 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001551
1552 /* If command and cmd_element string does not match set NULL to vector */
1553 for (i = 0; i < vector_active(v); i++)
1554 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001555 if (index >= vector_active(cmd_element->strvec))
1556 vector_slot(v, i) = NULL;
1557 else {
1558 unsigned int j;
1559 int matched = 0;
1560
1561 descvec =
1562 vector_slot(cmd_element->strvec, index);
1563
1564 for (j = 0; j < vector_active(descvec); j++)
1565 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001566 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001567
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001568 ret = cmd_match (desc->cmd, command, level, true);
1569
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001570 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001571 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001572
1573 if (match_type < ret)
1574 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001575 }
1576 if (!matched)
1577 vector_slot(v, i) = NULL;
1578 }
1579 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001580
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001581 if (match_type == NO_MATCH)
1582 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001583
1584 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1585 * go again and filter out commands whose argument (at this index) is
1586 * 'weaker'. E.g., if we have 2 commands:
1587 *
1588 * foo bar <1-255>
1589 * foo bar BLAH
1590 *
1591 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001592 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001593 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1594 *
1595 * If we don't do a 2nd pass and filter it out, the higher-layers will
1596 * consider this to be ambiguous.
1597 */
1598 for (i = 0; i < vector_active(v); i++)
1599 if ((cmd_element = vector_slot(v, i)) != NULL) {
1600 if (index >= vector_active(cmd_element->strvec))
1601 vector_slot(v, i) = NULL;
1602 else {
1603 unsigned int j;
1604 int matched = 0;
1605
1606 descvec =
1607 vector_slot(cmd_element->strvec, index);
1608
1609 for (j = 0; j < vector_active(descvec); j++)
1610 if ((desc = vector_slot(descvec, j))) {
1611 enum match_type ret;
1612
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001613 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001614
1615 if (ret >= match_type)
1616 matched++;
1617 }
1618 if (!matched)
1619 vector_slot(v, i) = NULL;
1620 }
1621 }
1622
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001623 return match_type;
1624}
1625
1626/* Check ambiguous match */
1627static int
1628is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1629{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001630 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001631 unsigned int i;
1632 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001633 struct cmd_element *cmd_element;
1634 const char *matched = NULL;
1635 vector descvec;
1636 struct desc *desc;
1637
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001638 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1639 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1640 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1641 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1642 * that case, the string must remain allocated until this function exits or another match comes
1643 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1644 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1645 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1646 void *cmd_deopt_ctx = NULL;
1647
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001648 for (i = 0; i < vector_active(v); i++) {
1649 cmd_element = vector_slot(v, i);
1650 if (!cmd_element)
1651 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001652
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001653 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001654
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001655 descvec = vector_slot(cmd_element->strvec, index);
1656
1657 for (j = 0; j < vector_active(descvec); j++) {
1658 desc = vector_slot(descvec, j);
1659 if (!desc)
1660 continue;
1661
1662 enum match_type mtype;
1663 const char *str = desc->cmd;
1664
1665 if (CMD_OPTION(str)) {
1666 if (!cmd_deopt_ctx)
1667 cmd_deopt_ctx =
1668 talloc_named_const(tall_vty_cmd_ctx, 0,
1669 __func__);
1670 str = cmd_deopt(cmd_deopt_ctx, str);
1671 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001672 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001673 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001674
1675 switch (type) {
1676 case EXACT_MATCH:
1677 if (!(CMD_VARIABLE (str))
1678 && strcmp(command, str) == 0)
1679 match++;
1680 break;
1681 case PARTLY_MATCH:
1682 if (!(CMD_VARIABLE (str))
1683 && strncmp(command, str, strlen (command)) == 0)
1684 {
1685 if (matched
1686 && strcmp(matched,
1687 str) != 0) {
1688 ret = 1; /* There is ambiguous match. */
1689 goto free_and_return;
1690 } else
1691 matched = str;
1692 match++;
1693 }
1694 break;
1695 case RANGE_MATCH:
1696 if (cmd_range_match
1697 (str, command)) {
1698 if (matched
1699 && strcmp(matched,
1700 str) != 0) {
1701 ret = 1;
1702 goto free_and_return;
1703 } else
1704 matched = str;
1705 match++;
1706 }
1707 break;
1708#ifdef HAVE_IPV6
1709 case IPV6_MATCH:
1710 if (CMD_IPV6(str))
1711 match++;
1712 break;
1713 case IPV6_PREFIX_MATCH:
1714 if ((mtype =
1715 cmd_ipv6_prefix_match
1716 (command)) != NO_MATCH) {
1717 if (mtype == PARTLY_MATCH) {
1718 ret = 2; /* There is incomplete match. */
1719 goto free_and_return;
1720 }
1721
1722 match++;
1723 }
1724 break;
1725#endif /* HAVE_IPV6 */
1726 case IPV4_MATCH:
1727 if (CMD_IPV4(str))
1728 match++;
1729 break;
1730 case IPV4_PREFIX_MATCH:
1731 if ((mtype =
1732 cmd_ipv4_prefix_match
1733 (command)) != NO_MATCH) {
1734 if (mtype == PARTLY_MATCH) {
1735 ret = 2; /* There is incomplete match. */
1736 goto free_and_return;
1737 }
1738
1739 match++;
1740 }
1741 break;
1742 case EXTEND_MATCH:
1743 if (CMD_VARIABLE (str))
1744 match++;
1745 break;
1746 case NO_MATCH:
1747 default:
1748 break;
1749 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001750 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001751 if (!match)
1752 vector_slot(v, i) = NULL;
1753 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001754
1755free_and_return:
1756 if (cmd_deopt_ctx)
1757 talloc_free(cmd_deopt_ctx);
1758 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001759}
1760
1761/* If src matches dst return dst string, otherwise return NULL */
1762static const char *cmd_entry_function(const char *src, const char *dst)
1763{
1764 /* Skip variable arguments. */
1765 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1766 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1767 return NULL;
1768
1769 /* In case of 'command \t', given src is NULL string. */
1770 if (src == NULL)
1771 return dst;
1772
1773 /* Matched with input string. */
1774 if (strncmp(src, dst, strlen(src)) == 0)
1775 return dst;
1776
1777 return NULL;
1778}
1779
1780/* If src matches dst return dst string, otherwise return NULL */
1781/* This version will return the dst string always if it is
1782 CMD_VARIABLE for '?' key processing */
1783static const char *cmd_entry_function_desc(const char *src, const char *dst)
1784{
1785 if (CMD_VARARG(dst))
1786 return dst;
1787
1788 if (CMD_RANGE(dst)) {
1789 if (cmd_range_match(dst, src))
1790 return dst;
1791 else
1792 return NULL;
1793 }
1794#ifdef HAVE_IPV6
1795 if (CMD_IPV6(dst)) {
1796 if (cmd_ipv6_match(src))
1797 return dst;
1798 else
1799 return NULL;
1800 }
1801
1802 if (CMD_IPV6_PREFIX(dst)) {
1803 if (cmd_ipv6_prefix_match(src))
1804 return dst;
1805 else
1806 return NULL;
1807 }
1808#endif /* HAVE_IPV6 */
1809
1810 if (CMD_IPV4(dst)) {
1811 if (cmd_ipv4_match(src))
1812 return dst;
1813 else
1814 return NULL;
1815 }
1816
1817 if (CMD_IPV4_PREFIX(dst)) {
1818 if (cmd_ipv4_prefix_match(src))
1819 return dst;
1820 else
1821 return NULL;
1822 }
1823
1824 /* Optional or variable commands always match on '?' */
1825 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1826 return dst;
1827
1828 /* In case of 'command \t', given src is NULL string. */
1829 if (src == NULL)
1830 return dst;
1831
1832 if (strncmp(src, dst, strlen(src)) == 0)
1833 return dst;
1834 else
1835 return NULL;
1836}
1837
1838/* Check same string element existence. If it isn't there return
1839 1. */
1840static int cmd_unique_string(vector v, const char *str)
1841{
1842 unsigned int i;
1843 char *match;
1844
1845 for (i = 0; i < vector_active(v); i++)
1846 if ((match = vector_slot(v, i)) != NULL)
1847 if (strcmp(match, str) == 0)
1848 return 0;
1849 return 1;
1850}
1851
1852/* Compare string to description vector. If there is same string
1853 return 1 else return 0. */
1854static int desc_unique_string(vector v, const char *str)
1855{
1856 unsigned int i;
1857 struct desc *desc;
1858
1859 for (i = 0; i < vector_active(v); i++)
1860 if ((desc = vector_slot(v, i)) != NULL)
1861 if (strcmp(desc->cmd, str) == 0)
1862 return 1;
1863 return 0;
1864}
1865
1866static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1867{
1868 if (first_word != NULL &&
1869 node != AUTH_NODE &&
1870 node != VIEW_NODE &&
1871 node != AUTH_ENABLE_NODE &&
1872 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1873 return 1;
1874 return 0;
1875}
1876
1877/* '?' describe command support. */
1878static vector
1879cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1880{
1881 unsigned int i;
1882 vector cmd_vector;
1883#define INIT_MATCHVEC_SIZE 10
1884 vector matchvec;
1885 struct cmd_element *cmd_element;
1886 unsigned int index;
1887 int ret;
1888 enum match_type match;
1889 char *command;
1890 static struct desc desc_cr = { "<cr>", "" };
1891
1892 /* Set index. */
1893 if (vector_active(vline) == 0) {
1894 *status = CMD_ERR_NO_MATCH;
1895 return NULL;
1896 } else
1897 index = vector_active(vline) - 1;
1898
1899 /* Make copy vector of current node's command vector. */
1900 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1901
1902 /* Prepare match vector */
1903 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1904
1905 /* Filter commands. */
1906 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001907 for (i = 0; i < index; i++) {
1908 command = vector_slot(vline, i);
1909 if (!command)
1910 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001911
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001912 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001913
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001914 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001915 struct cmd_element *cmd_element;
1916 vector descvec;
1917 unsigned int j, k;
1918
1919 for (j = 0; j < vector_active(cmd_vector); j++)
1920 if ((cmd_element =
1921 vector_slot(cmd_vector, j)) != NULL
1922 &&
1923 (vector_active(cmd_element->strvec))) {
1924 descvec =
1925 vector_slot(cmd_element->
1926 strvec,
1927 vector_active
1928 (cmd_element->
1929 strvec) - 1);
1930 for (k = 0;
1931 k < vector_active(descvec);
1932 k++) {
1933 struct desc *desc =
1934 vector_slot(descvec,
1935 k);
1936 vector_set(matchvec,
1937 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001938 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001939 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001940
Harald Welte80d30fe2013-02-12 11:08:57 +01001941 vector_set(matchvec, &desc_cr);
1942 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001943
Harald Welte80d30fe2013-02-12 11:08:57 +01001944 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001945 }
1946
Harald Welte80d30fe2013-02-12 11:08:57 +01001947 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1948 match)) == 1) {
1949 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001950 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001951 *status = CMD_ERR_AMBIGUOUS;
1952 return NULL;
1953 } else if (ret == 2) {
1954 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001955 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001956 *status = CMD_ERR_NO_MATCH;
1957 return NULL;
1958 }
1959 }
1960
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001961 /* Prepare match vector */
1962 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1963
1964 /* Make sure that cmd_vector is filtered based on current word */
1965 command = vector_slot(vline, index);
1966 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001967 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001968
1969 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001970 for (i = 0; i < vector_active(cmd_vector); i++) {
1971 const char *string = NULL;
1972 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001973
Harald Welte80d30fe2013-02-12 11:08:57 +01001974 cmd_element = vector_slot(cmd_vector, i);
1975 if (!cmd_element)
1976 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001977
Harald Welted17aa592013-02-12 11:11:34 +01001978 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1979 continue;
1980
Harald Welte80d30fe2013-02-12 11:08:57 +01001981 strvec = cmd_element->strvec;
1982
1983 /* if command is NULL, index may be equal to vector_active */
1984 if (command && index >= vector_active(strvec))
1985 vector_slot(cmd_vector, i) = NULL;
1986 else {
1987 /* Check if command is completed. */
1988 if (command == NULL
1989 && index == vector_active(strvec)) {
1990 string = "<cr>";
1991 if (!desc_unique_string(matchvec, string))
1992 vector_set(matchvec, &desc_cr);
1993 } else {
1994 unsigned int j;
1995 vector descvec = vector_slot(strvec, index);
1996 struct desc *desc;
1997
1998 for (j = 0; j < vector_active(descvec); j++) {
1999 desc = vector_slot(descvec, j);
2000 if (!desc)
2001 continue;
2002 string = cmd_entry_function_desc
2003 (command, desc->cmd);
2004 if (!string)
2005 continue;
2006 /* Uniqueness check */
2007 if (!desc_unique_string(matchvec, string))
2008 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002009 }
2010 }
2011 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002012 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002013 vector_free(cmd_vector);
2014
2015 if (vector_slot(matchvec, 0) == NULL) {
2016 vector_free(matchvec);
2017 *status = CMD_ERR_NO_MATCH;
2018 } else
2019 *status = CMD_SUCCESS;
2020
2021 return matchvec;
2022}
2023
2024vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2025{
2026 vector ret;
2027
2028 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2029 enum node_type onode;
2030 vector shifted_vline;
2031 unsigned int index;
2032
2033 onode = vty->node;
2034 vty->node = ENABLE_NODE;
2035 /* We can try it on enable node, cos' the vty is authenticated */
2036
2037 shifted_vline = vector_init(vector_count(vline));
2038 /* use memcpy? */
2039 for (index = 1; index < vector_active(vline); index++) {
2040 vector_set_index(shifted_vline, index - 1,
2041 vector_lookup(vline, index));
2042 }
2043
2044 ret = cmd_describe_command_real(shifted_vline, vty, status);
2045
2046 vector_free(shifted_vline);
2047 vty->node = onode;
2048 return ret;
2049 }
2050
2051 return cmd_describe_command_real(vline, vty, status);
2052}
2053
2054/* Check LCD of matched command. */
2055static int cmd_lcd(char **matched)
2056{
2057 int i;
2058 int j;
2059 int lcd = -1;
2060 char *s1, *s2;
2061 char c1, c2;
2062
2063 if (matched[0] == NULL || matched[1] == NULL)
2064 return 0;
2065
2066 for (i = 1; matched[i] != NULL; i++) {
2067 s1 = matched[i - 1];
2068 s2 = matched[i];
2069
2070 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2071 if (c1 != c2)
2072 break;
2073
2074 if (lcd < 0)
2075 lcd = j;
2076 else {
2077 if (lcd > j)
2078 lcd = j;
2079 }
2080 }
2081 return lcd;
2082}
2083
2084/* Command line completion support. */
2085static char **cmd_complete_command_real(vector vline, struct vty *vty,
2086 int *status)
2087{
2088 unsigned int i;
2089 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2090#define INIT_MATCHVEC_SIZE 10
2091 vector matchvec;
2092 struct cmd_element *cmd_element;
2093 unsigned int index;
2094 char **match_str;
2095 struct desc *desc;
2096 vector descvec;
2097 char *command;
2098 int lcd;
2099
2100 if (vector_active(vline) == 0) {
2101 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002102 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002103 return NULL;
2104 } else
2105 index = vector_active(vline) - 1;
2106
2107 /* First, filter by preceeding command string */
2108 for (i = 0; i < index; i++)
2109 if ((command = vector_slot(vline, i))) {
2110 enum match_type match;
2111 int ret;
2112
2113 /* First try completion match, if there is exactly match return 1 */
2114 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002115 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002116
2117 /* If there is exact match then filter ambiguous match else check
2118 ambiguousness. */
2119 if ((ret =
2120 is_cmd_ambiguous(command, cmd_vector, i,
2121 match)) == 1) {
2122 vector_free(cmd_vector);
2123 *status = CMD_ERR_AMBIGUOUS;
2124 return NULL;
2125 }
2126 /*
2127 else if (ret == 2)
2128 {
2129 vector_free (cmd_vector);
2130 *status = CMD_ERR_NO_MATCH;
2131 return NULL;
2132 }
2133 */
2134 }
2135
2136 /* Prepare match vector. */
2137 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2138
2139 /* Now we got into completion */
2140 for (i = 0; i < vector_active(cmd_vector); i++)
2141 if ((cmd_element = vector_slot(cmd_vector, i))) {
2142 const char *string;
2143 vector strvec = cmd_element->strvec;
2144
2145 /* Check field length */
2146 if (index >= vector_active(strvec))
2147 vector_slot(cmd_vector, i) = NULL;
2148 else {
2149 unsigned int j;
2150
2151 descvec = vector_slot(strvec, index);
2152 for (j = 0; j < vector_active(descvec); j++)
2153 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002154 const char *cmd = desc->cmd;
2155 char *tmp = NULL;
2156
2157 if (CMD_OPTION(desc->cmd)) {
2158 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2159 cmd = tmp;
2160 }
2161 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002162 if (cmd_unique_string (matchvec, string))
2163 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002164 if (tmp)
2165 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002166 }
2167 }
2168 }
2169
2170 /* We don't need cmd_vector any more. */
2171 vector_free(cmd_vector);
2172
2173 /* No matched command */
2174 if (vector_slot(matchvec, 0) == NULL) {
2175 vector_free(matchvec);
2176
2177 /* In case of 'command \t' pattern. Do you need '?' command at
2178 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002179 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002180 *status = CMD_ERR_NOTHING_TODO;
2181 else
2182 *status = CMD_ERR_NO_MATCH;
2183 return NULL;
2184 }
2185
2186 /* Only one matched */
2187 if (vector_slot(matchvec, 1) == NULL) {
2188 match_str = (char **)matchvec->index;
2189 vector_only_wrapper_free(matchvec);
2190 *status = CMD_COMPLETE_FULL_MATCH;
2191 return match_str;
2192 }
2193 /* Make it sure last element is NULL. */
2194 vector_set(matchvec, NULL);
2195
2196 /* Check LCD of matched strings. */
2197 if (vector_slot(vline, index) != NULL) {
2198 lcd = cmd_lcd((char **)matchvec->index);
2199
2200 if (lcd) {
2201 int len = strlen(vector_slot(vline, index));
2202
2203 if (len < lcd) {
2204 char *lcdstr;
2205
2206 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2207 "complete-lcdstr");
2208 memcpy(lcdstr, matchvec->index[0], lcd);
2209 lcdstr[lcd] = '\0';
2210
2211 /* match_str = (char **) &lcdstr; */
2212
2213 /* Free matchvec. */
2214 for (i = 0; i < vector_active(matchvec); i++) {
2215 if (vector_slot(matchvec, i))
2216 talloc_free(vector_slot(matchvec, i));
2217 }
2218 vector_free(matchvec);
2219
2220 /* Make new matchvec. */
2221 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2222 vector_set(matchvec, lcdstr);
2223 match_str = (char **)matchvec->index;
2224 vector_only_wrapper_free(matchvec);
2225
2226 *status = CMD_COMPLETE_MATCH;
2227 return match_str;
2228 }
2229 }
2230 }
2231
2232 match_str = (char **)matchvec->index;
2233 vector_only_wrapper_free(matchvec);
2234 *status = CMD_COMPLETE_LIST_MATCH;
2235 return match_str;
2236}
2237
2238char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2239{
2240 char **ret;
2241
2242 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2243 enum node_type onode;
2244 vector shifted_vline;
2245 unsigned int index;
2246
2247 onode = vty->node;
2248 vty->node = ENABLE_NODE;
2249 /* We can try it on enable node, cos' the vty is authenticated */
2250
2251 shifted_vline = vector_init(vector_count(vline));
2252 /* use memcpy? */
2253 for (index = 1; index < vector_active(vline); index++) {
2254 vector_set_index(shifted_vline, index - 1,
2255 vector_lookup(vline, index));
2256 }
2257
2258 ret = cmd_complete_command_real(shifted_vline, vty, status);
2259
2260 vector_free(shifted_vline);
2261 vty->node = onode;
2262 return ret;
2263 }
2264
2265 return cmd_complete_command_real(vline, vty, status);
2266}
2267
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002268static struct vty_parent_node *vty_parent(struct vty *vty)
2269{
2270 return llist_first_entry_or_null(&vty->parent_nodes,
2271 struct vty_parent_node,
2272 entry);
2273}
2274
2275static bool vty_pop_parent(struct vty *vty)
2276{
2277 struct vty_parent_node *parent = vty_parent(vty);
2278 if (!parent)
2279 return false;
2280 llist_del(&parent->entry);
2281 vty->node = parent->node;
2282 vty->priv = parent->priv;
2283 if (vty->indent)
2284 talloc_free(vty->indent);
2285 vty->indent = parent->indent;
2286 talloc_free(parent);
2287 return true;
2288}
2289
2290static void vty_clear_parents(struct vty *vty)
2291{
2292 while (vty_pop_parent(vty));
2293}
2294
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002295/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002296/*
2297 * This function MUST eventually converge on a node when called repeatedly,
2298 * there must not be any cycles.
2299 * All 'config' nodes shall converge on CONFIG_NODE.
2300 * All other 'enable' nodes shall converge on ENABLE_NODE.
2301 * All 'view' only nodes shall converge on VIEW_NODE.
2302 * All other nodes shall converge on themselves or it must be ensured,
2303 * that the user's rights are not extended anyhow by calling this function.
2304 *
2305 * Note that these requirements also apply to all functions that are used
2306 * as go_parent_cb.
2307 * Note also that this function relies on the is_config_child callback to
2308 * recognize non-config nodes if go_parent_cb is not set.
2309 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002310int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002311{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002312 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002313 case AUTH_NODE:
2314 case VIEW_NODE:
2315 case ENABLE_NODE:
2316 case CONFIG_NODE:
2317 vty_clear_parents(vty);
2318 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002319
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002320 case AUTH_ENABLE_NODE:
2321 vty->node = VIEW_NODE;
2322 vty_clear_parents(vty);
2323 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002324
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002325 default:
2326 if (host.app_info->go_parent_cb)
2327 host.app_info->go_parent_cb(vty);
2328 vty_pop_parent(vty);
2329 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002330 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002331
2332 return vty->node;
2333}
2334
2335/* Execute command by argument vline vector. */
2336static int
2337cmd_execute_command_real(vector vline, struct vty *vty,
2338 struct cmd_element **cmd)
2339{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002340 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002341 unsigned int index;
2342 vector cmd_vector;
2343 struct cmd_element *cmd_element;
2344 struct cmd_element *matched_element;
2345 unsigned int matched_count, incomplete_count;
2346 int argc;
2347 const char *argv[CMD_ARGC_MAX];
2348 enum match_type match = 0;
2349 int varflag;
2350 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002351 int rc;
2352 /* Used for temporary storage of cmd_deopt() allocated arguments during
2353 argv[] generation */
2354 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002355
2356 /* Make copy of command elements. */
2357 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2358
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002359 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002360 if ((command = vector_slot(vline, index))) {
2361 int ret;
2362
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002363 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002364 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002365
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002366 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002367 break;
2368
2369 ret =
2370 is_cmd_ambiguous(command, cmd_vector, index, match);
2371
2372 if (ret == 1) {
2373 vector_free(cmd_vector);
2374 return CMD_ERR_AMBIGUOUS;
2375 } else if (ret == 2) {
2376 vector_free(cmd_vector);
2377 return CMD_ERR_NO_MATCH;
2378 }
2379 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002380 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002381
2382 /* Check matched count. */
2383 matched_element = NULL;
2384 matched_count = 0;
2385 incomplete_count = 0;
2386
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002387 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002388 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002389 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002390 || index >= cmd_element->cmdsize) {
2391 matched_element = cmd_element;
2392#if 0
2393 printf("DEBUG: %s\n", cmd_element->string);
2394#endif
2395 matched_count++;
2396 } else {
2397 incomplete_count++;
2398 }
2399 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002400 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002401
2402 /* Finish of using cmd_vector. */
2403 vector_free(cmd_vector);
2404
2405 /* To execute command, matched_count must be 1. */
2406 if (matched_count == 0) {
2407 if (incomplete_count)
2408 return CMD_ERR_INCOMPLETE;
2409 else
2410 return CMD_ERR_NO_MATCH;
2411 }
2412
2413 if (matched_count > 1)
2414 return CMD_ERR_AMBIGUOUS;
2415
2416 /* Argument treatment */
2417 varflag = 0;
2418 argc = 0;
2419
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002420 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2421
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002422 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002423 if (argc == CMD_ARGC_MAX) {
2424 rc = CMD_ERR_EXEED_ARGC_MAX;
2425 goto rc_free_deopt_ctx;
2426 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002427 if (varflag) {
2428 argv[argc++] = vector_slot(vline, i);
2429 continue;
2430 }
2431
2432 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002433 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002434
2435 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002436 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002437 struct desc *desc = vector_slot(descvec, 0);
2438
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002439 if (CMD_OPTION(desc->cmd)) {
2440 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2441 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2442 } else {
2443 tmp_cmd = desc->cmd;
2444 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002445
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002446 if (CMD_VARARG(tmp_cmd))
2447 varflag = 1;
2448 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002449 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002450 else if (CMD_OPTION(desc->cmd))
2451 argv[argc++] = tmp_cmd;
2452 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002453 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002454 /* multi choice argument. look up which choice
2455 the user meant (can only be one after
2456 filtering and checking for ambigous). For instance,
2457 if user typed "th" for "(two|three)" arg, we
2458 want to pass "three" in argv[]. */
2459 for (j = 0; j < vector_active(descvec); j++) {
2460 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002461 if (!desc)
2462 continue;
2463 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2464 continue;
2465 if (CMD_OPTION(desc->cmd)) {
2466 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2467 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2468 } else {
2469 tmp_cmd = desc->cmd;
2470 }
2471
2472 if(CMD_VARIABLE(tmp_cmd)) {
2473 argv[argc++] = vector_slot(vline, i);
2474 } else {
2475 argv[argc++] = tmp_cmd;
2476 }
2477 break;
2478 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002479 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002480 }
2481
2482 /* For vtysh execution. */
2483 if (cmd)
2484 *cmd = matched_element;
2485
2486 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002487 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002488 else {
2489 /* Execute matched command. */
2490 struct vty_parent_node this_node = {
2491 .node = vty->node,
2492 .priv = vty->priv,
2493 .indent = vty->indent,
2494 };
2495 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002496 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002497
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002498 /* If we have stepped down into a child node, push a parent frame.
2499 * The causality is such: we don't expect every single node entry implementation to push
2500 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2501 * a parent node. Hence if the node changed without the parent node changing, we must
2502 * have stepped into a child node. */
2503 if (vty->node != this_node.node && parent == vty_parent(vty)
2504 && vty->node > CONFIG_NODE) {
2505 /* Push the parent node. */
2506 parent = talloc_zero(vty, struct vty_parent_node);
2507 *parent = this_node;
2508 llist_add(&parent->entry, &vty->parent_nodes);
2509 }
2510 }
2511
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002512rc_free_deopt_ctx:
2513 /* Now after we called the command func, we can free temporary strings */
2514 talloc_free(cmd_deopt_ctx);
2515 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002516}
2517
2518int
2519cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2520 int vtysh)
2521{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002522 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002523 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002524
2525 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002526
2527 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2528 vector shifted_vline;
2529 unsigned int index;
2530
2531 vty->node = ENABLE_NODE;
2532 /* We can try it on enable node, cos' the vty is authenticated */
2533
2534 shifted_vline = vector_init(vector_count(vline));
2535 /* use memcpy? */
2536 for (index = 1; index < vector_active(vline); index++) {
2537 vector_set_index(shifted_vline, index - 1,
2538 vector_lookup(vline, index));
2539 }
2540
2541 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2542
2543 vector_free(shifted_vline);
2544 vty->node = onode;
2545 return ret;
2546 }
2547
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002548 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002549}
2550
2551/* Execute command by argument readline. */
2552int
2553cmd_execute_command_strict(vector vline, struct vty *vty,
2554 struct cmd_element **cmd)
2555{
2556 unsigned int i;
2557 unsigned int index;
2558 vector cmd_vector;
2559 struct cmd_element *cmd_element;
2560 struct cmd_element *matched_element;
2561 unsigned int matched_count, incomplete_count;
2562 int argc;
2563 const char *argv[CMD_ARGC_MAX];
2564 int varflag;
2565 enum match_type match = 0;
2566 char *command;
2567
2568 /* Make copy of command element */
2569 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2570
2571 for (index = 0; index < vector_active(vline); index++)
2572 if ((command = vector_slot(vline, index))) {
2573 int ret;
2574
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002575 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002576 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002577
2578 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002579 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002580 break;
2581
2582 ret =
2583 is_cmd_ambiguous(command, cmd_vector, index, match);
2584 if (ret == 1) {
2585 vector_free(cmd_vector);
2586 return CMD_ERR_AMBIGUOUS;
2587 }
2588 if (ret == 2) {
2589 vector_free(cmd_vector);
2590 return CMD_ERR_NO_MATCH;
2591 }
2592 }
2593
2594 /* Check matched count. */
2595 matched_element = NULL;
2596 matched_count = 0;
2597 incomplete_count = 0;
2598 for (i = 0; i < vector_active(cmd_vector); i++)
2599 if (vector_slot(cmd_vector, i) != NULL) {
2600 cmd_element = vector_slot(cmd_vector, i);
2601
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002602 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002603 || index >= cmd_element->cmdsize) {
2604 matched_element = cmd_element;
2605 matched_count++;
2606 } else
2607 incomplete_count++;
2608 }
2609
2610 /* Finish of using cmd_vector. */
2611 vector_free(cmd_vector);
2612
2613 /* To execute command, matched_count must be 1. */
2614 if (matched_count == 0) {
2615 if (incomplete_count)
2616 return CMD_ERR_INCOMPLETE;
2617 else
2618 return CMD_ERR_NO_MATCH;
2619 }
2620
2621 if (matched_count > 1)
2622 return CMD_ERR_AMBIGUOUS;
2623
2624 /* Argument treatment */
2625 varflag = 0;
2626 argc = 0;
2627
2628 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002629 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002630 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002631 if (varflag) {
2632 argv[argc++] = vector_slot(vline, i);
2633 continue;
2634 }
2635
2636 vector descvec = vector_slot(matched_element->strvec, i);
2637
2638 if (vector_active(descvec) == 1) {
2639 struct desc *desc = vector_slot(descvec, 0);
2640
2641 if (CMD_VARARG(desc->cmd))
2642 varflag = 1;
2643
2644 if (varflag || CMD_VARIABLE(desc->cmd)
2645 || CMD_OPTION(desc->cmd))
2646 argv[argc++] = vector_slot(vline, i);
2647 } else {
2648 argv[argc++] = vector_slot(vline, i);
2649 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002650 }
2651
2652 /* For vtysh execution. */
2653 if (cmd)
2654 *cmd = matched_element;
2655
2656 if (matched_element->daemon)
2657 return CMD_SUCCESS_DAEMON;
2658
2659 /* Now execute matched command */
2660 return (*matched_element->func) (matched_element, vty, argc, argv);
2661}
2662
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002663static inline size_t len(const char *str)
2664{
2665 return str? strlen(str) : 0;
2666}
2667
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002668/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2669 * is longer than b, a must start with exactly b, and vice versa.
2670 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2671 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002672static int indent_cmp(const char *a, const char *b)
2673{
2674 size_t al, bl;
2675 al = len(a);
2676 bl = len(b);
2677 if (al > bl) {
2678 if (bl && strncmp(a, b, bl) != 0)
2679 return EINVAL;
2680 return 1;
2681 }
2682 /* al <= bl */
2683 if (al && strncmp(a, b, al) != 0)
2684 return EINVAL;
2685 return (al < bl)? -1 : 0;
2686}
2687
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002688/* Configration make from file. */
2689int config_from_file(struct vty *vty, FILE * fp)
2690{
2691 int ret;
2692 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002693 char *indent;
2694 int cmp;
2695 struct vty_parent_node this_node;
2696 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002697
2698 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002699 indent = NULL;
2700 vline = NULL;
2701 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002702
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002703 if (ret != CMD_SUCCESS)
2704 goto return_invalid_indent;
2705
2706 /* In case of comment or empty line */
2707 if (vline == NULL) {
2708 if (indent) {
2709 talloc_free(indent);
2710 indent = NULL;
2711 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002712 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002713 }
2714
Neels Hofmeyr43063632017-09-19 23:54:01 +02002715 /* We have a nonempty line. */
2716 if (!vty->indent) {
2717 /* We have just entered a node and expecting the first child to come up; but we
2718 * may also skip right back to a parent or ancestor level. */
2719 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002720
Neels Hofmeyr43063632017-09-19 23:54:01 +02002721 /* If there is no parent, record any indentation we encounter. */
2722 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2723
2724 if (cmp == EINVAL)
2725 goto return_invalid_indent;
2726
2727 if (cmp <= 0) {
2728 /* We have gone right back to the parent level or higher, we are skipping
2729 * this child node level entirely. Pop the parent to go back to a node
2730 * that was actually there (to reinstate vty->indent) and re-use below
2731 * go-parent while-loop to find an accurate match of indent in the node
2732 * ancestry. */
2733 vty_go_parent(vty);
2734 } else {
2735 /* The indent is deeper than the just entered parent, record the new
2736 * indentation characters. */
2737 vty->indent = talloc_strdup(vty, indent);
2738 /* This *is* the new indentation. */
2739 cmp = 0;
2740 }
2741 } else {
2742 /* There is a known indentation for this node level, validate and detect node
2743 * exits. */
2744 cmp = indent_cmp(indent, vty->indent);
2745 if (cmp == EINVAL)
2746 goto return_invalid_indent;
2747 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002748
2749 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2750 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2751 while (cmp < 0) {
2752 vty_go_parent(vty);
2753 cmp = indent_cmp(indent, vty->indent);
2754 if (cmp == EINVAL)
2755 goto return_invalid_indent;
2756 }
2757
2758 /* More indent without having entered a child node level? Either the parent node's indent
2759 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2760 * or the indentation increased even though the vty command didn't enter a child. */
2761 if (cmp > 0)
2762 goto return_invalid_indent;
2763
2764 /* Remember the current node before the command possibly changes it. */
2765 this_node = (struct vty_parent_node){
2766 .node = vty->node,
2767 .priv = vty->priv,
2768 .indent = vty->indent,
2769 };
2770
2771 parent = vty_parent(vty);
2772 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002773 cmd_free_strvec(vline);
2774
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002775 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002776 if (indent) {
2777 talloc_free(indent);
2778 indent = NULL;
2779 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002780 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002781 }
2782
2783 /* If we have stepped down into a child node, push a parent frame.
2784 * The causality is such: we don't expect every single node entry implementation to push
2785 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2786 * a parent node. Hence if the node changed without the parent node changing, we must
2787 * have stepped into a child node (and now expect a deeper indent). */
2788 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2789 /* Push the parent node. */
2790 parent = talloc_zero(vty, struct vty_parent_node);
2791 *parent = this_node;
2792 llist_add(&parent->entry, &vty->parent_nodes);
2793
2794 /* The current talloc'ed vty->indent string will now be owned by this parent
2795 * struct. Indicate that we don't know what deeper indent characters the user
2796 * will choose. */
2797 vty->indent = NULL;
2798 }
2799
2800 if (indent) {
2801 talloc_free(indent);
2802 indent = NULL;
2803 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002804 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002805 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2806 while (vty_parent(vty))
2807 vty_go_parent(vty);
2808
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002809 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002810
2811return_invalid_indent:
2812 if (vline)
2813 cmd_free_strvec(vline);
2814 if (indent) {
2815 talloc_free(indent);
2816 indent = NULL;
2817 }
2818 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002819}
2820
2821/* Configration from terminal */
2822DEFUN(config_terminal,
2823 config_terminal_cmd,
2824 "configure terminal",
2825 "Configuration from vty interface\n" "Configuration terminal\n")
2826{
2827 if (vty_config_lock(vty))
2828 vty->node = CONFIG_NODE;
2829 else {
2830 vty_out(vty, "VTY configuration is locked by other VTY%s",
2831 VTY_NEWLINE);
2832 return CMD_WARNING;
2833 }
2834 return CMD_SUCCESS;
2835}
2836
2837/* Enable command */
2838DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2839{
2840 /* If enable password is NULL, change to ENABLE_NODE */
2841 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2842 vty->type == VTY_SHELL_SERV)
2843 vty->node = ENABLE_NODE;
2844 else
2845 vty->node = AUTH_ENABLE_NODE;
2846
2847 return CMD_SUCCESS;
2848}
2849
2850/* Disable command */
2851DEFUN(disable,
2852 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2853{
2854 if (vty->node == ENABLE_NODE)
2855 vty->node = VIEW_NODE;
2856 return CMD_SUCCESS;
2857}
2858
2859/* Down vty node level. */
2860gDEFUN(config_exit,
2861 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2862{
2863 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002864 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002865 case VIEW_NODE:
2866 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002867 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002868 break;
2869 case CONFIG_NODE:
2870 vty->node = ENABLE_NODE;
2871 vty_config_unlock(vty);
2872 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002873 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002874 if (vty->node > CONFIG_NODE)
2875 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002876 break;
2877 }
2878 return CMD_SUCCESS;
2879}
2880
2881/* End of configuration. */
2882 gDEFUN(config_end,
2883 config_end_cmd, "end", "End current mode and change to enable mode.")
2884{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002885 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002886 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002887
2888 /* Repeatedly call go_parent until a top node is reached. */
2889 while (vty->node > CONFIG_NODE) {
2890 if (vty->node == last_node) {
2891 /* Ensure termination, this shouldn't happen. */
2892 break;
2893 }
2894 last_node = vty->node;
2895 vty_go_parent(vty);
2896 }
2897
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002898 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002899 if (vty->node > ENABLE_NODE)
2900 vty->node = ENABLE_NODE;
2901 vty->index = NULL;
2902 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002903 }
2904 return CMD_SUCCESS;
2905}
2906
2907/* Show version. */
2908DEFUN(show_version,
2909 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2910{
Harald Welte237f6242010-05-25 23:00:45 +02002911 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2912 host.app_info->version,
2913 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2914 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002915
2916 return CMD_SUCCESS;
2917}
2918
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002919DEFUN(show_online_help,
2920 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2921{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002922 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002923 return CMD_SUCCESS;
2924}
2925
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002926/* Help display function for all node. */
2927gDEFUN(config_help,
2928 config_help_cmd, "help", "Description of the interactive help system\n")
2929{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002930 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2931 "anytime at the command line please press '?'.%s%s"
2932 "If nothing matches, the help list will be empty and you must backup%s"
2933 " until entering a '?' shows the available options.%s"
2934 "Two styles of help are provided:%s"
2935 "1. Full help is available when you are ready to enter a%s"
2936 "command argument (e.g. 'show ?') and describes each possible%s"
2937 "argument.%s"
2938 "2. Partial help is provided when an abbreviated argument is entered%s"
2939 " and you want to know what arguments match the input%s"
2940 " (e.g. 'show me?'.)%s%s",
2941 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2942 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2943 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2944 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002945 return CMD_SUCCESS;
2946}
2947
2948/* Help display function for all node. */
2949gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2950{
2951 unsigned int i;
2952 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2953 struct cmd_element *cmd;
2954
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002955 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2956 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2957 continue;
2958 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2959 continue;
2960 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2961 }
2962
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002963 return CMD_SUCCESS;
2964}
2965
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002966static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002967{
2968 unsigned int i;
2969 int fd;
2970 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002971 char *config_file_tmp = NULL;
2972 char *config_file_sav = NULL;
2973 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002974 struct stat st;
2975
2976 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002977
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002978 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2979 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2980 * manually instead. */
2981
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002982 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002983 config_file_sav =
2984 _talloc_zero(tall_vty_cmd_ctx,
2985 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2986 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002987 if (!config_file_sav)
2988 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002989 strcpy(config_file_sav, config_file);
2990 strcat(config_file_sav, CONF_BACKUP_EXT);
2991
2992 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002993 "config_file_tmp");
2994 if (!config_file_tmp) {
2995 talloc_free(config_file_sav);
2996 return -1;
2997 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002998 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2999
3000 /* Open file to configuration write. */
3001 fd = mkstemp(config_file_tmp);
3002 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003003 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003004 talloc_free(config_file_tmp);
3005 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003006 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003007 }
3008
3009 /* Make vty for configuration file. */
3010 file_vty = vty_new();
3011 file_vty->fd = fd;
3012 file_vty->type = VTY_FILE;
3013
3014 /* Config file header print. */
3015 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003016 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003017 //vty_time_print (file_vty, 1);
3018 vty_out(file_vty, "!\n");
3019
3020 for (i = 0; i < vector_active(cmdvec); i++)
3021 if ((node = vector_slot(cmdvec, i)) && node->func) {
3022 if ((*node->func) (file_vty))
3023 vty_out(file_vty, "!\n");
3024 }
3025 vty_close(file_vty);
3026
3027 if (unlink(config_file_sav) != 0)
3028 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003029 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003030 talloc_free(config_file_sav);
3031 talloc_free(config_file_tmp);
3032 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003033 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003034 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003035
3036 /* Only link the .sav file if the original file exists */
3037 if (stat(config_file, &st) == 0) {
3038 if (link(config_file, config_file_sav) != 0) {
3039 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3040 talloc_free(config_file_sav);
3041 talloc_free(config_file_tmp);
3042 unlink(config_file_tmp);
3043 return -3;
3044 }
3045 sync();
3046 if (unlink(config_file) != 0) {
3047 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3048 talloc_free(config_file_sav);
3049 talloc_free(config_file_tmp);
3050 unlink(config_file_tmp);
3051 return -4;
3052 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003053 }
3054 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003055 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003056 talloc_free(config_file_sav);
3057 talloc_free(config_file_tmp);
3058 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003059 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003060 }
3061 unlink(config_file_tmp);
3062 sync();
3063
3064 talloc_free(config_file_sav);
3065 talloc_free(config_file_tmp);
3066
3067 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003068 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3069 return -6;
3070 }
3071
3072 return 0;
3073}
3074
3075
3076/* Write current configuration into file. */
3077DEFUN(config_write_file,
3078 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003079 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003080 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003081 "Write to configuration file\n"
3082 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003083{
3084 char *failed_file;
3085 int rc;
3086
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003087 if (host.app_info->config_is_consistent) {
3088 rc = host.app_info->config_is_consistent(vty);
3089 if (!rc) {
3090 vty_out(vty, "Configuration is not consistent%s",
3091 VTY_NEWLINE);
3092 return CMD_WARNING;
3093 }
3094 }
3095
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003096 if (argc == 1)
3097 host_config_set(argv[0]);
3098
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003099 if (host.config == NULL) {
3100 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3101 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003102 return CMD_WARNING;
3103 }
3104
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003105 rc = write_config_file(host.config, &failed_file);
3106 switch (rc) {
3107 case -1:
3108 vty_out(vty, "Can't open configuration file %s.%s",
3109 failed_file, VTY_NEWLINE);
3110 rc = CMD_WARNING;
3111 break;
3112 case -2:
3113 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3114 failed_file, VTY_NEWLINE);
3115 rc = CMD_WARNING;
3116 break;
3117 case -3:
3118 vty_out(vty, "Can't backup old configuration file %s.%s",
3119 failed_file, VTY_NEWLINE);
3120 rc = CMD_WARNING;
3121 break;
3122 case -4:
3123 vty_out(vty, "Can't unlink configuration file %s.%s",
3124 failed_file, VTY_NEWLINE);
3125 rc = CMD_WARNING;
3126 break;
3127 case -5:
3128 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3129 VTY_NEWLINE);
3130 rc = CMD_WARNING;
3131 break;
3132 case -6:
3133 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3134 failed_file, strerror(errno), errno, VTY_NEWLINE);
3135 rc = CMD_WARNING;
3136 break;
3137 default:
3138 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3139 rc = CMD_SUCCESS;
3140 break;
3141 }
3142
3143 talloc_free(failed_file);
3144 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003145}
3146
3147ALIAS(config_write_file,
3148 config_write_cmd,
3149 "write", "Write running configuration to memory, network, or terminal\n")
3150
3151 ALIAS(config_write_file,
3152 config_write_memory_cmd,
3153 "write memory",
3154 "Write running configuration to memory, network, or terminal\n"
3155 "Write configuration to the file (same as write file)\n")
3156
3157 ALIAS(config_write_file,
3158 copy_runningconfig_startupconfig_cmd,
3159 "copy running-config startup-config",
3160 "Copy configuration\n"
3161 "Copy running config to... \n"
3162 "Copy running config to startup config (same as write file)\n")
3163
3164/* Write current configuration into the terminal. */
3165 DEFUN(config_write_terminal,
3166 config_write_terminal_cmd,
3167 "write terminal",
3168 "Write running configuration to memory, network, or terminal\n"
3169 "Write to terminal\n")
3170{
3171 unsigned int i;
3172 struct cmd_node *node;
3173
3174 if (vty->type == VTY_SHELL_SERV) {
3175 for (i = 0; i < vector_active(cmdvec); i++)
3176 if ((node = vector_slot(cmdvec, i)) && node->func
3177 && node->vtysh) {
3178 if ((*node->func) (vty))
3179 vty_out(vty, "!%s", VTY_NEWLINE);
3180 }
3181 } else {
3182 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3183 VTY_NEWLINE);
3184 vty_out(vty, "!%s", VTY_NEWLINE);
3185
3186 for (i = 0; i < vector_active(cmdvec); i++)
3187 if ((node = vector_slot(cmdvec, i)) && node->func) {
3188 if ((*node->func) (vty))
3189 vty_out(vty, "!%s", VTY_NEWLINE);
3190 }
3191 vty_out(vty, "end%s", VTY_NEWLINE);
3192 }
3193 return CMD_SUCCESS;
3194}
3195
3196/* Write current configuration into the terminal. */
3197ALIAS(config_write_terminal,
3198 show_running_config_cmd,
3199 "show running-config", SHOW_STR "running configuration\n")
3200
3201/* Write startup configuration into the terminal. */
3202 DEFUN(show_startup_config,
3203 show_startup_config_cmd,
3204 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3205{
3206 char buf[BUFSIZ];
3207 FILE *confp;
3208
3209 confp = fopen(host.config, "r");
3210 if (confp == NULL) {
3211 vty_out(vty, "Can't open configuration file [%s]%s",
3212 host.config, VTY_NEWLINE);
3213 return CMD_WARNING;
3214 }
3215
3216 while (fgets(buf, BUFSIZ, confp)) {
3217 char *cp = buf;
3218
3219 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3220 cp++;
3221 *cp = '\0';
3222
3223 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3224 }
3225
3226 fclose(confp);
3227
3228 return CMD_SUCCESS;
3229}
3230
3231/* Hostname configuration */
3232DEFUN(config_hostname,
3233 hostname_cmd,
3234 "hostname WORD",
3235 "Set system's network name\n" "This system's network name\n")
3236{
3237 if (!isalpha((int)*argv[0])) {
3238 vty_out(vty, "Please specify string starting with alphabet%s",
3239 VTY_NEWLINE);
3240 return CMD_WARNING;
3241 }
3242
3243 if (host.name)
3244 talloc_free(host.name);
3245
3246 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3247 return CMD_SUCCESS;
3248}
3249
3250DEFUN(config_no_hostname,
3251 no_hostname_cmd,
3252 "no hostname [HOSTNAME]",
3253 NO_STR "Reset system's network name\n" "Host name of this router\n")
3254{
3255 if (host.name)
3256 talloc_free(host.name);
3257 host.name = NULL;
3258 return CMD_SUCCESS;
3259}
3260
3261/* VTY interface password set. */
3262DEFUN(config_password, password_cmd,
3263 "password (8|) WORD",
3264 "Assign the terminal connection password\n"
3265 "Specifies a HIDDEN password will follow\n"
3266 "dummy string \n" "The HIDDEN line password string\n")
3267{
3268 /* Argument check. */
3269 if (argc == 0) {
3270 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3271 return CMD_WARNING;
3272 }
3273
3274 if (argc == 2) {
3275 if (*argv[0] == '8') {
3276 if (host.password)
3277 talloc_free(host.password);
3278 host.password = NULL;
3279 if (host.password_encrypt)
3280 talloc_free(host.password_encrypt);
3281 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3282 return CMD_SUCCESS;
3283 } else {
3284 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3285 return CMD_WARNING;
3286 }
3287 }
3288
3289 if (!isalnum((int)*argv[0])) {
3290 vty_out(vty,
3291 "Please specify string starting with alphanumeric%s",
3292 VTY_NEWLINE);
3293 return CMD_WARNING;
3294 }
3295
3296 if (host.password)
3297 talloc_free(host.password);
3298 host.password = NULL;
3299
3300#ifdef VTY_CRYPT_PW
3301 if (host.encrypt) {
3302 if (host.password_encrypt)
3303 talloc_free(host.password_encrypt);
3304 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3305 } else
3306#endif
3307 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3308
3309 return CMD_SUCCESS;
3310}
3311
3312ALIAS(config_password, password_text_cmd,
3313 "password LINE",
3314 "Assign the terminal connection password\n"
3315 "The UNENCRYPTED (cleartext) line password\n")
3316
3317/* VTY enable password set. */
3318 DEFUN(config_enable_password, enable_password_cmd,
3319 "enable password (8|) WORD",
3320 "Modify enable password parameters\n"
3321 "Assign the privileged level password\n"
3322 "Specifies a HIDDEN password will follow\n"
3323 "dummy string \n" "The HIDDEN 'enable' password string\n")
3324{
3325 /* Argument check. */
3326 if (argc == 0) {
3327 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3328 return CMD_WARNING;
3329 }
3330
3331 /* Crypt type is specified. */
3332 if (argc == 2) {
3333 if (*argv[0] == '8') {
3334 if (host.enable)
3335 talloc_free(host.enable);
3336 host.enable = NULL;
3337
3338 if (host.enable_encrypt)
3339 talloc_free(host.enable_encrypt);
3340 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3341
3342 return CMD_SUCCESS;
3343 } else {
3344 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3345 return CMD_WARNING;
3346 }
3347 }
3348
3349 if (!isalnum((int)*argv[0])) {
3350 vty_out(vty,
3351 "Please specify string starting with alphanumeric%s",
3352 VTY_NEWLINE);
3353 return CMD_WARNING;
3354 }
3355
3356 if (host.enable)
3357 talloc_free(host.enable);
3358 host.enable = NULL;
3359
3360 /* Plain password input. */
3361#ifdef VTY_CRYPT_PW
3362 if (host.encrypt) {
3363 if (host.enable_encrypt)
3364 talloc_free(host.enable_encrypt);
3365 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3366 } else
3367#endif
3368 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3369
3370 return CMD_SUCCESS;
3371}
3372
3373ALIAS(config_enable_password,
3374 enable_password_text_cmd,
3375 "enable password LINE",
3376 "Modify enable password parameters\n"
3377 "Assign the privileged level password\n"
3378 "The UNENCRYPTED (cleartext) 'enable' password\n")
3379
3380/* VTY enable password delete. */
3381 DEFUN(no_config_enable_password, no_enable_password_cmd,
3382 "no enable password",
3383 NO_STR
3384 "Modify enable password parameters\n"
3385 "Assign the privileged level password\n")
3386{
3387 if (host.enable)
3388 talloc_free(host.enable);
3389 host.enable = NULL;
3390
3391 if (host.enable_encrypt)
3392 talloc_free(host.enable_encrypt);
3393 host.enable_encrypt = NULL;
3394
3395 return CMD_SUCCESS;
3396}
3397
3398#ifdef VTY_CRYPT_PW
3399DEFUN(service_password_encrypt,
3400 service_password_encrypt_cmd,
3401 "service password-encryption",
3402 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3403{
3404 if (host.encrypt)
3405 return CMD_SUCCESS;
3406
3407 host.encrypt = 1;
3408
3409 if (host.password) {
3410 if (host.password_encrypt)
3411 talloc_free(host.password_encrypt);
3412 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3413 }
3414 if (host.enable) {
3415 if (host.enable_encrypt)
3416 talloc_free(host.enable_encrypt);
3417 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3418 }
3419
3420 return CMD_SUCCESS;
3421}
3422
3423DEFUN(no_service_password_encrypt,
3424 no_service_password_encrypt_cmd,
3425 "no service password-encryption",
3426 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3427{
3428 if (!host.encrypt)
3429 return CMD_SUCCESS;
3430
3431 host.encrypt = 0;
3432
3433 if (host.password_encrypt)
3434 talloc_free(host.password_encrypt);
3435 host.password_encrypt = NULL;
3436
3437 if (host.enable_encrypt)
3438 talloc_free(host.enable_encrypt);
3439 host.enable_encrypt = NULL;
3440
3441 return CMD_SUCCESS;
3442}
3443#endif
3444
3445DEFUN(config_terminal_length, config_terminal_length_cmd,
3446 "terminal length <0-512>",
3447 "Set terminal line parameters\n"
3448 "Set number of lines on a screen\n"
3449 "Number of lines on screen (0 for no pausing)\n")
3450{
3451 int lines;
3452 char *endptr = NULL;
3453
3454 lines = strtol(argv[0], &endptr, 10);
3455 if (lines < 0 || lines > 512 || *endptr != '\0') {
3456 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3457 return CMD_WARNING;
3458 }
3459 vty->lines = lines;
3460
3461 return CMD_SUCCESS;
3462}
3463
3464DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3465 "terminal no length",
3466 "Set terminal line parameters\n"
3467 NO_STR "Set number of lines on a screen\n")
3468{
3469 vty->lines = -1;
3470 return CMD_SUCCESS;
3471}
3472
3473DEFUN(service_terminal_length, service_terminal_length_cmd,
3474 "service terminal-length <0-512>",
3475 "Set up miscellaneous service\n"
3476 "System wide terminal length configuration\n"
3477 "Number of lines of VTY (0 means no line control)\n")
3478{
3479 int lines;
3480 char *endptr = NULL;
3481
3482 lines = strtol(argv[0], &endptr, 10);
3483 if (lines < 0 || lines > 512 || *endptr != '\0') {
3484 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3485 return CMD_WARNING;
3486 }
3487 host.lines = lines;
3488
3489 return CMD_SUCCESS;
3490}
3491
3492DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3493 "no service terminal-length [<0-512>]",
3494 NO_STR
3495 "Set up miscellaneous service\n"
3496 "System wide terminal length configuration\n"
3497 "Number of lines of VTY (0 means no line control)\n")
3498{
3499 host.lines = -1;
3500 return CMD_SUCCESS;
3501}
3502
3503DEFUN_HIDDEN(do_echo,
3504 echo_cmd,
3505 "echo .MESSAGE",
3506 "Echo a message back to the vty\n" "The message to echo\n")
3507{
3508 char *message;
3509
3510 vty_out(vty, "%s%s",
3511 ((message =
3512 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3513 if (message)
3514 talloc_free(message);
3515 return CMD_SUCCESS;
3516}
3517
3518#if 0
3519DEFUN(config_logmsg,
3520 config_logmsg_cmd,
3521 "logmsg " LOG_LEVELS " .MESSAGE",
3522 "Send a message to enabled logging destinations\n"
3523 LOG_LEVEL_DESC "The message to send\n")
3524{
3525 int level;
3526 char *message;
3527
3528 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3529 return CMD_ERR_NO_MATCH;
3530
3531 zlog(NULL, level,
3532 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3533 if (message)
3534 talloc_free(message);
3535 return CMD_SUCCESS;
3536}
3537
3538DEFUN(show_logging,
3539 show_logging_cmd,
3540 "show logging", SHOW_STR "Show current logging configuration\n")
3541{
3542 struct zlog *zl = zlog_default;
3543
3544 vty_out(vty, "Syslog logging: ");
3545 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3546 vty_out(vty, "disabled");
3547 else
3548 vty_out(vty, "level %s, facility %s, ident %s",
3549 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3550 facility_name(zl->facility), zl->ident);
3551 vty_out(vty, "%s", VTY_NEWLINE);
3552
3553 vty_out(vty, "Stdout logging: ");
3554 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3555 vty_out(vty, "disabled");
3556 else
3557 vty_out(vty, "level %s",
3558 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3559 vty_out(vty, "%s", VTY_NEWLINE);
3560
3561 vty_out(vty, "Monitor logging: ");
3562 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3563 vty_out(vty, "disabled");
3564 else
3565 vty_out(vty, "level %s",
3566 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3567 vty_out(vty, "%s", VTY_NEWLINE);
3568
3569 vty_out(vty, "File logging: ");
3570 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3571 vty_out(vty, "disabled");
3572 else
3573 vty_out(vty, "level %s, filename %s",
3574 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3575 zl->filename);
3576 vty_out(vty, "%s", VTY_NEWLINE);
3577
3578 vty_out(vty, "Protocol name: %s%s",
3579 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3580 vty_out(vty, "Record priority: %s%s",
3581 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3582
3583 return CMD_SUCCESS;
3584}
3585
3586DEFUN(config_log_stdout,
3587 config_log_stdout_cmd,
3588 "log stdout", "Logging control\n" "Set stdout logging level\n")
3589{
3590 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3591 return CMD_SUCCESS;
3592}
3593
3594DEFUN(config_log_stdout_level,
3595 config_log_stdout_level_cmd,
3596 "log stdout " LOG_LEVELS,
3597 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3598{
3599 int level;
3600
3601 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3602 return CMD_ERR_NO_MATCH;
3603 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3604 return CMD_SUCCESS;
3605}
3606
3607DEFUN(no_config_log_stdout,
3608 no_config_log_stdout_cmd,
3609 "no log stdout [LEVEL]",
3610 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3611{
3612 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3613 return CMD_SUCCESS;
3614}
3615
3616DEFUN(config_log_monitor,
3617 config_log_monitor_cmd,
3618 "log monitor",
3619 "Logging control\n" "Set terminal line (monitor) logging level\n")
3620{
3621 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3622 return CMD_SUCCESS;
3623}
3624
3625DEFUN(config_log_monitor_level,
3626 config_log_monitor_level_cmd,
3627 "log monitor " LOG_LEVELS,
3628 "Logging control\n"
3629 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3630{
3631 int level;
3632
3633 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3634 return CMD_ERR_NO_MATCH;
3635 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3636 return CMD_SUCCESS;
3637}
3638
3639DEFUN(no_config_log_monitor,
3640 no_config_log_monitor_cmd,
3641 "no log monitor [LEVEL]",
3642 NO_STR
3643 "Logging control\n"
3644 "Disable terminal line (monitor) logging\n" "Logging level\n")
3645{
3646 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3647 return CMD_SUCCESS;
3648}
3649
3650static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3651{
3652 int ret;
3653 char *p = NULL;
3654 const char *fullpath;
3655
3656 /* Path detection. */
3657 if (!IS_DIRECTORY_SEP(*fname)) {
3658 char cwd[MAXPATHLEN + 1];
3659 cwd[MAXPATHLEN] = '\0';
3660
3661 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3662 zlog_err("config_log_file: Unable to alloc mem!");
3663 return CMD_WARNING;
3664 }
3665
3666 if ((p = _talloc_zero(tall_vcmd_ctx,
3667 strlen(cwd) + strlen(fname) + 2),
3668 "set_log_file")
3669 == NULL) {
3670 zlog_err("config_log_file: Unable to alloc mem!");
3671 return CMD_WARNING;
3672 }
3673 sprintf(p, "%s/%s", cwd, fname);
3674 fullpath = p;
3675 } else
3676 fullpath = fname;
3677
3678 ret = zlog_set_file(NULL, fullpath, loglevel);
3679
3680 if (p)
3681 talloc_free(p);
3682
3683 if (!ret) {
3684 vty_out(vty, "can't open logfile %s\n", fname);
3685 return CMD_WARNING;
3686 }
3687
3688 if (host.logfile)
3689 talloc_free(host.logfile);
3690
3691 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3692
3693 return CMD_SUCCESS;
3694}
3695
3696DEFUN(config_log_file,
3697 config_log_file_cmd,
3698 "log file FILENAME",
3699 "Logging control\n" "Logging to file\n" "Logging filename\n")
3700{
3701 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3702}
3703
3704DEFUN(config_log_file_level,
3705 config_log_file_level_cmd,
3706 "log file FILENAME " LOG_LEVELS,
3707 "Logging control\n"
3708 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3709{
3710 int level;
3711
3712 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3713 return CMD_ERR_NO_MATCH;
3714 return set_log_file(vty, argv[0], level);
3715}
3716
3717DEFUN(no_config_log_file,
3718 no_config_log_file_cmd,
3719 "no log file [FILENAME]",
3720 NO_STR
3721 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3722{
3723 zlog_reset_file(NULL);
3724
3725 if (host.logfile)
3726 talloc_free(host.logfile);
3727
3728 host.logfile = NULL;
3729
3730 return CMD_SUCCESS;
3731}
3732
3733ALIAS(no_config_log_file,
3734 no_config_log_file_level_cmd,
3735 "no log file FILENAME LEVEL",
3736 NO_STR
3737 "Logging control\n"
3738 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3739
3740 DEFUN(config_log_syslog,
3741 config_log_syslog_cmd,
3742 "log syslog", "Logging control\n" "Set syslog logging level\n")
3743{
3744 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3745 return CMD_SUCCESS;
3746}
3747
3748DEFUN(config_log_syslog_level,
3749 config_log_syslog_level_cmd,
3750 "log syslog " LOG_LEVELS,
3751 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3752{
3753 int level;
3754
3755 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3756 return CMD_ERR_NO_MATCH;
3757 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3758 return CMD_SUCCESS;
3759}
3760
3761DEFUN_DEPRECATED(config_log_syslog_facility,
3762 config_log_syslog_facility_cmd,
3763 "log syslog facility " LOG_FACILITIES,
3764 "Logging control\n"
3765 "Logging goes to syslog\n"
3766 "(Deprecated) Facility parameter for syslog messages\n"
3767 LOG_FACILITY_DESC)
3768{
3769 int facility;
3770
3771 if ((facility = facility_match(argv[0])) < 0)
3772 return CMD_ERR_NO_MATCH;
3773
3774 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3775 zlog_default->facility = facility;
3776 return CMD_SUCCESS;
3777}
3778
3779DEFUN(no_config_log_syslog,
3780 no_config_log_syslog_cmd,
3781 "no log syslog [LEVEL]",
3782 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3783{
3784 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3785 return CMD_SUCCESS;
3786}
3787
3788ALIAS(no_config_log_syslog,
3789 no_config_log_syslog_facility_cmd,
3790 "no log syslog facility " LOG_FACILITIES,
3791 NO_STR
3792 "Logging control\n"
3793 "Logging goes to syslog\n"
3794 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3795
3796 DEFUN(config_log_facility,
3797 config_log_facility_cmd,
3798 "log facility " LOG_FACILITIES,
3799 "Logging control\n"
3800 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3801{
3802 int facility;
3803
3804 if ((facility = facility_match(argv[0])) < 0)
3805 return CMD_ERR_NO_MATCH;
3806 zlog_default->facility = facility;
3807 return CMD_SUCCESS;
3808}
3809
3810DEFUN(no_config_log_facility,
3811 no_config_log_facility_cmd,
3812 "no log facility [FACILITY]",
3813 NO_STR
3814 "Logging control\n"
3815 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3816{
3817 zlog_default->facility = LOG_DAEMON;
3818 return CMD_SUCCESS;
3819}
3820
3821DEFUN_DEPRECATED(config_log_trap,
3822 config_log_trap_cmd,
3823 "log trap " LOG_LEVELS,
3824 "Logging control\n"
3825 "(Deprecated) Set logging level and default for all destinations\n"
3826 LOG_LEVEL_DESC)
3827{
3828 int new_level;
3829 int i;
3830
3831 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3832 return CMD_ERR_NO_MATCH;
3833
3834 zlog_default->default_lvl = new_level;
3835 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3836 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3837 zlog_default->maxlvl[i] = new_level;
3838 return CMD_SUCCESS;
3839}
3840
3841DEFUN_DEPRECATED(no_config_log_trap,
3842 no_config_log_trap_cmd,
3843 "no log trap [LEVEL]",
3844 NO_STR
3845 "Logging control\n"
3846 "Permit all logging information\n" "Logging level\n")
3847{
3848 zlog_default->default_lvl = LOG_DEBUG;
3849 return CMD_SUCCESS;
3850}
3851
3852DEFUN(config_log_record_priority,
3853 config_log_record_priority_cmd,
3854 "log record-priority",
3855 "Logging control\n"
3856 "Log the priority of the message within the message\n")
3857{
3858 zlog_default->record_priority = 1;
3859 return CMD_SUCCESS;
3860}
3861
3862DEFUN(no_config_log_record_priority,
3863 no_config_log_record_priority_cmd,
3864 "no log record-priority",
3865 NO_STR
3866 "Logging control\n"
3867 "Do not log the priority of the message within the message\n")
3868{
3869 zlog_default->record_priority = 0;
3870 return CMD_SUCCESS;
3871}
3872#endif
3873
3874DEFUN(banner_motd_file,
3875 banner_motd_file_cmd,
3876 "banner motd file [FILE]",
3877 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3878{
3879 if (host.motdfile)
3880 talloc_free(host.motdfile);
3881 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3882
3883 return CMD_SUCCESS;
3884}
3885
3886DEFUN(banner_motd_default,
3887 banner_motd_default_cmd,
3888 "banner motd default",
3889 "Set banner string\n" "Strings for motd\n" "Default string\n")
3890{
3891 host.motd = default_motd;
3892 return CMD_SUCCESS;
3893}
3894
3895DEFUN(no_banner_motd,
3896 no_banner_motd_cmd,
3897 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3898{
3899 host.motd = NULL;
3900 if (host.motdfile)
3901 talloc_free(host.motdfile);
3902 host.motdfile = NULL;
3903 return CMD_SUCCESS;
3904}
3905
3906/* Set config filename. Called from vty.c */
3907void host_config_set(const char *filename)
3908{
3909 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3910}
3911
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003912/*! Deprecated, now happens implicitly when calling install_node().
3913 * Users of the API may still attempt to call this function, hence
3914 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003915void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003916{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003917}
3918
3919/*! Deprecated, now happens implicitly when calling install_node().
3920 * Users of the API may still attempt to call this function, hence
3921 * leave it here as a no-op. */
3922void vty_install_default(int node)
3923{
3924}
3925
3926/*! Install common commands like 'exit' and 'list'. */
3927static void install_basic_node_commands(int node)
3928{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003929 install_lib_element(node, &config_help_cmd);
3930 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003931
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003932 install_lib_element(node, &config_write_terminal_cmd);
3933 install_lib_element(node, &config_write_file_cmd);
3934 install_lib_element(node, &config_write_memory_cmd);
3935 install_lib_element(node, &config_write_cmd);
3936 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003937
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003938 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003939
3940 if (node >= CONFIG_NODE) {
3941 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003942 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003943 }
3944}
3945
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003946/*! Return true if a node is installed by install_basic_node_commands(), so
3947 * that we can avoid repeating them for each and every node during 'show
3948 * running-config' */
3949static bool vty_command_is_common(struct cmd_element *cmd)
3950{
3951 if (cmd == &config_help_cmd
3952 || cmd == &config_list_cmd
3953 || cmd == &config_write_terminal_cmd
3954 || cmd == &config_write_file_cmd
3955 || cmd == &config_write_memory_cmd
3956 || cmd == &config_write_cmd
3957 || cmd == &show_running_config_cmd
3958 || cmd == &config_exit_cmd
3959 || cmd == &config_end_cmd)
3960 return true;
3961 return false;
3962}
3963
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003964/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003965 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003966 * \param[in] vty the vty of the code
3967 * \param[in] filename where to store the file
3968 * \return 0 in case of success.
3969 *
3970 * If the filename already exists create a filename.sav
3971 * version with the current code.
3972 *
3973 */
3974int osmo_vty_write_config_file(const char *filename)
3975{
3976 char *failed_file;
3977 int rc;
3978
3979 rc = write_config_file(filename, &failed_file);
3980 talloc_free(failed_file);
3981 return rc;
3982}
3983
3984/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003985 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003986 * \return 0 in case of success.
3987 *
3988 * If the filename already exists create a filename.sav
3989 * version with the current code.
3990 *
3991 */
3992int osmo_vty_save_config_file(void)
3993{
3994 char *failed_file;
3995 int rc;
3996
3997 if (host.config == NULL)
3998 return -7;
3999
4000 rc = write_config_file(host.config, &failed_file);
4001 talloc_free(failed_file);
4002 return rc;
4003}
4004
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004005/* Initialize command interface. Install basic nodes and commands. */
4006void cmd_init(int terminal)
4007{
4008 /* Allocate initial top vector of commands. */
4009 cmdvec = vector_init(VECTOR_MIN_SIZE);
4010
4011 /* Default host value settings. */
4012 host.name = NULL;
4013 host.password = NULL;
4014 host.enable = NULL;
4015 host.logfile = NULL;
4016 host.config = NULL;
4017 host.lines = -1;
4018 host.motd = default_motd;
4019 host.motdfile = NULL;
4020
4021 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004022 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004023 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004024 install_node_bare(&auth_node, NULL);
4025 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004026 install_node(&config_node, config_write_host);
4027
4028 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004029 install_lib_element(VIEW_NODE, &show_version_cmd);
4030 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004031 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004032 install_lib_element(VIEW_NODE, &config_list_cmd);
4033 install_lib_element(VIEW_NODE, &config_exit_cmd);
4034 install_lib_element(VIEW_NODE, &config_help_cmd);
4035 install_lib_element(VIEW_NODE, &config_enable_cmd);
4036 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4037 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4038 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004039 }
4040
4041 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004042 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4043 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4044 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004045 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004046 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4047 install_lib_element(ENABLE_NODE, &show_version_cmd);
4048 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004049
4050 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004051 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4052 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4053 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004054 }
4055
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004056 install_lib_element(CONFIG_NODE, &hostname_cmd);
4057 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004058
4059 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004060 install_lib_element(CONFIG_NODE, &password_cmd);
4061 install_lib_element(CONFIG_NODE, &password_text_cmd);
4062 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4063 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4064 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004065
4066#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004067 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4068 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004069#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004070 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4071 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4072 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4073 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4074 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004075
4076 }
4077 srand(time(NULL));
4078}
Harald Welte7acb30c2011-08-17 17:13:48 +02004079
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004080/* FIXME: execute this section in the unit test instead */
4081static __attribute__((constructor)) void on_dso_load(void)
4082{
4083 unsigned int i, j;
4084
4085 /* Check total number of the library specific attributes */
4086 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4087
4088 /* Check for duplicates in the list of library specific flags */
4089 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4090 if (cmd_lib_attr_letters[i] == '\0')
4091 continue;
4092
4093 /* Only upper case flag letters are allowed for libraries */
4094 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4095 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4096
4097 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4098 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4099 }
4100}
4101
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004102/*! @} */