blob: fae925ea10022591768fee160bb19c0af6766101 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020041#include <sys/time.h>
42#include <sys/stat.h>
43
44#include <osmocom/vty/vector.h>
45#include <osmocom/vty/vty.h>
46#include <osmocom/vty/command.h>
47
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010048#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010049#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050
Ruben Undheim766f77c2018-11-18 13:02:47 +010051#ifndef MAXPATHLEN
52 #define MAXPATHLEN 4096
53#endif
54
55
Harald Weltee881b1b2011-08-17 18:52:30 +020056/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020057 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020059 *
60 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020061
Harald Welte3fb0b6f2010-05-19 19:02:52 +020062#define CONFIGFILE_MASK 022
63
64void *tall_vty_cmd_ctx;
65
66/* Command vector which includes some level of command lists. Normally
67 each daemon maintains each own cmdvec. */
68vector cmdvec;
69
70/* Host information structure. */
71struct host host;
72
73/* Standard command node structures. */
74struct cmd_node auth_node = {
75 AUTH_NODE,
76 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node view_node = {
81 VIEW_NODE,
82 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node auth_enable_node = {
87 AUTH_ENABLE_NODE,
88 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node enable_node = {
93 ENABLE_NODE,
94 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010095 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096};
97
98struct cmd_node config_node = {
99 CONFIG_NODE,
100 "%s(config)# ",
101 1
102};
103
104/* Default motd string. */
105const char *default_motd = "";
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200108 *
109 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110void print_version(int print_copyright)
111{
Harald Welte237f6242010-05-25 23:00:45 +0200112 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200114 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115}
116
117/* Utility function to concatenate argv argument into a single string
118 with inserting ' ' character between each argument. */
119char *argv_concat(const char **argv, int argc, int shift)
120{
121 int i;
122 size_t len;
123 char *str;
124 char *p;
125
126 len = 0;
127 for (i = shift; i < argc; i++)
128 len += strlen(argv[i]) + 1;
129 if (!len)
130 return NULL;
131 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
132 for (i = shift; i < argc; i++) {
133 size_t arglen;
134 memcpy(p, argv[i], (arglen = strlen(argv[i])));
135 p += arglen;
136 *p++ = ' ';
137 }
138 *(p - 1) = '\0';
139 return str;
140}
141
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200142/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
143 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
144 * in turn, this name us used for XML IDs in 'show online-help'. */
145static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
146{
147 const char *pos;
148 int dest = 0;
149
150 if (!prompt || !*prompt)
151 return "";
152
153 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
154 if (pos[0] == '%' && pos[1]) {
155 /* skip "%s"; loop pos++ does the second one. */
156 pos++;
157 continue;
158 }
159 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
160 continue;
161 name_buf[dest] = pos[0];
162 dest++;
163 }
164 name_buf[dest] = '\0';
165 return name_buf;
166}
167
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200168static void install_basic_node_commands(int node);
169
170/*! Install top node of command vector, without adding basic node commands. */
171static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172{
173 vector_set_index(cmdvec, node->node, node);
174 node->func = func;
175 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200176 if (!*node->name)
177 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200178}
179
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200180/*! Install top node of command vector. */
181void install_node(struct cmd_node *node, int (*func) (struct vty *))
182{
183 install_node_bare(node, func);
184 install_basic_node_commands(node->node);
185}
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187/* Compare two command's string. Used in sort_node (). */
188static int cmp_node(const void *p, const void *q)
189{
190 struct cmd_element *a = *(struct cmd_element **)p;
191 struct cmd_element *b = *(struct cmd_element **)q;
192
193 return strcmp(a->string, b->string);
194}
195
196static int cmp_desc(const void *p, const void *q)
197{
198 struct desc *a = *(struct desc **)p;
199 struct desc *b = *(struct desc **)q;
200
201 return strcmp(a->cmd, b->cmd);
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200205void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200206{
207 unsigned int i, j;
208 struct cmd_node *cnode;
209 vector descvec;
210 struct cmd_element *cmd_element;
211
212 for (i = 0; i < vector_active(cmdvec); i++)
213 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
214 vector cmd_vector = cnode->cmd_vector;
215 qsort(cmd_vector->index, vector_active(cmd_vector),
216 sizeof(void *), cmp_node);
217
218 for (j = 0; j < vector_active(cmd_vector); j++)
219 if ((cmd_element =
220 vector_slot(cmd_vector, j)) != NULL
221 && vector_active(cmd_element->strvec)) {
222 descvec =
223 vector_slot(cmd_element->strvec,
224 vector_active
225 (cmd_element->strvec) -
226 1);
227 qsort(descvec->index,
228 vector_active(descvec),
229 sizeof(void *), cmp_desc);
230 }
231 }
232}
233
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200234/*! Break up string in command tokens. Return leading indents.
235 * \param[in] string String to split.
236 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
237 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
238 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
239 *
240 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
241 * so that \a indent can simply return the count of leading spaces.
242 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
243 */
244int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245{
246 const char *cp, *start;
247 char *token;
248 int strlen;
249 vector strvec;
250
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200251 *strvec_p = NULL;
252 if (indent)
253 *indent = 0;
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200257
258 cp = string;
259
260 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200261 while (isspace((int)*cp) && *cp != '\0') {
262 /* if we're counting indents, we need to be strict about them */
263 if (indent && (*cp != ' ') && (*cp != '\t')) {
264 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
265 if (*cp == '\n' || *cp == '\r') {
266 cp++;
267 string = cp;
268 continue;
269 }
270 return CMD_ERR_INVALID_INDENT;
271 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 }
274
275 if (indent)
276 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277
278 /* Return if there is only white spaces */
279 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Prepare return vector. */
286 strvec = vector_init(VECTOR_MIN_SIZE);
287
288 /* Copy each command piece and set into vector. */
289 while (1) {
290 start = cp;
291 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
292 *cp != '\0')
293 cp++;
294 strlen = cp - start;
295 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
296 memcpy(token, start, strlen);
297 *(token + strlen) = '\0';
298 vector_set(strvec, token);
299
300 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
301 *cp != '\0')
302 cp++;
303
304 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200307
308 *strvec_p = strvec;
309 return CMD_SUCCESS;
310}
311
312/*! Breaking up string into each command piece. I assume given
313 character is separated by a space character. Return value is a
314 vector which includes char ** data element. */
315vector cmd_make_strvec(const char *string)
316{
317 vector strvec;
318 cmd_make_strvec2(string, NULL, &strvec);
319 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200320}
321
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200322/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200323void cmd_free_strvec(vector v)
324{
325 unsigned int i;
326 char *cp;
327
328 if (!v)
329 return;
330
331 for (i = 0; i < vector_active(v); i++)
332 if ((cp = vector_slot(v, i)) != NULL)
333 talloc_free(cp);
334
335 vector_free(v);
336}
337
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200338/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200339static char *cmd_desc_str(const char **string)
340{
341 const char *cp, *start;
342 char *token;
343 int strlen;
344
345 cp = *string;
346
347 if (cp == NULL)
348 return NULL;
349
350 /* Skip white spaces. */
351 while (isspace((int)*cp) && *cp != '\0')
352 cp++;
353
354 /* Return if there is only white spaces */
355 if (*cp == '\0')
356 return NULL;
357
358 start = cp;
359
360 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
361 cp++;
362
363 strlen = cp - start;
364 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
365 memcpy(token, start, strlen);
366 *(token + strlen) = '\0';
367
368 *string = cp;
369
370 return token;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200374static vector cmd_make_descvec(const char *string, const char *descstr)
375{
376 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100377 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378 const char *sp;
379 char *token;
380 int len;
381 const char *cp;
382 const char *dp;
383 vector allvec;
384 vector strvec = NULL;
385 struct desc *desc;
386
387 cp = string;
388 dp = descstr;
389
390 if (cp == NULL)
391 return NULL;
392
393 allvec = vector_init(VECTOR_MIN_SIZE);
394
395 while (1) {
396 while (isspace((int)*cp) && *cp != '\0')
397 cp++;
398
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
400 if (cp[0] == '[' && cp[1] == '(') {
401 optional_brace = 1;
402 cp++;
403 }
404
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200405 if (*cp == '(') {
406 multiple = 1;
407 cp++;
408 }
409 if (*cp == ')') {
410 multiple = 0;
411 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100412 if (*cp == ']')
413 cp++;
414 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 }
416 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100417 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 cp++;
419 }
420
421 while (isspace((int)*cp) && *cp != '\0')
422 cp++;
423
424 if (*cp == '(') {
425 multiple = 1;
426 cp++;
427 }
428
429 if (*cp == '\0')
430 return allvec;
431
432 sp = cp;
433
434 while (!
435 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
436 || *cp == ')' || *cp == '|') && *cp != '\0')
437 cp++;
438
439 len = cp - sp;
440
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100441 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
442 if (optional_brace) {
443 /* Place each individual multi-choice token in its own square braces */
444 token[0] = '[';
445 memcpy(token + 1, sp, len);
446 token[1 + len] = ']';
447 token[2 + len] = '\0';
448 } else {
449 memcpy(token, sp, len);
450 *(token + len) = '\0';
451 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200452
453 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
454 desc->cmd = token;
455 desc->str = cmd_desc_str(&dp);
456
457 if (multiple) {
458 if (multiple == 1) {
459 strvec = vector_init(VECTOR_MIN_SIZE);
460 vector_set(allvec, strvec);
461 }
462 multiple++;
463 } else {
464 strvec = vector_init(VECTOR_MIN_SIZE);
465 vector_set(allvec, strvec);
466 }
467 vector_set(strvec, desc);
468 }
469}
470
471/* Count mandantory string vector size. This is to determine inputed
472 command has enough command length. */
473static int cmd_cmdsize(vector strvec)
474{
475 unsigned int i;
476 int size = 0;
477 vector descvec;
478 struct desc *desc;
479
480 for (i = 0; i < vector_active(strvec); i++)
481 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100482 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483 && (desc = vector_slot(descvec, 0)) != NULL) {
484 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
485 return size;
486 else
487 size++;
488 } else
489 size++;
490 }
491 return size;
492}
493
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200494/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200495const char *cmd_prompt(enum node_type node)
496{
497 struct cmd_node *cnode;
498
499 cnode = vector_slot(cmdvec, node);
500 return cnode->prompt;
501}
502
Alexander Couzensad580ba2016-05-16 16:01:45 +0200503/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200505 * \param unsafe string
506 * \return a new talloc char *
507 */
508char *osmo_asciidoc_escape(const char *inp)
509{
510 int _strlen;
511 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200512 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200513
514 if (!inp)
515 return NULL;
516 _strlen = strlen(inp);
517
518 for (i = 0; i < _strlen; ++i) {
519 switch (inp[i]) {
520 case '|':
521 len += 2;
522 break;
523 default:
524 len += 1;
525 break;
526 }
527 }
528
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200529 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200530 if (!out)
531 return NULL;
532
533 out_ptr = out;
534
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200538 /* Prepend escape character "\": */
539 *(out_ptr++) = '\\';
540 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 default:
542 *(out_ptr++) = inp[i];
543 break;
544 }
545 }
546
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 out_ptr[0] = '\0';
548 return out;
549}
550
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100551static char *xml_escape(const char *inp)
552{
553 int _strlen;
554 char *out, *out_ptr;
555 int len = 0, i, j;
556
557 if (!inp)
558 return NULL;
559 _strlen = strlen(inp);
560
561 for (i = 0; i < _strlen; ++i) {
562 switch (inp[i]) {
563 case '"':
564 len += 6;
565 break;
566 case '\'':
567 len += 6;
568 break;
569 case '<':
570 len += 4;
571 break;
572 case '>':
573 len += 4;
574 break;
575 case '&':
576 len += 5;
577 break;
578 default:
579 len += 1;
580 break;
581 }
582 }
583
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200584 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100585 if (!out)
586 return NULL;
587
588 out_ptr = out;
589
590#define ADD(out, str) \
591 for (j = 0; j < strlen(str); ++j) \
592 *(out++) = str[j];
593
594 for (i = 0; i < _strlen; ++i) {
595 switch (inp[i]) {
596 case '"':
597 ADD(out_ptr, "&quot;");
598 break;
599 case '\'':
600 ADD(out_ptr, "&apos;");
601 break;
602 case '<':
603 ADD(out_ptr, "&lt;");
604 break;
605 case '>':
606 ADD(out_ptr, "&gt;");
607 break;
608 case '&':
609 ADD(out_ptr, "&amp;");
610 break;
611 default:
612 *(out_ptr++) = inp[i];
613 break;
614 }
615 }
616
617#undef ADD
618
619 out_ptr[0] = '\0';
620 return out;
621}
622
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200623typedef int (*print_func_t)(void *data, const char *fmt, ...);
624
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700625static const struct value_string cmd_attr_desc[] = {
626 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
627 { CMD_ATTR_HIDDEN, "This command is hidden" },
628 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700629 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700630 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700631 { 0, NULL }
632};
633
Vadim 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) {
642 case CMD_ATTR_IMMEDIATE:
643 return '!';
644 case CMD_ATTR_NODE_EXIT:
645 return '@';
646 default:
647 return '.';
648 }
649}
650
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700651/* Description of attributes shared between the lib commands */
652static const char * const cmd_lib_attr_desc[32] = {
653 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
654 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200655 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
656 "This command applies on ASP restart",
Philipp Maiera5218ea2020-10-08 17:30:42 +0200657 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
658 "This command applies on IPA link establishment",
659 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
660 "This command applies on E1 line update",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700661};
662
663/* Flag letters of attributes shared between the lib commands.
664 * NOTE: uppercase letters only, the rest is reserved for applications. */
665static const char cmd_lib_attr_letters[32] = {
666 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200667 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Philipp Maiera5218ea2020-10-08 17:30:42 +0200668 [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
669 [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700670};
671
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100672/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200673 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100674 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200675static 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 +0100676{
677 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700678 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100679
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200680 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700681
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700682 /* Print global attributes and their description */
Vadim Yanitskiyf1fc9d32020-10-07 13:53:38 +0700683 if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700684 print_func(data, " <attributes scope='global'>%s", newline);
685
686 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
687 char *xml_att_desc;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700688 char flag;
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700689
690 if (~cmd->attr & cmd_attr_desc[i].value)
691 continue;
692
693 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700694 print_func(data, " <attribute doc='%s'",
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700695 xml_att_desc, newline);
696 talloc_free(xml_att_desc);
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +0700697
698 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
699 if (flag != '.')
700 print_func(data, " flag='%c'", flag);
701 print_func(data, " />%s", newline);
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700702 }
703
704 print_func(data, " </attributes>%s", newline);
705 }
706
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700707 /* Print application specific attributes and their description */
708 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700709 const char * const *desc;
710 const char *letters;
711
712 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
713 print_func(data, " <attributes scope='library'>%s", newline);
714 letters = &cmd_lib_attr_letters[0];
715 desc = &cmd_lib_attr_desc[0];
716 } else {
717 print_func(data, " <attributes scope='application'>%s", newline);
718 letters = &host.app_info->usr_attr_letters[0];
719 desc = &host.app_info->usr_attr_desc[0];
720 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700721
722 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
723 char *xml_att_desc;
724 char flag;
725
726 /* Skip attribute if *not* set */
727 if (~cmd->usrattr & (1 << i))
728 continue;
729
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700730 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700731 print_func(data, " <attribute doc='%s'", xml_att_desc);
732 talloc_free(xml_att_desc);
733
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700734 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700735 print_func(data, " flag='%c'", flag);
736 print_func(data, " />%s", newline);
737 }
738
739 print_func(data, " </attributes>%s", newline);
740 }
741
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200742 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100743
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700744 for (i = 0; i < vector_count(cmd->strvec); ++i) {
745 vector descvec = vector_slot(cmd->strvec, i);
746 int j;
747 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100748 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700749 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100750 if (desc == NULL)
751 continue;
752
753 xml_param = xml_escape(desc->cmd);
754 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200755 print_func(data, " <param name='%s' doc='%s' />%s",
756 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100757 talloc_free(xml_param);
758 talloc_free(xml_doc);
759 }
760 }
761
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200762 print_func(data, " </params>%s", newline);
763 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100764
765 talloc_free(xml_string);
766 return 0;
767}
768
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200769static bool vty_command_is_common(struct cmd_element *cmd);
770
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100771/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200772 * 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 +0100773 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200774static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100775{
776 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200777 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100778
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200779 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100780
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200781 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200782 print_func(data, " <node id='_common_cmds_'>%s", newline);
783 print_func(data, " <name>Common Commands</name>%s", newline);
784 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
785 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200786 for (i = 0; i < vector_active(cmdvec); ++i) {
787 struct cmd_node *cnode;
788 cnode = vector_slot(cmdvec, i);
789 if (!cnode)
790 continue;
791 if (cnode->node != CONFIG_NODE)
792 continue;
793
794 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
795 struct cmd_element *elem;
796 elem = vector_slot(cnode->cmd_vector, j);
797 if (!vty_command_is_common(elem))
798 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200799 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200800 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200801 }
802 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200803 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200804
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100805 for (i = 0; i < vector_active(cmdvec); ++i) {
806 struct cmd_node *cnode;
807 cnode = vector_slot(cmdvec, i);
808 if (!cnode)
809 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200810 if (vector_active(cnode->cmd_vector) < 1)
811 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100812
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200813 /* De-dup node IDs: how many times has this same name been used before? Count the first
814 * occurence as _1 and omit that first suffix, so that the first occurence is called
815 * 'name', the second becomes 'name_2', then 'name_3', ... */
816 same_name_count = 1;
817 for (j = 0; j < i; ++j) {
818 struct cmd_node *cnode2;
819 cnode2 = vector_slot(cmdvec, j);
820 if (!cnode2)
821 continue;
822 if (strcmp(cnode->name, cnode2->name) == 0)
823 same_name_count ++;
824 }
825
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200826 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200827 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200828 print_func(data, "_%d", same_name_count);
829 print_func(data, "'>%s", newline);
830 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100831
832 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
833 struct cmd_element *elem;
834 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200835 if (vty_command_is_common(elem))
836 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200837 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200838 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100839 }
840
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200841 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100842 }
843
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200844 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100845
846 return 0;
847}
848
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200849static int print_func_vty(void *data, const char *format, ...)
850{
851 struct vty *vty = data;
852 va_list args;
853 int rc;
854 va_start(args, format);
855 rc = vty_out_va(vty, format, args);
856 va_end(args);
857 return rc;
858}
859
860static int vty_dump_xml_ref_to_vty(struct vty *vty)
861{
862 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
863}
864
865static int print_func_stream(void *data, const char *format, ...)
866{
867 va_list args;
868 int rc;
869 va_start(args, format);
870 rc = vfprintf((FILE*)data, format, args);
871 va_end(args);
872 return rc;
873}
874
875/*! Print the XML reference of all VTY nodes to the given stream.
876 */
877int vty_dump_xml_ref(FILE *stream)
878{
879 return vty_dump_nodes(print_func_stream, stream, "\n");
880}
881
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200882/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100883static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
884{
885 int i;
886
887 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
888 struct cmd_element *elem;
889 elem = vector_slot(cnode->cmd_vector, i);
890 if (!elem->string)
891 continue;
892 if (!strcmp(elem->string, cmdstring))
893 return 1;
894 }
895 return 0;
896}
897
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200898/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200899 * \param[in] ntype Node Type
900 * \param[cmd] element to be installed
901 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000902void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200903{
904 struct cmd_node *cnode;
905
906 cnode = vector_slot(cmdvec, ntype);
907
Harald Weltea99d45a2015-11-12 13:48:23 +0100908 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100909 /* ensure no _identical_ command has been registered at this
910 * node so far */
911 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200912
913 vector_set(cnode->cmd_vector, cmd);
914
915 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
916 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
917}
918
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700919/*! Install a library command into a node
920 * \param[in] ntype Node Type
921 * \param[in] cmd element to be installed
922 */
923void install_lib_element(int ntype, struct cmd_element *cmd)
924{
925 cmd->attr |= CMD_ATTR_LIB_COMMAND;
926 install_element(ntype, cmd);
927}
928
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200929/* Install a command into VIEW and ENABLE node */
930void install_element_ve(struct cmd_element *cmd)
931{
932 install_element(VIEW_NODE, cmd);
933 install_element(ENABLE_NODE, cmd);
934}
935
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700936/* Install a library command into VIEW and ENABLE node */
937void install_lib_element_ve(struct cmd_element *cmd)
938{
939 cmd->attr |= CMD_ATTR_LIB_COMMAND;
940 install_element_ve(cmd);
941}
942
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200943#ifdef VTY_CRYPT_PW
944static unsigned char itoa64[] =
945 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
946
947static void to64(char *s, long v, int n)
948{
949 while (--n >= 0) {
950 *s++ = itoa64[v & 0x3f];
951 v >>= 6;
952 }
953}
954
955static char *zencrypt(const char *passwd)
956{
957 char salt[6];
958 struct timeval tv;
959 char *crypt(const char *, const char *);
960
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200961 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200962
963 to64(&salt[0], random(), 3);
964 to64(&salt[3], tv.tv_usec, 3);
965 salt[5] = '\0';
966
967 return crypt(passwd, salt);
968}
969#endif
970
971/* This function write configuration of this host. */
972static int config_write_host(struct vty *vty)
973{
974 if (host.name)
975 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
976
977 if (host.encrypt) {
978 if (host.password_encrypt)
979 vty_out(vty, "password 8 %s%s", host.password_encrypt,
980 VTY_NEWLINE);
981 if (host.enable_encrypt)
982 vty_out(vty, "enable password 8 %s%s",
983 host.enable_encrypt, VTY_NEWLINE);
984 } else {
985 if (host.password)
986 vty_out(vty, "password %s%s", host.password,
987 VTY_NEWLINE);
988 if (host.enable)
989 vty_out(vty, "enable password %s%s", host.enable,
990 VTY_NEWLINE);
991 }
992
993 if (host.advanced)
994 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
995
996 if (host.encrypt)
997 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
998
999 if (host.lines >= 0)
1000 vty_out(vty, "service terminal-length %d%s", host.lines,
1001 VTY_NEWLINE);
1002
1003 if (host.motdfile)
1004 vty_out(vty, "banner motd file %s%s", host.motdfile,
1005 VTY_NEWLINE);
1006 else if (!host.motd)
1007 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
1008
1009 return 1;
1010}
1011
1012/* Utility function for getting command vector. */
1013static vector cmd_node_vector(vector v, enum node_type ntype)
1014{
1015 struct cmd_node *cnode = vector_slot(v, ntype);
1016 return cnode->cmd_vector;
1017}
1018
1019/* Completion match types. */
1020enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001021 NO_MATCH = 0,
1022 ANY_MATCH,
1023 EXTEND_MATCH,
1024 IPV4_PREFIX_MATCH,
1025 IPV4_MATCH,
1026 IPV6_PREFIX_MATCH,
1027 IPV6_MATCH,
1028 RANGE_MATCH,
1029 VARARG_MATCH,
1030 PARTLY_MATCH,
1031 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001032};
1033
1034static enum match_type cmd_ipv4_match(const char *str)
1035{
1036 const char *sp;
1037 int dots = 0, nums = 0;
1038 char buf[4];
1039
1040 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001041 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001042
1043 for (;;) {
1044 memset(buf, 0, sizeof(buf));
1045 sp = str;
1046 while (*str != '\0') {
1047 if (*str == '.') {
1048 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001049 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001050
1051 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001052 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001053
1054 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001055 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001056
1057 dots++;
1058 break;
1059 }
1060 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001061 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001062
1063 str++;
1064 }
1065
1066 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001067 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001068
1069 strncpy(buf, sp, str - sp);
1070 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001071 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001072
1073 nums++;
1074
1075 if (*str == '\0')
1076 break;
1077
1078 str++;
1079 }
1080
1081 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001082 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001083
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001084 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001085}
1086
1087static enum match_type cmd_ipv4_prefix_match(const char *str)
1088{
1089 const char *sp;
1090 int dots = 0;
1091 char buf[4];
1092
1093 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001094 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001095
1096 for (;;) {
1097 memset(buf, 0, sizeof(buf));
1098 sp = str;
1099 while (*str != '\0' && *str != '/') {
1100 if (*str == '.') {
1101 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001102 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001103
1104 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001105 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001106
1107 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001108 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001109
1110 dots++;
1111 break;
1112 }
1113
1114 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001115 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116
1117 str++;
1118 }
1119
1120 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001121 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001122
1123 strncpy(buf, sp, str - sp);
1124 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001125 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001126
1127 if (dots == 3) {
1128 if (*str == '/') {
1129 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001130 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001131
1132 str++;
1133 break;
1134 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001135 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001136 }
1137
1138 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001139 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001140
1141 str++;
1142 }
1143
1144 sp = str;
1145 while (*str != '\0') {
1146 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001147 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 str++;
1150 }
1151
1152 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001153 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001154
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001155 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001156}
1157
1158#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1159#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1160#define STATE_START 1
1161#define STATE_COLON 2
1162#define STATE_DOUBLE 3
1163#define STATE_ADDR 4
1164#define STATE_DOT 5
1165#define STATE_SLASH 6
1166#define STATE_MASK 7
1167
1168#ifdef HAVE_IPV6
1169
1170static enum match_type cmd_ipv6_match(const char *str)
1171{
1172 int state = STATE_START;
1173 int colons = 0, nums = 0, double_colon = 0;
1174 const char *sp = NULL;
1175 struct sockaddr_in6 sin6_dummy;
1176 int ret;
1177
1178 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001179 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001180
1181 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001182 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001183
1184 /* use inet_pton that has a better support,
1185 * for example inet_pton can support the automatic addresses:
1186 * ::1.2.3.4
1187 */
1188 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1189
1190 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001191 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001192
1193 while (*str != '\0') {
1194 switch (state) {
1195 case STATE_START:
1196 if (*str == ':') {
1197 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001198 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001199 colons--;
1200 state = STATE_COLON;
1201 } else {
1202 sp = str;
1203 state = STATE_ADDR;
1204 }
1205
1206 continue;
1207 case STATE_COLON:
1208 colons++;
1209 if (*(str + 1) == ':')
1210 state = STATE_DOUBLE;
1211 else {
1212 sp = str + 1;
1213 state = STATE_ADDR;
1214 }
1215 break;
1216 case STATE_DOUBLE:
1217 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001218 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001219
1220 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001221 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001222 else {
1223 if (*(str + 1) != '\0')
1224 colons++;
1225 sp = str + 1;
1226 state = STATE_ADDR;
1227 }
1228
1229 double_colon++;
1230 nums++;
1231 break;
1232 case STATE_ADDR:
1233 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1234 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001235 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001236
1237 nums++;
1238 state = STATE_COLON;
1239 }
1240 if (*(str + 1) == '.')
1241 state = STATE_DOT;
1242 break;
1243 case STATE_DOT:
1244 state = STATE_ADDR;
1245 break;
1246 default:
1247 break;
1248 }
1249
1250 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001251 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001252
1253 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001254 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001255
1256 str++;
1257 }
1258
1259#if 0
1260 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001261 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001262#endif /* 0 */
1263
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001264 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001265}
1266
1267static enum match_type cmd_ipv6_prefix_match(const char *str)
1268{
1269 int state = STATE_START;
1270 int colons = 0, nums = 0, double_colon = 0;
1271 int mask;
1272 const char *sp = NULL;
1273 char *endptr = NULL;
1274
1275 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001276 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001277
1278 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001279 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001280
1281 while (*str != '\0' && state != STATE_MASK) {
1282 switch (state) {
1283 case STATE_START:
1284 if (*str == ':') {
1285 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001286 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001287 colons--;
1288 state = STATE_COLON;
1289 } else {
1290 sp = str;
1291 state = STATE_ADDR;
1292 }
1293
1294 continue;
1295 case STATE_COLON:
1296 colons++;
1297 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001298 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001299 else if (*(str + 1) == ':')
1300 state = STATE_DOUBLE;
1301 else {
1302 sp = str + 1;
1303 state = STATE_ADDR;
1304 }
1305 break;
1306 case STATE_DOUBLE:
1307 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001308 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001309
1310 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001311 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001312 else {
1313 if (*(str + 1) != '\0' && *(str + 1) != '/')
1314 colons++;
1315 sp = str + 1;
1316
1317 if (*(str + 1) == '/')
1318 state = STATE_SLASH;
1319 else
1320 state = STATE_ADDR;
1321 }
1322
1323 double_colon++;
1324 nums += 1;
1325 break;
1326 case STATE_ADDR:
1327 if (*(str + 1) == ':' || *(str + 1) == '.'
1328 || *(str + 1) == '\0' || *(str + 1) == '/') {
1329 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001330 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001331
1332 for (; sp <= str; sp++)
1333 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001334 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001335
1336 nums++;
1337
1338 if (*(str + 1) == ':')
1339 state = STATE_COLON;
1340 else if (*(str + 1) == '.')
1341 state = STATE_DOT;
1342 else if (*(str + 1) == '/')
1343 state = STATE_SLASH;
1344 }
1345 break;
1346 case STATE_DOT:
1347 state = STATE_ADDR;
1348 break;
1349 case STATE_SLASH:
1350 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001351 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001352
1353 state = STATE_MASK;
1354 break;
1355 default:
1356 break;
1357 }
1358
1359 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001360 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001361
1362 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001363 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001364
1365 str++;
1366 }
1367
1368 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001369 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001370
1371 mask = strtol(str, &endptr, 10);
1372 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001373 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001374
1375 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001376 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001377
1378/* I don't know why mask < 13 makes command match partly.
1379 Forgive me to make this comments. I Want to set static default route
1380 because of lack of function to originate default in ospf6d; sorry
1381 yasu
1382 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001383 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001384*/
1385
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001386 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001387}
1388
1389#endif /* HAVE_IPV6 */
1390
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001391
1392#if ULONG_MAX == 18446744073709551615UL
1393#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1394#elif ULONG_MAX == 4294967295UL
1395#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1396#else
1397#error "ULONG_MAX not defined!"
1398#endif
1399
1400#if LONG_MAX == 9223372036854775807L
1401#define DECIMAL_STRLEN_MAX_SIGNED 19
1402#elif LONG_MAX == 2147483647L
1403#define DECIMAL_STRLEN_MAX_SIGNED 10
1404#else
1405#error "LONG_MAX not defined!"
1406#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001407
1408static int cmd_range_match(const char *range, const char *str)
1409{
1410 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001411 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001412 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001413
1414 if (str == NULL)
1415 return 1;
1416
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001417 if (range[1] == '-') {
1418 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001419
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001420 val = strtol(str, &endptr, 10);
1421 if (*endptr != '\0')
1422 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001423
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001424 range += 2;
1425 p = strchr(range, '-');
1426 if (p == NULL)
1427 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001428 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001429 return 0;
1430 strncpy(buf, range, p - range);
1431 buf[p - range] = '\0';
1432 min = -strtol(buf, &endptr, 10);
1433 if (*endptr != '\0')
1434 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001435
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001436 range = p + 1;
1437 p = strchr(range, '>');
1438 if (p == NULL)
1439 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001440 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001441 return 0;
1442 strncpy(buf, range, p - range);
1443 buf[p - range] = '\0';
1444 max = strtol(buf, &endptr, 10);
1445 if (*endptr != '\0')
1446 return 0;
1447
1448 if (val < min || val > max)
1449 return 0;
1450 } else {
1451 unsigned long min, max, val;
1452
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001453 if (str[0] == '-')
1454 return 0;
1455
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001456 val = strtoul(str, &endptr, 10);
1457 if (*endptr != '\0')
1458 return 0;
1459
1460 range++;
1461 p = strchr(range, '-');
1462 if (p == NULL)
1463 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001464 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001465 return 0;
1466 strncpy(buf, range, p - range);
1467 buf[p - range] = '\0';
1468 min = strtoul(buf, &endptr, 10);
1469 if (*endptr != '\0')
1470 return 0;
1471
1472 range = p + 1;
1473 p = strchr(range, '>');
1474 if (p == NULL)
1475 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001476 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001477 return 0;
1478 strncpy(buf, range, p - range);
1479 buf[p - range] = '\0';
1480 max = strtoul(buf, &endptr, 10);
1481 if (*endptr != '\0')
1482 return 0;
1483
1484 if (val < min || val > max)
1485 return 0;
1486 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487
1488 return 1;
1489}
1490
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001491/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001492static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001493{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001494 /* we've got "[blah]". We want to strip off the []s and redo the
1495 * match check for "blah"
1496 */
1497 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001498
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001499 if (len < 3)
1500 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001501
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001502 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001503}
1504
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001505static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001506cmd_match(const char *str, const char *command,
1507 enum match_type min, bool recur)
1508{
1509
1510 if (recur && CMD_OPTION(str))
1511 {
1512 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001513 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001514
1515 /* this would be a bug in a command, however handle it gracefully
1516 * as it we only discover it if a user tries to run it
1517 */
1518 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001519 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001520
1521 ret = cmd_match(tmp, command, min, false);
1522
1523 talloc_free(tmp);
1524
1525 return ret;
1526 }
1527 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001528 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001529 else if (CMD_RANGE(str))
1530 {
1531 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001532 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001533 }
1534#ifdef HAVE_IPV6
1535 else if (CMD_IPV6(str))
1536 {
1537 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001538 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001539 }
1540 else if (CMD_IPV6_PREFIX(str))
1541 {
1542 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001543 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001544 }
1545#endif /* HAVE_IPV6 */
1546 else if (CMD_IPV4(str))
1547 {
1548 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001549 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001550 }
1551 else if (CMD_IPV4_PREFIX(str))
1552 {
1553 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001554 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001555 }
1556 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001557 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001558 else if (strncmp(command, str, strlen(command)) == 0)
1559 {
1560 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001561 return EXACT_MATCH;
1562 else if (PARTLY_MATCH >= min)
1563 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001564 }
1565
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001566 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001567}
1568
1569/* Filter vector at the specified index and by the given command string, to
1570 * the desired matching level (thus allowing part matches), and return match
1571 * type flag.
1572 */
1573static enum match_type
1574cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001575{
1576 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577 struct cmd_element *cmd_element;
1578 enum match_type match_type;
1579 vector descvec;
1580 struct desc *desc;
1581
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001582 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001583
1584 /* If command and cmd_element string does not match set NULL to vector */
1585 for (i = 0; i < vector_active(v); i++)
1586 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001587 if (index >= vector_active(cmd_element->strvec))
1588 vector_slot(v, i) = NULL;
1589 else {
1590 unsigned int j;
1591 int matched = 0;
1592
1593 descvec =
1594 vector_slot(cmd_element->strvec, index);
1595
1596 for (j = 0; j < vector_active(descvec); j++)
1597 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001598 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001599
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001600 ret = cmd_match (desc->cmd, command, level, true);
1601
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001602 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001603 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001604
1605 if (match_type < ret)
1606 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001607 }
1608 if (!matched)
1609 vector_slot(v, i) = NULL;
1610 }
1611 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001612
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001613 if (match_type == NO_MATCH)
1614 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001615
1616 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1617 * go again and filter out commands whose argument (at this index) is
1618 * 'weaker'. E.g., if we have 2 commands:
1619 *
1620 * foo bar <1-255>
1621 * foo bar BLAH
1622 *
1623 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001624 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001625 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1626 *
1627 * If we don't do a 2nd pass and filter it out, the higher-layers will
1628 * consider this to be ambiguous.
1629 */
1630 for (i = 0; i < vector_active(v); i++)
1631 if ((cmd_element = vector_slot(v, i)) != NULL) {
1632 if (index >= vector_active(cmd_element->strvec))
1633 vector_slot(v, i) = NULL;
1634 else {
1635 unsigned int j;
1636 int matched = 0;
1637
1638 descvec =
1639 vector_slot(cmd_element->strvec, index);
1640
1641 for (j = 0; j < vector_active(descvec); j++)
1642 if ((desc = vector_slot(descvec, j))) {
1643 enum match_type ret;
1644
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001645 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001646
1647 if (ret >= match_type)
1648 matched++;
1649 }
1650 if (!matched)
1651 vector_slot(v, i) = NULL;
1652 }
1653 }
1654
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001655 return match_type;
1656}
1657
1658/* Check ambiguous match */
1659static int
1660is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1661{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001662 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001663 unsigned int i;
1664 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001665 struct cmd_element *cmd_element;
1666 const char *matched = NULL;
1667 vector descvec;
1668 struct desc *desc;
1669
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001670 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1671 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1672 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1673 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1674 * that case, the string must remain allocated until this function exits or another match comes
1675 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1676 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1677 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1678 void *cmd_deopt_ctx = NULL;
1679
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001680 for (i = 0; i < vector_active(v); i++) {
1681 cmd_element = vector_slot(v, i);
1682 if (!cmd_element)
1683 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001684
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001685 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001686
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001687 descvec = vector_slot(cmd_element->strvec, index);
1688
1689 for (j = 0; j < vector_active(descvec); j++) {
1690 desc = vector_slot(descvec, j);
1691 if (!desc)
1692 continue;
1693
1694 enum match_type mtype;
1695 const char *str = desc->cmd;
1696
1697 if (CMD_OPTION(str)) {
1698 if (!cmd_deopt_ctx)
1699 cmd_deopt_ctx =
1700 talloc_named_const(tall_vty_cmd_ctx, 0,
1701 __func__);
1702 str = cmd_deopt(cmd_deopt_ctx, str);
1703 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001704 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001705 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001706
1707 switch (type) {
1708 case EXACT_MATCH:
1709 if (!(CMD_VARIABLE (str))
1710 && strcmp(command, str) == 0)
1711 match++;
1712 break;
1713 case PARTLY_MATCH:
1714 if (!(CMD_VARIABLE (str))
1715 && strncmp(command, str, strlen (command)) == 0)
1716 {
1717 if (matched
1718 && strcmp(matched,
1719 str) != 0) {
1720 ret = 1; /* There is ambiguous match. */
1721 goto free_and_return;
1722 } else
1723 matched = str;
1724 match++;
1725 }
1726 break;
1727 case RANGE_MATCH:
1728 if (cmd_range_match
1729 (str, command)) {
1730 if (matched
1731 && strcmp(matched,
1732 str) != 0) {
1733 ret = 1;
1734 goto free_and_return;
1735 } else
1736 matched = str;
1737 match++;
1738 }
1739 break;
1740#ifdef HAVE_IPV6
1741 case IPV6_MATCH:
1742 if (CMD_IPV6(str))
1743 match++;
1744 break;
1745 case IPV6_PREFIX_MATCH:
1746 if ((mtype =
1747 cmd_ipv6_prefix_match
1748 (command)) != NO_MATCH) {
1749 if (mtype == PARTLY_MATCH) {
1750 ret = 2; /* There is incomplete match. */
1751 goto free_and_return;
1752 }
1753
1754 match++;
1755 }
1756 break;
1757#endif /* HAVE_IPV6 */
1758 case IPV4_MATCH:
1759 if (CMD_IPV4(str))
1760 match++;
1761 break;
1762 case IPV4_PREFIX_MATCH:
1763 if ((mtype =
1764 cmd_ipv4_prefix_match
1765 (command)) != NO_MATCH) {
1766 if (mtype == PARTLY_MATCH) {
1767 ret = 2; /* There is incomplete match. */
1768 goto free_and_return;
1769 }
1770
1771 match++;
1772 }
1773 break;
1774 case EXTEND_MATCH:
1775 if (CMD_VARIABLE (str))
1776 match++;
1777 break;
1778 case NO_MATCH:
1779 default:
1780 break;
1781 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001782 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001783 if (!match)
1784 vector_slot(v, i) = NULL;
1785 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001786
1787free_and_return:
1788 if (cmd_deopt_ctx)
1789 talloc_free(cmd_deopt_ctx);
1790 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001791}
1792
1793/* If src matches dst return dst string, otherwise return NULL */
1794static const char *cmd_entry_function(const char *src, const char *dst)
1795{
1796 /* Skip variable arguments. */
1797 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1798 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1799 return NULL;
1800
1801 /* In case of 'command \t', given src is NULL string. */
1802 if (src == NULL)
1803 return dst;
1804
1805 /* Matched with input string. */
1806 if (strncmp(src, dst, strlen(src)) == 0)
1807 return dst;
1808
1809 return NULL;
1810}
1811
1812/* If src matches dst return dst string, otherwise return NULL */
1813/* This version will return the dst string always if it is
1814 CMD_VARIABLE for '?' key processing */
1815static const char *cmd_entry_function_desc(const char *src, const char *dst)
1816{
1817 if (CMD_VARARG(dst))
1818 return dst;
1819
1820 if (CMD_RANGE(dst)) {
1821 if (cmd_range_match(dst, src))
1822 return dst;
1823 else
1824 return NULL;
1825 }
1826#ifdef HAVE_IPV6
1827 if (CMD_IPV6(dst)) {
1828 if (cmd_ipv6_match(src))
1829 return dst;
1830 else
1831 return NULL;
1832 }
1833
1834 if (CMD_IPV6_PREFIX(dst)) {
1835 if (cmd_ipv6_prefix_match(src))
1836 return dst;
1837 else
1838 return NULL;
1839 }
1840#endif /* HAVE_IPV6 */
1841
1842 if (CMD_IPV4(dst)) {
1843 if (cmd_ipv4_match(src))
1844 return dst;
1845 else
1846 return NULL;
1847 }
1848
1849 if (CMD_IPV4_PREFIX(dst)) {
1850 if (cmd_ipv4_prefix_match(src))
1851 return dst;
1852 else
1853 return NULL;
1854 }
1855
1856 /* Optional or variable commands always match on '?' */
1857 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1858 return dst;
1859
1860 /* In case of 'command \t', given src is NULL string. */
1861 if (src == NULL)
1862 return dst;
1863
1864 if (strncmp(src, dst, strlen(src)) == 0)
1865 return dst;
1866 else
1867 return NULL;
1868}
1869
1870/* Check same string element existence. If it isn't there return
1871 1. */
1872static int cmd_unique_string(vector v, const char *str)
1873{
1874 unsigned int i;
1875 char *match;
1876
1877 for (i = 0; i < vector_active(v); i++)
1878 if ((match = vector_slot(v, i)) != NULL)
1879 if (strcmp(match, str) == 0)
1880 return 0;
1881 return 1;
1882}
1883
1884/* Compare string to description vector. If there is same string
1885 return 1 else return 0. */
1886static int desc_unique_string(vector v, const char *str)
1887{
1888 unsigned int i;
1889 struct desc *desc;
1890
1891 for (i = 0; i < vector_active(v); i++)
1892 if ((desc = vector_slot(v, i)) != NULL)
1893 if (strcmp(desc->cmd, str) == 0)
1894 return 1;
1895 return 0;
1896}
1897
1898static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1899{
1900 if (first_word != NULL &&
1901 node != AUTH_NODE &&
1902 node != VIEW_NODE &&
1903 node != AUTH_ENABLE_NODE &&
1904 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1905 return 1;
1906 return 0;
1907}
1908
1909/* '?' describe command support. */
1910static vector
1911cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1912{
1913 unsigned int i;
1914 vector cmd_vector;
1915#define INIT_MATCHVEC_SIZE 10
1916 vector matchvec;
1917 struct cmd_element *cmd_element;
1918 unsigned int index;
1919 int ret;
1920 enum match_type match;
1921 char *command;
1922 static struct desc desc_cr = { "<cr>", "" };
1923
1924 /* Set index. */
1925 if (vector_active(vline) == 0) {
1926 *status = CMD_ERR_NO_MATCH;
1927 return NULL;
1928 } else
1929 index = vector_active(vline) - 1;
1930
1931 /* Make copy vector of current node's command vector. */
1932 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1933
1934 /* Prepare match vector */
1935 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1936
1937 /* Filter commands. */
1938 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001939 for (i = 0; i < index; i++) {
1940 command = vector_slot(vline, i);
1941 if (!command)
1942 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001943
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001944 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001945
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001946 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001947 struct cmd_element *cmd_element;
1948 vector descvec;
1949 unsigned int j, k;
1950
1951 for (j = 0; j < vector_active(cmd_vector); j++)
1952 if ((cmd_element =
1953 vector_slot(cmd_vector, j)) != NULL
1954 &&
1955 (vector_active(cmd_element->strvec))) {
1956 descvec =
1957 vector_slot(cmd_element->
1958 strvec,
1959 vector_active
1960 (cmd_element->
1961 strvec) - 1);
1962 for (k = 0;
1963 k < vector_active(descvec);
1964 k++) {
1965 struct desc *desc =
1966 vector_slot(descvec,
1967 k);
1968 vector_set(matchvec,
1969 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001970 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001971 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001972
Harald Welte80d30fe2013-02-12 11:08:57 +01001973 vector_set(matchvec, &desc_cr);
1974 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001975
Harald Welte80d30fe2013-02-12 11:08:57 +01001976 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001977 }
1978
Harald Welte80d30fe2013-02-12 11:08:57 +01001979 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1980 match)) == 1) {
1981 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001982 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001983 *status = CMD_ERR_AMBIGUOUS;
1984 return NULL;
1985 } else if (ret == 2) {
1986 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001987 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001988 *status = CMD_ERR_NO_MATCH;
1989 return NULL;
1990 }
1991 }
1992
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001993 /* Prepare match vector */
1994 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1995
1996 /* Make sure that cmd_vector is filtered based on current word */
1997 command = vector_slot(vline, index);
1998 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001999 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002000
2001 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01002002 for (i = 0; i < vector_active(cmd_vector); i++) {
2003 const char *string = NULL;
2004 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002005
Harald Welte80d30fe2013-02-12 11:08:57 +01002006 cmd_element = vector_slot(cmd_vector, i);
2007 if (!cmd_element)
2008 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002009
Harald Welted17aa592013-02-12 11:11:34 +01002010 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
2011 continue;
2012
Harald Welte80d30fe2013-02-12 11:08:57 +01002013 strvec = cmd_element->strvec;
2014
2015 /* if command is NULL, index may be equal to vector_active */
2016 if (command && index >= vector_active(strvec))
2017 vector_slot(cmd_vector, i) = NULL;
2018 else {
2019 /* Check if command is completed. */
2020 if (command == NULL
2021 && index == vector_active(strvec)) {
2022 string = "<cr>";
2023 if (!desc_unique_string(matchvec, string))
2024 vector_set(matchvec, &desc_cr);
2025 } else {
2026 unsigned int j;
2027 vector descvec = vector_slot(strvec, index);
2028 struct desc *desc;
2029
2030 for (j = 0; j < vector_active(descvec); j++) {
2031 desc = vector_slot(descvec, j);
2032 if (!desc)
2033 continue;
2034 string = cmd_entry_function_desc
2035 (command, desc->cmd);
2036 if (!string)
2037 continue;
2038 /* Uniqueness check */
2039 if (!desc_unique_string(matchvec, string))
2040 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002041 }
2042 }
2043 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002044 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002045 vector_free(cmd_vector);
2046
2047 if (vector_slot(matchvec, 0) == NULL) {
2048 vector_free(matchvec);
2049 *status = CMD_ERR_NO_MATCH;
2050 } else
2051 *status = CMD_SUCCESS;
2052
2053 return matchvec;
2054}
2055
2056vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2057{
2058 vector ret;
2059
2060 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2061 enum node_type onode;
2062 vector shifted_vline;
2063 unsigned int index;
2064
2065 onode = vty->node;
2066 vty->node = ENABLE_NODE;
2067 /* We can try it on enable node, cos' the vty is authenticated */
2068
2069 shifted_vline = vector_init(vector_count(vline));
2070 /* use memcpy? */
2071 for (index = 1; index < vector_active(vline); index++) {
2072 vector_set_index(shifted_vline, index - 1,
2073 vector_lookup(vline, index));
2074 }
2075
2076 ret = cmd_describe_command_real(shifted_vline, vty, status);
2077
2078 vector_free(shifted_vline);
2079 vty->node = onode;
2080 return ret;
2081 }
2082
2083 return cmd_describe_command_real(vline, vty, status);
2084}
2085
2086/* Check LCD of matched command. */
2087static int cmd_lcd(char **matched)
2088{
2089 int i;
2090 int j;
2091 int lcd = -1;
2092 char *s1, *s2;
2093 char c1, c2;
2094
2095 if (matched[0] == NULL || matched[1] == NULL)
2096 return 0;
2097
2098 for (i = 1; matched[i] != NULL; i++) {
2099 s1 = matched[i - 1];
2100 s2 = matched[i];
2101
2102 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2103 if (c1 != c2)
2104 break;
2105
2106 if (lcd < 0)
2107 lcd = j;
2108 else {
2109 if (lcd > j)
2110 lcd = j;
2111 }
2112 }
2113 return lcd;
2114}
2115
2116/* Command line completion support. */
2117static char **cmd_complete_command_real(vector vline, struct vty *vty,
2118 int *status)
2119{
2120 unsigned int i;
2121 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2122#define INIT_MATCHVEC_SIZE 10
2123 vector matchvec;
2124 struct cmd_element *cmd_element;
2125 unsigned int index;
2126 char **match_str;
2127 struct desc *desc;
2128 vector descvec;
2129 char *command;
2130 int lcd;
2131
2132 if (vector_active(vline) == 0) {
2133 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002134 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002135 return NULL;
2136 } else
2137 index = vector_active(vline) - 1;
2138
2139 /* First, filter by preceeding command string */
2140 for (i = 0; i < index; i++)
2141 if ((command = vector_slot(vline, i))) {
2142 enum match_type match;
2143 int ret;
2144
2145 /* First try completion match, if there is exactly match return 1 */
2146 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002147 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002148
2149 /* If there is exact match then filter ambiguous match else check
2150 ambiguousness. */
2151 if ((ret =
2152 is_cmd_ambiguous(command, cmd_vector, i,
2153 match)) == 1) {
2154 vector_free(cmd_vector);
2155 *status = CMD_ERR_AMBIGUOUS;
2156 return NULL;
2157 }
2158 /*
2159 else if (ret == 2)
2160 {
2161 vector_free (cmd_vector);
2162 *status = CMD_ERR_NO_MATCH;
2163 return NULL;
2164 }
2165 */
2166 }
2167
2168 /* Prepare match vector. */
2169 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2170
2171 /* Now we got into completion */
2172 for (i = 0; i < vector_active(cmd_vector); i++)
2173 if ((cmd_element = vector_slot(cmd_vector, i))) {
2174 const char *string;
2175 vector strvec = cmd_element->strvec;
2176
2177 /* Check field length */
2178 if (index >= vector_active(strvec))
2179 vector_slot(cmd_vector, i) = NULL;
2180 else {
2181 unsigned int j;
2182
2183 descvec = vector_slot(strvec, index);
2184 for (j = 0; j < vector_active(descvec); j++)
2185 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002186 const char *cmd = desc->cmd;
2187 char *tmp = NULL;
2188
2189 if (CMD_OPTION(desc->cmd)) {
2190 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2191 cmd = tmp;
2192 }
2193 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002194 if (cmd_unique_string (matchvec, string))
2195 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002196 if (tmp)
2197 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002198 }
2199 }
2200 }
2201
2202 /* We don't need cmd_vector any more. */
2203 vector_free(cmd_vector);
2204
2205 /* No matched command */
2206 if (vector_slot(matchvec, 0) == NULL) {
2207 vector_free(matchvec);
2208
2209 /* In case of 'command \t' pattern. Do you need '?' command at
2210 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002211 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002212 *status = CMD_ERR_NOTHING_TODO;
2213 else
2214 *status = CMD_ERR_NO_MATCH;
2215 return NULL;
2216 }
2217
2218 /* Only one matched */
2219 if (vector_slot(matchvec, 1) == NULL) {
2220 match_str = (char **)matchvec->index;
2221 vector_only_wrapper_free(matchvec);
2222 *status = CMD_COMPLETE_FULL_MATCH;
2223 return match_str;
2224 }
2225 /* Make it sure last element is NULL. */
2226 vector_set(matchvec, NULL);
2227
2228 /* Check LCD of matched strings. */
2229 if (vector_slot(vline, index) != NULL) {
2230 lcd = cmd_lcd((char **)matchvec->index);
2231
2232 if (lcd) {
2233 int len = strlen(vector_slot(vline, index));
2234
2235 if (len < lcd) {
2236 char *lcdstr;
2237
2238 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2239 "complete-lcdstr");
2240 memcpy(lcdstr, matchvec->index[0], lcd);
2241 lcdstr[lcd] = '\0';
2242
2243 /* match_str = (char **) &lcdstr; */
2244
2245 /* Free matchvec. */
2246 for (i = 0; i < vector_active(matchvec); i++) {
2247 if (vector_slot(matchvec, i))
2248 talloc_free(vector_slot(matchvec, i));
2249 }
2250 vector_free(matchvec);
2251
2252 /* Make new matchvec. */
2253 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2254 vector_set(matchvec, lcdstr);
2255 match_str = (char **)matchvec->index;
2256 vector_only_wrapper_free(matchvec);
2257
2258 *status = CMD_COMPLETE_MATCH;
2259 return match_str;
2260 }
2261 }
2262 }
2263
2264 match_str = (char **)matchvec->index;
2265 vector_only_wrapper_free(matchvec);
2266 *status = CMD_COMPLETE_LIST_MATCH;
2267 return match_str;
2268}
2269
2270char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2271{
2272 char **ret;
2273
2274 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2275 enum node_type onode;
2276 vector shifted_vline;
2277 unsigned int index;
2278
2279 onode = vty->node;
2280 vty->node = ENABLE_NODE;
2281 /* We can try it on enable node, cos' the vty is authenticated */
2282
2283 shifted_vline = vector_init(vector_count(vline));
2284 /* use memcpy? */
2285 for (index = 1; index < vector_active(vline); index++) {
2286 vector_set_index(shifted_vline, index - 1,
2287 vector_lookup(vline, index));
2288 }
2289
2290 ret = cmd_complete_command_real(shifted_vline, vty, status);
2291
2292 vector_free(shifted_vline);
2293 vty->node = onode;
2294 return ret;
2295 }
2296
2297 return cmd_complete_command_real(vline, vty, status);
2298}
2299
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002300static struct vty_parent_node *vty_parent(struct vty *vty)
2301{
2302 return llist_first_entry_or_null(&vty->parent_nodes,
2303 struct vty_parent_node,
2304 entry);
2305}
2306
2307static bool vty_pop_parent(struct vty *vty)
2308{
2309 struct vty_parent_node *parent = vty_parent(vty);
2310 if (!parent)
2311 return false;
2312 llist_del(&parent->entry);
2313 vty->node = parent->node;
2314 vty->priv = parent->priv;
2315 if (vty->indent)
2316 talloc_free(vty->indent);
2317 vty->indent = parent->indent;
2318 talloc_free(parent);
2319 return true;
2320}
2321
2322static void vty_clear_parents(struct vty *vty)
2323{
2324 while (vty_pop_parent(vty));
2325}
2326
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002327/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002328/*
2329 * This function MUST eventually converge on a node when called repeatedly,
2330 * there must not be any cycles.
2331 * All 'config' nodes shall converge on CONFIG_NODE.
2332 * All other 'enable' nodes shall converge on ENABLE_NODE.
2333 * All 'view' only nodes shall converge on VIEW_NODE.
2334 * All other nodes shall converge on themselves or it must be ensured,
2335 * that the user's rights are not extended anyhow by calling this function.
2336 *
2337 * Note that these requirements also apply to all functions that are used
2338 * as go_parent_cb.
2339 * Note also that this function relies on the is_config_child callback to
2340 * recognize non-config nodes if go_parent_cb is not set.
2341 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002342int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002343{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002344 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002345 case AUTH_NODE:
2346 case VIEW_NODE:
2347 case ENABLE_NODE:
2348 case CONFIG_NODE:
2349 vty_clear_parents(vty);
2350 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002351
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002352 case AUTH_ENABLE_NODE:
2353 vty->node = VIEW_NODE;
2354 vty_clear_parents(vty);
2355 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002356
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002357 default:
2358 if (host.app_info->go_parent_cb)
2359 host.app_info->go_parent_cb(vty);
2360 vty_pop_parent(vty);
2361 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002362 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002363
2364 return vty->node;
2365}
2366
2367/* Execute command by argument vline vector. */
2368static int
2369cmd_execute_command_real(vector vline, struct vty *vty,
2370 struct cmd_element **cmd)
2371{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002372 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002373 unsigned int index;
2374 vector cmd_vector;
2375 struct cmd_element *cmd_element;
2376 struct cmd_element *matched_element;
2377 unsigned int matched_count, incomplete_count;
2378 int argc;
2379 const char *argv[CMD_ARGC_MAX];
2380 enum match_type match = 0;
2381 int varflag;
2382 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002383 int rc;
2384 /* Used for temporary storage of cmd_deopt() allocated arguments during
2385 argv[] generation */
2386 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002387
2388 /* Make copy of command elements. */
2389 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2390
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002391 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002392 if ((command = vector_slot(vline, index))) {
2393 int ret;
2394
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002395 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002396 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002397
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002398 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002399 break;
2400
2401 ret =
2402 is_cmd_ambiguous(command, cmd_vector, index, match);
2403
2404 if (ret == 1) {
2405 vector_free(cmd_vector);
2406 return CMD_ERR_AMBIGUOUS;
2407 } else if (ret == 2) {
2408 vector_free(cmd_vector);
2409 return CMD_ERR_NO_MATCH;
2410 }
2411 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002412 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002413
2414 /* Check matched count. */
2415 matched_element = NULL;
2416 matched_count = 0;
2417 incomplete_count = 0;
2418
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002419 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002420 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002421 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002422 || index >= cmd_element->cmdsize) {
2423 matched_element = cmd_element;
2424#if 0
2425 printf("DEBUG: %s\n", cmd_element->string);
2426#endif
2427 matched_count++;
2428 } else {
2429 incomplete_count++;
2430 }
2431 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002432 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002433
2434 /* Finish of using cmd_vector. */
2435 vector_free(cmd_vector);
2436
2437 /* To execute command, matched_count must be 1. */
2438 if (matched_count == 0) {
2439 if (incomplete_count)
2440 return CMD_ERR_INCOMPLETE;
2441 else
2442 return CMD_ERR_NO_MATCH;
2443 }
2444
2445 if (matched_count > 1)
2446 return CMD_ERR_AMBIGUOUS;
2447
2448 /* Argument treatment */
2449 varflag = 0;
2450 argc = 0;
2451
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002452 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2453
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002454 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002455 if (argc == CMD_ARGC_MAX) {
2456 rc = CMD_ERR_EXEED_ARGC_MAX;
2457 goto rc_free_deopt_ctx;
2458 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002459 if (varflag) {
2460 argv[argc++] = vector_slot(vline, i);
2461 continue;
2462 }
2463
2464 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002465 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002466
2467 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002468 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002469 struct desc *desc = vector_slot(descvec, 0);
2470
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002471 if (CMD_OPTION(desc->cmd)) {
2472 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2473 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2474 } else {
2475 tmp_cmd = desc->cmd;
2476 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002477
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002478 if (CMD_VARARG(tmp_cmd))
2479 varflag = 1;
2480 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002481 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002482 else if (CMD_OPTION(desc->cmd))
2483 argv[argc++] = tmp_cmd;
2484 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002485 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002486 /* multi choice argument. look up which choice
2487 the user meant (can only be one after
2488 filtering and checking for ambigous). For instance,
2489 if user typed "th" for "(two|three)" arg, we
2490 want to pass "three" in argv[]. */
2491 for (j = 0; j < vector_active(descvec); j++) {
2492 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002493 if (!desc)
2494 continue;
2495 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2496 continue;
2497 if (CMD_OPTION(desc->cmd)) {
2498 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2499 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2500 } else {
2501 tmp_cmd = desc->cmd;
2502 }
2503
2504 if(CMD_VARIABLE(tmp_cmd)) {
2505 argv[argc++] = vector_slot(vline, i);
2506 } else {
2507 argv[argc++] = tmp_cmd;
2508 }
2509 break;
2510 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002511 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002512 }
2513
2514 /* For vtysh execution. */
2515 if (cmd)
2516 *cmd = matched_element;
2517
2518 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002519 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002520 else {
2521 /* Execute matched command. */
2522 struct vty_parent_node this_node = {
2523 .node = vty->node,
2524 .priv = vty->priv,
2525 .indent = vty->indent,
2526 };
2527 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002528 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002529
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002530 /* If we have stepped down into a child node, push a parent frame.
2531 * The causality is such: we don't expect every single node entry implementation to push
2532 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2533 * a parent node. Hence if the node changed without the parent node changing, we must
2534 * have stepped into a child node. */
2535 if (vty->node != this_node.node && parent == vty_parent(vty)
2536 && vty->node > CONFIG_NODE) {
2537 /* Push the parent node. */
2538 parent = talloc_zero(vty, struct vty_parent_node);
2539 *parent = this_node;
2540 llist_add(&parent->entry, &vty->parent_nodes);
2541 }
2542 }
2543
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002544rc_free_deopt_ctx:
2545 /* Now after we called the command func, we can free temporary strings */
2546 talloc_free(cmd_deopt_ctx);
2547 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002548}
2549
2550int
2551cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2552 int vtysh)
2553{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002554 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002555 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002556
2557 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002558
2559 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2560 vector shifted_vline;
2561 unsigned int index;
2562
2563 vty->node = ENABLE_NODE;
2564 /* We can try it on enable node, cos' the vty is authenticated */
2565
2566 shifted_vline = vector_init(vector_count(vline));
2567 /* use memcpy? */
2568 for (index = 1; index < vector_active(vline); index++) {
2569 vector_set_index(shifted_vline, index - 1,
2570 vector_lookup(vline, index));
2571 }
2572
2573 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2574
2575 vector_free(shifted_vline);
2576 vty->node = onode;
2577 return ret;
2578 }
2579
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002580 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002581}
2582
2583/* Execute command by argument readline. */
2584int
2585cmd_execute_command_strict(vector vline, struct vty *vty,
2586 struct cmd_element **cmd)
2587{
2588 unsigned int i;
2589 unsigned int index;
2590 vector cmd_vector;
2591 struct cmd_element *cmd_element;
2592 struct cmd_element *matched_element;
2593 unsigned int matched_count, incomplete_count;
2594 int argc;
2595 const char *argv[CMD_ARGC_MAX];
2596 int varflag;
2597 enum match_type match = 0;
2598 char *command;
2599
2600 /* Make copy of command element */
2601 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2602
2603 for (index = 0; index < vector_active(vline); index++)
2604 if ((command = vector_slot(vline, index))) {
2605 int ret;
2606
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002607 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002608 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002609
2610 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002611 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002612 break;
2613
2614 ret =
2615 is_cmd_ambiguous(command, cmd_vector, index, match);
2616 if (ret == 1) {
2617 vector_free(cmd_vector);
2618 return CMD_ERR_AMBIGUOUS;
2619 }
2620 if (ret == 2) {
2621 vector_free(cmd_vector);
2622 return CMD_ERR_NO_MATCH;
2623 }
2624 }
2625
2626 /* Check matched count. */
2627 matched_element = NULL;
2628 matched_count = 0;
2629 incomplete_count = 0;
2630 for (i = 0; i < vector_active(cmd_vector); i++)
2631 if (vector_slot(cmd_vector, i) != NULL) {
2632 cmd_element = vector_slot(cmd_vector, i);
2633
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002634 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002635 || index >= cmd_element->cmdsize) {
2636 matched_element = cmd_element;
2637 matched_count++;
2638 } else
2639 incomplete_count++;
2640 }
2641
2642 /* Finish of using cmd_vector. */
2643 vector_free(cmd_vector);
2644
2645 /* To execute command, matched_count must be 1. */
2646 if (matched_count == 0) {
2647 if (incomplete_count)
2648 return CMD_ERR_INCOMPLETE;
2649 else
2650 return CMD_ERR_NO_MATCH;
2651 }
2652
2653 if (matched_count > 1)
2654 return CMD_ERR_AMBIGUOUS;
2655
2656 /* Argument treatment */
2657 varflag = 0;
2658 argc = 0;
2659
2660 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002661 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002662 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002663 if (varflag) {
2664 argv[argc++] = vector_slot(vline, i);
2665 continue;
2666 }
2667
2668 vector descvec = vector_slot(matched_element->strvec, i);
2669
2670 if (vector_active(descvec) == 1) {
2671 struct desc *desc = vector_slot(descvec, 0);
2672
2673 if (CMD_VARARG(desc->cmd))
2674 varflag = 1;
2675
2676 if (varflag || CMD_VARIABLE(desc->cmd)
2677 || CMD_OPTION(desc->cmd))
2678 argv[argc++] = vector_slot(vline, i);
2679 } else {
2680 argv[argc++] = vector_slot(vline, i);
2681 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002682 }
2683
2684 /* For vtysh execution. */
2685 if (cmd)
2686 *cmd = matched_element;
2687
2688 if (matched_element->daemon)
2689 return CMD_SUCCESS_DAEMON;
2690
2691 /* Now execute matched command */
2692 return (*matched_element->func) (matched_element, vty, argc, argv);
2693}
2694
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002695static inline size_t len(const char *str)
2696{
2697 return str? strlen(str) : 0;
2698}
2699
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002700/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2701 * is longer than b, a must start with exactly b, and vice versa.
2702 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2703 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002704static int indent_cmp(const char *a, const char *b)
2705{
2706 size_t al, bl;
2707 al = len(a);
2708 bl = len(b);
2709 if (al > bl) {
2710 if (bl && strncmp(a, b, bl) != 0)
2711 return EINVAL;
2712 return 1;
2713 }
2714 /* al <= bl */
2715 if (al && strncmp(a, b, al) != 0)
2716 return EINVAL;
2717 return (al < bl)? -1 : 0;
2718}
2719
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002720/* Configration make from file. */
2721int config_from_file(struct vty *vty, FILE * fp)
2722{
2723 int ret;
2724 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002725 char *indent;
2726 int cmp;
2727 struct vty_parent_node this_node;
2728 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002729
2730 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002731 indent = NULL;
2732 vline = NULL;
2733 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002734
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002735 if (ret != CMD_SUCCESS)
2736 goto return_invalid_indent;
2737
2738 /* In case of comment or empty line */
2739 if (vline == NULL) {
2740 if (indent) {
2741 talloc_free(indent);
2742 indent = NULL;
2743 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002744 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002745 }
2746
Neels Hofmeyr43063632017-09-19 23:54:01 +02002747 /* We have a nonempty line. */
2748 if (!vty->indent) {
2749 /* We have just entered a node and expecting the first child to come up; but we
2750 * may also skip right back to a parent or ancestor level. */
2751 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002752
Neels Hofmeyr43063632017-09-19 23:54:01 +02002753 /* If there is no parent, record any indentation we encounter. */
2754 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2755
2756 if (cmp == EINVAL)
2757 goto return_invalid_indent;
2758
2759 if (cmp <= 0) {
2760 /* We have gone right back to the parent level or higher, we are skipping
2761 * this child node level entirely. Pop the parent to go back to a node
2762 * that was actually there (to reinstate vty->indent) and re-use below
2763 * go-parent while-loop to find an accurate match of indent in the node
2764 * ancestry. */
2765 vty_go_parent(vty);
2766 } else {
2767 /* The indent is deeper than the just entered parent, record the new
2768 * indentation characters. */
2769 vty->indent = talloc_strdup(vty, indent);
2770 /* This *is* the new indentation. */
2771 cmp = 0;
2772 }
2773 } else {
2774 /* There is a known indentation for this node level, validate and detect node
2775 * exits. */
2776 cmp = indent_cmp(indent, vty->indent);
2777 if (cmp == EINVAL)
2778 goto return_invalid_indent;
2779 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002780
2781 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2782 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2783 while (cmp < 0) {
2784 vty_go_parent(vty);
2785 cmp = indent_cmp(indent, vty->indent);
2786 if (cmp == EINVAL)
2787 goto return_invalid_indent;
2788 }
2789
2790 /* More indent without having entered a child node level? Either the parent node's indent
2791 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2792 * or the indentation increased even though the vty command didn't enter a child. */
2793 if (cmp > 0)
2794 goto return_invalid_indent;
2795
2796 /* Remember the current node before the command possibly changes it. */
2797 this_node = (struct vty_parent_node){
2798 .node = vty->node,
2799 .priv = vty->priv,
2800 .indent = vty->indent,
2801 };
2802
2803 parent = vty_parent(vty);
2804 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002805 cmd_free_strvec(vline);
2806
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002807 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002808 if (indent) {
2809 talloc_free(indent);
2810 indent = NULL;
2811 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002812 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002813 }
2814
2815 /* If we have stepped down into a child node, push a parent frame.
2816 * The causality is such: we don't expect every single node entry implementation to push
2817 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2818 * a parent node. Hence if the node changed without the parent node changing, we must
2819 * have stepped into a child node (and now expect a deeper indent). */
2820 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2821 /* Push the parent node. */
2822 parent = talloc_zero(vty, struct vty_parent_node);
2823 *parent = this_node;
2824 llist_add(&parent->entry, &vty->parent_nodes);
2825
2826 /* The current talloc'ed vty->indent string will now be owned by this parent
2827 * struct. Indicate that we don't know what deeper indent characters the user
2828 * will choose. */
2829 vty->indent = NULL;
2830 }
2831
2832 if (indent) {
2833 talloc_free(indent);
2834 indent = NULL;
2835 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002836 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002837 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2838 while (vty_parent(vty))
2839 vty_go_parent(vty);
2840
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002841 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002842
2843return_invalid_indent:
2844 if (vline)
2845 cmd_free_strvec(vline);
2846 if (indent) {
2847 talloc_free(indent);
2848 indent = NULL;
2849 }
2850 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002851}
2852
2853/* Configration from terminal */
2854DEFUN(config_terminal,
2855 config_terminal_cmd,
2856 "configure terminal",
2857 "Configuration from vty interface\n" "Configuration terminal\n")
2858{
2859 if (vty_config_lock(vty))
2860 vty->node = CONFIG_NODE;
2861 else {
2862 vty_out(vty, "VTY configuration is locked by other VTY%s",
2863 VTY_NEWLINE);
2864 return CMD_WARNING;
2865 }
2866 return CMD_SUCCESS;
2867}
2868
2869/* Enable command */
2870DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2871{
2872 /* If enable password is NULL, change to ENABLE_NODE */
2873 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2874 vty->type == VTY_SHELL_SERV)
2875 vty->node = ENABLE_NODE;
2876 else
2877 vty->node = AUTH_ENABLE_NODE;
2878
2879 return CMD_SUCCESS;
2880}
2881
2882/* Disable command */
2883DEFUN(disable,
2884 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2885{
2886 if (vty->node == ENABLE_NODE)
2887 vty->node = VIEW_NODE;
2888 return CMD_SUCCESS;
2889}
2890
2891/* Down vty node level. */
2892gDEFUN(config_exit,
2893 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2894{
2895 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002896 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002897 case VIEW_NODE:
2898 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002899 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002900 break;
2901 case CONFIG_NODE:
2902 vty->node = ENABLE_NODE;
2903 vty_config_unlock(vty);
2904 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002905 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002906 if (vty->node > CONFIG_NODE)
2907 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002908 break;
2909 }
2910 return CMD_SUCCESS;
2911}
2912
2913/* End of configuration. */
2914 gDEFUN(config_end,
2915 config_end_cmd, "end", "End current mode and change to enable mode.")
2916{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002917 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002918 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002919
2920 /* Repeatedly call go_parent until a top node is reached. */
2921 while (vty->node > CONFIG_NODE) {
2922 if (vty->node == last_node) {
2923 /* Ensure termination, this shouldn't happen. */
2924 break;
2925 }
2926 last_node = vty->node;
2927 vty_go_parent(vty);
2928 }
2929
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002930 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002931 if (vty->node > ENABLE_NODE)
2932 vty->node = ENABLE_NODE;
2933 vty->index = NULL;
2934 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002935 }
2936 return CMD_SUCCESS;
2937}
2938
2939/* Show version. */
2940DEFUN(show_version,
2941 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2942{
Harald Welte237f6242010-05-25 23:00:45 +02002943 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2944 host.app_info->version,
2945 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2946 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002947
2948 return CMD_SUCCESS;
2949}
2950
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002951DEFUN(show_online_help,
2952 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2953{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002954 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002955 return CMD_SUCCESS;
2956}
2957
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002958/* Help display function for all node. */
2959gDEFUN(config_help,
2960 config_help_cmd, "help", "Description of the interactive help system\n")
2961{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002962 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2963 "anytime at the command line please press '?'.%s%s"
2964 "If nothing matches, the help list will be empty and you must backup%s"
2965 " until entering a '?' shows the available options.%s"
2966 "Two styles of help are provided:%s"
2967 "1. Full help is available when you are ready to enter a%s"
2968 "command argument (e.g. 'show ?') and describes each possible%s"
2969 "argument.%s"
2970 "2. Partial help is provided when an abbreviated argument is entered%s"
2971 " and you want to know what arguments match the input%s"
2972 " (e.g. 'show me?'.)%s%s",
2973 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2974 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2975 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2976 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002977 return CMD_SUCCESS;
2978}
2979
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002980enum {
2981 ATTR_TYPE_GLOBAL = (1 << 0),
2982 ATTR_TYPE_LIB = (1 << 1),
2983 ATTR_TYPE_APP = (1 << 2),
2984};
2985
2986static void print_attr_list(struct vty *vty, unsigned int attr_mask)
2987{
2988 const char *desc;
2989 unsigned int i;
2990 bool found;
2991 char flag;
2992
2993 if (attr_mask & ATTR_TYPE_GLOBAL) {
2994 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
2995
2996 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07002997 flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002998 desc = cmd_attr_desc[i].str;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07002999
3000 /* Skip attributes without flags */
3001 if (flag != '.')
3002 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07003003 }
3004 }
3005
3006 if (attr_mask & ATTR_TYPE_LIB) {
3007 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
3008
3009 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
3010 if ((desc = cmd_lib_attr_desc[i]) == NULL)
3011 continue;
3012 found = true;
3013
3014 flag = cmd_lib_attr_letters[i];
3015 if (flag == '\0')
3016 flag = '.';
3017
3018 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3019 }
3020
3021 if (!found)
3022 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3023 }
3024
3025 if (attr_mask & ATTR_TYPE_APP) {
3026 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
3027
3028 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
3029 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
3030 continue;
3031 found = true;
3032
3033 flag = host.app_info->usr_attr_letters[i];
3034 if (flag == '\0')
3035 flag = '.';
3036
3037 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3038 }
3039
3040 if (!found)
3041 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3042 }
3043}
3044
3045gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3046 "show vty-attributes",
3047 SHOW_STR "List of VTY attributes\n")
3048{
3049 print_attr_list(vty, 0xff);
3050 return CMD_SUCCESS;
3051}
3052
3053gDEFUN(show_vty_attr, show_vty_attr_cmd,
3054 "show vty-attributes (application|library|global)",
3055 SHOW_STR "List of VTY attributes\n"
3056 "Application specific attributes only\n"
3057 "Library specific attributes only\n"
3058 "Global attributes only\n")
3059{
3060 unsigned int attr_mask = 0;
3061
3062 if (argv[0][0] == 'g') /* global */
3063 attr_mask |= ATTR_TYPE_GLOBAL;
3064 else if (argv[0][0] == 'l') /* library */
3065 attr_mask |= ATTR_TYPE_LIB;
3066 else if (argv[0][0] == 'a') /* application */
3067 attr_mask |= ATTR_TYPE_APP;
3068
3069 print_attr_list(vty, attr_mask);
3070 return CMD_SUCCESS;
3071}
3072
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003073/* Compose flag bit-mask for all commands within the given node */
3074static unsigned int node_flag_mask(const struct cmd_node *cnode)
3075{
3076 unsigned int flag_mask = 0x00;
3077 unsigned int f, i;
3078
3079 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3080 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3081 const struct cmd_element *cmd;
3082 char flag_letter;
3083
3084 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3085 continue;
3086 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
3087 continue;
3088 if (~cmd->usrattr & (1 << f))
3089 continue;
3090
3091 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3092 flag_letter = cmd_lib_attr_letters[f];
3093 else
3094 flag_letter = host.app_info->usr_attr_letters[f];
3095
3096 if (flag_letter == '\0')
3097 continue;
3098
3099 flag_mask |= (1 << f);
3100 break;
3101 }
3102 }
3103
3104 return flag_mask;
3105}
3106
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003107/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
3108static const char *cmd_gflag_mask(const struct cmd_element *cmd)
3109{
3110 static char char_mask[8 + 1];
3111 char *ptr = &char_mask[0];
3112
3113 /* Mutually exclusive global attributes */
3114 if (cmd->attr & CMD_ATTR_IMMEDIATE)
3115 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
3116 else if (cmd->attr & CMD_ATTR_NODE_EXIT)
3117 *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
3118 else
3119 *(ptr++) = '.';
3120
3121 *ptr = '\0';
3122
3123 return char_mask;
3124}
3125
3126/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003127static const char *cmd_flag_mask(const struct cmd_element *cmd,
3128 unsigned int flag_mask)
3129{
3130 static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
3131 char *ptr = &char_mask[0];
3132 char flag_letter;
3133 unsigned int f;
3134
3135 for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
3136 if (~flag_mask & (1 << f))
3137 continue;
3138 if (~cmd->usrattr & (1 << f)) {
3139 *(ptr++) = '.';
3140 continue;
3141 }
3142
3143 if (cmd->attr & CMD_ATTR_LIB_COMMAND)
3144 flag_letter = cmd_lib_attr_letters[f];
3145 else
3146 flag_letter = host.app_info->usr_attr_letters[f];
3147
3148 *(ptr++) = flag_letter ? flag_letter : '.';
3149 }
3150
3151 *ptr = '\0';
3152
3153 return char_mask;
3154}
3155
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003156/* Help display function for all node. */
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003157gDEFUN(config_list, config_list_cmd,
3158 "list [with-flags]",
3159 "Print command list\n"
3160 "Also print the VTY attribute flags\n")
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003161{
3162 unsigned int i;
3163 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003164 unsigned int flag_mask = 0x00;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003165 struct cmd_element *cmd;
3166
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003167 if (argc > 0)
3168 flag_mask = node_flag_mask(cnode);
3169
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003170 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3171 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3172 continue;
3173 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
3174 continue;
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003175 if (!argc)
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003176 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3177 else {
Vadim Yanitskiy7e1a78f2020-10-07 13:13:07 +07003178 vty_out(vty, " %s %s %s%s",
3179 cmd_gflag_mask(cmd),
Vadim Yanitskiy75fec3a2020-10-06 00:13:44 +07003180 cmd_flag_mask(cmd, flag_mask),
3181 cmd->string, VTY_NEWLINE);
3182 }
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003183 }
3184
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003185 return CMD_SUCCESS;
3186}
3187
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003188static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003189{
3190 unsigned int i;
3191 int fd;
3192 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003193 char *config_file_tmp = NULL;
3194 char *config_file_sav = NULL;
3195 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003196 struct stat st;
3197
3198 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003199
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003200 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3201 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3202 * manually instead. */
3203
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003204 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003205 config_file_sav =
3206 _talloc_zero(tall_vty_cmd_ctx,
3207 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3208 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003209 if (!config_file_sav)
3210 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003211 strcpy(config_file_sav, config_file);
3212 strcat(config_file_sav, CONF_BACKUP_EXT);
3213
3214 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003215 "config_file_tmp");
3216 if (!config_file_tmp) {
3217 talloc_free(config_file_sav);
3218 return -1;
3219 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003220 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3221
3222 /* Open file to configuration write. */
3223 fd = mkstemp(config_file_tmp);
3224 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003225 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003226 talloc_free(config_file_tmp);
3227 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003228 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003229 }
3230
3231 /* Make vty for configuration file. */
3232 file_vty = vty_new();
3233 file_vty->fd = fd;
3234 file_vty->type = VTY_FILE;
3235
3236 /* Config file header print. */
3237 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003238 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003239 //vty_time_print (file_vty, 1);
3240 vty_out(file_vty, "!\n");
3241
3242 for (i = 0; i < vector_active(cmdvec); i++)
3243 if ((node = vector_slot(cmdvec, i)) && node->func) {
3244 if ((*node->func) (file_vty))
3245 vty_out(file_vty, "!\n");
3246 }
3247 vty_close(file_vty);
3248
3249 if (unlink(config_file_sav) != 0)
3250 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003251 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003252 talloc_free(config_file_sav);
3253 talloc_free(config_file_tmp);
3254 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003255 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003256 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003257
3258 /* Only link the .sav file if the original file exists */
3259 if (stat(config_file, &st) == 0) {
3260 if (link(config_file, config_file_sav) != 0) {
3261 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3262 talloc_free(config_file_sav);
3263 talloc_free(config_file_tmp);
3264 unlink(config_file_tmp);
3265 return -3;
3266 }
3267 sync();
3268 if (unlink(config_file) != 0) {
3269 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3270 talloc_free(config_file_sav);
3271 talloc_free(config_file_tmp);
3272 unlink(config_file_tmp);
3273 return -4;
3274 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003275 }
3276 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003277 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003278 talloc_free(config_file_sav);
3279 talloc_free(config_file_tmp);
3280 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003281 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003282 }
3283 unlink(config_file_tmp);
3284 sync();
3285
3286 talloc_free(config_file_sav);
3287 talloc_free(config_file_tmp);
3288
3289 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003290 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3291 return -6;
3292 }
3293
3294 return 0;
3295}
3296
3297
3298/* Write current configuration into file. */
3299DEFUN(config_write_file,
3300 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003301 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003302 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003303 "Write to configuration file\n"
3304 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003305{
3306 char *failed_file;
3307 int rc;
3308
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003309 if (host.app_info->config_is_consistent) {
3310 rc = host.app_info->config_is_consistent(vty);
3311 if (!rc) {
3312 vty_out(vty, "Configuration is not consistent%s",
3313 VTY_NEWLINE);
3314 return CMD_WARNING;
3315 }
3316 }
3317
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003318 if (argc == 1)
3319 host_config_set(argv[0]);
3320
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003321 if (host.config == NULL) {
3322 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3323 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003324 return CMD_WARNING;
3325 }
3326
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003327 rc = write_config_file(host.config, &failed_file);
3328 switch (rc) {
3329 case -1:
3330 vty_out(vty, "Can't open configuration file %s.%s",
3331 failed_file, VTY_NEWLINE);
3332 rc = CMD_WARNING;
3333 break;
3334 case -2:
3335 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3336 failed_file, VTY_NEWLINE);
3337 rc = CMD_WARNING;
3338 break;
3339 case -3:
3340 vty_out(vty, "Can't backup old configuration file %s.%s",
3341 failed_file, VTY_NEWLINE);
3342 rc = CMD_WARNING;
3343 break;
3344 case -4:
3345 vty_out(vty, "Can't unlink configuration file %s.%s",
3346 failed_file, VTY_NEWLINE);
3347 rc = CMD_WARNING;
3348 break;
3349 case -5:
3350 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3351 VTY_NEWLINE);
3352 rc = CMD_WARNING;
3353 break;
3354 case -6:
3355 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3356 failed_file, strerror(errno), errno, VTY_NEWLINE);
3357 rc = CMD_WARNING;
3358 break;
3359 default:
3360 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3361 rc = CMD_SUCCESS;
3362 break;
3363 }
3364
3365 talloc_free(failed_file);
3366 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003367}
3368
3369ALIAS(config_write_file,
3370 config_write_cmd,
3371 "write", "Write running configuration to memory, network, or terminal\n")
3372
3373 ALIAS(config_write_file,
3374 config_write_memory_cmd,
3375 "write memory",
3376 "Write running configuration to memory, network, or terminal\n"
3377 "Write configuration to the file (same as write file)\n")
3378
3379 ALIAS(config_write_file,
3380 copy_runningconfig_startupconfig_cmd,
3381 "copy running-config startup-config",
3382 "Copy configuration\n"
3383 "Copy running config to... \n"
3384 "Copy running config to startup config (same as write file)\n")
3385
3386/* Write current configuration into the terminal. */
3387 DEFUN(config_write_terminal,
3388 config_write_terminal_cmd,
3389 "write terminal",
3390 "Write running configuration to memory, network, or terminal\n"
3391 "Write to terminal\n")
3392{
3393 unsigned int i;
3394 struct cmd_node *node;
3395
3396 if (vty->type == VTY_SHELL_SERV) {
3397 for (i = 0; i < vector_active(cmdvec); i++)
3398 if ((node = vector_slot(cmdvec, i)) && node->func
3399 && node->vtysh) {
3400 if ((*node->func) (vty))
3401 vty_out(vty, "!%s", VTY_NEWLINE);
3402 }
3403 } else {
3404 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3405 VTY_NEWLINE);
3406 vty_out(vty, "!%s", VTY_NEWLINE);
3407
3408 for (i = 0; i < vector_active(cmdvec); i++)
3409 if ((node = vector_slot(cmdvec, i)) && node->func) {
3410 if ((*node->func) (vty))
3411 vty_out(vty, "!%s", VTY_NEWLINE);
3412 }
3413 vty_out(vty, "end%s", VTY_NEWLINE);
3414 }
3415 return CMD_SUCCESS;
3416}
3417
3418/* Write current configuration into the terminal. */
3419ALIAS(config_write_terminal,
3420 show_running_config_cmd,
3421 "show running-config", SHOW_STR "running configuration\n")
3422
3423/* Write startup configuration into the terminal. */
3424 DEFUN(show_startup_config,
3425 show_startup_config_cmd,
3426 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3427{
3428 char buf[BUFSIZ];
3429 FILE *confp;
3430
3431 confp = fopen(host.config, "r");
3432 if (confp == NULL) {
3433 vty_out(vty, "Can't open configuration file [%s]%s",
3434 host.config, VTY_NEWLINE);
3435 return CMD_WARNING;
3436 }
3437
3438 while (fgets(buf, BUFSIZ, confp)) {
3439 char *cp = buf;
3440
3441 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3442 cp++;
3443 *cp = '\0';
3444
3445 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3446 }
3447
3448 fclose(confp);
3449
3450 return CMD_SUCCESS;
3451}
3452
3453/* Hostname configuration */
3454DEFUN(config_hostname,
3455 hostname_cmd,
3456 "hostname WORD",
3457 "Set system's network name\n" "This system's network name\n")
3458{
3459 if (!isalpha((int)*argv[0])) {
3460 vty_out(vty, "Please specify string starting with alphabet%s",
3461 VTY_NEWLINE);
3462 return CMD_WARNING;
3463 }
3464
3465 if (host.name)
3466 talloc_free(host.name);
3467
3468 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3469 return CMD_SUCCESS;
3470}
3471
3472DEFUN(config_no_hostname,
3473 no_hostname_cmd,
3474 "no hostname [HOSTNAME]",
3475 NO_STR "Reset system's network name\n" "Host name of this router\n")
3476{
3477 if (host.name)
3478 talloc_free(host.name);
3479 host.name = NULL;
3480 return CMD_SUCCESS;
3481}
3482
3483/* VTY interface password set. */
3484DEFUN(config_password, password_cmd,
3485 "password (8|) WORD",
3486 "Assign the terminal connection password\n"
3487 "Specifies a HIDDEN password will follow\n"
3488 "dummy string \n" "The HIDDEN line password string\n")
3489{
3490 /* Argument check. */
3491 if (argc == 0) {
3492 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3493 return CMD_WARNING;
3494 }
3495
3496 if (argc == 2) {
3497 if (*argv[0] == '8') {
3498 if (host.password)
3499 talloc_free(host.password);
3500 host.password = NULL;
3501 if (host.password_encrypt)
3502 talloc_free(host.password_encrypt);
3503 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3504 return CMD_SUCCESS;
3505 } else {
3506 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3507 return CMD_WARNING;
3508 }
3509 }
3510
3511 if (!isalnum((int)*argv[0])) {
3512 vty_out(vty,
3513 "Please specify string starting with alphanumeric%s",
3514 VTY_NEWLINE);
3515 return CMD_WARNING;
3516 }
3517
3518 if (host.password)
3519 talloc_free(host.password);
3520 host.password = NULL;
3521
3522#ifdef VTY_CRYPT_PW
3523 if (host.encrypt) {
3524 if (host.password_encrypt)
3525 talloc_free(host.password_encrypt);
3526 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3527 } else
3528#endif
3529 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3530
3531 return CMD_SUCCESS;
3532}
3533
3534ALIAS(config_password, password_text_cmd,
3535 "password LINE",
3536 "Assign the terminal connection password\n"
3537 "The UNENCRYPTED (cleartext) line password\n")
3538
3539/* VTY enable password set. */
3540 DEFUN(config_enable_password, enable_password_cmd,
3541 "enable password (8|) WORD",
3542 "Modify enable password parameters\n"
3543 "Assign the privileged level password\n"
3544 "Specifies a HIDDEN password will follow\n"
3545 "dummy string \n" "The HIDDEN 'enable' password string\n")
3546{
3547 /* Argument check. */
3548 if (argc == 0) {
3549 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3550 return CMD_WARNING;
3551 }
3552
3553 /* Crypt type is specified. */
3554 if (argc == 2) {
3555 if (*argv[0] == '8') {
3556 if (host.enable)
3557 talloc_free(host.enable);
3558 host.enable = NULL;
3559
3560 if (host.enable_encrypt)
3561 talloc_free(host.enable_encrypt);
3562 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3563
3564 return CMD_SUCCESS;
3565 } else {
3566 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3567 return CMD_WARNING;
3568 }
3569 }
3570
3571 if (!isalnum((int)*argv[0])) {
3572 vty_out(vty,
3573 "Please specify string starting with alphanumeric%s",
3574 VTY_NEWLINE);
3575 return CMD_WARNING;
3576 }
3577
3578 if (host.enable)
3579 talloc_free(host.enable);
3580 host.enable = NULL;
3581
3582 /* Plain password input. */
3583#ifdef VTY_CRYPT_PW
3584 if (host.encrypt) {
3585 if (host.enable_encrypt)
3586 talloc_free(host.enable_encrypt);
3587 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3588 } else
3589#endif
3590 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3591
3592 return CMD_SUCCESS;
3593}
3594
3595ALIAS(config_enable_password,
3596 enable_password_text_cmd,
3597 "enable password LINE",
3598 "Modify enable password parameters\n"
3599 "Assign the privileged level password\n"
3600 "The UNENCRYPTED (cleartext) 'enable' password\n")
3601
3602/* VTY enable password delete. */
3603 DEFUN(no_config_enable_password, no_enable_password_cmd,
3604 "no enable password",
3605 NO_STR
3606 "Modify enable password parameters\n"
3607 "Assign the privileged level password\n")
3608{
3609 if (host.enable)
3610 talloc_free(host.enable);
3611 host.enable = NULL;
3612
3613 if (host.enable_encrypt)
3614 talloc_free(host.enable_encrypt);
3615 host.enable_encrypt = NULL;
3616
3617 return CMD_SUCCESS;
3618}
3619
3620#ifdef VTY_CRYPT_PW
3621DEFUN(service_password_encrypt,
3622 service_password_encrypt_cmd,
3623 "service password-encryption",
3624 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3625{
3626 if (host.encrypt)
3627 return CMD_SUCCESS;
3628
3629 host.encrypt = 1;
3630
3631 if (host.password) {
3632 if (host.password_encrypt)
3633 talloc_free(host.password_encrypt);
3634 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3635 }
3636 if (host.enable) {
3637 if (host.enable_encrypt)
3638 talloc_free(host.enable_encrypt);
3639 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3640 }
3641
3642 return CMD_SUCCESS;
3643}
3644
3645DEFUN(no_service_password_encrypt,
3646 no_service_password_encrypt_cmd,
3647 "no service password-encryption",
3648 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3649{
3650 if (!host.encrypt)
3651 return CMD_SUCCESS;
3652
3653 host.encrypt = 0;
3654
3655 if (host.password_encrypt)
3656 talloc_free(host.password_encrypt);
3657 host.password_encrypt = NULL;
3658
3659 if (host.enable_encrypt)
3660 talloc_free(host.enable_encrypt);
3661 host.enable_encrypt = NULL;
3662
3663 return CMD_SUCCESS;
3664}
3665#endif
3666
3667DEFUN(config_terminal_length, config_terminal_length_cmd,
3668 "terminal length <0-512>",
3669 "Set terminal line parameters\n"
3670 "Set number of lines on a screen\n"
3671 "Number of lines on screen (0 for no pausing)\n")
3672{
3673 int lines;
3674 char *endptr = NULL;
3675
3676 lines = strtol(argv[0], &endptr, 10);
3677 if (lines < 0 || lines > 512 || *endptr != '\0') {
3678 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3679 return CMD_WARNING;
3680 }
3681 vty->lines = lines;
3682
3683 return CMD_SUCCESS;
3684}
3685
3686DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3687 "terminal no length",
3688 "Set terminal line parameters\n"
3689 NO_STR "Set number of lines on a screen\n")
3690{
3691 vty->lines = -1;
3692 return CMD_SUCCESS;
3693}
3694
3695DEFUN(service_terminal_length, service_terminal_length_cmd,
3696 "service terminal-length <0-512>",
3697 "Set up miscellaneous service\n"
3698 "System wide terminal length configuration\n"
3699 "Number of lines of VTY (0 means no line control)\n")
3700{
3701 int lines;
3702 char *endptr = NULL;
3703
3704 lines = strtol(argv[0], &endptr, 10);
3705 if (lines < 0 || lines > 512 || *endptr != '\0') {
3706 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3707 return CMD_WARNING;
3708 }
3709 host.lines = lines;
3710
3711 return CMD_SUCCESS;
3712}
3713
3714DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3715 "no service terminal-length [<0-512>]",
3716 NO_STR
3717 "Set up miscellaneous service\n"
3718 "System wide terminal length configuration\n"
3719 "Number of lines of VTY (0 means no line control)\n")
3720{
3721 host.lines = -1;
3722 return CMD_SUCCESS;
3723}
3724
3725DEFUN_HIDDEN(do_echo,
3726 echo_cmd,
3727 "echo .MESSAGE",
3728 "Echo a message back to the vty\n" "The message to echo\n")
3729{
3730 char *message;
3731
3732 vty_out(vty, "%s%s",
3733 ((message =
3734 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3735 if (message)
3736 talloc_free(message);
3737 return CMD_SUCCESS;
3738}
3739
3740#if 0
3741DEFUN(config_logmsg,
3742 config_logmsg_cmd,
3743 "logmsg " LOG_LEVELS " .MESSAGE",
3744 "Send a message to enabled logging destinations\n"
3745 LOG_LEVEL_DESC "The message to send\n")
3746{
3747 int level;
3748 char *message;
3749
3750 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3751 return CMD_ERR_NO_MATCH;
3752
3753 zlog(NULL, level,
3754 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3755 if (message)
3756 talloc_free(message);
3757 return CMD_SUCCESS;
3758}
3759
3760DEFUN(show_logging,
3761 show_logging_cmd,
3762 "show logging", SHOW_STR "Show current logging configuration\n")
3763{
3764 struct zlog *zl = zlog_default;
3765
3766 vty_out(vty, "Syslog logging: ");
3767 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3768 vty_out(vty, "disabled");
3769 else
3770 vty_out(vty, "level %s, facility %s, ident %s",
3771 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3772 facility_name(zl->facility), zl->ident);
3773 vty_out(vty, "%s", VTY_NEWLINE);
3774
3775 vty_out(vty, "Stdout logging: ");
3776 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3777 vty_out(vty, "disabled");
3778 else
3779 vty_out(vty, "level %s",
3780 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3781 vty_out(vty, "%s", VTY_NEWLINE);
3782
3783 vty_out(vty, "Monitor logging: ");
3784 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3785 vty_out(vty, "disabled");
3786 else
3787 vty_out(vty, "level %s",
3788 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3789 vty_out(vty, "%s", VTY_NEWLINE);
3790
3791 vty_out(vty, "File logging: ");
3792 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3793 vty_out(vty, "disabled");
3794 else
3795 vty_out(vty, "level %s, filename %s",
3796 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3797 zl->filename);
3798 vty_out(vty, "%s", VTY_NEWLINE);
3799
3800 vty_out(vty, "Protocol name: %s%s",
3801 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3802 vty_out(vty, "Record priority: %s%s",
3803 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3804
3805 return CMD_SUCCESS;
3806}
3807
3808DEFUN(config_log_stdout,
3809 config_log_stdout_cmd,
3810 "log stdout", "Logging control\n" "Set stdout logging level\n")
3811{
3812 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3813 return CMD_SUCCESS;
3814}
3815
3816DEFUN(config_log_stdout_level,
3817 config_log_stdout_level_cmd,
3818 "log stdout " LOG_LEVELS,
3819 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3820{
3821 int level;
3822
3823 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3824 return CMD_ERR_NO_MATCH;
3825 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3826 return CMD_SUCCESS;
3827}
3828
3829DEFUN(no_config_log_stdout,
3830 no_config_log_stdout_cmd,
3831 "no log stdout [LEVEL]",
3832 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3833{
3834 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3835 return CMD_SUCCESS;
3836}
3837
3838DEFUN(config_log_monitor,
3839 config_log_monitor_cmd,
3840 "log monitor",
3841 "Logging control\n" "Set terminal line (monitor) logging level\n")
3842{
3843 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3844 return CMD_SUCCESS;
3845}
3846
3847DEFUN(config_log_monitor_level,
3848 config_log_monitor_level_cmd,
3849 "log monitor " LOG_LEVELS,
3850 "Logging control\n"
3851 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3852{
3853 int level;
3854
3855 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3856 return CMD_ERR_NO_MATCH;
3857 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3858 return CMD_SUCCESS;
3859}
3860
3861DEFUN(no_config_log_monitor,
3862 no_config_log_monitor_cmd,
3863 "no log monitor [LEVEL]",
3864 NO_STR
3865 "Logging control\n"
3866 "Disable terminal line (monitor) logging\n" "Logging level\n")
3867{
3868 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3869 return CMD_SUCCESS;
3870}
3871
3872static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3873{
3874 int ret;
3875 char *p = NULL;
3876 const char *fullpath;
3877
3878 /* Path detection. */
3879 if (!IS_DIRECTORY_SEP(*fname)) {
3880 char cwd[MAXPATHLEN + 1];
3881 cwd[MAXPATHLEN] = '\0';
3882
3883 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3884 zlog_err("config_log_file: Unable to alloc mem!");
3885 return CMD_WARNING;
3886 }
3887
3888 if ((p = _talloc_zero(tall_vcmd_ctx,
3889 strlen(cwd) + strlen(fname) + 2),
3890 "set_log_file")
3891 == NULL) {
3892 zlog_err("config_log_file: Unable to alloc mem!");
3893 return CMD_WARNING;
3894 }
3895 sprintf(p, "%s/%s", cwd, fname);
3896 fullpath = p;
3897 } else
3898 fullpath = fname;
3899
3900 ret = zlog_set_file(NULL, fullpath, loglevel);
3901
3902 if (p)
3903 talloc_free(p);
3904
3905 if (!ret) {
3906 vty_out(vty, "can't open logfile %s\n", fname);
3907 return CMD_WARNING;
3908 }
3909
3910 if (host.logfile)
3911 talloc_free(host.logfile);
3912
3913 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3914
3915 return CMD_SUCCESS;
3916}
3917
3918DEFUN(config_log_file,
3919 config_log_file_cmd,
3920 "log file FILENAME",
3921 "Logging control\n" "Logging to file\n" "Logging filename\n")
3922{
3923 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3924}
3925
3926DEFUN(config_log_file_level,
3927 config_log_file_level_cmd,
3928 "log file FILENAME " LOG_LEVELS,
3929 "Logging control\n"
3930 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3931{
3932 int level;
3933
3934 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3935 return CMD_ERR_NO_MATCH;
3936 return set_log_file(vty, argv[0], level);
3937}
3938
3939DEFUN(no_config_log_file,
3940 no_config_log_file_cmd,
3941 "no log file [FILENAME]",
3942 NO_STR
3943 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3944{
3945 zlog_reset_file(NULL);
3946
3947 if (host.logfile)
3948 talloc_free(host.logfile);
3949
3950 host.logfile = NULL;
3951
3952 return CMD_SUCCESS;
3953}
3954
3955ALIAS(no_config_log_file,
3956 no_config_log_file_level_cmd,
3957 "no log file FILENAME LEVEL",
3958 NO_STR
3959 "Logging control\n"
3960 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3961
3962 DEFUN(config_log_syslog,
3963 config_log_syslog_cmd,
3964 "log syslog", "Logging control\n" "Set syslog logging level\n")
3965{
3966 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3967 return CMD_SUCCESS;
3968}
3969
3970DEFUN(config_log_syslog_level,
3971 config_log_syslog_level_cmd,
3972 "log syslog " LOG_LEVELS,
3973 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3974{
3975 int level;
3976
3977 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3978 return CMD_ERR_NO_MATCH;
3979 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3980 return CMD_SUCCESS;
3981}
3982
3983DEFUN_DEPRECATED(config_log_syslog_facility,
3984 config_log_syslog_facility_cmd,
3985 "log syslog facility " LOG_FACILITIES,
3986 "Logging control\n"
3987 "Logging goes to syslog\n"
3988 "(Deprecated) Facility parameter for syslog messages\n"
3989 LOG_FACILITY_DESC)
3990{
3991 int facility;
3992
3993 if ((facility = facility_match(argv[0])) < 0)
3994 return CMD_ERR_NO_MATCH;
3995
3996 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3997 zlog_default->facility = facility;
3998 return CMD_SUCCESS;
3999}
4000
4001DEFUN(no_config_log_syslog,
4002 no_config_log_syslog_cmd,
4003 "no log syslog [LEVEL]",
4004 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
4005{
4006 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
4007 return CMD_SUCCESS;
4008}
4009
4010ALIAS(no_config_log_syslog,
4011 no_config_log_syslog_facility_cmd,
4012 "no log syslog facility " LOG_FACILITIES,
4013 NO_STR
4014 "Logging control\n"
4015 "Logging goes to syslog\n"
4016 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4017
4018 DEFUN(config_log_facility,
4019 config_log_facility_cmd,
4020 "log facility " LOG_FACILITIES,
4021 "Logging control\n"
4022 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
4023{
4024 int facility;
4025
4026 if ((facility = facility_match(argv[0])) < 0)
4027 return CMD_ERR_NO_MATCH;
4028 zlog_default->facility = facility;
4029 return CMD_SUCCESS;
4030}
4031
4032DEFUN(no_config_log_facility,
4033 no_config_log_facility_cmd,
4034 "no log facility [FACILITY]",
4035 NO_STR
4036 "Logging control\n"
4037 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
4038{
4039 zlog_default->facility = LOG_DAEMON;
4040 return CMD_SUCCESS;
4041}
4042
4043DEFUN_DEPRECATED(config_log_trap,
4044 config_log_trap_cmd,
4045 "log trap " LOG_LEVELS,
4046 "Logging control\n"
4047 "(Deprecated) Set logging level and default for all destinations\n"
4048 LOG_LEVEL_DESC)
4049{
4050 int new_level;
4051 int i;
4052
4053 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
4054 return CMD_ERR_NO_MATCH;
4055
4056 zlog_default->default_lvl = new_level;
4057 for (i = 0; i < ZLOG_NUM_DESTS; i++)
4058 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4059 zlog_default->maxlvl[i] = new_level;
4060 return CMD_SUCCESS;
4061}
4062
4063DEFUN_DEPRECATED(no_config_log_trap,
4064 no_config_log_trap_cmd,
4065 "no log trap [LEVEL]",
4066 NO_STR
4067 "Logging control\n"
4068 "Permit all logging information\n" "Logging level\n")
4069{
4070 zlog_default->default_lvl = LOG_DEBUG;
4071 return CMD_SUCCESS;
4072}
4073
4074DEFUN(config_log_record_priority,
4075 config_log_record_priority_cmd,
4076 "log record-priority",
4077 "Logging control\n"
4078 "Log the priority of the message within the message\n")
4079{
4080 zlog_default->record_priority = 1;
4081 return CMD_SUCCESS;
4082}
4083
4084DEFUN(no_config_log_record_priority,
4085 no_config_log_record_priority_cmd,
4086 "no log record-priority",
4087 NO_STR
4088 "Logging control\n"
4089 "Do not log the priority of the message within the message\n")
4090{
4091 zlog_default->record_priority = 0;
4092 return CMD_SUCCESS;
4093}
4094#endif
4095
4096DEFUN(banner_motd_file,
4097 banner_motd_file_cmd,
4098 "banner motd file [FILE]",
4099 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
4100{
4101 if (host.motdfile)
4102 talloc_free(host.motdfile);
4103 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
4104
4105 return CMD_SUCCESS;
4106}
4107
4108DEFUN(banner_motd_default,
4109 banner_motd_default_cmd,
4110 "banner motd default",
4111 "Set banner string\n" "Strings for motd\n" "Default string\n")
4112{
4113 host.motd = default_motd;
4114 return CMD_SUCCESS;
4115}
4116
4117DEFUN(no_banner_motd,
4118 no_banner_motd_cmd,
4119 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
4120{
4121 host.motd = NULL;
4122 if (host.motdfile)
4123 talloc_free(host.motdfile);
4124 host.motdfile = NULL;
4125 return CMD_SUCCESS;
4126}
4127
4128/* Set config filename. Called from vty.c */
4129void host_config_set(const char *filename)
4130{
4131 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4132}
4133
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004134/*! Deprecated, now happens implicitly when calling install_node().
4135 * Users of the API may still attempt to call this function, hence
4136 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004137void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004138{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004139}
4140
4141/*! Deprecated, now happens implicitly when calling install_node().
4142 * Users of the API may still attempt to call this function, hence
4143 * leave it here as a no-op. */
4144void vty_install_default(int node)
4145{
4146}
4147
4148/*! Install common commands like 'exit' and 'list'. */
4149static void install_basic_node_commands(int node)
4150{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004151 install_lib_element(node, &config_help_cmd);
4152 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004153
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004154 install_lib_element(node, &show_vty_attr_all_cmd);
4155 install_lib_element(node, &show_vty_attr_cmd);
4156
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004157 install_lib_element(node, &config_write_terminal_cmd);
4158 install_lib_element(node, &config_write_file_cmd);
4159 install_lib_element(node, &config_write_memory_cmd);
4160 install_lib_element(node, &config_write_cmd);
4161 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004162
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004163 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004164
4165 if (node >= CONFIG_NODE) {
4166 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004167 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004168 }
4169}
4170
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004171/*! Return true if a node is installed by install_basic_node_commands(), so
4172 * that we can avoid repeating them for each and every node during 'show
4173 * running-config' */
4174static bool vty_command_is_common(struct cmd_element *cmd)
4175{
4176 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004177 || cmd == &show_vty_attr_all_cmd
4178 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004179 || cmd == &config_list_cmd
4180 || cmd == &config_write_terminal_cmd
4181 || cmd == &config_write_file_cmd
4182 || cmd == &config_write_memory_cmd
4183 || cmd == &config_write_cmd
4184 || cmd == &show_running_config_cmd
4185 || cmd == &config_exit_cmd
4186 || cmd == &config_end_cmd)
4187 return true;
4188 return false;
4189}
4190
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004191/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004192 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004193 * \param[in] vty the vty of the code
4194 * \param[in] filename where to store the file
4195 * \return 0 in case of success.
4196 *
4197 * If the filename already exists create a filename.sav
4198 * version with the current code.
4199 *
4200 */
4201int osmo_vty_write_config_file(const char *filename)
4202{
4203 char *failed_file;
4204 int rc;
4205
4206 rc = write_config_file(filename, &failed_file);
4207 talloc_free(failed_file);
4208 return rc;
4209}
4210
4211/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004212 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004213 * \return 0 in case of success.
4214 *
4215 * If the filename already exists create a filename.sav
4216 * version with the current code.
4217 *
4218 */
4219int osmo_vty_save_config_file(void)
4220{
4221 char *failed_file;
4222 int rc;
4223
4224 if (host.config == NULL)
4225 return -7;
4226
4227 rc = write_config_file(host.config, &failed_file);
4228 talloc_free(failed_file);
4229 return rc;
4230}
4231
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004232/* Initialize command interface. Install basic nodes and commands. */
4233void cmd_init(int terminal)
4234{
4235 /* Allocate initial top vector of commands. */
4236 cmdvec = vector_init(VECTOR_MIN_SIZE);
4237
4238 /* Default host value settings. */
4239 host.name = NULL;
4240 host.password = NULL;
4241 host.enable = NULL;
4242 host.logfile = NULL;
4243 host.config = NULL;
4244 host.lines = -1;
4245 host.motd = default_motd;
4246 host.motdfile = NULL;
4247
4248 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004249 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004250 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004251 install_node_bare(&auth_node, NULL);
4252 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004253 install_node(&config_node, config_write_host);
4254
4255 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004256 install_lib_element(VIEW_NODE, &show_version_cmd);
4257 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004258 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004259 install_lib_element(VIEW_NODE, &config_list_cmd);
4260 install_lib_element(VIEW_NODE, &config_exit_cmd);
4261 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004262 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4263 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004264 install_lib_element(VIEW_NODE, &config_enable_cmd);
4265 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4266 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4267 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004268 }
4269
4270 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004271 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4272 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4273 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004274 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004275 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4276 install_lib_element(ENABLE_NODE, &show_version_cmd);
4277 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004278
4279 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004280 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4281 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4282 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004283 }
4284
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004285 install_lib_element(CONFIG_NODE, &hostname_cmd);
4286 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004287
4288 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004289 install_lib_element(CONFIG_NODE, &password_cmd);
4290 install_lib_element(CONFIG_NODE, &password_text_cmd);
4291 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4292 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4293 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004294
4295#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004296 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4297 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004298#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004299 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4300 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4301 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4302 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4303 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004304
4305 }
4306 srand(time(NULL));
4307}
Harald Welte7acb30c2011-08-17 17:13:48 +02004308
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004309/* FIXME: execute this section in the unit test instead */
4310static __attribute__((constructor)) void on_dso_load(void)
4311{
4312 unsigned int i, j;
4313
4314 /* Check total number of the library specific attributes */
4315 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4316
4317 /* Check for duplicates in the list of library specific flags */
4318 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4319 if (cmd_lib_attr_letters[i] == '\0')
4320 continue;
4321
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07004322 /* Some flag characters are reserved for global attributes */
4323 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
4324 for (j = 0; j < ARRAY_SIZE(rafc); j++)
4325 OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
4326
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004327 /* Only upper case flag letters are allowed for libraries */
4328 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4329 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4330
4331 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4332 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4333 }
4334}
4335
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004336/*! @} */