blob: faad9fe50efb280f11d9b71b48dd63eaa7f61e1f [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", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200638 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
639 "This command applies on ASP restart",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700640};
641
642/* Flag letters of attributes shared between the lib commands.
643 * NOTE: uppercase letters only, the rest is reserved for applications. */
644static const char cmd_lib_attr_letters[32] = {
645 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200646 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700647};
648
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100649/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200650 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100651 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200652static 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 +0100653{
654 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700655 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100656
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200657 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700658
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700659 /* Print global attributes and their description */
660 if (cmd->attr != 0x00) { /* ... if at least one flag is set */
661 print_func(data, " <attributes scope='global'>%s", newline);
662
663 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
664 char *xml_att_desc;
665
666 if (~cmd->attr & cmd_attr_desc[i].value)
667 continue;
668
669 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
670 print_func(data, " <attribute doc='%s' />%s",
671 xml_att_desc, newline);
672 talloc_free(xml_att_desc);
673 }
674
675 print_func(data, " </attributes>%s", newline);
676 }
677
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700678 /* Print application specific attributes and their description */
679 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700680 const char * const *desc;
681 const char *letters;
682
683 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
684 print_func(data, " <attributes scope='library'>%s", newline);
685 letters = &cmd_lib_attr_letters[0];
686 desc = &cmd_lib_attr_desc[0];
687 } else {
688 print_func(data, " <attributes scope='application'>%s", newline);
689 letters = &host.app_info->usr_attr_letters[0];
690 desc = &host.app_info->usr_attr_desc[0];
691 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700692
693 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
694 char *xml_att_desc;
695 char flag;
696
697 /* Skip attribute if *not* set */
698 if (~cmd->usrattr & (1 << i))
699 continue;
700
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700701 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700702 print_func(data, " <attribute doc='%s'", xml_att_desc);
703 talloc_free(xml_att_desc);
704
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700705 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700706 print_func(data, " flag='%c'", flag);
707 print_func(data, " />%s", newline);
708 }
709
710 print_func(data, " </attributes>%s", newline);
711 }
712
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200713 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100714
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700715 for (i = 0; i < vector_count(cmd->strvec); ++i) {
716 vector descvec = vector_slot(cmd->strvec, i);
717 int j;
718 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100719 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700720 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100721 if (desc == NULL)
722 continue;
723
724 xml_param = xml_escape(desc->cmd);
725 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200726 print_func(data, " <param name='%s' doc='%s' />%s",
727 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100728 talloc_free(xml_param);
729 talloc_free(xml_doc);
730 }
731 }
732
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200733 print_func(data, " </params>%s", newline);
734 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100735
736 talloc_free(xml_string);
737 return 0;
738}
739
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200740static bool vty_command_is_common(struct cmd_element *cmd);
741
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100742/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200743 * 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 +0100744 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200745static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100746{
747 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200748 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100749
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200750 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100751
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200752 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200753 print_func(data, " <node id='_common_cmds_'>%s", newline);
754 print_func(data, " <name>Common Commands</name>%s", newline);
755 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
756 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200757 for (i = 0; i < vector_active(cmdvec); ++i) {
758 struct cmd_node *cnode;
759 cnode = vector_slot(cmdvec, i);
760 if (!cnode)
761 continue;
762 if (cnode->node != CONFIG_NODE)
763 continue;
764
765 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
766 struct cmd_element *elem;
767 elem = vector_slot(cnode->cmd_vector, j);
768 if (!vty_command_is_common(elem))
769 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200770 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200771 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200772 }
773 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200774 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200775
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100776 for (i = 0; i < vector_active(cmdvec); ++i) {
777 struct cmd_node *cnode;
778 cnode = vector_slot(cmdvec, i);
779 if (!cnode)
780 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200781 if (vector_active(cnode->cmd_vector) < 1)
782 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100783
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200784 /* De-dup node IDs: how many times has this same name been used before? Count the first
785 * occurence as _1 and omit that first suffix, so that the first occurence is called
786 * 'name', the second becomes 'name_2', then 'name_3', ... */
787 same_name_count = 1;
788 for (j = 0; j < i; ++j) {
789 struct cmd_node *cnode2;
790 cnode2 = vector_slot(cmdvec, j);
791 if (!cnode2)
792 continue;
793 if (strcmp(cnode->name, cnode2->name) == 0)
794 same_name_count ++;
795 }
796
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200797 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200798 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200799 print_func(data, "_%d", same_name_count);
800 print_func(data, "'>%s", newline);
801 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100802
803 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
804 struct cmd_element *elem;
805 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200806 if (vty_command_is_common(elem))
807 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200808 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200809 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100810 }
811
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200812 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100813 }
814
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200815 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100816
817 return 0;
818}
819
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200820static int print_func_vty(void *data, const char *format, ...)
821{
822 struct vty *vty = data;
823 va_list args;
824 int rc;
825 va_start(args, format);
826 rc = vty_out_va(vty, format, args);
827 va_end(args);
828 return rc;
829}
830
831static int vty_dump_xml_ref_to_vty(struct vty *vty)
832{
833 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
834}
835
836static int print_func_stream(void *data, const char *format, ...)
837{
838 va_list args;
839 int rc;
840 va_start(args, format);
841 rc = vfprintf((FILE*)data, format, args);
842 va_end(args);
843 return rc;
844}
845
846/*! Print the XML reference of all VTY nodes to the given stream.
847 */
848int vty_dump_xml_ref(FILE *stream)
849{
850 return vty_dump_nodes(print_func_stream, stream, "\n");
851}
852
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200853/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100854static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
855{
856 int i;
857
858 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
859 struct cmd_element *elem;
860 elem = vector_slot(cnode->cmd_vector, i);
861 if (!elem->string)
862 continue;
863 if (!strcmp(elem->string, cmdstring))
864 return 1;
865 }
866 return 0;
867}
868
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200869/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200870 * \param[in] ntype Node Type
871 * \param[cmd] element to be installed
872 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000873void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200874{
875 struct cmd_node *cnode;
876
877 cnode = vector_slot(cmdvec, ntype);
878
Harald Weltea99d45a2015-11-12 13:48:23 +0100879 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100880 /* ensure no _identical_ command has been registered at this
881 * node so far */
882 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200883
884 vector_set(cnode->cmd_vector, cmd);
885
886 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
887 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
888}
889
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700890/*! Install a library command into a node
891 * \param[in] ntype Node Type
892 * \param[in] cmd element to be installed
893 */
894void install_lib_element(int ntype, struct cmd_element *cmd)
895{
896 cmd->attr |= CMD_ATTR_LIB_COMMAND;
897 install_element(ntype, cmd);
898}
899
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200900/* Install a command into VIEW and ENABLE node */
901void install_element_ve(struct cmd_element *cmd)
902{
903 install_element(VIEW_NODE, cmd);
904 install_element(ENABLE_NODE, cmd);
905}
906
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700907/* Install a library command into VIEW and ENABLE node */
908void install_lib_element_ve(struct cmd_element *cmd)
909{
910 cmd->attr |= CMD_ATTR_LIB_COMMAND;
911 install_element_ve(cmd);
912}
913
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200914#ifdef VTY_CRYPT_PW
915static unsigned char itoa64[] =
916 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
917
918static void to64(char *s, long v, int n)
919{
920 while (--n >= 0) {
921 *s++ = itoa64[v & 0x3f];
922 v >>= 6;
923 }
924}
925
926static char *zencrypt(const char *passwd)
927{
928 char salt[6];
929 struct timeval tv;
930 char *crypt(const char *, const char *);
931
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200932 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200933
934 to64(&salt[0], random(), 3);
935 to64(&salt[3], tv.tv_usec, 3);
936 salt[5] = '\0';
937
938 return crypt(passwd, salt);
939}
940#endif
941
942/* This function write configuration of this host. */
943static int config_write_host(struct vty *vty)
944{
945 if (host.name)
946 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
947
948 if (host.encrypt) {
949 if (host.password_encrypt)
950 vty_out(vty, "password 8 %s%s", host.password_encrypt,
951 VTY_NEWLINE);
952 if (host.enable_encrypt)
953 vty_out(vty, "enable password 8 %s%s",
954 host.enable_encrypt, VTY_NEWLINE);
955 } else {
956 if (host.password)
957 vty_out(vty, "password %s%s", host.password,
958 VTY_NEWLINE);
959 if (host.enable)
960 vty_out(vty, "enable password %s%s", host.enable,
961 VTY_NEWLINE);
962 }
963
964 if (host.advanced)
965 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
966
967 if (host.encrypt)
968 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
969
970 if (host.lines >= 0)
971 vty_out(vty, "service terminal-length %d%s", host.lines,
972 VTY_NEWLINE);
973
974 if (host.motdfile)
975 vty_out(vty, "banner motd file %s%s", host.motdfile,
976 VTY_NEWLINE);
977 else if (!host.motd)
978 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
979
980 return 1;
981}
982
983/* Utility function for getting command vector. */
984static vector cmd_node_vector(vector v, enum node_type ntype)
985{
986 struct cmd_node *cnode = vector_slot(v, ntype);
987 return cnode->cmd_vector;
988}
989
990/* Completion match types. */
991enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200992 NO_MATCH = 0,
993 ANY_MATCH,
994 EXTEND_MATCH,
995 IPV4_PREFIX_MATCH,
996 IPV4_MATCH,
997 IPV6_PREFIX_MATCH,
998 IPV6_MATCH,
999 RANGE_MATCH,
1000 VARARG_MATCH,
1001 PARTLY_MATCH,
1002 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001003};
1004
1005static enum match_type cmd_ipv4_match(const char *str)
1006{
1007 const char *sp;
1008 int dots = 0, nums = 0;
1009 char buf[4];
1010
1011 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001012 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001013
1014 for (;;) {
1015 memset(buf, 0, sizeof(buf));
1016 sp = str;
1017 while (*str != '\0') {
1018 if (*str == '.') {
1019 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001020 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001021
1022 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001023 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001024
1025 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001026 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001027
1028 dots++;
1029 break;
1030 }
1031 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001032 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001033
1034 str++;
1035 }
1036
1037 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001038 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001039
1040 strncpy(buf, sp, str - sp);
1041 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001042 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001043
1044 nums++;
1045
1046 if (*str == '\0')
1047 break;
1048
1049 str++;
1050 }
1051
1052 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001053 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001054
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001055 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001056}
1057
1058static enum match_type cmd_ipv4_prefix_match(const char *str)
1059{
1060 const char *sp;
1061 int dots = 0;
1062 char buf[4];
1063
1064 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001065 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001066
1067 for (;;) {
1068 memset(buf, 0, sizeof(buf));
1069 sp = str;
1070 while (*str != '\0' && *str != '/') {
1071 if (*str == '.') {
1072 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001073 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001074
1075 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001076 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001077
1078 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001079 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001080
1081 dots++;
1082 break;
1083 }
1084
1085 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001086 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001087
1088 str++;
1089 }
1090
1091 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001092 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001093
1094 strncpy(buf, sp, str - sp);
1095 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001096 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001097
1098 if (dots == 3) {
1099 if (*str == '/') {
1100 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001101 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001102
1103 str++;
1104 break;
1105 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001106 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001107 }
1108
1109 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001110 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111
1112 str++;
1113 }
1114
1115 sp = str;
1116 while (*str != '\0') {
1117 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001118 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001119
1120 str++;
1121 }
1122
1123 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001124 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001126 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001127}
1128
1129#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1130#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1131#define STATE_START 1
1132#define STATE_COLON 2
1133#define STATE_DOUBLE 3
1134#define STATE_ADDR 4
1135#define STATE_DOT 5
1136#define STATE_SLASH 6
1137#define STATE_MASK 7
1138
1139#ifdef HAVE_IPV6
1140
1141static enum match_type cmd_ipv6_match(const char *str)
1142{
1143 int state = STATE_START;
1144 int colons = 0, nums = 0, double_colon = 0;
1145 const char *sp = NULL;
1146 struct sockaddr_in6 sin6_dummy;
1147 int ret;
1148
1149 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001153 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001154
1155 /* use inet_pton that has a better support,
1156 * for example inet_pton can support the automatic addresses:
1157 * ::1.2.3.4
1158 */
1159 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1160
1161 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001162 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001163
1164 while (*str != '\0') {
1165 switch (state) {
1166 case STATE_START:
1167 if (*str == ':') {
1168 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001169 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001170 colons--;
1171 state = STATE_COLON;
1172 } else {
1173 sp = str;
1174 state = STATE_ADDR;
1175 }
1176
1177 continue;
1178 case STATE_COLON:
1179 colons++;
1180 if (*(str + 1) == ':')
1181 state = STATE_DOUBLE;
1182 else {
1183 sp = str + 1;
1184 state = STATE_ADDR;
1185 }
1186 break;
1187 case STATE_DOUBLE:
1188 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001189 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190
1191 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001192 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001193 else {
1194 if (*(str + 1) != '\0')
1195 colons++;
1196 sp = str + 1;
1197 state = STATE_ADDR;
1198 }
1199
1200 double_colon++;
1201 nums++;
1202 break;
1203 case STATE_ADDR:
1204 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1205 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001206 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207
1208 nums++;
1209 state = STATE_COLON;
1210 }
1211 if (*(str + 1) == '.')
1212 state = STATE_DOT;
1213 break;
1214 case STATE_DOT:
1215 state = STATE_ADDR;
1216 break;
1217 default:
1218 break;
1219 }
1220
1221 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001222 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001223
1224 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226
1227 str++;
1228 }
1229
1230#if 0
1231 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001232 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233#endif /* 0 */
1234
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001235 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001236}
1237
1238static enum match_type cmd_ipv6_prefix_match(const char *str)
1239{
1240 int state = STATE_START;
1241 int colons = 0, nums = 0, double_colon = 0;
1242 int mask;
1243 const char *sp = NULL;
1244 char *endptr = NULL;
1245
1246 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001247 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001248
1249 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001250 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001251
1252 while (*str != '\0' && state != STATE_MASK) {
1253 switch (state) {
1254 case STATE_START:
1255 if (*str == ':') {
1256 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001257 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001258 colons--;
1259 state = STATE_COLON;
1260 } else {
1261 sp = str;
1262 state = STATE_ADDR;
1263 }
1264
1265 continue;
1266 case STATE_COLON:
1267 colons++;
1268 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001269 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001270 else if (*(str + 1) == ':')
1271 state = STATE_DOUBLE;
1272 else {
1273 sp = str + 1;
1274 state = STATE_ADDR;
1275 }
1276 break;
1277 case STATE_DOUBLE:
1278 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001279 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001280
1281 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001282 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001283 else {
1284 if (*(str + 1) != '\0' && *(str + 1) != '/')
1285 colons++;
1286 sp = str + 1;
1287
1288 if (*(str + 1) == '/')
1289 state = STATE_SLASH;
1290 else
1291 state = STATE_ADDR;
1292 }
1293
1294 double_colon++;
1295 nums += 1;
1296 break;
1297 case STATE_ADDR:
1298 if (*(str + 1) == ':' || *(str + 1) == '.'
1299 || *(str + 1) == '\0' || *(str + 1) == '/') {
1300 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001301 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302
1303 for (; sp <= str; sp++)
1304 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001305 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001306
1307 nums++;
1308
1309 if (*(str + 1) == ':')
1310 state = STATE_COLON;
1311 else if (*(str + 1) == '.')
1312 state = STATE_DOT;
1313 else if (*(str + 1) == '/')
1314 state = STATE_SLASH;
1315 }
1316 break;
1317 case STATE_DOT:
1318 state = STATE_ADDR;
1319 break;
1320 case STATE_SLASH:
1321 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001322 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001323
1324 state = STATE_MASK;
1325 break;
1326 default:
1327 break;
1328 }
1329
1330 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001331 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001332
1333 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001334 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001335
1336 str++;
1337 }
1338
1339 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001341
1342 mask = strtol(str, &endptr, 10);
1343 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001344 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001345
1346 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001347 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001348
1349/* I don't know why mask < 13 makes command match partly.
1350 Forgive me to make this comments. I Want to set static default route
1351 because of lack of function to originate default in ospf6d; sorry
1352 yasu
1353 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001354 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001355*/
1356
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001357 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001358}
1359
1360#endif /* HAVE_IPV6 */
1361
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001362
1363#if ULONG_MAX == 18446744073709551615UL
1364#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1365#elif ULONG_MAX == 4294967295UL
1366#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1367#else
1368#error "ULONG_MAX not defined!"
1369#endif
1370
1371#if LONG_MAX == 9223372036854775807L
1372#define DECIMAL_STRLEN_MAX_SIGNED 19
1373#elif LONG_MAX == 2147483647L
1374#define DECIMAL_STRLEN_MAX_SIGNED 10
1375#else
1376#error "LONG_MAX not defined!"
1377#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378
1379static int cmd_range_match(const char *range, const char *str)
1380{
1381 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001382 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001383 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001384
1385 if (str == NULL)
1386 return 1;
1387
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001388 if (range[1] == '-') {
1389 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001390
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001391 val = strtol(str, &endptr, 10);
1392 if (*endptr != '\0')
1393 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001394
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001395 range += 2;
1396 p = strchr(range, '-');
1397 if (p == NULL)
1398 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001399 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001400 return 0;
1401 strncpy(buf, range, p - range);
1402 buf[p - range] = '\0';
1403 min = -strtol(buf, &endptr, 10);
1404 if (*endptr != '\0')
1405 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001406
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001407 range = p + 1;
1408 p = strchr(range, '>');
1409 if (p == NULL)
1410 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001411 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001412 return 0;
1413 strncpy(buf, range, p - range);
1414 buf[p - range] = '\0';
1415 max = strtol(buf, &endptr, 10);
1416 if (*endptr != '\0')
1417 return 0;
1418
1419 if (val < min || val > max)
1420 return 0;
1421 } else {
1422 unsigned long min, max, val;
1423
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001424 if (str[0] == '-')
1425 return 0;
1426
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001427 val = strtoul(str, &endptr, 10);
1428 if (*endptr != '\0')
1429 return 0;
1430
1431 range++;
1432 p = strchr(range, '-');
1433 if (p == NULL)
1434 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001435 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001436 return 0;
1437 strncpy(buf, range, p - range);
1438 buf[p - range] = '\0';
1439 min = strtoul(buf, &endptr, 10);
1440 if (*endptr != '\0')
1441 return 0;
1442
1443 range = p + 1;
1444 p = strchr(range, '>');
1445 if (p == NULL)
1446 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001447 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001448 return 0;
1449 strncpy(buf, range, p - range);
1450 buf[p - range] = '\0';
1451 max = strtoul(buf, &endptr, 10);
1452 if (*endptr != '\0')
1453 return 0;
1454
1455 if (val < min || val > max)
1456 return 0;
1457 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001458
1459 return 1;
1460}
1461
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001462/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001463static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001464{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001465 /* we've got "[blah]". We want to strip off the []s and redo the
1466 * match check for "blah"
1467 */
1468 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001469
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001470 if (len < 3)
1471 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001472
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001473 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001474}
1475
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001476static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001477cmd_match(const char *str, const char *command,
1478 enum match_type min, bool recur)
1479{
1480
1481 if (recur && CMD_OPTION(str))
1482 {
1483 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001484 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001485
1486 /* this would be a bug in a command, however handle it gracefully
1487 * as it we only discover it if a user tries to run it
1488 */
1489 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001490 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001491
1492 ret = cmd_match(tmp, command, min, false);
1493
1494 talloc_free(tmp);
1495
1496 return ret;
1497 }
1498 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001499 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001500 else if (CMD_RANGE(str))
1501 {
1502 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001503 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001504 }
1505#ifdef HAVE_IPV6
1506 else if (CMD_IPV6(str))
1507 {
1508 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001509 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001510 }
1511 else if (CMD_IPV6_PREFIX(str))
1512 {
1513 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001514 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001515 }
1516#endif /* HAVE_IPV6 */
1517 else if (CMD_IPV4(str))
1518 {
1519 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001520 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001521 }
1522 else if (CMD_IPV4_PREFIX(str))
1523 {
1524 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001525 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001526 }
1527 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001528 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001529 else if (strncmp(command, str, strlen(command)) == 0)
1530 {
1531 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001532 return EXACT_MATCH;
1533 else if (PARTLY_MATCH >= min)
1534 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001535 }
1536
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001537 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001538}
1539
1540/* Filter vector at the specified index and by the given command string, to
1541 * the desired matching level (thus allowing part matches), and return match
1542 * type flag.
1543 */
1544static enum match_type
1545cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001546{
1547 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001548 struct cmd_element *cmd_element;
1549 enum match_type match_type;
1550 vector descvec;
1551 struct desc *desc;
1552
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001553 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001554
1555 /* If command and cmd_element string does not match set NULL to vector */
1556 for (i = 0; i < vector_active(v); i++)
1557 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001558 if (index >= vector_active(cmd_element->strvec))
1559 vector_slot(v, i) = NULL;
1560 else {
1561 unsigned int j;
1562 int matched = 0;
1563
1564 descvec =
1565 vector_slot(cmd_element->strvec, index);
1566
1567 for (j = 0; j < vector_active(descvec); j++)
1568 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001569 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001570
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001571 ret = cmd_match (desc->cmd, command, level, true);
1572
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001573 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001574 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001575
1576 if (match_type < ret)
1577 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001578 }
1579 if (!matched)
1580 vector_slot(v, i) = NULL;
1581 }
1582 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001583
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001584 if (match_type == NO_MATCH)
1585 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001586
1587 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1588 * go again and filter out commands whose argument (at this index) is
1589 * 'weaker'. E.g., if we have 2 commands:
1590 *
1591 * foo bar <1-255>
1592 * foo bar BLAH
1593 *
1594 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001595 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001596 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1597 *
1598 * If we don't do a 2nd pass and filter it out, the higher-layers will
1599 * consider this to be ambiguous.
1600 */
1601 for (i = 0; i < vector_active(v); i++)
1602 if ((cmd_element = vector_slot(v, i)) != NULL) {
1603 if (index >= vector_active(cmd_element->strvec))
1604 vector_slot(v, i) = NULL;
1605 else {
1606 unsigned int j;
1607 int matched = 0;
1608
1609 descvec =
1610 vector_slot(cmd_element->strvec, index);
1611
1612 for (j = 0; j < vector_active(descvec); j++)
1613 if ((desc = vector_slot(descvec, j))) {
1614 enum match_type ret;
1615
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001616 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001617
1618 if (ret >= match_type)
1619 matched++;
1620 }
1621 if (!matched)
1622 vector_slot(v, i) = NULL;
1623 }
1624 }
1625
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001626 return match_type;
1627}
1628
1629/* Check ambiguous match */
1630static int
1631is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1632{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001633 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001634 unsigned int i;
1635 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001636 struct cmd_element *cmd_element;
1637 const char *matched = NULL;
1638 vector descvec;
1639 struct desc *desc;
1640
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001641 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1642 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1643 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1644 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1645 * that case, the string must remain allocated until this function exits or another match comes
1646 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1647 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1648 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1649 void *cmd_deopt_ctx = NULL;
1650
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001651 for (i = 0; i < vector_active(v); i++) {
1652 cmd_element = vector_slot(v, i);
1653 if (!cmd_element)
1654 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001655
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001656 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001657
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001658 descvec = vector_slot(cmd_element->strvec, index);
1659
1660 for (j = 0; j < vector_active(descvec); j++) {
1661 desc = vector_slot(descvec, j);
1662 if (!desc)
1663 continue;
1664
1665 enum match_type mtype;
1666 const char *str = desc->cmd;
1667
1668 if (CMD_OPTION(str)) {
1669 if (!cmd_deopt_ctx)
1670 cmd_deopt_ctx =
1671 talloc_named_const(tall_vty_cmd_ctx, 0,
1672 __func__);
1673 str = cmd_deopt(cmd_deopt_ctx, str);
1674 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001675 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001676 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001677
1678 switch (type) {
1679 case EXACT_MATCH:
1680 if (!(CMD_VARIABLE (str))
1681 && strcmp(command, str) == 0)
1682 match++;
1683 break;
1684 case PARTLY_MATCH:
1685 if (!(CMD_VARIABLE (str))
1686 && strncmp(command, str, strlen (command)) == 0)
1687 {
1688 if (matched
1689 && strcmp(matched,
1690 str) != 0) {
1691 ret = 1; /* There is ambiguous match. */
1692 goto free_and_return;
1693 } else
1694 matched = str;
1695 match++;
1696 }
1697 break;
1698 case RANGE_MATCH:
1699 if (cmd_range_match
1700 (str, command)) {
1701 if (matched
1702 && strcmp(matched,
1703 str) != 0) {
1704 ret = 1;
1705 goto free_and_return;
1706 } else
1707 matched = str;
1708 match++;
1709 }
1710 break;
1711#ifdef HAVE_IPV6
1712 case IPV6_MATCH:
1713 if (CMD_IPV6(str))
1714 match++;
1715 break;
1716 case IPV6_PREFIX_MATCH:
1717 if ((mtype =
1718 cmd_ipv6_prefix_match
1719 (command)) != NO_MATCH) {
1720 if (mtype == PARTLY_MATCH) {
1721 ret = 2; /* There is incomplete match. */
1722 goto free_and_return;
1723 }
1724
1725 match++;
1726 }
1727 break;
1728#endif /* HAVE_IPV6 */
1729 case IPV4_MATCH:
1730 if (CMD_IPV4(str))
1731 match++;
1732 break;
1733 case IPV4_PREFIX_MATCH:
1734 if ((mtype =
1735 cmd_ipv4_prefix_match
1736 (command)) != NO_MATCH) {
1737 if (mtype == PARTLY_MATCH) {
1738 ret = 2; /* There is incomplete match. */
1739 goto free_and_return;
1740 }
1741
1742 match++;
1743 }
1744 break;
1745 case EXTEND_MATCH:
1746 if (CMD_VARIABLE (str))
1747 match++;
1748 break;
1749 case NO_MATCH:
1750 default:
1751 break;
1752 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001753 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001754 if (!match)
1755 vector_slot(v, i) = NULL;
1756 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001757
1758free_and_return:
1759 if (cmd_deopt_ctx)
1760 talloc_free(cmd_deopt_ctx);
1761 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001762}
1763
1764/* If src matches dst return dst string, otherwise return NULL */
1765static const char *cmd_entry_function(const char *src, const char *dst)
1766{
1767 /* Skip variable arguments. */
1768 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1769 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1770 return NULL;
1771
1772 /* In case of 'command \t', given src is NULL string. */
1773 if (src == NULL)
1774 return dst;
1775
1776 /* Matched with input string. */
1777 if (strncmp(src, dst, strlen(src)) == 0)
1778 return dst;
1779
1780 return NULL;
1781}
1782
1783/* If src matches dst return dst string, otherwise return NULL */
1784/* This version will return the dst string always if it is
1785 CMD_VARIABLE for '?' key processing */
1786static const char *cmd_entry_function_desc(const char *src, const char *dst)
1787{
1788 if (CMD_VARARG(dst))
1789 return dst;
1790
1791 if (CMD_RANGE(dst)) {
1792 if (cmd_range_match(dst, src))
1793 return dst;
1794 else
1795 return NULL;
1796 }
1797#ifdef HAVE_IPV6
1798 if (CMD_IPV6(dst)) {
1799 if (cmd_ipv6_match(src))
1800 return dst;
1801 else
1802 return NULL;
1803 }
1804
1805 if (CMD_IPV6_PREFIX(dst)) {
1806 if (cmd_ipv6_prefix_match(src))
1807 return dst;
1808 else
1809 return NULL;
1810 }
1811#endif /* HAVE_IPV6 */
1812
1813 if (CMD_IPV4(dst)) {
1814 if (cmd_ipv4_match(src))
1815 return dst;
1816 else
1817 return NULL;
1818 }
1819
1820 if (CMD_IPV4_PREFIX(dst)) {
1821 if (cmd_ipv4_prefix_match(src))
1822 return dst;
1823 else
1824 return NULL;
1825 }
1826
1827 /* Optional or variable commands always match on '?' */
1828 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1829 return dst;
1830
1831 /* In case of 'command \t', given src is NULL string. */
1832 if (src == NULL)
1833 return dst;
1834
1835 if (strncmp(src, dst, strlen(src)) == 0)
1836 return dst;
1837 else
1838 return NULL;
1839}
1840
1841/* Check same string element existence. If it isn't there return
1842 1. */
1843static int cmd_unique_string(vector v, const char *str)
1844{
1845 unsigned int i;
1846 char *match;
1847
1848 for (i = 0; i < vector_active(v); i++)
1849 if ((match = vector_slot(v, i)) != NULL)
1850 if (strcmp(match, str) == 0)
1851 return 0;
1852 return 1;
1853}
1854
1855/* Compare string to description vector. If there is same string
1856 return 1 else return 0. */
1857static int desc_unique_string(vector v, const char *str)
1858{
1859 unsigned int i;
1860 struct desc *desc;
1861
1862 for (i = 0; i < vector_active(v); i++)
1863 if ((desc = vector_slot(v, i)) != NULL)
1864 if (strcmp(desc->cmd, str) == 0)
1865 return 1;
1866 return 0;
1867}
1868
1869static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1870{
1871 if (first_word != NULL &&
1872 node != AUTH_NODE &&
1873 node != VIEW_NODE &&
1874 node != AUTH_ENABLE_NODE &&
1875 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1876 return 1;
1877 return 0;
1878}
1879
1880/* '?' describe command support. */
1881static vector
1882cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1883{
1884 unsigned int i;
1885 vector cmd_vector;
1886#define INIT_MATCHVEC_SIZE 10
1887 vector matchvec;
1888 struct cmd_element *cmd_element;
1889 unsigned int index;
1890 int ret;
1891 enum match_type match;
1892 char *command;
1893 static struct desc desc_cr = { "<cr>", "" };
1894
1895 /* Set index. */
1896 if (vector_active(vline) == 0) {
1897 *status = CMD_ERR_NO_MATCH;
1898 return NULL;
1899 } else
1900 index = vector_active(vline) - 1;
1901
1902 /* Make copy vector of current node's command vector. */
1903 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1904
1905 /* Prepare match vector */
1906 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1907
1908 /* Filter commands. */
1909 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001910 for (i = 0; i < index; i++) {
1911 command = vector_slot(vline, i);
1912 if (!command)
1913 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001914
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001915 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001916
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001917 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001918 struct cmd_element *cmd_element;
1919 vector descvec;
1920 unsigned int j, k;
1921
1922 for (j = 0; j < vector_active(cmd_vector); j++)
1923 if ((cmd_element =
1924 vector_slot(cmd_vector, j)) != NULL
1925 &&
1926 (vector_active(cmd_element->strvec))) {
1927 descvec =
1928 vector_slot(cmd_element->
1929 strvec,
1930 vector_active
1931 (cmd_element->
1932 strvec) - 1);
1933 for (k = 0;
1934 k < vector_active(descvec);
1935 k++) {
1936 struct desc *desc =
1937 vector_slot(descvec,
1938 k);
1939 vector_set(matchvec,
1940 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001941 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001942 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001943
Harald Welte80d30fe2013-02-12 11:08:57 +01001944 vector_set(matchvec, &desc_cr);
1945 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001946
Harald Welte80d30fe2013-02-12 11:08:57 +01001947 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001948 }
1949
Harald Welte80d30fe2013-02-12 11:08:57 +01001950 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1951 match)) == 1) {
1952 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001953 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001954 *status = CMD_ERR_AMBIGUOUS;
1955 return NULL;
1956 } else if (ret == 2) {
1957 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001958 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001959 *status = CMD_ERR_NO_MATCH;
1960 return NULL;
1961 }
1962 }
1963
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001964 /* Prepare match vector */
1965 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1966
1967 /* Make sure that cmd_vector is filtered based on current word */
1968 command = vector_slot(vline, index);
1969 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001970 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001971
1972 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001973 for (i = 0; i < vector_active(cmd_vector); i++) {
1974 const char *string = NULL;
1975 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001976
Harald Welte80d30fe2013-02-12 11:08:57 +01001977 cmd_element = vector_slot(cmd_vector, i);
1978 if (!cmd_element)
1979 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001980
Harald Welted17aa592013-02-12 11:11:34 +01001981 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1982 continue;
1983
Harald Welte80d30fe2013-02-12 11:08:57 +01001984 strvec = cmd_element->strvec;
1985
1986 /* if command is NULL, index may be equal to vector_active */
1987 if (command && index >= vector_active(strvec))
1988 vector_slot(cmd_vector, i) = NULL;
1989 else {
1990 /* Check if command is completed. */
1991 if (command == NULL
1992 && index == vector_active(strvec)) {
1993 string = "<cr>";
1994 if (!desc_unique_string(matchvec, string))
1995 vector_set(matchvec, &desc_cr);
1996 } else {
1997 unsigned int j;
1998 vector descvec = vector_slot(strvec, index);
1999 struct desc *desc;
2000
2001 for (j = 0; j < vector_active(descvec); j++) {
2002 desc = vector_slot(descvec, j);
2003 if (!desc)
2004 continue;
2005 string = cmd_entry_function_desc
2006 (command, desc->cmd);
2007 if (!string)
2008 continue;
2009 /* Uniqueness check */
2010 if (!desc_unique_string(matchvec, string))
2011 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002012 }
2013 }
2014 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002015 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002016 vector_free(cmd_vector);
2017
2018 if (vector_slot(matchvec, 0) == NULL) {
2019 vector_free(matchvec);
2020 *status = CMD_ERR_NO_MATCH;
2021 } else
2022 *status = CMD_SUCCESS;
2023
2024 return matchvec;
2025}
2026
2027vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2028{
2029 vector ret;
2030
2031 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2032 enum node_type onode;
2033 vector shifted_vline;
2034 unsigned int index;
2035
2036 onode = vty->node;
2037 vty->node = ENABLE_NODE;
2038 /* We can try it on enable node, cos' the vty is authenticated */
2039
2040 shifted_vline = vector_init(vector_count(vline));
2041 /* use memcpy? */
2042 for (index = 1; index < vector_active(vline); index++) {
2043 vector_set_index(shifted_vline, index - 1,
2044 vector_lookup(vline, index));
2045 }
2046
2047 ret = cmd_describe_command_real(shifted_vline, vty, status);
2048
2049 vector_free(shifted_vline);
2050 vty->node = onode;
2051 return ret;
2052 }
2053
2054 return cmd_describe_command_real(vline, vty, status);
2055}
2056
2057/* Check LCD of matched command. */
2058static int cmd_lcd(char **matched)
2059{
2060 int i;
2061 int j;
2062 int lcd = -1;
2063 char *s1, *s2;
2064 char c1, c2;
2065
2066 if (matched[0] == NULL || matched[1] == NULL)
2067 return 0;
2068
2069 for (i = 1; matched[i] != NULL; i++) {
2070 s1 = matched[i - 1];
2071 s2 = matched[i];
2072
2073 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2074 if (c1 != c2)
2075 break;
2076
2077 if (lcd < 0)
2078 lcd = j;
2079 else {
2080 if (lcd > j)
2081 lcd = j;
2082 }
2083 }
2084 return lcd;
2085}
2086
2087/* Command line completion support. */
2088static char **cmd_complete_command_real(vector vline, struct vty *vty,
2089 int *status)
2090{
2091 unsigned int i;
2092 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2093#define INIT_MATCHVEC_SIZE 10
2094 vector matchvec;
2095 struct cmd_element *cmd_element;
2096 unsigned int index;
2097 char **match_str;
2098 struct desc *desc;
2099 vector descvec;
2100 char *command;
2101 int lcd;
2102
2103 if (vector_active(vline) == 0) {
2104 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002105 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002106 return NULL;
2107 } else
2108 index = vector_active(vline) - 1;
2109
2110 /* First, filter by preceeding command string */
2111 for (i = 0; i < index; i++)
2112 if ((command = vector_slot(vline, i))) {
2113 enum match_type match;
2114 int ret;
2115
2116 /* First try completion match, if there is exactly match return 1 */
2117 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002118 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002119
2120 /* If there is exact match then filter ambiguous match else check
2121 ambiguousness. */
2122 if ((ret =
2123 is_cmd_ambiguous(command, cmd_vector, i,
2124 match)) == 1) {
2125 vector_free(cmd_vector);
2126 *status = CMD_ERR_AMBIGUOUS;
2127 return NULL;
2128 }
2129 /*
2130 else if (ret == 2)
2131 {
2132 vector_free (cmd_vector);
2133 *status = CMD_ERR_NO_MATCH;
2134 return NULL;
2135 }
2136 */
2137 }
2138
2139 /* Prepare match vector. */
2140 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2141
2142 /* Now we got into completion */
2143 for (i = 0; i < vector_active(cmd_vector); i++)
2144 if ((cmd_element = vector_slot(cmd_vector, i))) {
2145 const char *string;
2146 vector strvec = cmd_element->strvec;
2147
2148 /* Check field length */
2149 if (index >= vector_active(strvec))
2150 vector_slot(cmd_vector, i) = NULL;
2151 else {
2152 unsigned int j;
2153
2154 descvec = vector_slot(strvec, index);
2155 for (j = 0; j < vector_active(descvec); j++)
2156 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002157 const char *cmd = desc->cmd;
2158 char *tmp = NULL;
2159
2160 if (CMD_OPTION(desc->cmd)) {
2161 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2162 cmd = tmp;
2163 }
2164 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002165 if (cmd_unique_string (matchvec, string))
2166 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002167 if (tmp)
2168 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002169 }
2170 }
2171 }
2172
2173 /* We don't need cmd_vector any more. */
2174 vector_free(cmd_vector);
2175
2176 /* No matched command */
2177 if (vector_slot(matchvec, 0) == NULL) {
2178 vector_free(matchvec);
2179
2180 /* In case of 'command \t' pattern. Do you need '?' command at
2181 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002182 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002183 *status = CMD_ERR_NOTHING_TODO;
2184 else
2185 *status = CMD_ERR_NO_MATCH;
2186 return NULL;
2187 }
2188
2189 /* Only one matched */
2190 if (vector_slot(matchvec, 1) == NULL) {
2191 match_str = (char **)matchvec->index;
2192 vector_only_wrapper_free(matchvec);
2193 *status = CMD_COMPLETE_FULL_MATCH;
2194 return match_str;
2195 }
2196 /* Make it sure last element is NULL. */
2197 vector_set(matchvec, NULL);
2198
2199 /* Check LCD of matched strings. */
2200 if (vector_slot(vline, index) != NULL) {
2201 lcd = cmd_lcd((char **)matchvec->index);
2202
2203 if (lcd) {
2204 int len = strlen(vector_slot(vline, index));
2205
2206 if (len < lcd) {
2207 char *lcdstr;
2208
2209 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2210 "complete-lcdstr");
2211 memcpy(lcdstr, matchvec->index[0], lcd);
2212 lcdstr[lcd] = '\0';
2213
2214 /* match_str = (char **) &lcdstr; */
2215
2216 /* Free matchvec. */
2217 for (i = 0; i < vector_active(matchvec); i++) {
2218 if (vector_slot(matchvec, i))
2219 talloc_free(vector_slot(matchvec, i));
2220 }
2221 vector_free(matchvec);
2222
2223 /* Make new matchvec. */
2224 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2225 vector_set(matchvec, lcdstr);
2226 match_str = (char **)matchvec->index;
2227 vector_only_wrapper_free(matchvec);
2228
2229 *status = CMD_COMPLETE_MATCH;
2230 return match_str;
2231 }
2232 }
2233 }
2234
2235 match_str = (char **)matchvec->index;
2236 vector_only_wrapper_free(matchvec);
2237 *status = CMD_COMPLETE_LIST_MATCH;
2238 return match_str;
2239}
2240
2241char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2242{
2243 char **ret;
2244
2245 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2246 enum node_type onode;
2247 vector shifted_vline;
2248 unsigned int index;
2249
2250 onode = vty->node;
2251 vty->node = ENABLE_NODE;
2252 /* We can try it on enable node, cos' the vty is authenticated */
2253
2254 shifted_vline = vector_init(vector_count(vline));
2255 /* use memcpy? */
2256 for (index = 1; index < vector_active(vline); index++) {
2257 vector_set_index(shifted_vline, index - 1,
2258 vector_lookup(vline, index));
2259 }
2260
2261 ret = cmd_complete_command_real(shifted_vline, vty, status);
2262
2263 vector_free(shifted_vline);
2264 vty->node = onode;
2265 return ret;
2266 }
2267
2268 return cmd_complete_command_real(vline, vty, status);
2269}
2270
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002271static struct vty_parent_node *vty_parent(struct vty *vty)
2272{
2273 return llist_first_entry_or_null(&vty->parent_nodes,
2274 struct vty_parent_node,
2275 entry);
2276}
2277
2278static bool vty_pop_parent(struct vty *vty)
2279{
2280 struct vty_parent_node *parent = vty_parent(vty);
2281 if (!parent)
2282 return false;
2283 llist_del(&parent->entry);
2284 vty->node = parent->node;
2285 vty->priv = parent->priv;
2286 if (vty->indent)
2287 talloc_free(vty->indent);
2288 vty->indent = parent->indent;
2289 talloc_free(parent);
2290 return true;
2291}
2292
2293static void vty_clear_parents(struct vty *vty)
2294{
2295 while (vty_pop_parent(vty));
2296}
2297
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002298/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002299/*
2300 * This function MUST eventually converge on a node when called repeatedly,
2301 * there must not be any cycles.
2302 * All 'config' nodes shall converge on CONFIG_NODE.
2303 * All other 'enable' nodes shall converge on ENABLE_NODE.
2304 * All 'view' only nodes shall converge on VIEW_NODE.
2305 * All other nodes shall converge on themselves or it must be ensured,
2306 * that the user's rights are not extended anyhow by calling this function.
2307 *
2308 * Note that these requirements also apply to all functions that are used
2309 * as go_parent_cb.
2310 * Note also that this function relies on the is_config_child callback to
2311 * recognize non-config nodes if go_parent_cb is not set.
2312 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002313int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002314{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002315 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002316 case AUTH_NODE:
2317 case VIEW_NODE:
2318 case ENABLE_NODE:
2319 case CONFIG_NODE:
2320 vty_clear_parents(vty);
2321 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002322
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002323 case AUTH_ENABLE_NODE:
2324 vty->node = VIEW_NODE;
2325 vty_clear_parents(vty);
2326 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002327
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002328 default:
2329 if (host.app_info->go_parent_cb)
2330 host.app_info->go_parent_cb(vty);
2331 vty_pop_parent(vty);
2332 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002333 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002334
2335 return vty->node;
2336}
2337
2338/* Execute command by argument vline vector. */
2339static int
2340cmd_execute_command_real(vector vline, struct vty *vty,
2341 struct cmd_element **cmd)
2342{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002343 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002344 unsigned int index;
2345 vector cmd_vector;
2346 struct cmd_element *cmd_element;
2347 struct cmd_element *matched_element;
2348 unsigned int matched_count, incomplete_count;
2349 int argc;
2350 const char *argv[CMD_ARGC_MAX];
2351 enum match_type match = 0;
2352 int varflag;
2353 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002354 int rc;
2355 /* Used for temporary storage of cmd_deopt() allocated arguments during
2356 argv[] generation */
2357 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002358
2359 /* Make copy of command elements. */
2360 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2361
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002362 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002363 if ((command = vector_slot(vline, index))) {
2364 int ret;
2365
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002366 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002367 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002368
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002369 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002370 break;
2371
2372 ret =
2373 is_cmd_ambiguous(command, cmd_vector, index, match);
2374
2375 if (ret == 1) {
2376 vector_free(cmd_vector);
2377 return CMD_ERR_AMBIGUOUS;
2378 } else if (ret == 2) {
2379 vector_free(cmd_vector);
2380 return CMD_ERR_NO_MATCH;
2381 }
2382 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002383 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002384
2385 /* Check matched count. */
2386 matched_element = NULL;
2387 matched_count = 0;
2388 incomplete_count = 0;
2389
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002390 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002391 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002392 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002393 || index >= cmd_element->cmdsize) {
2394 matched_element = cmd_element;
2395#if 0
2396 printf("DEBUG: %s\n", cmd_element->string);
2397#endif
2398 matched_count++;
2399 } else {
2400 incomplete_count++;
2401 }
2402 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002403 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002404
2405 /* Finish of using cmd_vector. */
2406 vector_free(cmd_vector);
2407
2408 /* To execute command, matched_count must be 1. */
2409 if (matched_count == 0) {
2410 if (incomplete_count)
2411 return CMD_ERR_INCOMPLETE;
2412 else
2413 return CMD_ERR_NO_MATCH;
2414 }
2415
2416 if (matched_count > 1)
2417 return CMD_ERR_AMBIGUOUS;
2418
2419 /* Argument treatment */
2420 varflag = 0;
2421 argc = 0;
2422
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002423 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2424
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002425 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002426 if (argc == CMD_ARGC_MAX) {
2427 rc = CMD_ERR_EXEED_ARGC_MAX;
2428 goto rc_free_deopt_ctx;
2429 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002430 if (varflag) {
2431 argv[argc++] = vector_slot(vline, i);
2432 continue;
2433 }
2434
2435 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002436 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002437
2438 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002439 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002440 struct desc *desc = vector_slot(descvec, 0);
2441
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002442 if (CMD_OPTION(desc->cmd)) {
2443 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2444 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2445 } else {
2446 tmp_cmd = desc->cmd;
2447 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002448
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002449 if (CMD_VARARG(tmp_cmd))
2450 varflag = 1;
2451 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002452 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002453 else if (CMD_OPTION(desc->cmd))
2454 argv[argc++] = tmp_cmd;
2455 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002456 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002457 /* multi choice argument. look up which choice
2458 the user meant (can only be one after
2459 filtering and checking for ambigous). For instance,
2460 if user typed "th" for "(two|three)" arg, we
2461 want to pass "three" in argv[]. */
2462 for (j = 0; j < vector_active(descvec); j++) {
2463 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002464 if (!desc)
2465 continue;
2466 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2467 continue;
2468 if (CMD_OPTION(desc->cmd)) {
2469 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2470 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2471 } else {
2472 tmp_cmd = desc->cmd;
2473 }
2474
2475 if(CMD_VARIABLE(tmp_cmd)) {
2476 argv[argc++] = vector_slot(vline, i);
2477 } else {
2478 argv[argc++] = tmp_cmd;
2479 }
2480 break;
2481 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002482 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002483 }
2484
2485 /* For vtysh execution. */
2486 if (cmd)
2487 *cmd = matched_element;
2488
2489 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002490 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002491 else {
2492 /* Execute matched command. */
2493 struct vty_parent_node this_node = {
2494 .node = vty->node,
2495 .priv = vty->priv,
2496 .indent = vty->indent,
2497 };
2498 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002499 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002500
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002501 /* If we have stepped down into a child node, push a parent frame.
2502 * The causality is such: we don't expect every single node entry implementation to push
2503 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2504 * a parent node. Hence if the node changed without the parent node changing, we must
2505 * have stepped into a child node. */
2506 if (vty->node != this_node.node && parent == vty_parent(vty)
2507 && vty->node > CONFIG_NODE) {
2508 /* Push the parent node. */
2509 parent = talloc_zero(vty, struct vty_parent_node);
2510 *parent = this_node;
2511 llist_add(&parent->entry, &vty->parent_nodes);
2512 }
2513 }
2514
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002515rc_free_deopt_ctx:
2516 /* Now after we called the command func, we can free temporary strings */
2517 talloc_free(cmd_deopt_ctx);
2518 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002519}
2520
2521int
2522cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2523 int vtysh)
2524{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002525 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002526 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002527
2528 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002529
2530 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2531 vector shifted_vline;
2532 unsigned int index;
2533
2534 vty->node = ENABLE_NODE;
2535 /* We can try it on enable node, cos' the vty is authenticated */
2536
2537 shifted_vline = vector_init(vector_count(vline));
2538 /* use memcpy? */
2539 for (index = 1; index < vector_active(vline); index++) {
2540 vector_set_index(shifted_vline, index - 1,
2541 vector_lookup(vline, index));
2542 }
2543
2544 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2545
2546 vector_free(shifted_vline);
2547 vty->node = onode;
2548 return ret;
2549 }
2550
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002551 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002552}
2553
2554/* Execute command by argument readline. */
2555int
2556cmd_execute_command_strict(vector vline, struct vty *vty,
2557 struct cmd_element **cmd)
2558{
2559 unsigned int i;
2560 unsigned int index;
2561 vector cmd_vector;
2562 struct cmd_element *cmd_element;
2563 struct cmd_element *matched_element;
2564 unsigned int matched_count, incomplete_count;
2565 int argc;
2566 const char *argv[CMD_ARGC_MAX];
2567 int varflag;
2568 enum match_type match = 0;
2569 char *command;
2570
2571 /* Make copy of command element */
2572 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2573
2574 for (index = 0; index < vector_active(vline); index++)
2575 if ((command = vector_slot(vline, index))) {
2576 int ret;
2577
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002578 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002579 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002580
2581 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002582 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002583 break;
2584
2585 ret =
2586 is_cmd_ambiguous(command, cmd_vector, index, match);
2587 if (ret == 1) {
2588 vector_free(cmd_vector);
2589 return CMD_ERR_AMBIGUOUS;
2590 }
2591 if (ret == 2) {
2592 vector_free(cmd_vector);
2593 return CMD_ERR_NO_MATCH;
2594 }
2595 }
2596
2597 /* Check matched count. */
2598 matched_element = NULL;
2599 matched_count = 0;
2600 incomplete_count = 0;
2601 for (i = 0; i < vector_active(cmd_vector); i++)
2602 if (vector_slot(cmd_vector, i) != NULL) {
2603 cmd_element = vector_slot(cmd_vector, i);
2604
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002605 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002606 || index >= cmd_element->cmdsize) {
2607 matched_element = cmd_element;
2608 matched_count++;
2609 } else
2610 incomplete_count++;
2611 }
2612
2613 /* Finish of using cmd_vector. */
2614 vector_free(cmd_vector);
2615
2616 /* To execute command, matched_count must be 1. */
2617 if (matched_count == 0) {
2618 if (incomplete_count)
2619 return CMD_ERR_INCOMPLETE;
2620 else
2621 return CMD_ERR_NO_MATCH;
2622 }
2623
2624 if (matched_count > 1)
2625 return CMD_ERR_AMBIGUOUS;
2626
2627 /* Argument treatment */
2628 varflag = 0;
2629 argc = 0;
2630
2631 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002632 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002633 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002634 if (varflag) {
2635 argv[argc++] = vector_slot(vline, i);
2636 continue;
2637 }
2638
2639 vector descvec = vector_slot(matched_element->strvec, i);
2640
2641 if (vector_active(descvec) == 1) {
2642 struct desc *desc = vector_slot(descvec, 0);
2643
2644 if (CMD_VARARG(desc->cmd))
2645 varflag = 1;
2646
2647 if (varflag || CMD_VARIABLE(desc->cmd)
2648 || CMD_OPTION(desc->cmd))
2649 argv[argc++] = vector_slot(vline, i);
2650 } else {
2651 argv[argc++] = vector_slot(vline, i);
2652 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002653 }
2654
2655 /* For vtysh execution. */
2656 if (cmd)
2657 *cmd = matched_element;
2658
2659 if (matched_element->daemon)
2660 return CMD_SUCCESS_DAEMON;
2661
2662 /* Now execute matched command */
2663 return (*matched_element->func) (matched_element, vty, argc, argv);
2664}
2665
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002666static inline size_t len(const char *str)
2667{
2668 return str? strlen(str) : 0;
2669}
2670
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002671/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2672 * is longer than b, a must start with exactly b, and vice versa.
2673 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2674 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002675static int indent_cmp(const char *a, const char *b)
2676{
2677 size_t al, bl;
2678 al = len(a);
2679 bl = len(b);
2680 if (al > bl) {
2681 if (bl && strncmp(a, b, bl) != 0)
2682 return EINVAL;
2683 return 1;
2684 }
2685 /* al <= bl */
2686 if (al && strncmp(a, b, al) != 0)
2687 return EINVAL;
2688 return (al < bl)? -1 : 0;
2689}
2690
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002691/* Configration make from file. */
2692int config_from_file(struct vty *vty, FILE * fp)
2693{
2694 int ret;
2695 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002696 char *indent;
2697 int cmp;
2698 struct vty_parent_node this_node;
2699 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002700
2701 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002702 indent = NULL;
2703 vline = NULL;
2704 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002705
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002706 if (ret != CMD_SUCCESS)
2707 goto return_invalid_indent;
2708
2709 /* In case of comment or empty line */
2710 if (vline == NULL) {
2711 if (indent) {
2712 talloc_free(indent);
2713 indent = NULL;
2714 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002715 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002716 }
2717
Neels Hofmeyr43063632017-09-19 23:54:01 +02002718 /* We have a nonempty line. */
2719 if (!vty->indent) {
2720 /* We have just entered a node and expecting the first child to come up; but we
2721 * may also skip right back to a parent or ancestor level. */
2722 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002723
Neels Hofmeyr43063632017-09-19 23:54:01 +02002724 /* If there is no parent, record any indentation we encounter. */
2725 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2726
2727 if (cmp == EINVAL)
2728 goto return_invalid_indent;
2729
2730 if (cmp <= 0) {
2731 /* We have gone right back to the parent level or higher, we are skipping
2732 * this child node level entirely. Pop the parent to go back to a node
2733 * that was actually there (to reinstate vty->indent) and re-use below
2734 * go-parent while-loop to find an accurate match of indent in the node
2735 * ancestry. */
2736 vty_go_parent(vty);
2737 } else {
2738 /* The indent is deeper than the just entered parent, record the new
2739 * indentation characters. */
2740 vty->indent = talloc_strdup(vty, indent);
2741 /* This *is* the new indentation. */
2742 cmp = 0;
2743 }
2744 } else {
2745 /* There is a known indentation for this node level, validate and detect node
2746 * exits. */
2747 cmp = indent_cmp(indent, vty->indent);
2748 if (cmp == EINVAL)
2749 goto return_invalid_indent;
2750 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002751
2752 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2753 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2754 while (cmp < 0) {
2755 vty_go_parent(vty);
2756 cmp = indent_cmp(indent, vty->indent);
2757 if (cmp == EINVAL)
2758 goto return_invalid_indent;
2759 }
2760
2761 /* More indent without having entered a child node level? Either the parent node's indent
2762 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2763 * or the indentation increased even though the vty command didn't enter a child. */
2764 if (cmp > 0)
2765 goto return_invalid_indent;
2766
2767 /* Remember the current node before the command possibly changes it. */
2768 this_node = (struct vty_parent_node){
2769 .node = vty->node,
2770 .priv = vty->priv,
2771 .indent = vty->indent,
2772 };
2773
2774 parent = vty_parent(vty);
2775 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002776 cmd_free_strvec(vline);
2777
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002778 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002779 if (indent) {
2780 talloc_free(indent);
2781 indent = NULL;
2782 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002783 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002784 }
2785
2786 /* If we have stepped down into a child node, push a parent frame.
2787 * The causality is such: we don't expect every single node entry implementation to push
2788 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2789 * a parent node. Hence if the node changed without the parent node changing, we must
2790 * have stepped into a child node (and now expect a deeper indent). */
2791 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2792 /* Push the parent node. */
2793 parent = talloc_zero(vty, struct vty_parent_node);
2794 *parent = this_node;
2795 llist_add(&parent->entry, &vty->parent_nodes);
2796
2797 /* The current talloc'ed vty->indent string will now be owned by this parent
2798 * struct. Indicate that we don't know what deeper indent characters the user
2799 * will choose. */
2800 vty->indent = NULL;
2801 }
2802
2803 if (indent) {
2804 talloc_free(indent);
2805 indent = NULL;
2806 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002807 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002808 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2809 while (vty_parent(vty))
2810 vty_go_parent(vty);
2811
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002812 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002813
2814return_invalid_indent:
2815 if (vline)
2816 cmd_free_strvec(vline);
2817 if (indent) {
2818 talloc_free(indent);
2819 indent = NULL;
2820 }
2821 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002822}
2823
2824/* Configration from terminal */
2825DEFUN(config_terminal,
2826 config_terminal_cmd,
2827 "configure terminal",
2828 "Configuration from vty interface\n" "Configuration terminal\n")
2829{
2830 if (vty_config_lock(vty))
2831 vty->node = CONFIG_NODE;
2832 else {
2833 vty_out(vty, "VTY configuration is locked by other VTY%s",
2834 VTY_NEWLINE);
2835 return CMD_WARNING;
2836 }
2837 return CMD_SUCCESS;
2838}
2839
2840/* Enable command */
2841DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2842{
2843 /* If enable password is NULL, change to ENABLE_NODE */
2844 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2845 vty->type == VTY_SHELL_SERV)
2846 vty->node = ENABLE_NODE;
2847 else
2848 vty->node = AUTH_ENABLE_NODE;
2849
2850 return CMD_SUCCESS;
2851}
2852
2853/* Disable command */
2854DEFUN(disable,
2855 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2856{
2857 if (vty->node == ENABLE_NODE)
2858 vty->node = VIEW_NODE;
2859 return CMD_SUCCESS;
2860}
2861
2862/* Down vty node level. */
2863gDEFUN(config_exit,
2864 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2865{
2866 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002867 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002868 case VIEW_NODE:
2869 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002870 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002871 break;
2872 case CONFIG_NODE:
2873 vty->node = ENABLE_NODE;
2874 vty_config_unlock(vty);
2875 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002876 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002877 if (vty->node > CONFIG_NODE)
2878 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002879 break;
2880 }
2881 return CMD_SUCCESS;
2882}
2883
2884/* End of configuration. */
2885 gDEFUN(config_end,
2886 config_end_cmd, "end", "End current mode and change to enable mode.")
2887{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002888 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002889 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002890
2891 /* Repeatedly call go_parent until a top node is reached. */
2892 while (vty->node > CONFIG_NODE) {
2893 if (vty->node == last_node) {
2894 /* Ensure termination, this shouldn't happen. */
2895 break;
2896 }
2897 last_node = vty->node;
2898 vty_go_parent(vty);
2899 }
2900
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002901 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002902 if (vty->node > ENABLE_NODE)
2903 vty->node = ENABLE_NODE;
2904 vty->index = NULL;
2905 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002906 }
2907 return CMD_SUCCESS;
2908}
2909
2910/* Show version. */
2911DEFUN(show_version,
2912 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2913{
Harald Welte237f6242010-05-25 23:00:45 +02002914 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2915 host.app_info->version,
2916 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2917 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002918
2919 return CMD_SUCCESS;
2920}
2921
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002922DEFUN(show_online_help,
2923 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2924{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002925 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002926 return CMD_SUCCESS;
2927}
2928
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002929/* Help display function for all node. */
2930gDEFUN(config_help,
2931 config_help_cmd, "help", "Description of the interactive help system\n")
2932{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002933 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2934 "anytime at the command line please press '?'.%s%s"
2935 "If nothing matches, the help list will be empty and you must backup%s"
2936 " until entering a '?' shows the available options.%s"
2937 "Two styles of help are provided:%s"
2938 "1. Full help is available when you are ready to enter a%s"
2939 "command argument (e.g. 'show ?') and describes each possible%s"
2940 "argument.%s"
2941 "2. Partial help is provided when an abbreviated argument is entered%s"
2942 " and you want to know what arguments match the input%s"
2943 " (e.g. 'show me?'.)%s%s",
2944 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2945 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2946 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2947 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002948 return CMD_SUCCESS;
2949}
2950
2951/* Help display function for all node. */
2952gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2953{
2954 unsigned int i;
2955 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2956 struct cmd_element *cmd;
2957
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07002958 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
2959 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
2960 continue;
2961 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
2962 continue;
2963 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2964 }
2965
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002966 return CMD_SUCCESS;
2967}
2968
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002969static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002970{
2971 unsigned int i;
2972 int fd;
2973 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002974 char *config_file_tmp = NULL;
2975 char *config_file_sav = NULL;
2976 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002977 struct stat st;
2978
2979 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002980
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002981 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2982 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2983 * manually instead. */
2984
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002985 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002986 config_file_sav =
2987 _talloc_zero(tall_vty_cmd_ctx,
2988 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2989 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002990 if (!config_file_sav)
2991 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002992 strcpy(config_file_sav, config_file);
2993 strcat(config_file_sav, CONF_BACKUP_EXT);
2994
2995 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002996 "config_file_tmp");
2997 if (!config_file_tmp) {
2998 talloc_free(config_file_sav);
2999 return -1;
3000 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003001 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3002
3003 /* Open file to configuration write. */
3004 fd = mkstemp(config_file_tmp);
3005 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003006 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003007 talloc_free(config_file_tmp);
3008 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003009 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003010 }
3011
3012 /* Make vty for configuration file. */
3013 file_vty = vty_new();
3014 file_vty->fd = fd;
3015 file_vty->type = VTY_FILE;
3016
3017 /* Config file header print. */
3018 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003019 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003020 //vty_time_print (file_vty, 1);
3021 vty_out(file_vty, "!\n");
3022
3023 for (i = 0; i < vector_active(cmdvec); i++)
3024 if ((node = vector_slot(cmdvec, i)) && node->func) {
3025 if ((*node->func) (file_vty))
3026 vty_out(file_vty, "!\n");
3027 }
3028 vty_close(file_vty);
3029
3030 if (unlink(config_file_sav) != 0)
3031 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003032 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003033 talloc_free(config_file_sav);
3034 talloc_free(config_file_tmp);
3035 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003036 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003037 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003038
3039 /* Only link the .sav file if the original file exists */
3040 if (stat(config_file, &st) == 0) {
3041 if (link(config_file, config_file_sav) != 0) {
3042 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3043 talloc_free(config_file_sav);
3044 talloc_free(config_file_tmp);
3045 unlink(config_file_tmp);
3046 return -3;
3047 }
3048 sync();
3049 if (unlink(config_file) != 0) {
3050 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3051 talloc_free(config_file_sav);
3052 talloc_free(config_file_tmp);
3053 unlink(config_file_tmp);
3054 return -4;
3055 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003056 }
3057 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003058 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003059 talloc_free(config_file_sav);
3060 talloc_free(config_file_tmp);
3061 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003062 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003063 }
3064 unlink(config_file_tmp);
3065 sync();
3066
3067 talloc_free(config_file_sav);
3068 talloc_free(config_file_tmp);
3069
3070 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003071 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3072 return -6;
3073 }
3074
3075 return 0;
3076}
3077
3078
3079/* Write current configuration into file. */
3080DEFUN(config_write_file,
3081 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003082 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003083 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003084 "Write to configuration file\n"
3085 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003086{
3087 char *failed_file;
3088 int rc;
3089
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003090 if (host.app_info->config_is_consistent) {
3091 rc = host.app_info->config_is_consistent(vty);
3092 if (!rc) {
3093 vty_out(vty, "Configuration is not consistent%s",
3094 VTY_NEWLINE);
3095 return CMD_WARNING;
3096 }
3097 }
3098
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003099 if (argc == 1)
3100 host_config_set(argv[0]);
3101
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003102 if (host.config == NULL) {
3103 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3104 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003105 return CMD_WARNING;
3106 }
3107
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003108 rc = write_config_file(host.config, &failed_file);
3109 switch (rc) {
3110 case -1:
3111 vty_out(vty, "Can't open configuration file %s.%s",
3112 failed_file, VTY_NEWLINE);
3113 rc = CMD_WARNING;
3114 break;
3115 case -2:
3116 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3117 failed_file, VTY_NEWLINE);
3118 rc = CMD_WARNING;
3119 break;
3120 case -3:
3121 vty_out(vty, "Can't backup old configuration file %s.%s",
3122 failed_file, VTY_NEWLINE);
3123 rc = CMD_WARNING;
3124 break;
3125 case -4:
3126 vty_out(vty, "Can't unlink configuration file %s.%s",
3127 failed_file, VTY_NEWLINE);
3128 rc = CMD_WARNING;
3129 break;
3130 case -5:
3131 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3132 VTY_NEWLINE);
3133 rc = CMD_WARNING;
3134 break;
3135 case -6:
3136 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3137 failed_file, strerror(errno), errno, VTY_NEWLINE);
3138 rc = CMD_WARNING;
3139 break;
3140 default:
3141 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3142 rc = CMD_SUCCESS;
3143 break;
3144 }
3145
3146 talloc_free(failed_file);
3147 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003148}
3149
3150ALIAS(config_write_file,
3151 config_write_cmd,
3152 "write", "Write running configuration to memory, network, or terminal\n")
3153
3154 ALIAS(config_write_file,
3155 config_write_memory_cmd,
3156 "write memory",
3157 "Write running configuration to memory, network, or terminal\n"
3158 "Write configuration to the file (same as write file)\n")
3159
3160 ALIAS(config_write_file,
3161 copy_runningconfig_startupconfig_cmd,
3162 "copy running-config startup-config",
3163 "Copy configuration\n"
3164 "Copy running config to... \n"
3165 "Copy running config to startup config (same as write file)\n")
3166
3167/* Write current configuration into the terminal. */
3168 DEFUN(config_write_terminal,
3169 config_write_terminal_cmd,
3170 "write terminal",
3171 "Write running configuration to memory, network, or terminal\n"
3172 "Write to terminal\n")
3173{
3174 unsigned int i;
3175 struct cmd_node *node;
3176
3177 if (vty->type == VTY_SHELL_SERV) {
3178 for (i = 0; i < vector_active(cmdvec); i++)
3179 if ((node = vector_slot(cmdvec, i)) && node->func
3180 && node->vtysh) {
3181 if ((*node->func) (vty))
3182 vty_out(vty, "!%s", VTY_NEWLINE);
3183 }
3184 } else {
3185 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3186 VTY_NEWLINE);
3187 vty_out(vty, "!%s", VTY_NEWLINE);
3188
3189 for (i = 0; i < vector_active(cmdvec); i++)
3190 if ((node = vector_slot(cmdvec, i)) && node->func) {
3191 if ((*node->func) (vty))
3192 vty_out(vty, "!%s", VTY_NEWLINE);
3193 }
3194 vty_out(vty, "end%s", VTY_NEWLINE);
3195 }
3196 return CMD_SUCCESS;
3197}
3198
3199/* Write current configuration into the terminal. */
3200ALIAS(config_write_terminal,
3201 show_running_config_cmd,
3202 "show running-config", SHOW_STR "running configuration\n")
3203
3204/* Write startup configuration into the terminal. */
3205 DEFUN(show_startup_config,
3206 show_startup_config_cmd,
3207 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3208{
3209 char buf[BUFSIZ];
3210 FILE *confp;
3211
3212 confp = fopen(host.config, "r");
3213 if (confp == NULL) {
3214 vty_out(vty, "Can't open configuration file [%s]%s",
3215 host.config, VTY_NEWLINE);
3216 return CMD_WARNING;
3217 }
3218
3219 while (fgets(buf, BUFSIZ, confp)) {
3220 char *cp = buf;
3221
3222 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3223 cp++;
3224 *cp = '\0';
3225
3226 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3227 }
3228
3229 fclose(confp);
3230
3231 return CMD_SUCCESS;
3232}
3233
3234/* Hostname configuration */
3235DEFUN(config_hostname,
3236 hostname_cmd,
3237 "hostname WORD",
3238 "Set system's network name\n" "This system's network name\n")
3239{
3240 if (!isalpha((int)*argv[0])) {
3241 vty_out(vty, "Please specify string starting with alphabet%s",
3242 VTY_NEWLINE);
3243 return CMD_WARNING;
3244 }
3245
3246 if (host.name)
3247 talloc_free(host.name);
3248
3249 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3250 return CMD_SUCCESS;
3251}
3252
3253DEFUN(config_no_hostname,
3254 no_hostname_cmd,
3255 "no hostname [HOSTNAME]",
3256 NO_STR "Reset system's network name\n" "Host name of this router\n")
3257{
3258 if (host.name)
3259 talloc_free(host.name);
3260 host.name = NULL;
3261 return CMD_SUCCESS;
3262}
3263
3264/* VTY interface password set. */
3265DEFUN(config_password, password_cmd,
3266 "password (8|) WORD",
3267 "Assign the terminal connection password\n"
3268 "Specifies a HIDDEN password will follow\n"
3269 "dummy string \n" "The HIDDEN line password string\n")
3270{
3271 /* Argument check. */
3272 if (argc == 0) {
3273 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3274 return CMD_WARNING;
3275 }
3276
3277 if (argc == 2) {
3278 if (*argv[0] == '8') {
3279 if (host.password)
3280 talloc_free(host.password);
3281 host.password = NULL;
3282 if (host.password_encrypt)
3283 talloc_free(host.password_encrypt);
3284 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3285 return CMD_SUCCESS;
3286 } else {
3287 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3288 return CMD_WARNING;
3289 }
3290 }
3291
3292 if (!isalnum((int)*argv[0])) {
3293 vty_out(vty,
3294 "Please specify string starting with alphanumeric%s",
3295 VTY_NEWLINE);
3296 return CMD_WARNING;
3297 }
3298
3299 if (host.password)
3300 talloc_free(host.password);
3301 host.password = NULL;
3302
3303#ifdef VTY_CRYPT_PW
3304 if (host.encrypt) {
3305 if (host.password_encrypt)
3306 talloc_free(host.password_encrypt);
3307 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3308 } else
3309#endif
3310 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3311
3312 return CMD_SUCCESS;
3313}
3314
3315ALIAS(config_password, password_text_cmd,
3316 "password LINE",
3317 "Assign the terminal connection password\n"
3318 "The UNENCRYPTED (cleartext) line password\n")
3319
3320/* VTY enable password set. */
3321 DEFUN(config_enable_password, enable_password_cmd,
3322 "enable password (8|) WORD",
3323 "Modify enable password parameters\n"
3324 "Assign the privileged level password\n"
3325 "Specifies a HIDDEN password will follow\n"
3326 "dummy string \n" "The HIDDEN 'enable' password string\n")
3327{
3328 /* Argument check. */
3329 if (argc == 0) {
3330 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3331 return CMD_WARNING;
3332 }
3333
3334 /* Crypt type is specified. */
3335 if (argc == 2) {
3336 if (*argv[0] == '8') {
3337 if (host.enable)
3338 talloc_free(host.enable);
3339 host.enable = NULL;
3340
3341 if (host.enable_encrypt)
3342 talloc_free(host.enable_encrypt);
3343 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3344
3345 return CMD_SUCCESS;
3346 } else {
3347 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3348 return CMD_WARNING;
3349 }
3350 }
3351
3352 if (!isalnum((int)*argv[0])) {
3353 vty_out(vty,
3354 "Please specify string starting with alphanumeric%s",
3355 VTY_NEWLINE);
3356 return CMD_WARNING;
3357 }
3358
3359 if (host.enable)
3360 talloc_free(host.enable);
3361 host.enable = NULL;
3362
3363 /* Plain password input. */
3364#ifdef VTY_CRYPT_PW
3365 if (host.encrypt) {
3366 if (host.enable_encrypt)
3367 talloc_free(host.enable_encrypt);
3368 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3369 } else
3370#endif
3371 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3372
3373 return CMD_SUCCESS;
3374}
3375
3376ALIAS(config_enable_password,
3377 enable_password_text_cmd,
3378 "enable password LINE",
3379 "Modify enable password parameters\n"
3380 "Assign the privileged level password\n"
3381 "The UNENCRYPTED (cleartext) 'enable' password\n")
3382
3383/* VTY enable password delete. */
3384 DEFUN(no_config_enable_password, no_enable_password_cmd,
3385 "no enable password",
3386 NO_STR
3387 "Modify enable password parameters\n"
3388 "Assign the privileged level password\n")
3389{
3390 if (host.enable)
3391 talloc_free(host.enable);
3392 host.enable = NULL;
3393
3394 if (host.enable_encrypt)
3395 talloc_free(host.enable_encrypt);
3396 host.enable_encrypt = NULL;
3397
3398 return CMD_SUCCESS;
3399}
3400
3401#ifdef VTY_CRYPT_PW
3402DEFUN(service_password_encrypt,
3403 service_password_encrypt_cmd,
3404 "service password-encryption",
3405 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3406{
3407 if (host.encrypt)
3408 return CMD_SUCCESS;
3409
3410 host.encrypt = 1;
3411
3412 if (host.password) {
3413 if (host.password_encrypt)
3414 talloc_free(host.password_encrypt);
3415 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3416 }
3417 if (host.enable) {
3418 if (host.enable_encrypt)
3419 talloc_free(host.enable_encrypt);
3420 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3421 }
3422
3423 return CMD_SUCCESS;
3424}
3425
3426DEFUN(no_service_password_encrypt,
3427 no_service_password_encrypt_cmd,
3428 "no service password-encryption",
3429 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3430{
3431 if (!host.encrypt)
3432 return CMD_SUCCESS;
3433
3434 host.encrypt = 0;
3435
3436 if (host.password_encrypt)
3437 talloc_free(host.password_encrypt);
3438 host.password_encrypt = NULL;
3439
3440 if (host.enable_encrypt)
3441 talloc_free(host.enable_encrypt);
3442 host.enable_encrypt = NULL;
3443
3444 return CMD_SUCCESS;
3445}
3446#endif
3447
3448DEFUN(config_terminal_length, config_terminal_length_cmd,
3449 "terminal length <0-512>",
3450 "Set terminal line parameters\n"
3451 "Set number of lines on a screen\n"
3452 "Number of lines on screen (0 for no pausing)\n")
3453{
3454 int lines;
3455 char *endptr = NULL;
3456
3457 lines = strtol(argv[0], &endptr, 10);
3458 if (lines < 0 || lines > 512 || *endptr != '\0') {
3459 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3460 return CMD_WARNING;
3461 }
3462 vty->lines = lines;
3463
3464 return CMD_SUCCESS;
3465}
3466
3467DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3468 "terminal no length",
3469 "Set terminal line parameters\n"
3470 NO_STR "Set number of lines on a screen\n")
3471{
3472 vty->lines = -1;
3473 return CMD_SUCCESS;
3474}
3475
3476DEFUN(service_terminal_length, service_terminal_length_cmd,
3477 "service terminal-length <0-512>",
3478 "Set up miscellaneous service\n"
3479 "System wide terminal length configuration\n"
3480 "Number of lines of VTY (0 means no line control)\n")
3481{
3482 int lines;
3483 char *endptr = NULL;
3484
3485 lines = strtol(argv[0], &endptr, 10);
3486 if (lines < 0 || lines > 512 || *endptr != '\0') {
3487 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3488 return CMD_WARNING;
3489 }
3490 host.lines = lines;
3491
3492 return CMD_SUCCESS;
3493}
3494
3495DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3496 "no service terminal-length [<0-512>]",
3497 NO_STR
3498 "Set up miscellaneous service\n"
3499 "System wide terminal length configuration\n"
3500 "Number of lines of VTY (0 means no line control)\n")
3501{
3502 host.lines = -1;
3503 return CMD_SUCCESS;
3504}
3505
3506DEFUN_HIDDEN(do_echo,
3507 echo_cmd,
3508 "echo .MESSAGE",
3509 "Echo a message back to the vty\n" "The message to echo\n")
3510{
3511 char *message;
3512
3513 vty_out(vty, "%s%s",
3514 ((message =
3515 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3516 if (message)
3517 talloc_free(message);
3518 return CMD_SUCCESS;
3519}
3520
3521#if 0
3522DEFUN(config_logmsg,
3523 config_logmsg_cmd,
3524 "logmsg " LOG_LEVELS " .MESSAGE",
3525 "Send a message to enabled logging destinations\n"
3526 LOG_LEVEL_DESC "The message to send\n")
3527{
3528 int level;
3529 char *message;
3530
3531 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3532 return CMD_ERR_NO_MATCH;
3533
3534 zlog(NULL, level,
3535 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3536 if (message)
3537 talloc_free(message);
3538 return CMD_SUCCESS;
3539}
3540
3541DEFUN(show_logging,
3542 show_logging_cmd,
3543 "show logging", SHOW_STR "Show current logging configuration\n")
3544{
3545 struct zlog *zl = zlog_default;
3546
3547 vty_out(vty, "Syslog logging: ");
3548 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3549 vty_out(vty, "disabled");
3550 else
3551 vty_out(vty, "level %s, facility %s, ident %s",
3552 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3553 facility_name(zl->facility), zl->ident);
3554 vty_out(vty, "%s", VTY_NEWLINE);
3555
3556 vty_out(vty, "Stdout logging: ");
3557 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3558 vty_out(vty, "disabled");
3559 else
3560 vty_out(vty, "level %s",
3561 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3562 vty_out(vty, "%s", VTY_NEWLINE);
3563
3564 vty_out(vty, "Monitor logging: ");
3565 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3566 vty_out(vty, "disabled");
3567 else
3568 vty_out(vty, "level %s",
3569 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3570 vty_out(vty, "%s", VTY_NEWLINE);
3571
3572 vty_out(vty, "File logging: ");
3573 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3574 vty_out(vty, "disabled");
3575 else
3576 vty_out(vty, "level %s, filename %s",
3577 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3578 zl->filename);
3579 vty_out(vty, "%s", VTY_NEWLINE);
3580
3581 vty_out(vty, "Protocol name: %s%s",
3582 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3583 vty_out(vty, "Record priority: %s%s",
3584 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3585
3586 return CMD_SUCCESS;
3587}
3588
3589DEFUN(config_log_stdout,
3590 config_log_stdout_cmd,
3591 "log stdout", "Logging control\n" "Set stdout logging level\n")
3592{
3593 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3594 return CMD_SUCCESS;
3595}
3596
3597DEFUN(config_log_stdout_level,
3598 config_log_stdout_level_cmd,
3599 "log stdout " LOG_LEVELS,
3600 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3601{
3602 int level;
3603
3604 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3605 return CMD_ERR_NO_MATCH;
3606 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3607 return CMD_SUCCESS;
3608}
3609
3610DEFUN(no_config_log_stdout,
3611 no_config_log_stdout_cmd,
3612 "no log stdout [LEVEL]",
3613 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3614{
3615 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3616 return CMD_SUCCESS;
3617}
3618
3619DEFUN(config_log_monitor,
3620 config_log_monitor_cmd,
3621 "log monitor",
3622 "Logging control\n" "Set terminal line (monitor) logging level\n")
3623{
3624 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3625 return CMD_SUCCESS;
3626}
3627
3628DEFUN(config_log_monitor_level,
3629 config_log_monitor_level_cmd,
3630 "log monitor " LOG_LEVELS,
3631 "Logging control\n"
3632 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3633{
3634 int level;
3635
3636 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3637 return CMD_ERR_NO_MATCH;
3638 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3639 return CMD_SUCCESS;
3640}
3641
3642DEFUN(no_config_log_monitor,
3643 no_config_log_monitor_cmd,
3644 "no log monitor [LEVEL]",
3645 NO_STR
3646 "Logging control\n"
3647 "Disable terminal line (monitor) logging\n" "Logging level\n")
3648{
3649 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3650 return CMD_SUCCESS;
3651}
3652
3653static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3654{
3655 int ret;
3656 char *p = NULL;
3657 const char *fullpath;
3658
3659 /* Path detection. */
3660 if (!IS_DIRECTORY_SEP(*fname)) {
3661 char cwd[MAXPATHLEN + 1];
3662 cwd[MAXPATHLEN] = '\0';
3663
3664 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3665 zlog_err("config_log_file: Unable to alloc mem!");
3666 return CMD_WARNING;
3667 }
3668
3669 if ((p = _talloc_zero(tall_vcmd_ctx,
3670 strlen(cwd) + strlen(fname) + 2),
3671 "set_log_file")
3672 == NULL) {
3673 zlog_err("config_log_file: Unable to alloc mem!");
3674 return CMD_WARNING;
3675 }
3676 sprintf(p, "%s/%s", cwd, fname);
3677 fullpath = p;
3678 } else
3679 fullpath = fname;
3680
3681 ret = zlog_set_file(NULL, fullpath, loglevel);
3682
3683 if (p)
3684 talloc_free(p);
3685
3686 if (!ret) {
3687 vty_out(vty, "can't open logfile %s\n", fname);
3688 return CMD_WARNING;
3689 }
3690
3691 if (host.logfile)
3692 talloc_free(host.logfile);
3693
3694 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3695
3696 return CMD_SUCCESS;
3697}
3698
3699DEFUN(config_log_file,
3700 config_log_file_cmd,
3701 "log file FILENAME",
3702 "Logging control\n" "Logging to file\n" "Logging filename\n")
3703{
3704 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3705}
3706
3707DEFUN(config_log_file_level,
3708 config_log_file_level_cmd,
3709 "log file FILENAME " LOG_LEVELS,
3710 "Logging control\n"
3711 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3712{
3713 int level;
3714
3715 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3716 return CMD_ERR_NO_MATCH;
3717 return set_log_file(vty, argv[0], level);
3718}
3719
3720DEFUN(no_config_log_file,
3721 no_config_log_file_cmd,
3722 "no log file [FILENAME]",
3723 NO_STR
3724 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3725{
3726 zlog_reset_file(NULL);
3727
3728 if (host.logfile)
3729 talloc_free(host.logfile);
3730
3731 host.logfile = NULL;
3732
3733 return CMD_SUCCESS;
3734}
3735
3736ALIAS(no_config_log_file,
3737 no_config_log_file_level_cmd,
3738 "no log file FILENAME LEVEL",
3739 NO_STR
3740 "Logging control\n"
3741 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3742
3743 DEFUN(config_log_syslog,
3744 config_log_syslog_cmd,
3745 "log syslog", "Logging control\n" "Set syslog logging level\n")
3746{
3747 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3748 return CMD_SUCCESS;
3749}
3750
3751DEFUN(config_log_syslog_level,
3752 config_log_syslog_level_cmd,
3753 "log syslog " LOG_LEVELS,
3754 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3755{
3756 int level;
3757
3758 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3759 return CMD_ERR_NO_MATCH;
3760 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3761 return CMD_SUCCESS;
3762}
3763
3764DEFUN_DEPRECATED(config_log_syslog_facility,
3765 config_log_syslog_facility_cmd,
3766 "log syslog facility " LOG_FACILITIES,
3767 "Logging control\n"
3768 "Logging goes to syslog\n"
3769 "(Deprecated) Facility parameter for syslog messages\n"
3770 LOG_FACILITY_DESC)
3771{
3772 int facility;
3773
3774 if ((facility = facility_match(argv[0])) < 0)
3775 return CMD_ERR_NO_MATCH;
3776
3777 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3778 zlog_default->facility = facility;
3779 return CMD_SUCCESS;
3780}
3781
3782DEFUN(no_config_log_syslog,
3783 no_config_log_syslog_cmd,
3784 "no log syslog [LEVEL]",
3785 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3786{
3787 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3788 return CMD_SUCCESS;
3789}
3790
3791ALIAS(no_config_log_syslog,
3792 no_config_log_syslog_facility_cmd,
3793 "no log syslog facility " LOG_FACILITIES,
3794 NO_STR
3795 "Logging control\n"
3796 "Logging goes to syslog\n"
3797 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3798
3799 DEFUN(config_log_facility,
3800 config_log_facility_cmd,
3801 "log facility " LOG_FACILITIES,
3802 "Logging control\n"
3803 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3804{
3805 int facility;
3806
3807 if ((facility = facility_match(argv[0])) < 0)
3808 return CMD_ERR_NO_MATCH;
3809 zlog_default->facility = facility;
3810 return CMD_SUCCESS;
3811}
3812
3813DEFUN(no_config_log_facility,
3814 no_config_log_facility_cmd,
3815 "no log facility [FACILITY]",
3816 NO_STR
3817 "Logging control\n"
3818 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3819{
3820 zlog_default->facility = LOG_DAEMON;
3821 return CMD_SUCCESS;
3822}
3823
3824DEFUN_DEPRECATED(config_log_trap,
3825 config_log_trap_cmd,
3826 "log trap " LOG_LEVELS,
3827 "Logging control\n"
3828 "(Deprecated) Set logging level and default for all destinations\n"
3829 LOG_LEVEL_DESC)
3830{
3831 int new_level;
3832 int i;
3833
3834 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3835 return CMD_ERR_NO_MATCH;
3836
3837 zlog_default->default_lvl = new_level;
3838 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3839 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3840 zlog_default->maxlvl[i] = new_level;
3841 return CMD_SUCCESS;
3842}
3843
3844DEFUN_DEPRECATED(no_config_log_trap,
3845 no_config_log_trap_cmd,
3846 "no log trap [LEVEL]",
3847 NO_STR
3848 "Logging control\n"
3849 "Permit all logging information\n" "Logging level\n")
3850{
3851 zlog_default->default_lvl = LOG_DEBUG;
3852 return CMD_SUCCESS;
3853}
3854
3855DEFUN(config_log_record_priority,
3856 config_log_record_priority_cmd,
3857 "log record-priority",
3858 "Logging control\n"
3859 "Log the priority of the message within the message\n")
3860{
3861 zlog_default->record_priority = 1;
3862 return CMD_SUCCESS;
3863}
3864
3865DEFUN(no_config_log_record_priority,
3866 no_config_log_record_priority_cmd,
3867 "no log record-priority",
3868 NO_STR
3869 "Logging control\n"
3870 "Do not log the priority of the message within the message\n")
3871{
3872 zlog_default->record_priority = 0;
3873 return CMD_SUCCESS;
3874}
3875#endif
3876
3877DEFUN(banner_motd_file,
3878 banner_motd_file_cmd,
3879 "banner motd file [FILE]",
3880 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3881{
3882 if (host.motdfile)
3883 talloc_free(host.motdfile);
3884 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3885
3886 return CMD_SUCCESS;
3887}
3888
3889DEFUN(banner_motd_default,
3890 banner_motd_default_cmd,
3891 "banner motd default",
3892 "Set banner string\n" "Strings for motd\n" "Default string\n")
3893{
3894 host.motd = default_motd;
3895 return CMD_SUCCESS;
3896}
3897
3898DEFUN(no_banner_motd,
3899 no_banner_motd_cmd,
3900 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3901{
3902 host.motd = NULL;
3903 if (host.motdfile)
3904 talloc_free(host.motdfile);
3905 host.motdfile = NULL;
3906 return CMD_SUCCESS;
3907}
3908
3909/* Set config filename. Called from vty.c */
3910void host_config_set(const char *filename)
3911{
3912 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3913}
3914
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003915/*! Deprecated, now happens implicitly when calling install_node().
3916 * Users of the API may still attempt to call this function, hence
3917 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003918void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003919{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003920}
3921
3922/*! Deprecated, now happens implicitly when calling install_node().
3923 * Users of the API may still attempt to call this function, hence
3924 * leave it here as a no-op. */
3925void vty_install_default(int node)
3926{
3927}
3928
3929/*! Install common commands like 'exit' and 'list'. */
3930static void install_basic_node_commands(int node)
3931{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003932 install_lib_element(node, &config_help_cmd);
3933 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003934
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003935 install_lib_element(node, &config_write_terminal_cmd);
3936 install_lib_element(node, &config_write_file_cmd);
3937 install_lib_element(node, &config_write_memory_cmd);
3938 install_lib_element(node, &config_write_cmd);
3939 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003940
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003941 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003942
3943 if (node >= CONFIG_NODE) {
3944 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07003945 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003946 }
3947}
3948
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003949/*! Return true if a node is installed by install_basic_node_commands(), so
3950 * that we can avoid repeating them for each and every node during 'show
3951 * running-config' */
3952static bool vty_command_is_common(struct cmd_element *cmd)
3953{
3954 if (cmd == &config_help_cmd
3955 || cmd == &config_list_cmd
3956 || cmd == &config_write_terminal_cmd
3957 || cmd == &config_write_file_cmd
3958 || cmd == &config_write_memory_cmd
3959 || cmd == &config_write_cmd
3960 || cmd == &show_running_config_cmd
3961 || cmd == &config_exit_cmd
3962 || cmd == &config_end_cmd)
3963 return true;
3964 return false;
3965}
3966
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003967/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003968 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003969 * \param[in] vty the vty of the code
3970 * \param[in] filename where to store the file
3971 * \return 0 in case of success.
3972 *
3973 * If the filename already exists create a filename.sav
3974 * version with the current code.
3975 *
3976 */
3977int osmo_vty_write_config_file(const char *filename)
3978{
3979 char *failed_file;
3980 int rc;
3981
3982 rc = write_config_file(filename, &failed_file);
3983 talloc_free(failed_file);
3984 return rc;
3985}
3986
3987/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003988 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003989 * \return 0 in case of success.
3990 *
3991 * If the filename already exists create a filename.sav
3992 * version with the current code.
3993 *
3994 */
3995int osmo_vty_save_config_file(void)
3996{
3997 char *failed_file;
3998 int rc;
3999
4000 if (host.config == NULL)
4001 return -7;
4002
4003 rc = write_config_file(host.config, &failed_file);
4004 talloc_free(failed_file);
4005 return rc;
4006}
4007
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004008/* Initialize command interface. Install basic nodes and commands. */
4009void cmd_init(int terminal)
4010{
4011 /* Allocate initial top vector of commands. */
4012 cmdvec = vector_init(VECTOR_MIN_SIZE);
4013
4014 /* Default host value settings. */
4015 host.name = NULL;
4016 host.password = NULL;
4017 host.enable = NULL;
4018 host.logfile = NULL;
4019 host.config = NULL;
4020 host.lines = -1;
4021 host.motd = default_motd;
4022 host.motdfile = NULL;
4023
4024 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004025 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004026 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004027 install_node_bare(&auth_node, NULL);
4028 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004029 install_node(&config_node, config_write_host);
4030
4031 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004032 install_lib_element(VIEW_NODE, &show_version_cmd);
4033 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004034 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004035 install_lib_element(VIEW_NODE, &config_list_cmd);
4036 install_lib_element(VIEW_NODE, &config_exit_cmd);
4037 install_lib_element(VIEW_NODE, &config_help_cmd);
4038 install_lib_element(VIEW_NODE, &config_enable_cmd);
4039 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4040 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4041 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004042 }
4043
4044 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004045 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4046 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4047 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004048 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004049 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4050 install_lib_element(ENABLE_NODE, &show_version_cmd);
4051 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004052
4053 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004054 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4055 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4056 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004057 }
4058
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004059 install_lib_element(CONFIG_NODE, &hostname_cmd);
4060 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004061
4062 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004063 install_lib_element(CONFIG_NODE, &password_cmd);
4064 install_lib_element(CONFIG_NODE, &password_text_cmd);
4065 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4066 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4067 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004068
4069#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004070 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4071 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004072#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004073 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4074 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4075 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4076 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4077 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004078
4079 }
4080 srand(time(NULL));
4081}
Harald Welte7acb30c2011-08-17 17:13:48 +02004082
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004083/* FIXME: execute this section in the unit test instead */
4084static __attribute__((constructor)) void on_dso_load(void)
4085{
4086 unsigned int i, j;
4087
4088 /* Check total number of the library specific attributes */
4089 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4090
4091 /* Check for duplicates in the list of library specific flags */
4092 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4093 if (cmd_lib_attr_letters[i] == '\0')
4094 continue;
4095
4096 /* Only upper case flag letters are allowed for libraries */
4097 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4098 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4099
4100 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4101 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4102 }
4103}
4104
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004105/*! @} */