blob: 0edfce5a05d092a338c72360b6ead91df124f729 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020041#include <sys/time.h>
42#include <sys/stat.h>
43
44#include <osmocom/vty/vector.h>
45#include <osmocom/vty/vty.h>
46#include <osmocom/vty/command.h>
47
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010048#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010049#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050
Ruben Undheim766f77c2018-11-18 13:02:47 +010051#ifndef MAXPATHLEN
52 #define MAXPATHLEN 4096
53#endif
54
55
Harald Weltee881b1b2011-08-17 18:52:30 +020056/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020057 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020059 *
60 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020061
Harald Welte3fb0b6f2010-05-19 19:02:52 +020062#define CONFIGFILE_MASK 022
63
64void *tall_vty_cmd_ctx;
65
66/* Command vector which includes some level of command lists. Normally
67 each daemon maintains each own cmdvec. */
68vector cmdvec;
69
70/* Host information structure. */
71struct host host;
72
73/* Standard command node structures. */
74struct cmd_node auth_node = {
75 AUTH_NODE,
76 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node view_node = {
81 VIEW_NODE,
82 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node auth_enable_node = {
87 AUTH_ENABLE_NODE,
88 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node enable_node = {
93 ENABLE_NODE,
94 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010095 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096};
97
98struct cmd_node config_node = {
99 CONFIG_NODE,
100 "%s(config)# ",
101 1
102};
103
104/* Default motd string. */
105const char *default_motd = "";
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200108 *
109 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110void print_version(int print_copyright)
111{
Harald Welte237f6242010-05-25 23:00:45 +0200112 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200114 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115}
116
117/* Utility function to concatenate argv argument into a single string
118 with inserting ' ' character between each argument. */
119char *argv_concat(const char **argv, int argc, int shift)
120{
121 int i;
122 size_t len;
123 char *str;
124 char *p;
125
126 len = 0;
127 for (i = shift; i < argc; i++)
128 len += strlen(argv[i]) + 1;
129 if (!len)
130 return NULL;
131 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
132 for (i = shift; i < argc; i++) {
133 size_t arglen;
134 memcpy(p, argv[i], (arglen = strlen(argv[i])));
135 p += arglen;
136 *p++ = ' ';
137 }
138 *(p - 1) = '\0';
139 return str;
140}
141
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200142/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
143 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
144 * in turn, this name us used for XML IDs in 'show online-help'. */
145static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
146{
147 const char *pos;
148 int dest = 0;
149
150 if (!prompt || !*prompt)
151 return "";
152
153 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
154 if (pos[0] == '%' && pos[1]) {
155 /* skip "%s"; loop pos++ does the second one. */
156 pos++;
157 continue;
158 }
159 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
160 continue;
161 name_buf[dest] = pos[0];
162 dest++;
163 }
164 name_buf[dest] = '\0';
165 return name_buf;
166}
167
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200168static void install_basic_node_commands(int node);
169
170/*! Install top node of command vector, without adding basic node commands. */
171static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172{
173 vector_set_index(cmdvec, node->node, node);
174 node->func = func;
175 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200176 if (!*node->name)
177 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200178}
179
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200180/*! Install top node of command vector. */
181void install_node(struct cmd_node *node, int (*func) (struct vty *))
182{
183 install_node_bare(node, func);
184 install_basic_node_commands(node->node);
185}
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187/* Compare two command's string. Used in sort_node (). */
188static int cmp_node(const void *p, const void *q)
189{
190 struct cmd_element *a = *(struct cmd_element **)p;
191 struct cmd_element *b = *(struct cmd_element **)q;
192
193 return strcmp(a->string, b->string);
194}
195
196static int cmp_desc(const void *p, const void *q)
197{
198 struct desc *a = *(struct desc **)p;
199 struct desc *b = *(struct desc **)q;
200
201 return strcmp(a->cmd, b->cmd);
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200205void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200206{
207 unsigned int i, j;
208 struct cmd_node *cnode;
209 vector descvec;
210 struct cmd_element *cmd_element;
211
212 for (i = 0; i < vector_active(cmdvec); i++)
213 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
214 vector cmd_vector = cnode->cmd_vector;
215 qsort(cmd_vector->index, vector_active(cmd_vector),
216 sizeof(void *), cmp_node);
217
218 for (j = 0; j < vector_active(cmd_vector); j++)
219 if ((cmd_element =
220 vector_slot(cmd_vector, j)) != NULL
221 && vector_active(cmd_element->strvec)) {
222 descvec =
223 vector_slot(cmd_element->strvec,
224 vector_active
225 (cmd_element->strvec) -
226 1);
227 qsort(descvec->index,
228 vector_active(descvec),
229 sizeof(void *), cmp_desc);
230 }
231 }
232}
233
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200234/*! Break up string in command tokens. Return leading indents.
235 * \param[in] string String to split.
236 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
237 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
238 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
239 *
240 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
241 * so that \a indent can simply return the count of leading spaces.
242 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
243 */
244int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245{
246 const char *cp, *start;
247 char *token;
248 int strlen;
249 vector strvec;
250
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200251 *strvec_p = NULL;
252 if (indent)
253 *indent = 0;
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200257
258 cp = string;
259
260 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200261 while (isspace((int)*cp) && *cp != '\0') {
262 /* if we're counting indents, we need to be strict about them */
263 if (indent && (*cp != ' ') && (*cp != '\t')) {
264 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
265 if (*cp == '\n' || *cp == '\r') {
266 cp++;
267 string = cp;
268 continue;
269 }
270 return CMD_ERR_INVALID_INDENT;
271 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 }
274
275 if (indent)
276 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277
278 /* Return if there is only white spaces */
279 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Prepare return vector. */
286 strvec = vector_init(VECTOR_MIN_SIZE);
287
288 /* Copy each command piece and set into vector. */
289 while (1) {
290 start = cp;
291 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
292 *cp != '\0')
293 cp++;
294 strlen = cp - start;
295 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
296 memcpy(token, start, strlen);
297 *(token + strlen) = '\0';
298 vector_set(strvec, token);
299
300 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
301 *cp != '\0')
302 cp++;
303
304 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200307
308 *strvec_p = strvec;
309 return CMD_SUCCESS;
310}
311
312/*! Breaking up string into each command piece. I assume given
313 character is separated by a space character. Return value is a
314 vector which includes char ** data element. */
315vector cmd_make_strvec(const char *string)
316{
317 vector strvec;
318 cmd_make_strvec2(string, NULL, &strvec);
319 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200320}
321
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200322/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200323void cmd_free_strvec(vector v)
324{
325 unsigned int i;
326 char *cp;
327
328 if (!v)
329 return;
330
331 for (i = 0; i < vector_active(v); i++)
332 if ((cp = vector_slot(v, i)) != NULL)
333 talloc_free(cp);
334
335 vector_free(v);
336}
337
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200338/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200339static char *cmd_desc_str(const char **string)
340{
341 const char *cp, *start;
342 char *token;
343 int strlen;
344
345 cp = *string;
346
347 if (cp == NULL)
348 return NULL;
349
350 /* Skip white spaces. */
351 while (isspace((int)*cp) && *cp != '\0')
352 cp++;
353
354 /* Return if there is only white spaces */
355 if (*cp == '\0')
356 return NULL;
357
358 start = cp;
359
360 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
361 cp++;
362
363 strlen = cp - start;
364 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
365 memcpy(token, start, strlen);
366 *(token + strlen) = '\0';
367
368 *string = cp;
369
370 return token;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200374static vector cmd_make_descvec(const char *string, const char *descstr)
375{
376 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100377 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378 const char *sp;
379 char *token;
380 int len;
381 const char *cp;
382 const char *dp;
383 vector allvec;
384 vector strvec = NULL;
385 struct desc *desc;
386
387 cp = string;
388 dp = descstr;
389
390 if (cp == NULL)
391 return NULL;
392
393 allvec = vector_init(VECTOR_MIN_SIZE);
394
395 while (1) {
396 while (isspace((int)*cp) && *cp != '\0')
397 cp++;
398
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
400 if (cp[0] == '[' && cp[1] == '(') {
401 optional_brace = 1;
402 cp++;
403 }
404
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200405 if (*cp == '(') {
406 multiple = 1;
407 cp++;
408 }
409 if (*cp == ')') {
410 multiple = 0;
411 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100412 if (*cp == ']')
413 cp++;
414 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 }
416 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100417 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 cp++;
419 }
420
421 while (isspace((int)*cp) && *cp != '\0')
422 cp++;
423
424 if (*cp == '(') {
425 multiple = 1;
426 cp++;
427 }
428
429 if (*cp == '\0')
430 return allvec;
431
432 sp = cp;
433
434 while (!
435 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
436 || *cp == ')' || *cp == '|') && *cp != '\0')
437 cp++;
438
439 len = cp - sp;
440
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100441 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
442 if (optional_brace) {
443 /* Place each individual multi-choice token in its own square braces */
444 token[0] = '[';
445 memcpy(token + 1, sp, len);
446 token[1 + len] = ']';
447 token[2 + len] = '\0';
448 } else {
449 memcpy(token, sp, len);
450 *(token + len) = '\0';
451 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200452
453 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
454 desc->cmd = token;
455 desc->str = cmd_desc_str(&dp);
456
457 if (multiple) {
458 if (multiple == 1) {
459 strvec = vector_init(VECTOR_MIN_SIZE);
460 vector_set(allvec, strvec);
461 }
462 multiple++;
463 } else {
464 strvec = vector_init(VECTOR_MIN_SIZE);
465 vector_set(allvec, strvec);
466 }
467 vector_set(strvec, desc);
468 }
469}
470
471/* Count mandantory string vector size. This is to determine inputed
472 command has enough command length. */
473static int cmd_cmdsize(vector strvec)
474{
475 unsigned int i;
476 int size = 0;
477 vector descvec;
478 struct desc *desc;
479
480 for (i = 0; i < vector_active(strvec); i++)
481 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100482 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483 && (desc = vector_slot(descvec, 0)) != NULL) {
484 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
485 return size;
486 else
487 size++;
488 } else
489 size++;
490 }
491 return size;
492}
493
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200494/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200495const char *cmd_prompt(enum node_type node)
496{
497 struct cmd_node *cnode;
498
499 cnode = vector_slot(cmdvec, node);
500 return cnode->prompt;
501}
502
Alexander Couzensad580ba2016-05-16 16:01:45 +0200503/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200505 * \param unsafe string
506 * \return a new talloc char *
507 */
508char *osmo_asciidoc_escape(const char *inp)
509{
510 int _strlen;
511 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200512 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200513
514 if (!inp)
515 return NULL;
516 _strlen = strlen(inp);
517
518 for (i = 0; i < _strlen; ++i) {
519 switch (inp[i]) {
520 case '|':
521 len += 2;
522 break;
523 default:
524 len += 1;
525 break;
526 }
527 }
528
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200529 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200530 if (!out)
531 return NULL;
532
533 out_ptr = out;
534
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200538 /* Prepend escape character "\": */
539 *(out_ptr++) = '\\';
540 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 default:
542 *(out_ptr++) = inp[i];
543 break;
544 }
545 }
546
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 out_ptr[0] = '\0';
548 return out;
549}
550
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100551static char *xml_escape(const char *inp)
552{
553 int _strlen;
554 char *out, *out_ptr;
555 int len = 0, i, j;
556
557 if (!inp)
558 return NULL;
559 _strlen = strlen(inp);
560
561 for (i = 0; i < _strlen; ++i) {
562 switch (inp[i]) {
563 case '"':
564 len += 6;
565 break;
566 case '\'':
567 len += 6;
568 break;
569 case '<':
570 len += 4;
571 break;
572 case '>':
573 len += 4;
574 break;
575 case '&':
576 len += 5;
577 break;
578 default:
579 len += 1;
580 break;
581 }
582 }
583
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200584 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100585 if (!out)
586 return NULL;
587
588 out_ptr = out;
589
590#define ADD(out, str) \
591 for (j = 0; j < strlen(str); ++j) \
592 *(out++) = str[j];
593
594 for (i = 0; i < _strlen; ++i) {
595 switch (inp[i]) {
596 case '"':
597 ADD(out_ptr, "&quot;");
598 break;
599 case '\'':
600 ADD(out_ptr, "&apos;");
601 break;
602 case '<':
603 ADD(out_ptr, "&lt;");
604 break;
605 case '>':
606 ADD(out_ptr, "&gt;");
607 break;
608 case '&':
609 ADD(out_ptr, "&amp;");
610 break;
611 default:
612 *(out_ptr++) = inp[i];
613 break;
614 }
615 }
616
617#undef ADD
618
619 out_ptr[0] = '\0';
620 return out;
621}
622
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200623typedef int (*print_func_t)(void *data, const char *fmt, ...);
624
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700625static const struct value_string cmd_attr_desc[] = {
626 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700627 { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700628 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700629 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700630 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700631 { 0, NULL }
632};
633
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700634/* Public attributes (to be printed in the VTY / XML reference) */
635#define CMD_ATTR_PUBLIC_MASK \
636 (CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
637
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700638/* Get a flag character for a global VTY command attribute */
639static char cmd_attr_get_flag(unsigned int attr)
640{
641 switch (attr) {
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700642 case CMD_ATTR_HIDDEN:
643 return '^';
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700644 case CMD_ATTR_IMMEDIATE:
645 return '!';
646 case CMD_ATTR_NODE_EXIT:
647 return '@';
648 default:
649 return '.';
650 }
651}
652
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700653/* Description of attributes shared between the lib commands */
654static const char * const cmd_lib_attr_desc[32] = {
655 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
656 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200657 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
658 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200659 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
660 "This command applies on IPA link establishment",
661 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
662 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700663};
664
665/* Flag letters of attributes shared between the lib commands.
666 * NOTE: uppercase letters only, the rest is reserved for applications. */
667static const char cmd_lib_attr_letters[32] = {
668 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200669 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200670 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
671 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700672};
673
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100674/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200675 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100676 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200677static 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 +0100678{
679 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700680 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100681
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200682 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700683
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700684 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700685 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700686 print_func(data, " <attributes scope='global'>%s", newline);
687
688 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
689 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700690 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700691
692 if (~cmd->attr & cmd_attr_desc[i].value)
693 continue;
694
695 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700696 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700697 xml_att_desc, newline);
698 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700699
700 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
701 if (flag != '.')
702 print_func(data, " flag='%c'", flag);
703 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700704 }
705
706 print_func(data, " </attributes>%s", newline);
707 }
708
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700709 /* Print application specific attributes and their description */
710 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700711 const char * const *desc;
712 const char *letters;
713
714 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
715 print_func(data, " <attributes scope='library'>%s", newline);
716 letters = &cmd_lib_attr_letters[0];
717 desc = &cmd_lib_attr_desc[0];
718 } else {
719 print_func(data, " <attributes scope='application'>%s", newline);
720 letters = &host.app_info->usr_attr_letters[0];
721 desc = &host.app_info->usr_attr_desc[0];
722 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700723
724 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
725 char *xml_att_desc;
726 char flag;
727
728 /* Skip attribute if *not* set */
729 if (~cmd->usrattr & (1 << i))
730 continue;
731
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700732 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700733 print_func(data, " <attribute doc='%s'", xml_att_desc);
734 talloc_free(xml_att_desc);
735
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700736 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700737 print_func(data, " flag='%c'", flag);
738 print_func(data, " />%s", newline);
739 }
740
741 print_func(data, " </attributes>%s", newline);
742 }
743
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200744 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100745
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700746 for (i = 0; i < vector_count(cmd->strvec); ++i) {
747 vector descvec = vector_slot(cmd->strvec, i);
748 int j;
749 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100750 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700751 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100752 if (desc == NULL)
753 continue;
754
755 xml_param = xml_escape(desc->cmd);
756 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200757 print_func(data, " <param name='%s' doc='%s' />%s",
758 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100759 talloc_free(xml_param);
760 talloc_free(xml_doc);
761 }
762 }
763
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200764 print_func(data, " </params>%s", newline);
765 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100766
767 talloc_free(xml_string);
768 return 0;
769}
770
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200771static bool vty_command_is_common(struct cmd_element *cmd);
772
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100773/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200774 * 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 +0100775 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200776static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100777{
778 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200779 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100780
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200781 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100782
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200783 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200784 print_func(data, " <node id='_common_cmds_'>%s", newline);
785 print_func(data, " <name>Common Commands</name>%s", newline);
786 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
787 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200788 for (i = 0; i < vector_active(cmdvec); ++i) {
789 struct cmd_node *cnode;
790 cnode = vector_slot(cmdvec, i);
791 if (!cnode)
792 continue;
793 if (cnode->node != CONFIG_NODE)
794 continue;
795
796 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
797 struct cmd_element *elem;
798 elem = vector_slot(cnode->cmd_vector, j);
799 if (!vty_command_is_common(elem))
800 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700801 if (elem->attr & CMD_ATTR_DEPRECATED)
802 continue;
803 if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
804 continue;
805 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200806 }
807 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200808 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200809
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100810 for (i = 0; i < vector_active(cmdvec); ++i) {
811 struct cmd_node *cnode;
812 cnode = vector_slot(cmdvec, i);
813 if (!cnode)
814 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200815 if (vector_active(cnode->cmd_vector) < 1)
816 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100817
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200818 /* De-dup node IDs: how many times has this same name been used before? Count the first
819 * occurence as _1 and omit that first suffix, so that the first occurence is called
820 * 'name', the second becomes 'name_2', then 'name_3', ... */
821 same_name_count = 1;
822 for (j = 0; j < i; ++j) {
823 struct cmd_node *cnode2;
824 cnode2 = vector_slot(cmdvec, j);
825 if (!cnode2)
826 continue;
827 if (strcmp(cnode->name, cnode2->name) == 0)
828 same_name_count ++;
829 }
830
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200831 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200832 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200833 print_func(data, "_%d", same_name_count);
834 print_func(data, "'>%s", newline);
835 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100836
837 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
838 struct cmd_element *elem;
839 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200840 if (vty_command_is_common(elem))
841 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +0700842 if (elem->attr & CMD_ATTR_DEPRECATED)
843 continue;
844 if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
845 continue;
846 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100847 }
848
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200849 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100850 }
851
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200852 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100853
854 return 0;
855}
856
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200857static int print_func_vty(void *data, const char *format, ...)
858{
859 struct vty *vty = data;
860 va_list args;
861 int rc;
862 va_start(args, format);
863 rc = vty_out_va(vty, format, args);
864 va_end(args);
865 return rc;
866}
867
868static int vty_dump_xml_ref_to_vty(struct vty *vty)
869{
870 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
871}
872
873static int print_func_stream(void *data, const char *format, ...)
874{
875 va_list args;
876 int rc;
877 va_start(args, format);
878 rc = vfprintf((FILE*)data, format, args);
879 va_end(args);
880 return rc;
881}
882
883/*! Print the XML reference of all VTY nodes to the given stream.
884 */
885int vty_dump_xml_ref(FILE *stream)
886{
887 return vty_dump_nodes(print_func_stream, stream, "\n");
888}
889
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200890/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100891static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
892{
893 int i;
894
895 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
896 struct cmd_element *elem;
897 elem = vector_slot(cnode->cmd_vector, i);
898 if (!elem->string)
899 continue;
900 if (!strcmp(elem->string, cmdstring))
901 return 1;
902 }
903 return 0;
904}
905
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200906/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200907 * \param[in] ntype Node Type
908 * \param[cmd] element to be installed
909 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000910void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200911{
912 struct cmd_node *cnode;
913
914 cnode = vector_slot(cmdvec, ntype);
915
Harald Weltea99d45a2015-11-12 13:48:23 +0100916 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100917 /* ensure no _identical_ command has been registered at this
918 * node so far */
919 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200920
921 vector_set(cnode->cmd_vector, cmd);
922
923 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
924 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
925}
926
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700927/*! Install a library command into a node
928 * \param[in] ntype Node Type
929 * \param[in] cmd element to be installed
930 */
931void install_lib_element(int ntype, struct cmd_element *cmd)
932{
933 cmd->attr |= CMD_ATTR_LIB_COMMAND;
934 install_element(ntype, cmd);
935}
936
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200937/* Install a command into VIEW and ENABLE node */
938void install_element_ve(struct cmd_element *cmd)
939{
940 install_element(VIEW_NODE, cmd);
941 install_element(ENABLE_NODE, cmd);
942}
943
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700944/* Install a library command into VIEW and ENABLE node */
945void install_lib_element_ve(struct cmd_element *cmd)
946{
947 cmd->attr |= CMD_ATTR_LIB_COMMAND;
948 install_element_ve(cmd);
949}
950
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200951#ifdef VTY_CRYPT_PW
952static unsigned char itoa64[] =
953 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
954
955static void to64(char *s, long v, int n)
956{
957 while (--n >= 0) {
958 *s++ = itoa64[v & 0x3f];
959 v >>= 6;
960 }
961}
962
963static char *zencrypt(const char *passwd)
964{
965 char salt[6];
966 struct timeval tv;
967 char *crypt(const char *, const char *);
968
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200969 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200970
971 to64(&salt[0], random(), 3);
972 to64(&salt[3], tv.tv_usec, 3);
973 salt[5] = '\0';
974
975 return crypt(passwd, salt);
976}
977#endif
978
979/* This function write configuration of this host. */
980static int config_write_host(struct vty *vty)
981{
982 if (host.name)
983 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
984
985 if (host.encrypt) {
986 if (host.password_encrypt)
987 vty_out(vty, "password 8 %s%s", host.password_encrypt,
988 VTY_NEWLINE);
989 if (host.enable_encrypt)
990 vty_out(vty, "enable password 8 %s%s",
991 host.enable_encrypt, VTY_NEWLINE);
992 } else {
993 if (host.password)
994 vty_out(vty, "password %s%s", host.password,
995 VTY_NEWLINE);
996 if (host.enable)
997 vty_out(vty, "enable password %s%s", host.enable,
998 VTY_NEWLINE);
999 }
1000
1001 if (host.advanced)
1002 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
1003
1004 if (host.encrypt)
1005 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
1006
1007 if (host.lines >= 0)
1008 vty_out(vty, "service terminal-length %d%s", host.lines,
1009 VTY_NEWLINE);
1010
1011 if (host.motdfile)
1012 vty_out(vty, "banner motd file %s%s", host.motdfile,
1013 VTY_NEWLINE);
1014 else if (!host.motd)
1015 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1016
1017 return 1;
1018}
1019
1020/* Utility function for getting command vector. */
1021static vector cmd_node_vector(vector v, enum node_type ntype)
1022{
1023 struct cmd_node *cnode = vector_slot(v, ntype);
1024 return cnode->cmd_vector;
1025}
1026
1027/* Completion match types. */
1028enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001029 NO_MATCH = 0,
1030 ANY_MATCH,
1031 EXTEND_MATCH,
1032 IPV4_PREFIX_MATCH,
1033 IPV4_MATCH,
1034 IPV6_PREFIX_MATCH,
1035 IPV6_MATCH,
1036 RANGE_MATCH,
1037 VARARG_MATCH,
1038 PARTLY_MATCH,
1039 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001040};
1041
1042static enum match_type cmd_ipv4_match(const char *str)
1043{
1044 const char *sp;
1045 int dots = 0, nums = 0;
1046 char buf[4];
1047
1048 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001049 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001050
1051 for (;;) {
1052 memset(buf, 0, sizeof(buf));
1053 sp = str;
1054 while (*str != '\0') {
1055 if (*str == '.') {
1056 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001057 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001058
1059 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001060 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001061
1062 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001063 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001064
1065 dots++;
1066 break;
1067 }
1068 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001069 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001070
1071 str++;
1072 }
1073
1074 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001075 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001076
1077 strncpy(buf, sp, str - sp);
1078 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001079 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001080
1081 nums++;
1082
1083 if (*str == '\0')
1084 break;
1085
1086 str++;
1087 }
1088
1089 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001090 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001091
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001092 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001093}
1094
1095static enum match_type cmd_ipv4_prefix_match(const char *str)
1096{
1097 const char *sp;
1098 int dots = 0;
1099 char buf[4];
1100
1101 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001102 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001103
1104 for (;;) {
1105 memset(buf, 0, sizeof(buf));
1106 sp = str;
1107 while (*str != '\0' && *str != '/') {
1108 if (*str == '.') {
1109 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001110 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111
1112 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001113 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001114
1115 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001116 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001117
1118 dots++;
1119 break;
1120 }
1121
1122 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001123 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001124
1125 str++;
1126 }
1127
1128 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001129 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130
1131 strncpy(buf, sp, str - sp);
1132 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001133 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001134
1135 if (dots == 3) {
1136 if (*str == '/') {
1137 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001138 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001139
1140 str++;
1141 break;
1142 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001143 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001144 }
1145
1146 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 str++;
1150 }
1151
1152 sp = str;
1153 while (*str != '\0') {
1154 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001155 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001156
1157 str++;
1158 }
1159
1160 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001161 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001162
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001163 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001164}
1165
1166#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1167#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1168#define STATE_START 1
1169#define STATE_COLON 2
1170#define STATE_DOUBLE 3
1171#define STATE_ADDR 4
1172#define STATE_DOT 5
1173#define STATE_SLASH 6
1174#define STATE_MASK 7
1175
1176#ifdef HAVE_IPV6
1177
1178static enum match_type cmd_ipv6_match(const char *str)
1179{
1180 int state = STATE_START;
1181 int colons = 0, nums = 0, double_colon = 0;
1182 const char *sp = NULL;
1183 struct sockaddr_in6 sin6_dummy;
1184 int ret;
1185
1186 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001187 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001188
1189 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001190 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001191
1192 /* use inet_pton that has a better support,
1193 * for example inet_pton can support the automatic addresses:
1194 * ::1.2.3.4
1195 */
1196 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1197
1198 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001199 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001200
1201 while (*str != '\0') {
1202 switch (state) {
1203 case STATE_START:
1204 if (*str == ':') {
1205 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001206 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207 colons--;
1208 state = STATE_COLON;
1209 } else {
1210 sp = str;
1211 state = STATE_ADDR;
1212 }
1213
1214 continue;
1215 case STATE_COLON:
1216 colons++;
1217 if (*(str + 1) == ':')
1218 state = STATE_DOUBLE;
1219 else {
1220 sp = str + 1;
1221 state = STATE_ADDR;
1222 }
1223 break;
1224 case STATE_DOUBLE:
1225 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001226 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001227
1228 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001229 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001230 else {
1231 if (*(str + 1) != '\0')
1232 colons++;
1233 sp = str + 1;
1234 state = STATE_ADDR;
1235 }
1236
1237 double_colon++;
1238 nums++;
1239 break;
1240 case STATE_ADDR:
1241 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1242 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001243 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001244
1245 nums++;
1246 state = STATE_COLON;
1247 }
1248 if (*(str + 1) == '.')
1249 state = STATE_DOT;
1250 break;
1251 case STATE_DOT:
1252 state = STATE_ADDR;
1253 break;
1254 default:
1255 break;
1256 }
1257
1258 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001259 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001260
1261 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001262 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001263
1264 str++;
1265 }
1266
1267#if 0
1268 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001269 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001270#endif /* 0 */
1271
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001272 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001273}
1274
1275static enum match_type cmd_ipv6_prefix_match(const char *str)
1276{
1277 int state = STATE_START;
1278 int colons = 0, nums = 0, double_colon = 0;
1279 int mask;
1280 const char *sp = NULL;
1281 char *endptr = NULL;
1282
1283 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001284 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001285
1286 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001287 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001288
1289 while (*str != '\0' && state != STATE_MASK) {
1290 switch (state) {
1291 case STATE_START:
1292 if (*str == ':') {
1293 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001294 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001295 colons--;
1296 state = STATE_COLON;
1297 } else {
1298 sp = str;
1299 state = STATE_ADDR;
1300 }
1301
1302 continue;
1303 case STATE_COLON:
1304 colons++;
1305 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001306 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001307 else if (*(str + 1) == ':')
1308 state = STATE_DOUBLE;
1309 else {
1310 sp = str + 1;
1311 state = STATE_ADDR;
1312 }
1313 break;
1314 case STATE_DOUBLE:
1315 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001316 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001317
1318 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001319 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001320 else {
1321 if (*(str + 1) != '\0' && *(str + 1) != '/')
1322 colons++;
1323 sp = str + 1;
1324
1325 if (*(str + 1) == '/')
1326 state = STATE_SLASH;
1327 else
1328 state = STATE_ADDR;
1329 }
1330
1331 double_colon++;
1332 nums += 1;
1333 break;
1334 case STATE_ADDR:
1335 if (*(str + 1) == ':' || *(str + 1) == '.'
1336 || *(str + 1) == '\0' || *(str + 1) == '/') {
1337 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001338 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001339
1340 for (; sp <= str; sp++)
1341 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001342 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001343
1344 nums++;
1345
1346 if (*(str + 1) == ':')
1347 state = STATE_COLON;
1348 else if (*(str + 1) == '.')
1349 state = STATE_DOT;
1350 else if (*(str + 1) == '/')
1351 state = STATE_SLASH;
1352 }
1353 break;
1354 case STATE_DOT:
1355 state = STATE_ADDR;
1356 break;
1357 case STATE_SLASH:
1358 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001359 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001360
1361 state = STATE_MASK;
1362 break;
1363 default:
1364 break;
1365 }
1366
1367 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001368 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001369
1370 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001371 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001372
1373 str++;
1374 }
1375
1376 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001377 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378
1379 mask = strtol(str, &endptr, 10);
1380 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001381 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001382
1383 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001384 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001385
1386/* I don't know why mask < 13 makes command match partly.
1387 Forgive me to make this comments. I Want to set static default route
1388 because of lack of function to originate default in ospf6d; sorry
1389 yasu
1390 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001391 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001392*/
1393
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001394 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001395}
1396
1397#endif /* HAVE_IPV6 */
1398
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001399
1400#if ULONG_MAX == 18446744073709551615UL
1401#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1402#elif ULONG_MAX == 4294967295UL
1403#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1404#else
1405#error "ULONG_MAX not defined!"
1406#endif
1407
1408#if LONG_MAX == 9223372036854775807L
1409#define DECIMAL_STRLEN_MAX_SIGNED 19
1410#elif LONG_MAX == 2147483647L
1411#define DECIMAL_STRLEN_MAX_SIGNED 10
1412#else
1413#error "LONG_MAX not defined!"
1414#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001415
1416static int cmd_range_match(const char *range, const char *str)
1417{
1418 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001419 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001420 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001421
1422 if (str == NULL)
1423 return 1;
1424
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001425 if (range[1] == '-') {
1426 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001427
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001428 val = strtol(str, &endptr, 10);
1429 if (*endptr != '\0')
1430 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001431
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001432 range += 2;
1433 p = strchr(range, '-');
1434 if (p == NULL)
1435 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001436 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001437 return 0;
1438 strncpy(buf, range, p - range);
1439 buf[p - range] = '\0';
1440 min = -strtol(buf, &endptr, 10);
1441 if (*endptr != '\0')
1442 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001443
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001444 range = p + 1;
1445 p = strchr(range, '>');
1446 if (p == NULL)
1447 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001448 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001449 return 0;
1450 strncpy(buf, range, p - range);
1451 buf[p - range] = '\0';
1452 max = strtol(buf, &endptr, 10);
1453 if (*endptr != '\0')
1454 return 0;
1455
1456 if (val < min || val > max)
1457 return 0;
1458 } else {
1459 unsigned long min, max, val;
1460
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001461 if (str[0] == '-')
1462 return 0;
1463
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001464 val = strtoul(str, &endptr, 10);
1465 if (*endptr != '\0')
1466 return 0;
1467
1468 range++;
1469 p = strchr(range, '-');
1470 if (p == NULL)
1471 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001472 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001473 return 0;
1474 strncpy(buf, range, p - range);
1475 buf[p - range] = '\0';
1476 min = strtoul(buf, &endptr, 10);
1477 if (*endptr != '\0')
1478 return 0;
1479
1480 range = p + 1;
1481 p = strchr(range, '>');
1482 if (p == NULL)
1483 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001484 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001485 return 0;
1486 strncpy(buf, range, p - range);
1487 buf[p - range] = '\0';
1488 max = strtoul(buf, &endptr, 10);
1489 if (*endptr != '\0')
1490 return 0;
1491
1492 if (val < min || val > max)
1493 return 0;
1494 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001495
1496 return 1;
1497}
1498
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001499/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001500static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001501{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001502 /* we've got "[blah]". We want to strip off the []s and redo the
1503 * match check for "blah"
1504 */
1505 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001506
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001507 if (len < 3)
1508 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001509
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001510 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001511}
1512
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001513static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001514cmd_match(const char *str, const char *command,
1515 enum match_type min, bool recur)
1516{
1517
1518 if (recur && CMD_OPTION(str))
1519 {
1520 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001521 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001522
1523 /* this would be a bug in a command, however handle it gracefully
1524 * as it we only discover it if a user tries to run it
1525 */
1526 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001527 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001528
1529 ret = cmd_match(tmp, command, min, false);
1530
1531 talloc_free(tmp);
1532
1533 return ret;
1534 }
1535 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001536 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001537 else if (CMD_RANGE(str))
1538 {
1539 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001540 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001541 }
1542#ifdef HAVE_IPV6
1543 else if (CMD_IPV6(str))
1544 {
1545 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001546 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001547 }
1548 else if (CMD_IPV6_PREFIX(str))
1549 {
1550 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001551 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001552 }
1553#endif /* HAVE_IPV6 */
1554 else if (CMD_IPV4(str))
1555 {
1556 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001557 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001558 }
1559 else if (CMD_IPV4_PREFIX(str))
1560 {
1561 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001562 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001563 }
1564 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001565 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001566 else if (strncmp(command, str, strlen(command)) == 0)
1567 {
1568 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001569 return EXACT_MATCH;
1570 else if (PARTLY_MATCH >= min)
1571 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001572 }
1573
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001574 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001575}
1576
1577/* Filter vector at the specified index and by the given command string, to
1578 * the desired matching level (thus allowing part matches), and return match
1579 * type flag.
1580 */
1581static enum match_type
1582cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001583{
1584 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001585 struct cmd_element *cmd_element;
1586 enum match_type match_type;
1587 vector descvec;
1588 struct desc *desc;
1589
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001590 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001591
1592 /* If command and cmd_element string does not match set NULL to vector */
1593 for (i = 0; i < vector_active(v); i++)
1594 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001595 if (index >= vector_active(cmd_element->strvec))
1596 vector_slot(v, i) = NULL;
1597 else {
1598 unsigned int j;
1599 int matched = 0;
1600
1601 descvec =
1602 vector_slot(cmd_element->strvec, index);
1603
1604 for (j = 0; j < vector_active(descvec); j++)
1605 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001606 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001607
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001608 ret = cmd_match (desc->cmd, command, level, true);
1609
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001610 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001611 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001612
1613 if (match_type < ret)
1614 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001615 }
1616 if (!matched)
1617 vector_slot(v, i) = NULL;
1618 }
1619 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001620
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001621 if (match_type == NO_MATCH)
1622 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001623
1624 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1625 * go again and filter out commands whose argument (at this index) is
1626 * 'weaker'. E.g., if we have 2 commands:
1627 *
1628 * foo bar <1-255>
1629 * foo bar BLAH
1630 *
1631 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001632 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001633 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1634 *
1635 * If we don't do a 2nd pass and filter it out, the higher-layers will
1636 * consider this to be ambiguous.
1637 */
1638 for (i = 0; i < vector_active(v); i++)
1639 if ((cmd_element = vector_slot(v, i)) != NULL) {
1640 if (index >= vector_active(cmd_element->strvec))
1641 vector_slot(v, i) = NULL;
1642 else {
1643 unsigned int j;
1644 int matched = 0;
1645
1646 descvec =
1647 vector_slot(cmd_element->strvec, index);
1648
1649 for (j = 0; j < vector_active(descvec); j++)
1650 if ((desc = vector_slot(descvec, j))) {
1651 enum match_type ret;
1652
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001653 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001654
1655 if (ret >= match_type)
1656 matched++;
1657 }
1658 if (!matched)
1659 vector_slot(v, i) = NULL;
1660 }
1661 }
1662
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001663 return match_type;
1664}
1665
1666/* Check ambiguous match */
1667static int
1668is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1669{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001670 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001671 unsigned int i;
1672 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001673 struct cmd_element *cmd_element;
1674 const char *matched = NULL;
1675 vector descvec;
1676 struct desc *desc;
1677
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001678 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1679 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1680 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1681 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1682 * that case, the string must remain allocated until this function exits or another match comes
1683 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1684 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1685 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1686 void *cmd_deopt_ctx = NULL;
1687
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001688 for (i = 0; i < vector_active(v); i++) {
1689 cmd_element = vector_slot(v, i);
1690 if (!cmd_element)
1691 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001692
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001693 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001694
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001695 descvec = vector_slot(cmd_element->strvec, index);
1696
1697 for (j = 0; j < vector_active(descvec); j++) {
1698 desc = vector_slot(descvec, j);
1699 if (!desc)
1700 continue;
1701
1702 enum match_type mtype;
1703 const char *str = desc->cmd;
1704
1705 if (CMD_OPTION(str)) {
1706 if (!cmd_deopt_ctx)
1707 cmd_deopt_ctx =
1708 talloc_named_const(tall_vty_cmd_ctx, 0,
1709 __func__);
1710 str = cmd_deopt(cmd_deopt_ctx, str);
1711 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001712 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001713 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001714
1715 switch (type) {
1716 case EXACT_MATCH:
1717 if (!(CMD_VARIABLE (str))
1718 && strcmp(command, str) == 0)
1719 match++;
1720 break;
1721 case PARTLY_MATCH:
1722 if (!(CMD_VARIABLE (str))
1723 && strncmp(command, str, strlen (command)) == 0)
1724 {
1725 if (matched
1726 && strcmp(matched,
1727 str) != 0) {
1728 ret = 1; /* There is ambiguous match. */
1729 goto free_and_return;
1730 } else
1731 matched = str;
1732 match++;
1733 }
1734 break;
1735 case RANGE_MATCH:
1736 if (cmd_range_match
1737 (str, command)) {
1738 if (matched
1739 && strcmp(matched,
1740 str) != 0) {
1741 ret = 1;
1742 goto free_and_return;
1743 } else
1744 matched = str;
1745 match++;
1746 }
1747 break;
1748#ifdef HAVE_IPV6
1749 case IPV6_MATCH:
1750 if (CMD_IPV6(str))
1751 match++;
1752 break;
1753 case IPV6_PREFIX_MATCH:
1754 if ((mtype =
1755 cmd_ipv6_prefix_match
1756 (command)) != NO_MATCH) {
1757 if (mtype == PARTLY_MATCH) {
1758 ret = 2; /* There is incomplete match. */
1759 goto free_and_return;
1760 }
1761
1762 match++;
1763 }
1764 break;
1765#endif /* HAVE_IPV6 */
1766 case IPV4_MATCH:
1767 if (CMD_IPV4(str))
1768 match++;
1769 break;
1770 case IPV4_PREFIX_MATCH:
1771 if ((mtype =
1772 cmd_ipv4_prefix_match
1773 (command)) != NO_MATCH) {
1774 if (mtype == PARTLY_MATCH) {
1775 ret = 2; /* There is incomplete match. */
1776 goto free_and_return;
1777 }
1778
1779 match++;
1780 }
1781 break;
1782 case EXTEND_MATCH:
1783 if (CMD_VARIABLE (str))
1784 match++;
1785 break;
1786 case NO_MATCH:
1787 default:
1788 break;
1789 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001790 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001791 if (!match)
1792 vector_slot(v, i) = NULL;
1793 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001794
1795free_and_return:
1796 if (cmd_deopt_ctx)
1797 talloc_free(cmd_deopt_ctx);
1798 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001799}
1800
1801/* If src matches dst return dst string, otherwise return NULL */
1802static const char *cmd_entry_function(const char *src, const char *dst)
1803{
1804 /* Skip variable arguments. */
1805 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1806 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1807 return NULL;
1808
1809 /* In case of 'command \t', given src is NULL string. */
1810 if (src == NULL)
1811 return dst;
1812
1813 /* Matched with input string. */
1814 if (strncmp(src, dst, strlen(src)) == 0)
1815 return dst;
1816
1817 return NULL;
1818}
1819
1820/* If src matches dst return dst string, otherwise return NULL */
1821/* This version will return the dst string always if it is
1822 CMD_VARIABLE for '?' key processing */
1823static const char *cmd_entry_function_desc(const char *src, const char *dst)
1824{
1825 if (CMD_VARARG(dst))
1826 return dst;
1827
1828 if (CMD_RANGE(dst)) {
1829 if (cmd_range_match(dst, src))
1830 return dst;
1831 else
1832 return NULL;
1833 }
1834#ifdef HAVE_IPV6
1835 if (CMD_IPV6(dst)) {
1836 if (cmd_ipv6_match(src))
1837 return dst;
1838 else
1839 return NULL;
1840 }
1841
1842 if (CMD_IPV6_PREFIX(dst)) {
1843 if (cmd_ipv6_prefix_match(src))
1844 return dst;
1845 else
1846 return NULL;
1847 }
1848#endif /* HAVE_IPV6 */
1849
1850 if (CMD_IPV4(dst)) {
1851 if (cmd_ipv4_match(src))
1852 return dst;
1853 else
1854 return NULL;
1855 }
1856
1857 if (CMD_IPV4_PREFIX(dst)) {
1858 if (cmd_ipv4_prefix_match(src))
1859 return dst;
1860 else
1861 return NULL;
1862 }
1863
1864 /* Optional or variable commands always match on '?' */
1865 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1866 return dst;
1867
1868 /* In case of 'command \t', given src is NULL string. */
1869 if (src == NULL)
1870 return dst;
1871
1872 if (strncmp(src, dst, strlen(src)) == 0)
1873 return dst;
1874 else
1875 return NULL;
1876}
1877
1878/* Check same string element existence. If it isn't there return
1879 1. */
1880static int cmd_unique_string(vector v, const char *str)
1881{
1882 unsigned int i;
1883 char *match;
1884
1885 for (i = 0; i < vector_active(v); i++)
1886 if ((match = vector_slot(v, i)) != NULL)
1887 if (strcmp(match, str) == 0)
1888 return 0;
1889 return 1;
1890}
1891
1892/* Compare string to description vector. If there is same string
1893 return 1 else return 0. */
1894static int desc_unique_string(vector v, const char *str)
1895{
1896 unsigned int i;
1897 struct desc *desc;
1898
1899 for (i = 0; i < vector_active(v); i++)
1900 if ((desc = vector_slot(v, i)) != NULL)
1901 if (strcmp(desc->cmd, str) == 0)
1902 return 1;
1903 return 0;
1904}
1905
1906static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1907{
1908 if (first_word != NULL &&
1909 node != AUTH_NODE &&
1910 node != VIEW_NODE &&
1911 node != AUTH_ENABLE_NODE &&
1912 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1913 return 1;
1914 return 0;
1915}
1916
1917/* '?' describe command support. */
1918static vector
1919cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1920{
1921 unsigned int i;
1922 vector cmd_vector;
1923#define INIT_MATCHVEC_SIZE 10
1924 vector matchvec;
1925 struct cmd_element *cmd_element;
1926 unsigned int index;
1927 int ret;
1928 enum match_type match;
1929 char *command;
1930 static struct desc desc_cr = { "<cr>", "" };
1931
1932 /* Set index. */
1933 if (vector_active(vline) == 0) {
1934 *status = CMD_ERR_NO_MATCH;
1935 return NULL;
1936 } else
1937 index = vector_active(vline) - 1;
1938
1939 /* Make copy vector of current node's command vector. */
1940 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1941
1942 /* Prepare match vector */
1943 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1944
1945 /* Filter commands. */
1946 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001947 for (i = 0; i < index; i++) {
1948 command = vector_slot(vline, i);
1949 if (!command)
1950 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001951
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001952 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001953
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001954 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001955 struct cmd_element *cmd_element;
1956 vector descvec;
1957 unsigned int j, k;
1958
1959 for (j = 0; j < vector_active(cmd_vector); j++)
1960 if ((cmd_element =
1961 vector_slot(cmd_vector, j)) != NULL
1962 &&
1963 (vector_active(cmd_element->strvec))) {
1964 descvec =
1965 vector_slot(cmd_element->
1966 strvec,
1967 vector_active
1968 (cmd_element->
1969 strvec) - 1);
1970 for (k = 0;
1971 k < vector_active(descvec);
1972 k++) {
1973 struct desc *desc =
1974 vector_slot(descvec,
1975 k);
1976 vector_set(matchvec,
1977 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001978 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001979 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001980
Harald Welte80d30fe2013-02-12 11:08:57 +01001981 vector_set(matchvec, &desc_cr);
1982 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001983
Harald Welte80d30fe2013-02-12 11:08:57 +01001984 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001985 }
1986
Harald Welte80d30fe2013-02-12 11:08:57 +01001987 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1988 match)) == 1) {
1989 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001990 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001991 *status = CMD_ERR_AMBIGUOUS;
1992 return NULL;
1993 } else if (ret == 2) {
1994 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001995 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001996 *status = CMD_ERR_NO_MATCH;
1997 return NULL;
1998 }
1999 }
2000
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002001 /* Prepare match vector */
2002 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
2003
2004 /* Make sure that cmd_vector is filtered based on current word */
2005 command = vector_slot(vline, index);
2006 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002007 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002008
2009 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002010 for (i = 0; i < vector_active(cmd_vector); i++) {
2011 const char *string = NULL;
2012 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002013
Harald Welte80d30fe2013-02-12 11:08:57 +01002014 cmd_element = vector_slot(cmd_vector, i);
2015 if (!cmd_element)
2016 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002017
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002018 if (cmd_element->attr & CMD_ATTR_DEPRECATED)
2019 continue;
2020 if (!host.expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
Harald Welted17aa592013-02-12 11:11:34 +01002021 continue;
2022
Harald Welte80d30fe2013-02-12 11:08:57 +01002023 strvec = cmd_element->strvec;
2024
2025 /* if command is NULL, index may be equal to vector_active */
2026 if (command && index >= vector_active(strvec))
2027 vector_slot(cmd_vector, i) = NULL;
2028 else {
2029 /* Check if command is completed. */
2030 if (command == NULL
2031 && index == vector_active(strvec)) {
2032 string = "<cr>";
2033 if (!desc_unique_string(matchvec, string))
2034 vector_set(matchvec, &desc_cr);
2035 } else {
2036 unsigned int j;
2037 vector descvec = vector_slot(strvec, index);
2038 struct desc *desc;
2039
2040 for (j = 0; j < vector_active(descvec); j++) {
2041 desc = vector_slot(descvec, j);
2042 if (!desc)
2043 continue;
2044 string = cmd_entry_function_desc
2045 (command, desc->cmd);
2046 if (!string)
2047 continue;
2048 /* Uniqueness check */
2049 if (!desc_unique_string(matchvec, string))
2050 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002051 }
2052 }
2053 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002054 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002055 vector_free(cmd_vector);
2056
2057 if (vector_slot(matchvec, 0) == NULL) {
2058 vector_free(matchvec);
2059 *status = CMD_ERR_NO_MATCH;
2060 } else
2061 *status = CMD_SUCCESS;
2062
2063 return matchvec;
2064}
2065
2066vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2067{
2068 vector ret;
2069
2070 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2071 enum node_type onode;
2072 vector shifted_vline;
2073 unsigned int index;
2074
2075 onode = vty->node;
2076 vty->node = ENABLE_NODE;
2077 /* We can try it on enable node, cos' the vty is authenticated */
2078
2079 shifted_vline = vector_init(vector_count(vline));
2080 /* use memcpy? */
2081 for (index = 1; index < vector_active(vline); index++) {
2082 vector_set_index(shifted_vline, index - 1,
2083 vector_lookup(vline, index));
2084 }
2085
2086 ret = cmd_describe_command_real(shifted_vline, vty, status);
2087
2088 vector_free(shifted_vline);
2089 vty->node = onode;
2090 return ret;
2091 }
2092
2093 return cmd_describe_command_real(vline, vty, status);
2094}
2095
2096/* Check LCD of matched command. */
2097static int cmd_lcd(char **matched)
2098{
2099 int i;
2100 int j;
2101 int lcd = -1;
2102 char *s1, *s2;
2103 char c1, c2;
2104
2105 if (matched[0] == NULL || matched[1] == NULL)
2106 return 0;
2107
2108 for (i = 1; matched[i] != NULL; i++) {
2109 s1 = matched[i - 1];
2110 s2 = matched[i];
2111
2112 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2113 if (c1 != c2)
2114 break;
2115
2116 if (lcd < 0)
2117 lcd = j;
2118 else {
2119 if (lcd > j)
2120 lcd = j;
2121 }
2122 }
2123 return lcd;
2124}
2125
2126/* Command line completion support. */
2127static char **cmd_complete_command_real(vector vline, struct vty *vty,
2128 int *status)
2129{
2130 unsigned int i;
2131 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2132#define INIT_MATCHVEC_SIZE 10
2133 vector matchvec;
2134 struct cmd_element *cmd_element;
2135 unsigned int index;
2136 char **match_str;
2137 struct desc *desc;
2138 vector descvec;
2139 char *command;
2140 int lcd;
2141
2142 if (vector_active(vline) == 0) {
2143 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002144 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002145 return NULL;
2146 } else
2147 index = vector_active(vline) - 1;
2148
2149 /* First, filter by preceeding command string */
2150 for (i = 0; i < index; i++)
2151 if ((command = vector_slot(vline, i))) {
2152 enum match_type match;
2153 int ret;
2154
2155 /* First try completion match, if there is exactly match return 1 */
2156 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002157 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002158
2159 /* If there is exact match then filter ambiguous match else check
2160 ambiguousness. */
2161 if ((ret =
2162 is_cmd_ambiguous(command, cmd_vector, i,
2163 match)) == 1) {
2164 vector_free(cmd_vector);
2165 *status = CMD_ERR_AMBIGUOUS;
2166 return NULL;
2167 }
2168 /*
2169 else if (ret == 2)
2170 {
2171 vector_free (cmd_vector);
2172 *status = CMD_ERR_NO_MATCH;
2173 return NULL;
2174 }
2175 */
2176 }
2177
2178 /* Prepare match vector. */
2179 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2180
2181 /* Now we got into completion */
2182 for (i = 0; i < vector_active(cmd_vector); i++)
2183 if ((cmd_element = vector_slot(cmd_vector, i))) {
2184 const char *string;
2185 vector strvec = cmd_element->strvec;
2186
2187 /* Check field length */
2188 if (index >= vector_active(strvec))
2189 vector_slot(cmd_vector, i) = NULL;
2190 else {
2191 unsigned int j;
2192
2193 descvec = vector_slot(strvec, index);
2194 for (j = 0; j < vector_active(descvec); j++)
2195 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002196 const char *cmd = desc->cmd;
2197 char *tmp = NULL;
2198
2199 if (CMD_OPTION(desc->cmd)) {
2200 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2201 cmd = tmp;
2202 }
2203 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002204 if (cmd_unique_string (matchvec, string))
2205 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002206 if (tmp)
2207 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002208 }
2209 }
2210 }
2211
2212 /* We don't need cmd_vector any more. */
2213 vector_free(cmd_vector);
2214
2215 /* No matched command */
2216 if (vector_slot(matchvec, 0) == NULL) {
2217 vector_free(matchvec);
2218
2219 /* In case of 'command \t' pattern. Do you need '?' command at
2220 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002221 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002222 *status = CMD_ERR_NOTHING_TODO;
2223 else
2224 *status = CMD_ERR_NO_MATCH;
2225 return NULL;
2226 }
2227
2228 /* Only one matched */
2229 if (vector_slot(matchvec, 1) == NULL) {
2230 match_str = (char **)matchvec->index;
2231 vector_only_wrapper_free(matchvec);
2232 *status = CMD_COMPLETE_FULL_MATCH;
2233 return match_str;
2234 }
2235 /* Make it sure last element is NULL. */
2236 vector_set(matchvec, NULL);
2237
2238 /* Check LCD of matched strings. */
2239 if (vector_slot(vline, index) != NULL) {
2240 lcd = cmd_lcd((char **)matchvec->index);
2241
2242 if (lcd) {
2243 int len = strlen(vector_slot(vline, index));
2244
2245 if (len < lcd) {
2246 char *lcdstr;
2247
2248 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2249 "complete-lcdstr");
2250 memcpy(lcdstr, matchvec->index[0], lcd);
2251 lcdstr[lcd] = '\0';
2252
2253 /* match_str = (char **) &lcdstr; */
2254
2255 /* Free matchvec. */
2256 for (i = 0; i < vector_active(matchvec); i++) {
2257 if (vector_slot(matchvec, i))
2258 talloc_free(vector_slot(matchvec, i));
2259 }
2260 vector_free(matchvec);
2261
2262 /* Make new matchvec. */
2263 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2264 vector_set(matchvec, lcdstr);
2265 match_str = (char **)matchvec->index;
2266 vector_only_wrapper_free(matchvec);
2267
2268 *status = CMD_COMPLETE_MATCH;
2269 return match_str;
2270 }
2271 }
2272 }
2273
2274 match_str = (char **)matchvec->index;
2275 vector_only_wrapper_free(matchvec);
2276 *status = CMD_COMPLETE_LIST_MATCH;
2277 return match_str;
2278}
2279
2280char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2281{
2282 char **ret;
2283
2284 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2285 enum node_type onode;
2286 vector shifted_vline;
2287 unsigned int index;
2288
2289 onode = vty->node;
2290 vty->node = ENABLE_NODE;
2291 /* We can try it on enable node, cos' the vty is authenticated */
2292
2293 shifted_vline = vector_init(vector_count(vline));
2294 /* use memcpy? */
2295 for (index = 1; index < vector_active(vline); index++) {
2296 vector_set_index(shifted_vline, index - 1,
2297 vector_lookup(vline, index));
2298 }
2299
2300 ret = cmd_complete_command_real(shifted_vline, vty, status);
2301
2302 vector_free(shifted_vline);
2303 vty->node = onode;
2304 return ret;
2305 }
2306
2307 return cmd_complete_command_real(vline, vty, status);
2308}
2309
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002310static struct vty_parent_node *vty_parent(struct vty *vty)
2311{
2312 return llist_first_entry_or_null(&vty->parent_nodes,
2313 struct vty_parent_node,
2314 entry);
2315}
2316
2317static bool vty_pop_parent(struct vty *vty)
2318{
2319 struct vty_parent_node *parent = vty_parent(vty);
2320 if (!parent)
2321 return false;
2322 llist_del(&parent->entry);
2323 vty->node = parent->node;
2324 vty->priv = parent->priv;
2325 if (vty->indent)
2326 talloc_free(vty->indent);
2327 vty->indent = parent->indent;
2328 talloc_free(parent);
2329 return true;
2330}
2331
2332static void vty_clear_parents(struct vty *vty)
2333{
2334 while (vty_pop_parent(vty));
2335}
2336
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002337/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002338/*
2339 * This function MUST eventually converge on a node when called repeatedly,
2340 * there must not be any cycles.
2341 * All 'config' nodes shall converge on CONFIG_NODE.
2342 * All other 'enable' nodes shall converge on ENABLE_NODE.
2343 * All 'view' only nodes shall converge on VIEW_NODE.
2344 * All other nodes shall converge on themselves or it must be ensured,
2345 * that the user's rights are not extended anyhow by calling this function.
2346 *
2347 * Note that these requirements also apply to all functions that are used
2348 * as go_parent_cb.
2349 * Note also that this function relies on the is_config_child callback to
2350 * recognize non-config nodes if go_parent_cb is not set.
2351 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002352int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002353{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002354 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002355 case AUTH_NODE:
2356 case VIEW_NODE:
2357 case ENABLE_NODE:
2358 case CONFIG_NODE:
2359 vty_clear_parents(vty);
2360 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002361
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002362 case AUTH_ENABLE_NODE:
2363 vty->node = VIEW_NODE;
2364 vty_clear_parents(vty);
2365 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002366
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002367 default:
2368 if (host.app_info->go_parent_cb)
2369 host.app_info->go_parent_cb(vty);
2370 vty_pop_parent(vty);
2371 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002372 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002373
2374 return vty->node;
2375}
2376
2377/* Execute command by argument vline vector. */
2378static int
2379cmd_execute_command_real(vector vline, struct vty *vty,
2380 struct cmd_element **cmd)
2381{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002382 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002383 unsigned int index;
2384 vector cmd_vector;
2385 struct cmd_element *cmd_element;
2386 struct cmd_element *matched_element;
2387 unsigned int matched_count, incomplete_count;
2388 int argc;
2389 const char *argv[CMD_ARGC_MAX];
2390 enum match_type match = 0;
2391 int varflag;
2392 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002393 int rc;
2394 /* Used for temporary storage of cmd_deopt() allocated arguments during
2395 argv[] generation */
2396 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002397
2398 /* Make copy of command elements. */
2399 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2400
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002401 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002402 if ((command = vector_slot(vline, index))) {
2403 int ret;
2404
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002405 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002406 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002407
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002408 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002409 break;
2410
2411 ret =
2412 is_cmd_ambiguous(command, cmd_vector, index, match);
2413
2414 if (ret == 1) {
2415 vector_free(cmd_vector);
2416 return CMD_ERR_AMBIGUOUS;
2417 } else if (ret == 2) {
2418 vector_free(cmd_vector);
2419 return CMD_ERR_NO_MATCH;
2420 }
2421 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002422 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002423
2424 /* Check matched count. */
2425 matched_element = NULL;
2426 matched_count = 0;
2427 incomplete_count = 0;
2428
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002429 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002430 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002431 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002432 || index >= cmd_element->cmdsize) {
2433 matched_element = cmd_element;
2434#if 0
2435 printf("DEBUG: %s\n", cmd_element->string);
2436#endif
2437 matched_count++;
2438 } else {
2439 incomplete_count++;
2440 }
2441 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002442 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002443
2444 /* Finish of using cmd_vector. */
2445 vector_free(cmd_vector);
2446
2447 /* To execute command, matched_count must be 1. */
2448 if (matched_count == 0) {
2449 if (incomplete_count)
2450 return CMD_ERR_INCOMPLETE;
2451 else
2452 return CMD_ERR_NO_MATCH;
2453 }
2454
2455 if (matched_count > 1)
2456 return CMD_ERR_AMBIGUOUS;
2457
2458 /* Argument treatment */
2459 varflag = 0;
2460 argc = 0;
2461
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002462 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2463
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002464 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002465 if (argc == CMD_ARGC_MAX) {
2466 rc = CMD_ERR_EXEED_ARGC_MAX;
2467 goto rc_free_deopt_ctx;
2468 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002469 if (varflag) {
2470 argv[argc++] = vector_slot(vline, i);
2471 continue;
2472 }
2473
2474 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002475 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002476
2477 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002478 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002479 struct desc *desc = vector_slot(descvec, 0);
2480
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002481 if (CMD_OPTION(desc->cmd)) {
2482 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2483 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2484 } else {
2485 tmp_cmd = desc->cmd;
2486 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002487
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002488 if (CMD_VARARG(tmp_cmd))
2489 varflag = 1;
2490 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002491 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002492 else if (CMD_OPTION(desc->cmd))
2493 argv[argc++] = tmp_cmd;
2494 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002495 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002496 /* multi choice argument. look up which choice
2497 the user meant (can only be one after
2498 filtering and checking for ambigous). For instance,
2499 if user typed "th" for "(two|three)" arg, we
2500 want to pass "three" in argv[]. */
2501 for (j = 0; j < vector_active(descvec); j++) {
2502 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002503 if (!desc)
2504 continue;
2505 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2506 continue;
2507 if (CMD_OPTION(desc->cmd)) {
2508 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2509 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2510 } else {
2511 tmp_cmd = desc->cmd;
2512 }
2513
2514 if(CMD_VARIABLE(tmp_cmd)) {
2515 argv[argc++] = vector_slot(vline, i);
2516 } else {
2517 argv[argc++] = tmp_cmd;
2518 }
2519 break;
2520 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002521 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002522 }
2523
2524 /* For vtysh execution. */
2525 if (cmd)
2526 *cmd = matched_element;
2527
2528 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002529 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002530 else {
2531 /* Execute matched command. */
2532 struct vty_parent_node this_node = {
2533 .node = vty->node,
2534 .priv = vty->priv,
2535 .indent = vty->indent,
2536 };
2537 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002538 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002539
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002540 /* If we have stepped down into a child node, push a parent frame.
2541 * The causality is such: we don't expect every single node entry implementation to push
2542 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2543 * a parent node. Hence if the node changed without the parent node changing, we must
2544 * have stepped into a child node. */
2545 if (vty->node != this_node.node && parent == vty_parent(vty)
2546 && vty->node > CONFIG_NODE) {
2547 /* Push the parent node. */
2548 parent = talloc_zero(vty, struct vty_parent_node);
2549 *parent = this_node;
2550 llist_add(&parent->entry, &vty->parent_nodes);
2551 }
2552 }
2553
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002554rc_free_deopt_ctx:
2555 /* Now after we called the command func, we can free temporary strings */
2556 talloc_free(cmd_deopt_ctx);
2557 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002558}
2559
2560int
2561cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2562 int vtysh)
2563{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002564 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002565 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002566
2567 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002568
2569 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2570 vector shifted_vline;
2571 unsigned int index;
2572
2573 vty->node = ENABLE_NODE;
2574 /* We can try it on enable node, cos' the vty is authenticated */
2575
2576 shifted_vline = vector_init(vector_count(vline));
2577 /* use memcpy? */
2578 for (index = 1; index < vector_active(vline); index++) {
2579 vector_set_index(shifted_vline, index - 1,
2580 vector_lookup(vline, index));
2581 }
2582
2583 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2584
2585 vector_free(shifted_vline);
2586 vty->node = onode;
2587 return ret;
2588 }
2589
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002590 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002591}
2592
2593/* Execute command by argument readline. */
2594int
2595cmd_execute_command_strict(vector vline, struct vty *vty,
2596 struct cmd_element **cmd)
2597{
2598 unsigned int i;
2599 unsigned int index;
2600 vector cmd_vector;
2601 struct cmd_element *cmd_element;
2602 struct cmd_element *matched_element;
2603 unsigned int matched_count, incomplete_count;
2604 int argc;
2605 const char *argv[CMD_ARGC_MAX];
2606 int varflag;
2607 enum match_type match = 0;
2608 char *command;
2609
2610 /* Make copy of command element */
2611 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2612
2613 for (index = 0; index < vector_active(vline); index++)
2614 if ((command = vector_slot(vline, index))) {
2615 int ret;
2616
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002617 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002618 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002619
2620 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002621 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002622 break;
2623
2624 ret =
2625 is_cmd_ambiguous(command, cmd_vector, index, match);
2626 if (ret == 1) {
2627 vector_free(cmd_vector);
2628 return CMD_ERR_AMBIGUOUS;
2629 }
2630 if (ret == 2) {
2631 vector_free(cmd_vector);
2632 return CMD_ERR_NO_MATCH;
2633 }
2634 }
2635
2636 /* Check matched count. */
2637 matched_element = NULL;
2638 matched_count = 0;
2639 incomplete_count = 0;
2640 for (i = 0; i < vector_active(cmd_vector); i++)
2641 if (vector_slot(cmd_vector, i) != NULL) {
2642 cmd_element = vector_slot(cmd_vector, i);
2643
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002644 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002645 || index >= cmd_element->cmdsize) {
2646 matched_element = cmd_element;
2647 matched_count++;
2648 } else
2649 incomplete_count++;
2650 }
2651
2652 /* Finish of using cmd_vector. */
2653 vector_free(cmd_vector);
2654
2655 /* To execute command, matched_count must be 1. */
2656 if (matched_count == 0) {
2657 if (incomplete_count)
2658 return CMD_ERR_INCOMPLETE;
2659 else
2660 return CMD_ERR_NO_MATCH;
2661 }
2662
2663 if (matched_count > 1)
2664 return CMD_ERR_AMBIGUOUS;
2665
2666 /* Argument treatment */
2667 varflag = 0;
2668 argc = 0;
2669
2670 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002671 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002672 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002673 if (varflag) {
2674 argv[argc++] = vector_slot(vline, i);
2675 continue;
2676 }
2677
2678 vector descvec = vector_slot(matched_element->strvec, i);
2679
2680 if (vector_active(descvec) == 1) {
2681 struct desc *desc = vector_slot(descvec, 0);
2682
2683 if (CMD_VARARG(desc->cmd))
2684 varflag = 1;
2685
2686 if (varflag || CMD_VARIABLE(desc->cmd)
2687 || CMD_OPTION(desc->cmd))
2688 argv[argc++] = vector_slot(vline, i);
2689 } else {
2690 argv[argc++] = vector_slot(vline, i);
2691 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002692 }
2693
2694 /* For vtysh execution. */
2695 if (cmd)
2696 *cmd = matched_element;
2697
2698 if (matched_element->daemon)
2699 return CMD_SUCCESS_DAEMON;
2700
2701 /* Now execute matched command */
2702 return (*matched_element->func) (matched_element, vty, argc, argv);
2703}
2704
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002705static inline size_t len(const char *str)
2706{
2707 return str? strlen(str) : 0;
2708}
2709
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002710/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2711 * is longer than b, a must start with exactly b, and vice versa.
2712 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2713 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002714static int indent_cmp(const char *a, const char *b)
2715{
2716 size_t al, bl;
2717 al = len(a);
2718 bl = len(b);
2719 if (al > bl) {
2720 if (bl && strncmp(a, b, bl) != 0)
2721 return EINVAL;
2722 return 1;
2723 }
2724 /* al <= bl */
2725 if (al && strncmp(a, b, al) != 0)
2726 return EINVAL;
2727 return (al < bl)? -1 : 0;
2728}
2729
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002730/* Configration make from file. */
2731int config_from_file(struct vty *vty, FILE * fp)
2732{
2733 int ret;
2734 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002735 char *indent;
2736 int cmp;
2737 struct vty_parent_node this_node;
2738 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002739
2740 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002741 indent = NULL;
2742 vline = NULL;
2743 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002744
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002745 if (ret != CMD_SUCCESS)
2746 goto return_invalid_indent;
2747
2748 /* In case of comment or empty line */
2749 if (vline == NULL) {
2750 if (indent) {
2751 talloc_free(indent);
2752 indent = NULL;
2753 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002754 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002755 }
2756
Neels Hofmeyr43063632017-09-19 23:54:01 +02002757 /* We have a nonempty line. */
2758 if (!vty->indent) {
2759 /* We have just entered a node and expecting the first child to come up; but we
2760 * may also skip right back to a parent or ancestor level. */
2761 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002762
Neels Hofmeyr43063632017-09-19 23:54:01 +02002763 /* If there is no parent, record any indentation we encounter. */
2764 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2765
2766 if (cmp == EINVAL)
2767 goto return_invalid_indent;
2768
2769 if (cmp <= 0) {
2770 /* We have gone right back to the parent level or higher, we are skipping
2771 * this child node level entirely. Pop the parent to go back to a node
2772 * that was actually there (to reinstate vty->indent) and re-use below
2773 * go-parent while-loop to find an accurate match of indent in the node
2774 * ancestry. */
2775 vty_go_parent(vty);
2776 } else {
2777 /* The indent is deeper than the just entered parent, record the new
2778 * indentation characters. */
2779 vty->indent = talloc_strdup(vty, indent);
2780 /* This *is* the new indentation. */
2781 cmp = 0;
2782 }
2783 } else {
2784 /* There is a known indentation for this node level, validate and detect node
2785 * exits. */
2786 cmp = indent_cmp(indent, vty->indent);
2787 if (cmp == EINVAL)
2788 goto return_invalid_indent;
2789 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002790
2791 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2792 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2793 while (cmp < 0) {
2794 vty_go_parent(vty);
2795 cmp = indent_cmp(indent, vty->indent);
2796 if (cmp == EINVAL)
2797 goto return_invalid_indent;
2798 }
2799
2800 /* More indent without having entered a child node level? Either the parent node's indent
2801 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2802 * or the indentation increased even though the vty command didn't enter a child. */
2803 if (cmp > 0)
2804 goto return_invalid_indent;
2805
2806 /* Remember the current node before the command possibly changes it. */
2807 this_node = (struct vty_parent_node){
2808 .node = vty->node,
2809 .priv = vty->priv,
2810 .indent = vty->indent,
2811 };
2812
2813 parent = vty_parent(vty);
2814 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002815 cmd_free_strvec(vline);
2816
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002817 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002818 if (indent) {
2819 talloc_free(indent);
2820 indent = NULL;
2821 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002822 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002823 }
2824
2825 /* If we have stepped down into a child node, push a parent frame.
2826 * The causality is such: we don't expect every single node entry implementation to push
2827 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2828 * a parent node. Hence if the node changed without the parent node changing, we must
2829 * have stepped into a child node (and now expect a deeper indent). */
2830 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2831 /* Push the parent node. */
2832 parent = talloc_zero(vty, struct vty_parent_node);
2833 *parent = this_node;
2834 llist_add(&parent->entry, &vty->parent_nodes);
2835
2836 /* The current talloc'ed vty->indent string will now be owned by this parent
2837 * struct. Indicate that we don't know what deeper indent characters the user
2838 * will choose. */
2839 vty->indent = NULL;
2840 }
2841
2842 if (indent) {
2843 talloc_free(indent);
2844 indent = NULL;
2845 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002846 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002847 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2848 while (vty_parent(vty))
2849 vty_go_parent(vty);
2850
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002851 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002852
2853return_invalid_indent:
2854 if (vline)
2855 cmd_free_strvec(vline);
2856 if (indent) {
2857 talloc_free(indent);
2858 indent = NULL;
2859 }
2860 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002861}
2862
2863/* Configration from terminal */
2864DEFUN(config_terminal,
2865 config_terminal_cmd,
2866 "configure terminal",
2867 "Configuration from vty interface\n" "Configuration terminal\n")
2868{
2869 if (vty_config_lock(vty))
2870 vty->node = CONFIG_NODE;
2871 else {
2872 vty_out(vty, "VTY configuration is locked by other VTY%s",
2873 VTY_NEWLINE);
2874 return CMD_WARNING;
2875 }
2876 return CMD_SUCCESS;
2877}
2878
2879/* Enable command */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002880DEFUN(enable, config_enable_cmd,
2881 "enable [expert-mode]",
2882 "Turn on privileged mode command\n"
2883 "Enable the expert mode (show hidden commands)\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002884{
2885 /* If enable password is NULL, change to ENABLE_NODE */
2886 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2887 vty->type == VTY_SHELL_SERV)
2888 vty->node = ENABLE_NODE;
2889 else
2890 vty->node = AUTH_ENABLE_NODE;
2891
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002892 host.expert_mode = argc > 0;
2893
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002894 return CMD_SUCCESS;
2895}
2896
2897/* Disable command */
2898DEFUN(disable,
2899 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2900{
2901 if (vty->node == ENABLE_NODE)
2902 vty->node = VIEW_NODE;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07002903
2904 host.expert_mode = false;
2905
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002906 return CMD_SUCCESS;
2907}
2908
2909/* Down vty node level. */
2910gDEFUN(config_exit,
2911 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2912{
2913 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002914 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002915 case VIEW_NODE:
2916 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002917 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002918 break;
2919 case CONFIG_NODE:
2920 vty->node = ENABLE_NODE;
2921 vty_config_unlock(vty);
2922 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002923 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002924 if (vty->node > CONFIG_NODE)
2925 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002926 break;
2927 }
2928 return CMD_SUCCESS;
2929}
2930
2931/* End of configuration. */
2932 gDEFUN(config_end,
2933 config_end_cmd, "end", "End current mode and change to enable mode.")
2934{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002935 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002936 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002937
2938 /* Repeatedly call go_parent until a top node is reached. */
2939 while (vty->node > CONFIG_NODE) {
2940 if (vty->node == last_node) {
2941 /* Ensure termination, this shouldn't happen. */
2942 break;
2943 }
2944 last_node = vty->node;
2945 vty_go_parent(vty);
2946 }
2947
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002948 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002949 if (vty->node > ENABLE_NODE)
2950 vty->node = ENABLE_NODE;
2951 vty->index = NULL;
2952 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002953 }
2954 return CMD_SUCCESS;
2955}
2956
2957/* Show version. */
2958DEFUN(show_version,
2959 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2960{
Harald Welte237f6242010-05-25 23:00:45 +02002961 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2962 host.app_info->version,
2963 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2964 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002965
2966 return CMD_SUCCESS;
2967}
2968
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002969DEFUN(show_online_help,
2970 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2971{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002972 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002973 return CMD_SUCCESS;
2974}
2975
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002976/* Help display function for all node. */
2977gDEFUN(config_help,
2978 config_help_cmd, "help", "Description of the interactive help system\n")
2979{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002980 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2981 "anytime at the command line please press '?'.%s%s"
2982 "If nothing matches, the help list will be empty and you must backup%s"
2983 " until entering a '?' shows the available options.%s"
2984 "Two styles of help are provided:%s"
2985 "1. Full help is available when you are ready to enter a%s"
2986 "command argument (e.g. 'show ?') and describes each possible%s"
2987 "argument.%s"
2988 "2. Partial help is provided when an abbreviated argument is entered%s"
2989 " and you want to know what arguments match the input%s"
2990 " (e.g. 'show me?'.)%s%s",
2991 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2992 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2993 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2994 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002995 return CMD_SUCCESS;
2996}
2997
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002998enum {
2999 ATTR_TYPE_GLOBAL = (1 << 0),
3000 ATTR_TYPE_LIB = (1 << 1),
3001 ATTR_TYPE_APP = (1 << 2),
3002};
3003
3004static void print_attr_list(struct vty *vty, unsigned int attr_mask)
3005{
3006 const char *desc;
3007 unsigned int i;
3008 bool found;
3009 char flag;
3010
3011 if (attr_mask & ATTR_TYPE_GLOBAL) {
3012 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
3013
3014 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003015 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003016 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003017
3018 /* Skip attributes without flags */
3019 if (flag != '.')
3020 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003021 }
3022 }
3023
3024 if (attr_mask & ATTR_TYPE_LIB) {
3025 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3026
3027 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3028 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3029 continue;
3030 found = true;
3031
3032 flag = cmd_lib_attr_letters[i];
3033 if (flag == '\0')
3034 flag = '.';
3035
3036 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3037 }
3038
3039 if (!found)
3040 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3041 }
3042
3043 if (attr_mask & ATTR_TYPE_APP) {
3044 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3045
3046 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3047 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3048 continue;
3049 found = true;
3050
3051 flag = host.app_info->usr_attr_letters[i];
3052 if (flag == '\0')
3053 flag = '.';
3054
3055 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3056 }
3057
3058 if (!found)
3059 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3060 }
3061}
3062
3063gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3064 "show vty-attributes",
3065 SHOW_STR "List of VTY attributes\n")
3066{
3067 print_attr_list(vty, 0xff);
3068 return CMD_SUCCESS;
3069}
3070
3071gDEFUN(show_vty_attr, show_vty_attr_cmd,
3072 "show vty-attributes (application|library|global)",
3073 SHOW_STR "List of VTY attributes\n"
3074 "Application specific attributes only\n"
3075 "Library specific attributes only\n"
3076 "Global attributes only\n")
3077{
3078 unsigned int attr_mask = 0;
3079
3080 if (argv[0][0] == 'g') /* global */
3081 attr_mask |= ATTR_TYPE_GLOBAL;
3082 else if (argv[0][0] == 'l') /* library */
3083 attr_mask |= ATTR_TYPE_LIB;
3084 else if (argv[0][0] == 'a') /* application */
3085 attr_mask |= ATTR_TYPE_APP;
3086
3087 print_attr_list(vty, attr_mask);
3088 return CMD_SUCCESS;
3089}
3090
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003091/* Compose flag bit-mask for all commands within the given node */
3092static unsigned int node_flag_mask(const struct cmd_node *cnode)
3093{
3094 unsigned int flag_mask = 0x00;
3095 unsigned int f, i;
3096
3097 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3098 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3099 const struct cmd_element *cmd;
3100 char flag_letter;
3101
3102 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3103 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003104 if (cmd->attr & CMD_ATTR_DEPRECATED)
3105 continue;
3106 if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003107 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003108 if (~cmd->usrattr & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003109 continue;
3110
3111 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3112 flag_letter = cmd_lib_attr_letters[f];
3113 else
3114 flag_letter = host.app_info->usr_attr_letters[f];
3115
3116 if (flag_letter == '\0')
3117 continue;
3118
3119 flag_mask |= (1 << f);
3120 break;
3121 }
3122 }
3123
3124 return flag_mask;
3125}
3126
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003127/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3128static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3129{
3130 static char char_mask[8 + 1];
3131 char *ptr = &char_mask[0];
3132
3133 /* Mutually exclusive global attributes */
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003134 if (cmd->attr & CMD_ATTR_HIDDEN)
3135 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
3136 else if (cmd->attr & CMD_ATTR_IMMEDIATE)
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003137 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3138 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3139 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3140 else
3141 *(ptr++) = '.';
3142
3143 *ptr = '\0';
3144
3145 return char_mask;
3146}
3147
3148/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003149static const char *cmd_flag_mask(const struct cmd_element *cmd,
3150 unsigned int flag_mask)
3151{
3152 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3153 char *ptr = &char_mask[0];
3154 char flag_letter;
3155 unsigned int f;
3156
3157 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003158 if (~flag_mask & ((unsigned)1 << f))
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003159 continue;
Pau Espin Pedrol715a6122020-10-12 15:47:05 +02003160 if (~cmd->usrattr & ((unsigned)1 << f)) {
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003161 *(ptr++) = '.';
3162 continue;
3163 }
3164
3165 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3166 flag_letter = cmd_lib_attr_letters[f];
3167 else
3168 flag_letter = host.app_info->usr_attr_letters[f];
3169
3170 *(ptr++) = flag_letter ? flag_letter : '.';
3171 }
3172
3173 *ptr = '\0';
3174
3175 return char_mask;
3176}
3177
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003178/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003179gDEFUN(config_list, config_list_cmd,
3180 "list [with-flags]",
3181 "Print command list\n"
3182 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003183{
3184 unsigned int i;
3185 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003186 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003187 struct cmd_element *cmd;
3188
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003189 if (argc > 0)
3190 flag_mask = node_flag_mask(cnode);
3191
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003192 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3193 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3194 continue;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07003195 if (cmd->attr & CMD_ATTR_DEPRECATED)
3196 continue;
3197 if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003198 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003199 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003200 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3201 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003202 vty_out(vty, " %s %s %s%s",
3203 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003204 cmd_flag_mask(cmd, flag_mask),
3205 cmd->string, VTY_NEWLINE);
3206 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003207 }
3208
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003209 return CMD_SUCCESS;
3210}
3211
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003212static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003213{
3214 unsigned int i;
3215 int fd;
3216 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003217 char *config_file_tmp = NULL;
3218 char *config_file_sav = NULL;
3219 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003220 struct stat st;
3221
3222 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003223
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003224 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3225 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3226 * manually instead. */
3227
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003228 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003229 config_file_sav =
3230 _talloc_zero(tall_vty_cmd_ctx,
3231 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3232 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003233 if (!config_file_sav)
3234 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003235 strcpy(config_file_sav, config_file);
3236 strcat(config_file_sav, CONF_BACKUP_EXT);
3237
3238 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003239 "config_file_tmp");
3240 if (!config_file_tmp) {
3241 talloc_free(config_file_sav);
3242 return -1;
3243 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003244 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3245
3246 /* Open file to configuration write. */
3247 fd = mkstemp(config_file_tmp);
3248 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003249 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003250 talloc_free(config_file_tmp);
3251 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003252 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003253 }
3254
3255 /* Make vty for configuration file. */
3256 file_vty = vty_new();
3257 file_vty->fd = fd;
3258 file_vty->type = VTY_FILE;
3259
3260 /* Config file header print. */
3261 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003262 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003263 //vty_time_print (file_vty, 1);
3264 vty_out(file_vty, "!\n");
3265
3266 for (i = 0; i < vector_active(cmdvec); i++)
3267 if ((node = vector_slot(cmdvec, i)) && node->func) {
3268 if ((*node->func) (file_vty))
3269 vty_out(file_vty, "!\n");
3270 }
3271 vty_close(file_vty);
3272
3273 if (unlink(config_file_sav) != 0)
3274 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003275 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003276 talloc_free(config_file_sav);
3277 talloc_free(config_file_tmp);
3278 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003279 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003280 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003281
3282 /* Only link the .sav file if the original file exists */
3283 if (stat(config_file, &st) == 0) {
3284 if (link(config_file, config_file_sav) != 0) {
3285 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3286 talloc_free(config_file_sav);
3287 talloc_free(config_file_tmp);
3288 unlink(config_file_tmp);
3289 return -3;
3290 }
3291 sync();
3292 if (unlink(config_file) != 0) {
3293 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3294 talloc_free(config_file_sav);
3295 talloc_free(config_file_tmp);
3296 unlink(config_file_tmp);
3297 return -4;
3298 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003299 }
3300 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003301 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003302 talloc_free(config_file_sav);
3303 talloc_free(config_file_tmp);
3304 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003305 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003306 }
3307 unlink(config_file_tmp);
3308 sync();
3309
3310 talloc_free(config_file_sav);
3311 talloc_free(config_file_tmp);
3312
3313 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003314 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3315 return -6;
3316 }
3317
3318 return 0;
3319}
3320
3321
3322/* Write current configuration into file. */
3323DEFUN(config_write_file,
3324 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003325 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003326 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003327 "Write to configuration file\n"
3328 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003329{
3330 char *failed_file;
3331 int rc;
3332
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003333 if (host.app_info->config_is_consistent) {
3334 rc = host.app_info->config_is_consistent(vty);
3335 if (!rc) {
3336 vty_out(vty, "Configuration is not consistent%s",
3337 VTY_NEWLINE);
3338 return CMD_WARNING;
3339 }
3340 }
3341
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003342 if (argc == 1)
3343 host_config_set(argv[0]);
3344
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003345 if (host.config == NULL) {
3346 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3347 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003348 return CMD_WARNING;
3349 }
3350
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003351 rc = write_config_file(host.config, &failed_file);
3352 switch (rc) {
3353 case -1:
3354 vty_out(vty, "Can't open configuration file %s.%s",
3355 failed_file, VTY_NEWLINE);
3356 rc = CMD_WARNING;
3357 break;
3358 case -2:
3359 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3360 failed_file, VTY_NEWLINE);
3361 rc = CMD_WARNING;
3362 break;
3363 case -3:
3364 vty_out(vty, "Can't backup old configuration file %s.%s",
3365 failed_file, VTY_NEWLINE);
3366 rc = CMD_WARNING;
3367 break;
3368 case -4:
3369 vty_out(vty, "Can't unlink configuration file %s.%s",
3370 failed_file, VTY_NEWLINE);
3371 rc = CMD_WARNING;
3372 break;
3373 case -5:
3374 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3375 VTY_NEWLINE);
3376 rc = CMD_WARNING;
3377 break;
3378 case -6:
3379 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3380 failed_file, strerror(errno), errno, VTY_NEWLINE);
3381 rc = CMD_WARNING;
3382 break;
3383 default:
3384 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3385 rc = CMD_SUCCESS;
3386 break;
3387 }
3388
3389 talloc_free(failed_file);
3390 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003391}
3392
3393ALIAS(config_write_file,
3394 config_write_cmd,
3395 "write", "Write running configuration to memory, network, or terminal\n")
3396
3397 ALIAS(config_write_file,
3398 config_write_memory_cmd,
3399 "write memory",
3400 "Write running configuration to memory, network, or terminal\n"
3401 "Write configuration to the file (same as write file)\n")
3402
3403 ALIAS(config_write_file,
3404 copy_runningconfig_startupconfig_cmd,
3405 "copy running-config startup-config",
3406 "Copy configuration\n"
3407 "Copy running config to... \n"
3408 "Copy running config to startup config (same as write file)\n")
3409
3410/* Write current configuration into the terminal. */
3411 DEFUN(config_write_terminal,
3412 config_write_terminal_cmd,
3413 "write terminal",
3414 "Write running configuration to memory, network, or terminal\n"
3415 "Write to terminal\n")
3416{
3417 unsigned int i;
3418 struct cmd_node *node;
3419
3420 if (vty->type == VTY_SHELL_SERV) {
3421 for (i = 0; i < vector_active(cmdvec); i++)
3422 if ((node = vector_slot(cmdvec, i)) && node->func
3423 && node->vtysh) {
3424 if ((*node->func) (vty))
3425 vty_out(vty, "!%s", VTY_NEWLINE);
3426 }
3427 } else {
3428 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3429 VTY_NEWLINE);
3430 vty_out(vty, "!%s", VTY_NEWLINE);
3431
3432 for (i = 0; i < vector_active(cmdvec); i++)
3433 if ((node = vector_slot(cmdvec, i)) && node->func) {
3434 if ((*node->func) (vty))
3435 vty_out(vty, "!%s", VTY_NEWLINE);
3436 }
3437 vty_out(vty, "end%s", VTY_NEWLINE);
3438 }
3439 return CMD_SUCCESS;
3440}
3441
3442/* Write current configuration into the terminal. */
3443ALIAS(config_write_terminal,
3444 show_running_config_cmd,
3445 "show running-config", SHOW_STR "running configuration\n")
3446
3447/* Write startup configuration into the terminal. */
3448 DEFUN(show_startup_config,
3449 show_startup_config_cmd,
3450 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3451{
3452 char buf[BUFSIZ];
3453 FILE *confp;
3454
3455 confp = fopen(host.config, "r");
3456 if (confp == NULL) {
3457 vty_out(vty, "Can't open configuration file [%s]%s",
3458 host.config, VTY_NEWLINE);
3459 return CMD_WARNING;
3460 }
3461
3462 while (fgets(buf, BUFSIZ, confp)) {
3463 char *cp = buf;
3464
3465 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3466 cp++;
3467 *cp = '\0';
3468
3469 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3470 }
3471
3472 fclose(confp);
3473
3474 return CMD_SUCCESS;
3475}
3476
3477/* Hostname configuration */
3478DEFUN(config_hostname,
3479 hostname_cmd,
3480 "hostname WORD",
3481 "Set system's network name\n" "This system's network name\n")
3482{
3483 if (!isalpha((int)*argv[0])) {
3484 vty_out(vty, "Please specify string starting with alphabet%s",
3485 VTY_NEWLINE);
3486 return CMD_WARNING;
3487 }
3488
3489 if (host.name)
3490 talloc_free(host.name);
3491
3492 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3493 return CMD_SUCCESS;
3494}
3495
3496DEFUN(config_no_hostname,
3497 no_hostname_cmd,
3498 "no hostname [HOSTNAME]",
3499 NO_STR "Reset system's network name\n" "Host name of this router\n")
3500{
3501 if (host.name)
3502 talloc_free(host.name);
3503 host.name = NULL;
3504 return CMD_SUCCESS;
3505}
3506
3507/* VTY interface password set. */
3508DEFUN(config_password, password_cmd,
3509 "password (8|) WORD",
3510 "Assign the terminal connection password\n"
3511 "Specifies a HIDDEN password will follow\n"
3512 "dummy string \n" "The HIDDEN line password string\n")
3513{
3514 /* Argument check. */
3515 if (argc == 0) {
3516 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3517 return CMD_WARNING;
3518 }
3519
3520 if (argc == 2) {
3521 if (*argv[0] == '8') {
3522 if (host.password)
3523 talloc_free(host.password);
3524 host.password = NULL;
3525 if (host.password_encrypt)
3526 talloc_free(host.password_encrypt);
3527 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3528 return CMD_SUCCESS;
3529 } else {
3530 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3531 return CMD_WARNING;
3532 }
3533 }
3534
3535 if (!isalnum((int)*argv[0])) {
3536 vty_out(vty,
3537 "Please specify string starting with alphanumeric%s",
3538 VTY_NEWLINE);
3539 return CMD_WARNING;
3540 }
3541
3542 if (host.password)
3543 talloc_free(host.password);
3544 host.password = NULL;
3545
3546#ifdef VTY_CRYPT_PW
3547 if (host.encrypt) {
3548 if (host.password_encrypt)
3549 talloc_free(host.password_encrypt);
3550 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3551 } else
3552#endif
3553 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3554
3555 return CMD_SUCCESS;
3556}
3557
3558ALIAS(config_password, password_text_cmd,
3559 "password LINE",
3560 "Assign the terminal connection password\n"
3561 "The UNENCRYPTED (cleartext) line password\n")
3562
3563/* VTY enable password set. */
3564 DEFUN(config_enable_password, enable_password_cmd,
3565 "enable password (8|) WORD",
3566 "Modify enable password parameters\n"
3567 "Assign the privileged level password\n"
3568 "Specifies a HIDDEN password will follow\n"
3569 "dummy string \n" "The HIDDEN 'enable' password string\n")
3570{
3571 /* Argument check. */
3572 if (argc == 0) {
3573 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3574 return CMD_WARNING;
3575 }
3576
3577 /* Crypt type is specified. */
3578 if (argc == 2) {
3579 if (*argv[0] == '8') {
3580 if (host.enable)
3581 talloc_free(host.enable);
3582 host.enable = NULL;
3583
3584 if (host.enable_encrypt)
3585 talloc_free(host.enable_encrypt);
3586 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3587
3588 return CMD_SUCCESS;
3589 } else {
3590 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3591 return CMD_WARNING;
3592 }
3593 }
3594
3595 if (!isalnum((int)*argv[0])) {
3596 vty_out(vty,
3597 "Please specify string starting with alphanumeric%s",
3598 VTY_NEWLINE);
3599 return CMD_WARNING;
3600 }
3601
3602 if (host.enable)
3603 talloc_free(host.enable);
3604 host.enable = NULL;
3605
3606 /* Plain password input. */
3607#ifdef VTY_CRYPT_PW
3608 if (host.encrypt) {
3609 if (host.enable_encrypt)
3610 talloc_free(host.enable_encrypt);
3611 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3612 } else
3613#endif
3614 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3615
3616 return CMD_SUCCESS;
3617}
3618
3619ALIAS(config_enable_password,
3620 enable_password_text_cmd,
3621 "enable password LINE",
3622 "Modify enable password parameters\n"
3623 "Assign the privileged level password\n"
3624 "The UNENCRYPTED (cleartext) 'enable' password\n")
3625
3626/* VTY enable password delete. */
3627 DEFUN(no_config_enable_password, no_enable_password_cmd,
3628 "no enable password",
3629 NO_STR
3630 "Modify enable password parameters\n"
3631 "Assign the privileged level password\n")
3632{
3633 if (host.enable)
3634 talloc_free(host.enable);
3635 host.enable = NULL;
3636
3637 if (host.enable_encrypt)
3638 talloc_free(host.enable_encrypt);
3639 host.enable_encrypt = NULL;
3640
3641 return CMD_SUCCESS;
3642}
3643
3644#ifdef VTY_CRYPT_PW
3645DEFUN(service_password_encrypt,
3646 service_password_encrypt_cmd,
3647 "service password-encryption",
3648 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3649{
3650 if (host.encrypt)
3651 return CMD_SUCCESS;
3652
3653 host.encrypt = 1;
3654
3655 if (host.password) {
3656 if (host.password_encrypt)
3657 talloc_free(host.password_encrypt);
3658 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3659 }
3660 if (host.enable) {
3661 if (host.enable_encrypt)
3662 talloc_free(host.enable_encrypt);
3663 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3664 }
3665
3666 return CMD_SUCCESS;
3667}
3668
3669DEFUN(no_service_password_encrypt,
3670 no_service_password_encrypt_cmd,
3671 "no service password-encryption",
3672 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3673{
3674 if (!host.encrypt)
3675 return CMD_SUCCESS;
3676
3677 host.encrypt = 0;
3678
3679 if (host.password_encrypt)
3680 talloc_free(host.password_encrypt);
3681 host.password_encrypt = NULL;
3682
3683 if (host.enable_encrypt)
3684 talloc_free(host.enable_encrypt);
3685 host.enable_encrypt = NULL;
3686
3687 return CMD_SUCCESS;
3688}
3689#endif
3690
3691DEFUN(config_terminal_length, config_terminal_length_cmd,
3692 "terminal length <0-512>",
3693 "Set terminal line parameters\n"
3694 "Set number of lines on a screen\n"
3695 "Number of lines on screen (0 for no pausing)\n")
3696{
3697 int lines;
3698 char *endptr = NULL;
3699
3700 lines = strtol(argv[0], &endptr, 10);
3701 if (lines < 0 || lines > 512 || *endptr != '\0') {
3702 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3703 return CMD_WARNING;
3704 }
3705 vty->lines = lines;
3706
3707 return CMD_SUCCESS;
3708}
3709
3710DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3711 "terminal no length",
3712 "Set terminal line parameters\n"
3713 NO_STR "Set number of lines on a screen\n")
3714{
3715 vty->lines = -1;
3716 return CMD_SUCCESS;
3717}
3718
3719DEFUN(service_terminal_length, service_terminal_length_cmd,
3720 "service terminal-length <0-512>",
3721 "Set up miscellaneous service\n"
3722 "System wide terminal length configuration\n"
3723 "Number of lines of VTY (0 means no line control)\n")
3724{
3725 int lines;
3726 char *endptr = NULL;
3727
3728 lines = strtol(argv[0], &endptr, 10);
3729 if (lines < 0 || lines > 512 || *endptr != '\0') {
3730 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3731 return CMD_WARNING;
3732 }
3733 host.lines = lines;
3734
3735 return CMD_SUCCESS;
3736}
3737
3738DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3739 "no service terminal-length [<0-512>]",
3740 NO_STR
3741 "Set up miscellaneous service\n"
3742 "System wide terminal length configuration\n"
3743 "Number of lines of VTY (0 means no line control)\n")
3744{
3745 host.lines = -1;
3746 return CMD_SUCCESS;
3747}
3748
3749DEFUN_HIDDEN(do_echo,
3750 echo_cmd,
3751 "echo .MESSAGE",
3752 "Echo a message back to the vty\n" "The message to echo\n")
3753{
3754 char *message;
3755
3756 vty_out(vty, "%s%s",
3757 ((message =
3758 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3759 if (message)
3760 talloc_free(message);
3761 return CMD_SUCCESS;
3762}
3763
3764#if 0
3765DEFUN(config_logmsg,
3766 config_logmsg_cmd,
3767 "logmsg " LOG_LEVELS " .MESSAGE",
3768 "Send a message to enabled logging destinations\n"
3769 LOG_LEVEL_DESC "The message to send\n")
3770{
3771 int level;
3772 char *message;
3773
3774 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3775 return CMD_ERR_NO_MATCH;
3776
3777 zlog(NULL, level,
3778 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3779 if (message)
3780 talloc_free(message);
3781 return CMD_SUCCESS;
3782}
3783
3784DEFUN(show_logging,
3785 show_logging_cmd,
3786 "show logging", SHOW_STR "Show current logging configuration\n")
3787{
3788 struct zlog *zl = zlog_default;
3789
3790 vty_out(vty, "Syslog logging: ");
3791 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3792 vty_out(vty, "disabled");
3793 else
3794 vty_out(vty, "level %s, facility %s, ident %s",
3795 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3796 facility_name(zl->facility), zl->ident);
3797 vty_out(vty, "%s", VTY_NEWLINE);
3798
3799 vty_out(vty, "Stdout logging: ");
3800 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3801 vty_out(vty, "disabled");
3802 else
3803 vty_out(vty, "level %s",
3804 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3805 vty_out(vty, "%s", VTY_NEWLINE);
3806
3807 vty_out(vty, "Monitor logging: ");
3808 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3809 vty_out(vty, "disabled");
3810 else
3811 vty_out(vty, "level %s",
3812 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3813 vty_out(vty, "%s", VTY_NEWLINE);
3814
3815 vty_out(vty, "File logging: ");
3816 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3817 vty_out(vty, "disabled");
3818 else
3819 vty_out(vty, "level %s, filename %s",
3820 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3821 zl->filename);
3822 vty_out(vty, "%s", VTY_NEWLINE);
3823
3824 vty_out(vty, "Protocol name: %s%s",
3825 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3826 vty_out(vty, "Record priority: %s%s",
3827 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3828
3829 return CMD_SUCCESS;
3830}
3831
3832DEFUN(config_log_stdout,
3833 config_log_stdout_cmd,
3834 "log stdout", "Logging control\n" "Set stdout logging level\n")
3835{
3836 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3837 return CMD_SUCCESS;
3838}
3839
3840DEFUN(config_log_stdout_level,
3841 config_log_stdout_level_cmd,
3842 "log stdout " LOG_LEVELS,
3843 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3844{
3845 int level;
3846
3847 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3848 return CMD_ERR_NO_MATCH;
3849 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3850 return CMD_SUCCESS;
3851}
3852
3853DEFUN(no_config_log_stdout,
3854 no_config_log_stdout_cmd,
3855 "no log stdout [LEVEL]",
3856 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3857{
3858 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3859 return CMD_SUCCESS;
3860}
3861
3862DEFUN(config_log_monitor,
3863 config_log_monitor_cmd,
3864 "log monitor",
3865 "Logging control\n" "Set terminal line (monitor) logging level\n")
3866{
3867 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3868 return CMD_SUCCESS;
3869}
3870
3871DEFUN(config_log_monitor_level,
3872 config_log_monitor_level_cmd,
3873 "log monitor " LOG_LEVELS,
3874 "Logging control\n"
3875 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3876{
3877 int level;
3878
3879 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3880 return CMD_ERR_NO_MATCH;
3881 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3882 return CMD_SUCCESS;
3883}
3884
3885DEFUN(no_config_log_monitor,
3886 no_config_log_monitor_cmd,
3887 "no log monitor [LEVEL]",
3888 NO_STR
3889 "Logging control\n"
3890 "Disable terminal line (monitor) logging\n" "Logging level\n")
3891{
3892 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3893 return CMD_SUCCESS;
3894}
3895
3896static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3897{
3898 int ret;
3899 char *p = NULL;
3900 const char *fullpath;
3901
3902 /* Path detection. */
3903 if (!IS_DIRECTORY_SEP(*fname)) {
3904 char cwd[MAXPATHLEN + 1];
3905 cwd[MAXPATHLEN] = '\0';
3906
3907 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3908 zlog_err("config_log_file: Unable to alloc mem!");
3909 return CMD_WARNING;
3910 }
3911
3912 if ((p = _talloc_zero(tall_vcmd_ctx,
3913 strlen(cwd) + strlen(fname) + 2),
3914 "set_log_file")
3915 == NULL) {
3916 zlog_err("config_log_file: Unable to alloc mem!");
3917 return CMD_WARNING;
3918 }
3919 sprintf(p, "%s/%s", cwd, fname);
3920 fullpath = p;
3921 } else
3922 fullpath = fname;
3923
3924 ret = zlog_set_file(NULL, fullpath, loglevel);
3925
3926 if (p)
3927 talloc_free(p);
3928
3929 if (!ret) {
3930 vty_out(vty, "can't open logfile %s\n", fname);
3931 return CMD_WARNING;
3932 }
3933
3934 if (host.logfile)
3935 talloc_free(host.logfile);
3936
3937 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3938
3939 return CMD_SUCCESS;
3940}
3941
3942DEFUN(config_log_file,
3943 config_log_file_cmd,
3944 "log file FILENAME",
3945 "Logging control\n" "Logging to file\n" "Logging filename\n")
3946{
3947 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3948}
3949
3950DEFUN(config_log_file_level,
3951 config_log_file_level_cmd,
3952 "log file FILENAME " LOG_LEVELS,
3953 "Logging control\n"
3954 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3955{
3956 int level;
3957
3958 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3959 return CMD_ERR_NO_MATCH;
3960 return set_log_file(vty, argv[0], level);
3961}
3962
3963DEFUN(no_config_log_file,
3964 no_config_log_file_cmd,
3965 "no log file [FILENAME]",
3966 NO_STR
3967 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3968{
3969 zlog_reset_file(NULL);
3970
3971 if (host.logfile)
3972 talloc_free(host.logfile);
3973
3974 host.logfile = NULL;
3975
3976 return CMD_SUCCESS;
3977}
3978
3979ALIAS(no_config_log_file,
3980 no_config_log_file_level_cmd,
3981 "no log file FILENAME LEVEL",
3982 NO_STR
3983 "Logging control\n"
3984 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3985
3986 DEFUN(config_log_syslog,
3987 config_log_syslog_cmd,
3988 "log syslog", "Logging control\n" "Set syslog logging level\n")
3989{
3990 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3991 return CMD_SUCCESS;
3992}
3993
3994DEFUN(config_log_syslog_level,
3995 config_log_syslog_level_cmd,
3996 "log syslog " LOG_LEVELS,
3997 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3998{
3999 int level;
4000
4001 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
4002 return CMD_ERR_NO_MATCH;
4003 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
4004 return CMD_SUCCESS;
4005}
4006
4007DEFUN_DEPRECATED(config_log_syslog_facility,
4008 config_log_syslog_facility_cmd,
4009 "log syslog facility " LOG_FACILITIES,
4010 "Logging control\n"
4011 "Logging goes to syslog\n"
4012 "(Deprecated) Facility parameter for syslog messages\n"
4013 LOG_FACILITY_DESC)
4014{
4015 int facility;
4016
4017 if ((facility = facility_match(argv[0])) < 0)
4018 return CMD_ERR_NO_MATCH;
4019
4020 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4021 zlog_default->facility = facility;
4022 return CMD_SUCCESS;
4023}
4024
4025DEFUN(no_config_log_syslog,
4026 no_config_log_syslog_cmd,
4027 "no log syslog [LEVEL]",
4028 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4029{
4030 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4031 return CMD_SUCCESS;
4032}
4033
4034ALIAS(no_config_log_syslog,
4035 no_config_log_syslog_facility_cmd,
4036 "no log syslog facility " LOG_FACILITIES,
4037 NO_STR
4038 "Logging control\n"
4039 "Logging goes to syslog\n"
4040 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4041
4042 DEFUN(config_log_facility,
4043 config_log_facility_cmd,
4044 "log facility " LOG_FACILITIES,
4045 "Logging control\n"
4046 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4047{
4048 int facility;
4049
4050 if ((facility = facility_match(argv[0])) < 0)
4051 return CMD_ERR_NO_MATCH;
4052 zlog_default->facility = facility;
4053 return CMD_SUCCESS;
4054}
4055
4056DEFUN(no_config_log_facility,
4057 no_config_log_facility_cmd,
4058 "no log facility [FACILITY]",
4059 NO_STR
4060 "Logging control\n"
4061 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4062{
4063 zlog_default->facility = LOG_DAEMON;
4064 return CMD_SUCCESS;
4065}
4066
4067DEFUN_DEPRECATED(config_log_trap,
4068 config_log_trap_cmd,
4069 "log trap " LOG_LEVELS,
4070 "Logging control\n"
4071 "(Deprecated) Set logging level and default for all destinations\n"
4072 LOG_LEVEL_DESC)
4073{
4074 int new_level;
4075 int i;
4076
4077 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4078 return CMD_ERR_NO_MATCH;
4079
4080 zlog_default->default_lvl = new_level;
4081 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4082 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4083 zlog_default->maxlvl[i] = new_level;
4084 return CMD_SUCCESS;
4085}
4086
4087DEFUN_DEPRECATED(no_config_log_trap,
4088 no_config_log_trap_cmd,
4089 "no log trap [LEVEL]",
4090 NO_STR
4091 "Logging control\n"
4092 "Permit all logging information\n" "Logging level\n")
4093{
4094 zlog_default->default_lvl = LOG_DEBUG;
4095 return CMD_SUCCESS;
4096}
4097
4098DEFUN(config_log_record_priority,
4099 config_log_record_priority_cmd,
4100 "log record-priority",
4101 "Logging control\n"
4102 "Log the priority of the message within the message\n")
4103{
4104 zlog_default->record_priority = 1;
4105 return CMD_SUCCESS;
4106}
4107
4108DEFUN(no_config_log_record_priority,
4109 no_config_log_record_priority_cmd,
4110 "no log record-priority",
4111 NO_STR
4112 "Logging control\n"
4113 "Do not log the priority of the message within the message\n")
4114{
4115 zlog_default->record_priority = 0;
4116 return CMD_SUCCESS;
4117}
4118#endif
4119
4120DEFUN(banner_motd_file,
4121 banner_motd_file_cmd,
4122 "banner motd file [FILE]",
4123 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4124{
4125 if (host.motdfile)
4126 talloc_free(host.motdfile);
4127 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4128
4129 return CMD_SUCCESS;
4130}
4131
4132DEFUN(banner_motd_default,
4133 banner_motd_default_cmd,
4134 "banner motd default",
4135 "Set banner string\n" "Strings for motd\n" "Default string\n")
4136{
4137 host.motd = default_motd;
4138 return CMD_SUCCESS;
4139}
4140
4141DEFUN(no_banner_motd,
4142 no_banner_motd_cmd,
4143 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4144{
4145 host.motd = NULL;
4146 if (host.motdfile)
4147 talloc_free(host.motdfile);
4148 host.motdfile = NULL;
4149 return CMD_SUCCESS;
4150}
4151
4152/* Set config filename. Called from vty.c */
4153void host_config_set(const char *filename)
4154{
4155 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4156}
4157
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004158/*! Deprecated, now happens implicitly when calling install_node().
4159 * Users of the API may still attempt to call this function, hence
4160 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004161void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004162{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004163}
4164
4165/*! Deprecated, now happens implicitly when calling install_node().
4166 * Users of the API may still attempt to call this function, hence
4167 * leave it here as a no-op. */
4168void vty_install_default(int node)
4169{
4170}
4171
4172/*! Install common commands like 'exit' and 'list'. */
4173static void install_basic_node_commands(int node)
4174{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004175 install_lib_element(node, &config_help_cmd);
4176 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004177
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004178 install_lib_element(node, &show_vty_attr_all_cmd);
4179 install_lib_element(node, &show_vty_attr_cmd);
4180
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004181 install_lib_element(node, &config_write_terminal_cmd);
4182 install_lib_element(node, &config_write_file_cmd);
4183 install_lib_element(node, &config_write_memory_cmd);
4184 install_lib_element(node, &config_write_cmd);
4185 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004186
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004187 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004188
4189 if (node >= CONFIG_NODE) {
4190 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004191 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004192 }
4193}
4194
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004195/*! Return true if a node is installed by install_basic_node_commands(), so
4196 * that we can avoid repeating them for each and every node during 'show
4197 * running-config' */
4198static bool vty_command_is_common(struct cmd_element *cmd)
4199{
4200 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004201 || cmd == &show_vty_attr_all_cmd
4202 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004203 || cmd == &config_list_cmd
4204 || cmd == &config_write_terminal_cmd
4205 || cmd == &config_write_file_cmd
4206 || cmd == &config_write_memory_cmd
4207 || cmd == &config_write_cmd
4208 || cmd == &show_running_config_cmd
4209 || cmd == &config_exit_cmd
4210 || cmd == &config_end_cmd)
4211 return true;
4212 return false;
4213}
4214
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004215/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004216 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004217 * \param[in] vty the vty of the code
4218 * \param[in] filename where to store the file
4219 * \return 0 in case of success.
4220 *
4221 * If the filename already exists create a filename.sav
4222 * version with the current code.
4223 *
4224 */
4225int osmo_vty_write_config_file(const char *filename)
4226{
4227 char *failed_file;
4228 int rc;
4229
4230 rc = write_config_file(filename, &failed_file);
4231 talloc_free(failed_file);
4232 return rc;
4233}
4234
4235/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004236 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004237 * \return 0 in case of success.
4238 *
4239 * If the filename already exists create a filename.sav
4240 * version with the current code.
4241 *
4242 */
4243int osmo_vty_save_config_file(void)
4244{
4245 char *failed_file;
4246 int rc;
4247
4248 if (host.config == NULL)
4249 return -7;
4250
4251 rc = write_config_file(host.config, &failed_file);
4252 talloc_free(failed_file);
4253 return rc;
4254}
4255
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004256/* Initialize command interface. Install basic nodes and commands. */
4257void cmd_init(int terminal)
4258{
4259 /* Allocate initial top vector of commands. */
4260 cmdvec = vector_init(VECTOR_MIN_SIZE);
4261
4262 /* Default host value settings. */
4263 host.name = NULL;
4264 host.password = NULL;
4265 host.enable = NULL;
4266 host.logfile = NULL;
4267 host.config = NULL;
4268 host.lines = -1;
4269 host.motd = default_motd;
4270 host.motdfile = NULL;
Vadim Yanitskiy72b90882020-10-21 05:07:34 +07004271 host.expert_mode = false;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004272
4273 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004274 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004275 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004276 install_node_bare(&auth_node, NULL);
4277 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004278 install_node(&config_node, config_write_host);
4279
4280 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004281 install_lib_element(VIEW_NODE, &show_version_cmd);
4282 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004283 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004284 install_lib_element(VIEW_NODE, &config_list_cmd);
4285 install_lib_element(VIEW_NODE, &config_exit_cmd);
4286 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004287 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4288 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004289 install_lib_element(VIEW_NODE, &config_enable_cmd);
4290 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4291 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4292 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004293 }
4294
4295 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004296 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4297 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4298 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004299 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004300 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4301 install_lib_element(ENABLE_NODE, &show_version_cmd);
4302 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004303
4304 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004305 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4306 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4307 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004308 }
4309
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004310 install_lib_element(CONFIG_NODE, &hostname_cmd);
4311 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004312
4313 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004314 install_lib_element(CONFIG_NODE, &password_cmd);
4315 install_lib_element(CONFIG_NODE, &password_text_cmd);
4316 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4317 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4318 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004319
4320#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004321 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4322 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004323#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004324 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4325 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4326 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4327 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4328 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004329
4330 }
4331 srand(time(NULL));
4332}
Harald Welte7acb30c2011-08-17 17:13:48 +02004333
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004334/* FIXME: execute this section in the unit test instead */
4335static __attribute__((constructor)) void on_dso_load(void)
4336{
4337 unsigned int i, j;
4338
4339 /* Check total number of the library specific attributes */
4340 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4341
4342 /* Check for duplicates in the list of library specific flags */
4343 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4344 if (cmd_lib_attr_letters[i] == '\0')
4345 continue;
4346
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004347 /* Some flag characters are reserved for global attributes */
4348 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4349 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4350 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4351
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004352 /* Only upper case flag letters are allowed for libraries */
4353 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4354 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4355
4356 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4357 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4358 }
4359}
4360
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004361/*! @} */