blob: 46fe854c8335b77149c7c440d435508b278790c8 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +020040#include <limits.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020041#include <sys/time.h>
42#include <sys/stat.h>
43
44#include <osmocom/vty/vector.h>
45#include <osmocom/vty/vty.h>
46#include <osmocom/vty/command.h>
47
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010048#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010049#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050
Ruben Undheim766f77c2018-11-18 13:02:47 +010051#ifndef MAXPATHLEN
52 #define MAXPATHLEN 4096
53#endif
54
55
Harald Weltee881b1b2011-08-17 18:52:30 +020056/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020057 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020059 *
60 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020061
Harald Welte3fb0b6f2010-05-19 19:02:52 +020062#define CONFIGFILE_MASK 022
63
64void *tall_vty_cmd_ctx;
65
66/* Command vector which includes some level of command lists. Normally
67 each daemon maintains each own cmdvec. */
68vector cmdvec;
69
70/* Host information structure. */
71struct host host;
72
73/* Standard command node structures. */
74struct cmd_node auth_node = {
75 AUTH_NODE,
76 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node view_node = {
81 VIEW_NODE,
82 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node auth_enable_node = {
87 AUTH_ENABLE_NODE,
88 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node enable_node = {
93 ENABLE_NODE,
94 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010095 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096};
97
98struct cmd_node config_node = {
99 CONFIG_NODE,
100 "%s(config)# ",
101 1
102};
103
104/* Default motd string. */
105const char *default_motd = "";
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200108 *
109 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110void print_version(int print_copyright)
111{
Harald Welte237f6242010-05-25 23:00:45 +0200112 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200114 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115}
116
117/* Utility function to concatenate argv argument into a single string
118 with inserting ' ' character between each argument. */
119char *argv_concat(const char **argv, int argc, int shift)
120{
121 int i;
122 size_t len;
123 char *str;
124 char *p;
125
126 len = 0;
127 for (i = shift; i < argc; i++)
128 len += strlen(argv[i]) + 1;
129 if (!len)
130 return NULL;
131 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
132 for (i = shift; i < argc; i++) {
133 size_t arglen;
134 memcpy(p, argv[i], (arglen = strlen(argv[i])));
135 p += arglen;
136 *p++ = ' ';
137 }
138 *(p - 1) = '\0';
139 return str;
140}
141
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200142/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
143 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
144 * in turn, this name us used for XML IDs in 'show online-help'. */
145static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
146{
147 const char *pos;
148 int dest = 0;
149
150 if (!prompt || !*prompt)
151 return "";
152
153 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
154 if (pos[0] == '%' && pos[1]) {
155 /* skip "%s"; loop pos++ does the second one. */
156 pos++;
157 continue;
158 }
159 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
160 continue;
161 name_buf[dest] = pos[0];
162 dest++;
163 }
164 name_buf[dest] = '\0';
165 return name_buf;
166}
167
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200168static void install_basic_node_commands(int node);
169
170/*! Install top node of command vector, without adding basic node commands. */
171static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172{
173 vector_set_index(cmdvec, node->node, node);
174 node->func = func;
175 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200176 if (!*node->name)
177 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200178}
179
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200180/*! Install top node of command vector. */
181void install_node(struct cmd_node *node, int (*func) (struct vty *))
182{
183 install_node_bare(node, func);
184 install_basic_node_commands(node->node);
185}
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187/* Compare two command's string. Used in sort_node (). */
188static int cmp_node(const void *p, const void *q)
189{
190 struct cmd_element *a = *(struct cmd_element **)p;
191 struct cmd_element *b = *(struct cmd_element **)q;
192
193 return strcmp(a->string, b->string);
194}
195
196static int cmp_desc(const void *p, const void *q)
197{
198 struct desc *a = *(struct desc **)p;
199 struct desc *b = *(struct desc **)q;
200
201 return strcmp(a->cmd, b->cmd);
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200205void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200206{
207 unsigned int i, j;
208 struct cmd_node *cnode;
209 vector descvec;
210 struct cmd_element *cmd_element;
211
212 for (i = 0; i < vector_active(cmdvec); i++)
213 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
214 vector cmd_vector = cnode->cmd_vector;
215 qsort(cmd_vector->index, vector_active(cmd_vector),
216 sizeof(void *), cmp_node);
217
218 for (j = 0; j < vector_active(cmd_vector); j++)
219 if ((cmd_element =
220 vector_slot(cmd_vector, j)) != NULL
221 && vector_active(cmd_element->strvec)) {
222 descvec =
223 vector_slot(cmd_element->strvec,
224 vector_active
225 (cmd_element->strvec) -
226 1);
227 qsort(descvec->index,
228 vector_active(descvec),
229 sizeof(void *), cmp_desc);
230 }
231 }
232}
233
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200234/*! Break up string in command tokens. Return leading indents.
235 * \param[in] string String to split.
236 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
237 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
238 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
239 *
240 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
241 * so that \a indent can simply return the count of leading spaces.
242 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
243 */
244int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245{
246 const char *cp, *start;
247 char *token;
248 int strlen;
249 vector strvec;
250
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200251 *strvec_p = NULL;
252 if (indent)
253 *indent = 0;
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200256 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200257
258 cp = string;
259
260 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200261 while (isspace((int)*cp) && *cp != '\0') {
262 /* if we're counting indents, we need to be strict about them */
263 if (indent && (*cp != ' ') && (*cp != '\t')) {
264 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
265 if (*cp == '\n' || *cp == '\r') {
266 cp++;
267 string = cp;
268 continue;
269 }
270 return CMD_ERR_INVALID_INDENT;
271 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200273 }
274
275 if (indent)
276 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277
278 /* Return if there is only white spaces */
279 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200280 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281
282 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200283 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200284
285 /* Prepare return vector. */
286 strvec = vector_init(VECTOR_MIN_SIZE);
287
288 /* Copy each command piece and set into vector. */
289 while (1) {
290 start = cp;
291 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
292 *cp != '\0')
293 cp++;
294 strlen = cp - start;
295 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
296 memcpy(token, start, strlen);
297 *(token + strlen) = '\0';
298 vector_set(strvec, token);
299
300 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
301 *cp != '\0')
302 cp++;
303
304 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200305 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200306 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200307
308 *strvec_p = strvec;
309 return CMD_SUCCESS;
310}
311
312/*! Breaking up string into each command piece. I assume given
313 character is separated by a space character. Return value is a
314 vector which includes char ** data element. */
315vector cmd_make_strvec(const char *string)
316{
317 vector strvec;
318 cmd_make_strvec2(string, NULL, &strvec);
319 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200320}
321
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200322/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200323void cmd_free_strvec(vector v)
324{
325 unsigned int i;
326 char *cp;
327
328 if (!v)
329 return;
330
331 for (i = 0; i < vector_active(v); i++)
332 if ((cp = vector_slot(v, i)) != NULL)
333 talloc_free(cp);
334
335 vector_free(v);
336}
337
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200338/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200339static char *cmd_desc_str(const char **string)
340{
341 const char *cp, *start;
342 char *token;
343 int strlen;
344
345 cp = *string;
346
347 if (cp == NULL)
348 return NULL;
349
350 /* Skip white spaces. */
351 while (isspace((int)*cp) && *cp != '\0')
352 cp++;
353
354 /* Return if there is only white spaces */
355 if (*cp == '\0')
356 return NULL;
357
358 start = cp;
359
360 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
361 cp++;
362
363 strlen = cp - start;
364 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
365 memcpy(token, start, strlen);
366 *(token + strlen) = '\0';
367
368 *string = cp;
369
370 return token;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200374static vector cmd_make_descvec(const char *string, const char *descstr)
375{
376 int multiple = 0;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100377 int optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200378 const char *sp;
379 char *token;
380 int len;
381 const char *cp;
382 const char *dp;
383 vector allvec;
384 vector strvec = NULL;
385 struct desc *desc;
386
387 cp = string;
388 dp = descstr;
389
390 if (cp == NULL)
391 return NULL;
392
393 allvec = vector_init(VECTOR_MIN_SIZE);
394
395 while (1) {
396 while (isspace((int)*cp) && *cp != '\0')
397 cp++;
398
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100399 /* Explicitly detect optional multi-choice braces like [(one|two)]. */
400 if (cp[0] == '[' && cp[1] == '(') {
401 optional_brace = 1;
402 cp++;
403 }
404
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200405 if (*cp == '(') {
406 multiple = 1;
407 cp++;
408 }
409 if (*cp == ')') {
410 multiple = 0;
411 cp++;
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100412 if (*cp == ']')
413 cp++;
414 optional_brace = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 }
416 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100417 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 cp++;
419 }
420
421 while (isspace((int)*cp) && *cp != '\0')
422 cp++;
423
424 if (*cp == '(') {
425 multiple = 1;
426 cp++;
427 }
428
429 if (*cp == '\0')
430 return allvec;
431
432 sp = cp;
433
434 while (!
435 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
436 || *cp == ')' || *cp == '|') && *cp != '\0')
437 cp++;
438
439 len = cp - sp;
440
Neels Hofmeyrb55f4d22019-01-31 08:13:31 +0100441 token = _talloc_zero(tall_vty_cmd_ctx, len + (optional_brace? 2 : 0) + 1, "cmd_make_descvec");
442 if (optional_brace) {
443 /* Place each individual multi-choice token in its own square braces */
444 token[0] = '[';
445 memcpy(token + 1, sp, len);
446 token[1 + len] = ']';
447 token[2 + len] = '\0';
448 } else {
449 memcpy(token, sp, len);
450 *(token + len) = '\0';
451 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200452
453 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
454 desc->cmd = token;
455 desc->str = cmd_desc_str(&dp);
456
457 if (multiple) {
458 if (multiple == 1) {
459 strvec = vector_init(VECTOR_MIN_SIZE);
460 vector_set(allvec, strvec);
461 }
462 multiple++;
463 } else {
464 strvec = vector_init(VECTOR_MIN_SIZE);
465 vector_set(allvec, strvec);
466 }
467 vector_set(strvec, desc);
468 }
469}
470
471/* Count mandantory string vector size. This is to determine inputed
472 command has enough command length. */
473static int cmd_cmdsize(vector strvec)
474{
475 unsigned int i;
476 int size = 0;
477 vector descvec;
478 struct desc *desc;
479
480 for (i = 0; i < vector_active(strvec); i++)
481 if ((descvec = vector_slot(strvec, i)) != NULL) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100482 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483 && (desc = vector_slot(descvec, 0)) != NULL) {
484 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
485 return size;
486 else
487 size++;
488 } else
489 size++;
490 }
491 return size;
492}
493
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200494/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200495const char *cmd_prompt(enum node_type node)
496{
497 struct cmd_node *cnode;
498
499 cnode = vector_slot(cmdvec, node);
500 return cnode->prompt;
501}
502
Alexander Couzensad580ba2016-05-16 16:01:45 +0200503/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200505 * \param unsafe string
506 * \return a new talloc char *
507 */
508char *osmo_asciidoc_escape(const char *inp)
509{
510 int _strlen;
511 char *out, *out_ptr;
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200512 int len = 0, i;
Alexander Couzensad580ba2016-05-16 16:01:45 +0200513
514 if (!inp)
515 return NULL;
516 _strlen = strlen(inp);
517
518 for (i = 0; i < _strlen; ++i) {
519 switch (inp[i]) {
520 case '|':
521 len += 2;
522 break;
523 default:
524 len += 1;
525 break;
526 }
527 }
528
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200529 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200530 if (!out)
531 return NULL;
532
533 out_ptr = out;
534
Alexander Couzensad580ba2016-05-16 16:01:45 +0200535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '|':
Pau Espin Pedrole1e1ec32019-06-19 14:55:45 +0200538 /* Prepend escape character "\": */
539 *(out_ptr++) = '\\';
540 /* fall through */
Alexander Couzensad580ba2016-05-16 16:01:45 +0200541 default:
542 *(out_ptr++) = inp[i];
543 break;
544 }
545 }
546
Alexander Couzensad580ba2016-05-16 16:01:45 +0200547 out_ptr[0] = '\0';
548 return out;
549}
550
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100551static char *xml_escape(const char *inp)
552{
553 int _strlen;
554 char *out, *out_ptr;
555 int len = 0, i, j;
556
557 if (!inp)
558 return NULL;
559 _strlen = strlen(inp);
560
561 for (i = 0; i < _strlen; ++i) {
562 switch (inp[i]) {
563 case '"':
564 len += 6;
565 break;
566 case '\'':
567 len += 6;
568 break;
569 case '<':
570 len += 4;
571 break;
572 case '>':
573 len += 4;
574 break;
575 case '&':
576 len += 5;
577 break;
578 default:
579 len += 1;
580 break;
581 }
582 }
583
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200584 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100585 if (!out)
586 return NULL;
587
588 out_ptr = out;
589
590#define ADD(out, str) \
591 for (j = 0; j < strlen(str); ++j) \
592 *(out++) = str[j];
593
594 for (i = 0; i < _strlen; ++i) {
595 switch (inp[i]) {
596 case '"':
597 ADD(out_ptr, "&quot;");
598 break;
599 case '\'':
600 ADD(out_ptr, "&apos;");
601 break;
602 case '<':
603 ADD(out_ptr, "&lt;");
604 break;
605 case '>':
606 ADD(out_ptr, "&gt;");
607 break;
608 case '&':
609 ADD(out_ptr, "&amp;");
610 break;
611 default:
612 *(out_ptr++) = inp[i];
613 break;
614 }
615 }
616
617#undef ADD
618
619 out_ptr[0] = '\0';
620 return out;
621}
622
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200623typedef int (*print_func_t)(void *data, const char *fmt, ...);
624
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700625static const struct value_string cmd_attr_desc[] = {
626 { CMD_ATTR_DEPRECATED, "This command is deprecated" },
627 { CMD_ATTR_HIDDEN, "This command is hidden" },
628 { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
Vadim Yanitskiyceb3b392020-10-06 00:20:24 +0700629 { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700630 /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700631 { 0, NULL }
632};
633
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700634/* Description of attributes shared between the lib commands */
635static const char * const cmd_lib_attr_desc[32] = {
636 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
637 * "Brief but meaningful description", */
Philipp Maier608b1a42020-10-05 20:54:51 +0200638 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
639 "This command applies on ASP restart",
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700640};
641
642/* Flag letters of attributes shared between the lib commands.
643 * NOTE: uppercase letters only, the rest is reserved for applications. */
644static const char cmd_lib_attr_letters[32] = {
645 /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
Philipp Maier608b1a42020-10-05 20:54:51 +0200646 [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700647};
648
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100649/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200650 * Write one cmd_element as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100651 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200652static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100653{
654 char *xml_string = xml_escape(cmd->string);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700655 unsigned int i;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100656
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200657 print_func(data, " <command id='%s'>%s", xml_string, newline);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700658
Vadim Yanitskiy7f6c87d2020-09-28 13:03:49 +0700659 /* Print global attributes and their description */
660 if (cmd->attr != 0x00) { /* ... if at least one flag is set */
661 print_func(data, " <attributes scope='global'>%s", newline);
662
663 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
664 char *xml_att_desc;
665
666 if (~cmd->attr & cmd_attr_desc[i].value)
667 continue;
668
669 xml_att_desc = xml_escape(cmd_attr_desc[i].str);
670 print_func(data, " <attribute doc='%s' />%s",
671 xml_att_desc, newline);
672 talloc_free(xml_att_desc);
673 }
674
675 print_func(data, " </attributes>%s", newline);
676 }
677
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700678 /* Print application specific attributes and their description */
679 if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700680 const char * const *desc;
681 const char *letters;
682
683 if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
684 print_func(data, " <attributes scope='library'>%s", newline);
685 letters = &cmd_lib_attr_letters[0];
686 desc = &cmd_lib_attr_desc[0];
687 } else {
688 print_func(data, " <attributes scope='application'>%s", newline);
689 letters = &host.app_info->usr_attr_letters[0];
690 desc = &host.app_info->usr_attr_desc[0];
691 }
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700692
693 for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
694 char *xml_att_desc;
695 char flag;
696
697 /* Skip attribute if *not* set */
698 if (~cmd->usrattr & (1 << i))
699 continue;
700
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700701 xml_att_desc = xml_escape(desc[i]);
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700702 print_func(data, " <attribute doc='%s'", xml_att_desc);
703 talloc_free(xml_att_desc);
704
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +0700705 if ((flag = letters[i]) != '\0')
Vadim Yanitskiy34e94f32020-08-15 23:27:55 +0700706 print_func(data, " flag='%c'", flag);
707 print_func(data, " />%s", newline);
708 }
709
710 print_func(data, " </attributes>%s", newline);
711 }
712
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200713 print_func(data, " <params>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100714
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700715 for (i = 0; i < vector_count(cmd->strvec); ++i) {
716 vector descvec = vector_slot(cmd->strvec, i);
717 int j;
718 for (j = 0; j < vector_active(descvec); ++j) {
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100719 char *xml_param, *xml_doc;
Vadim Yanitskiy478dd1b2020-08-18 18:40:16 +0700720 struct desc *desc = vector_slot(descvec, j);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100721 if (desc == NULL)
722 continue;
723
724 xml_param = xml_escape(desc->cmd);
725 xml_doc = xml_escape(desc->str);
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200726 print_func(data, " <param name='%s' doc='%s' />%s",
727 xml_param, xml_doc, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100728 talloc_free(xml_param);
729 talloc_free(xml_doc);
730 }
731 }
732
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200733 print_func(data, " </params>%s", newline);
734 print_func(data, " </command>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100735
736 talloc_free(xml_string);
737 return 0;
738}
739
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200740static bool vty_command_is_common(struct cmd_element *cmd);
741
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100742/*
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200743 * Dump all nodes and commands associated with a given node as XML via a print_func_t.
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100744 */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200745static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100746{
747 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200748 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100749
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200750 print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100751
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200752 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200753 print_func(data, " <node id='_common_cmds_'>%s", newline);
754 print_func(data, " <name>Common Commands</name>%s", newline);
755 print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
756 " here only once, to unclutter the VTY reference.</description>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200757 for (i = 0; i < vector_active(cmdvec); ++i) {
758 struct cmd_node *cnode;
759 cnode = vector_slot(cmdvec, i);
760 if (!cnode)
761 continue;
762 if (cnode->node != CONFIG_NODE)
763 continue;
764
765 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
766 struct cmd_element *elem;
767 elem = vector_slot(cnode->cmd_vector, j);
768 if (!vty_command_is_common(elem))
769 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200770 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200771 vty_dump_element(elem, print_func, data, newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200772 }
773 }
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200774 print_func(data, " </node>%s", newline);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200775
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100776 for (i = 0; i < vector_active(cmdvec); ++i) {
777 struct cmd_node *cnode;
778 cnode = vector_slot(cmdvec, i);
779 if (!cnode)
780 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200781 if (vector_active(cnode->cmd_vector) < 1)
782 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100783
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200784 /* De-dup node IDs: how many times has this same name been used before? Count the first
785 * occurence as _1 and omit that first suffix, so that the first occurence is called
786 * 'name', the second becomes 'name_2', then 'name_3', ... */
787 same_name_count = 1;
788 for (j = 0; j < i; ++j) {
789 struct cmd_node *cnode2;
790 cnode2 = vector_slot(cmdvec, j);
791 if (!cnode2)
792 continue;
793 if (strcmp(cnode->name, cnode2->name) == 0)
794 same_name_count ++;
795 }
796
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200797 print_func(data, " <node id='%s", cnode->name);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200798 if (same_name_count > 1 || !*cnode->name)
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200799 print_func(data, "_%d", same_name_count);
800 print_func(data, "'>%s", newline);
801 print_func(data, " <name>%s</name>%s", cnode->name, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100802
803 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
804 struct cmd_element *elem;
805 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200806 if (vty_command_is_common(elem))
807 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200808 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200809 vty_dump_element(elem, print_func, data, newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100810 }
811
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200812 print_func(data, " </node>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100813 }
814
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200815 print_func(data, "</vtydoc>%s", newline);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100816
817 return 0;
818}
819
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +0200820static int print_func_vty(void *data, const char *format, ...)
821{
822 struct vty *vty = data;
823 va_list args;
824 int rc;
825 va_start(args, format);
826 rc = vty_out_va(vty, format, args);
827 va_end(args);
828 return rc;
829}
830
831static int vty_dump_xml_ref_to_vty(struct vty *vty)
832{
833 return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
834}
835
836static int print_func_stream(void *data, const char *format, ...)
837{
838 va_list args;
839 int rc;
840 va_start(args, format);
841 rc = vfprintf((FILE*)data, format, args);
842 va_end(args);
843 return rc;
844}
845
846/*! Print the XML reference of all VTY nodes to the given stream.
847 */
848int vty_dump_xml_ref(FILE *stream)
849{
850 return vty_dump_nodes(print_func_stream, stream, "\n");
851}
852
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200853/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100854static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
855{
856 int i;
857
858 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
859 struct cmd_element *elem;
860 elem = vector_slot(cnode->cmd_vector, i);
861 if (!elem->string)
862 continue;
863 if (!strcmp(elem->string, cmdstring))
864 return 1;
865 }
866 return 0;
867}
868
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200869/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200870 * \param[in] ntype Node Type
871 * \param[cmd] element to be installed
872 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000873void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200874{
875 struct cmd_node *cnode;
876
877 cnode = vector_slot(cmdvec, ntype);
878
Harald Weltea99d45a2015-11-12 13:48:23 +0100879 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100880 /* ensure no _identical_ command has been registered at this
881 * node so far */
882 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200883
884 vector_set(cnode->cmd_vector, cmd);
885
886 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
887 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
888}
889
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700890/*! Install a library command into a node
891 * \param[in] ntype Node Type
892 * \param[in] cmd element to be installed
893 */
894void install_lib_element(int ntype, struct cmd_element *cmd)
895{
896 cmd->attr |= CMD_ATTR_LIB_COMMAND;
897 install_element(ntype, cmd);
898}
899
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200900/* Install a command into VIEW and ENABLE node */
901void install_element_ve(struct cmd_element *cmd)
902{
903 install_element(VIEW_NODE, cmd);
904 install_element(ENABLE_NODE, cmd);
905}
906
Vadim Yanitskiy99d5c2d2020-10-06 00:22:06 +0700907/* Install a library command into VIEW and ENABLE node */
908void install_lib_element_ve(struct cmd_element *cmd)
909{
910 cmd->attr |= CMD_ATTR_LIB_COMMAND;
911 install_element_ve(cmd);
912}
913
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200914#ifdef VTY_CRYPT_PW
915static unsigned char itoa64[] =
916 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
917
918static void to64(char *s, long v, int n)
919{
920 while (--n >= 0) {
921 *s++ = itoa64[v & 0x3f];
922 v >>= 6;
923 }
924}
925
926static char *zencrypt(const char *passwd)
927{
928 char salt[6];
929 struct timeval tv;
930 char *crypt(const char *, const char *);
931
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200932 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200933
934 to64(&salt[0], random(), 3);
935 to64(&salt[3], tv.tv_usec, 3);
936 salt[5] = '\0';
937
938 return crypt(passwd, salt);
939}
940#endif
941
942/* This function write configuration of this host. */
943static int config_write_host(struct vty *vty)
944{
945 if (host.name)
946 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
947
948 if (host.encrypt) {
949 if (host.password_encrypt)
950 vty_out(vty, "password 8 %s%s", host.password_encrypt,
951 VTY_NEWLINE);
952 if (host.enable_encrypt)
953 vty_out(vty, "enable password 8 %s%s",
954 host.enable_encrypt, VTY_NEWLINE);
955 } else {
956 if (host.password)
957 vty_out(vty, "password %s%s", host.password,
958 VTY_NEWLINE);
959 if (host.enable)
960 vty_out(vty, "enable password %s%s", host.enable,
961 VTY_NEWLINE);
962 }
963
964 if (host.advanced)
965 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
966
967 if (host.encrypt)
968 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
969
970 if (host.lines >= 0)
971 vty_out(vty, "service terminal-length %d%s", host.lines,
972 VTY_NEWLINE);
973
974 if (host.motdfile)
975 vty_out(vty, "banner motd file %s%s", host.motdfile,
976 VTY_NEWLINE);
977 else if (!host.motd)
978 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
979
980 return 1;
981}
982
983/* Utility function for getting command vector. */
984static vector cmd_node_vector(vector v, enum node_type ntype)
985{
986 struct cmd_node *cnode = vector_slot(v, ntype);
987 return cnode->cmd_vector;
988}
989
990/* Completion match types. */
991enum match_type {
Pau Espin Pedrolde890992019-06-12 12:27:32 +0200992 NO_MATCH = 0,
993 ANY_MATCH,
994 EXTEND_MATCH,
995 IPV4_PREFIX_MATCH,
996 IPV4_MATCH,
997 IPV6_PREFIX_MATCH,
998 IPV6_MATCH,
999 RANGE_MATCH,
1000 VARARG_MATCH,
1001 PARTLY_MATCH,
1002 EXACT_MATCH,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001003};
1004
1005static enum match_type cmd_ipv4_match(const char *str)
1006{
1007 const char *sp;
1008 int dots = 0, nums = 0;
1009 char buf[4];
1010
1011 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001012 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001013
1014 for (;;) {
1015 memset(buf, 0, sizeof(buf));
1016 sp = str;
1017 while (*str != '\0') {
1018 if (*str == '.') {
1019 if (dots >= 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001020 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001021
1022 if (*(str + 1) == '.')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001023 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001024
1025 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001026 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001027
1028 dots++;
1029 break;
1030 }
1031 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001032 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001033
1034 str++;
1035 }
1036
1037 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001038 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001039
1040 strncpy(buf, sp, str - sp);
1041 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001042 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001043
1044 nums++;
1045
1046 if (*str == '\0')
1047 break;
1048
1049 str++;
1050 }
1051
1052 if (nums < 4)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001053 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001054
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001055 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001056}
1057
1058static enum match_type cmd_ipv4_prefix_match(const char *str)
1059{
1060 const char *sp;
1061 int dots = 0;
1062 char buf[4];
1063
1064 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001065 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001066
1067 for (;;) {
1068 memset(buf, 0, sizeof(buf));
1069 sp = str;
1070 while (*str != '\0' && *str != '/') {
1071 if (*str == '.') {
1072 if (dots == 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001073 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001074
1075 if (*(str + 1) == '.' || *(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001076 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001077
1078 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001079 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001080
1081 dots++;
1082 break;
1083 }
1084
1085 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001086 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001087
1088 str++;
1089 }
1090
1091 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001092 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001093
1094 strncpy(buf, sp, str - sp);
1095 if (atoi(buf) > 255)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001096 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001097
1098 if (dots == 3) {
1099 if (*str == '/') {
1100 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001101 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001102
1103 str++;
1104 break;
1105 } else if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001106 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001107 }
1108
1109 if (*str == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001110 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001111
1112 str++;
1113 }
1114
1115 sp = str;
1116 while (*str != '\0') {
1117 if (!isdigit((int)*str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001118 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001119
1120 str++;
1121 }
1122
1123 if (atoi(sp) > 32)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001124 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001126 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001127}
1128
1129#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
1130#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
1131#define STATE_START 1
1132#define STATE_COLON 2
1133#define STATE_DOUBLE 3
1134#define STATE_ADDR 4
1135#define STATE_DOT 5
1136#define STATE_SLASH 6
1137#define STATE_MASK 7
1138
1139#ifdef HAVE_IPV6
1140
1141static enum match_type cmd_ipv6_match(const char *str)
1142{
1143 int state = STATE_START;
1144 int colons = 0, nums = 0, double_colon = 0;
1145 const char *sp = NULL;
1146 struct sockaddr_in6 sin6_dummy;
1147 int ret;
1148
1149 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001150 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001151
1152 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001153 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001154
1155 /* use inet_pton that has a better support,
1156 * for example inet_pton can support the automatic addresses:
1157 * ::1.2.3.4
1158 */
1159 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1160
1161 if (ret == 1)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001162 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001163
1164 while (*str != '\0') {
1165 switch (state) {
1166 case STATE_START:
1167 if (*str == ':') {
1168 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001169 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001170 colons--;
1171 state = STATE_COLON;
1172 } else {
1173 sp = str;
1174 state = STATE_ADDR;
1175 }
1176
1177 continue;
1178 case STATE_COLON:
1179 colons++;
1180 if (*(str + 1) == ':')
1181 state = STATE_DOUBLE;
1182 else {
1183 sp = str + 1;
1184 state = STATE_ADDR;
1185 }
1186 break;
1187 case STATE_DOUBLE:
1188 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001189 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190
1191 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001192 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001193 else {
1194 if (*(str + 1) != '\0')
1195 colons++;
1196 sp = str + 1;
1197 state = STATE_ADDR;
1198 }
1199
1200 double_colon++;
1201 nums++;
1202 break;
1203 case STATE_ADDR:
1204 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1205 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001206 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207
1208 nums++;
1209 state = STATE_COLON;
1210 }
1211 if (*(str + 1) == '.')
1212 state = STATE_DOT;
1213 break;
1214 case STATE_DOT:
1215 state = STATE_ADDR;
1216 break;
1217 default:
1218 break;
1219 }
1220
1221 if (nums > 8)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001222 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001223
1224 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001225 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001226
1227 str++;
1228 }
1229
1230#if 0
1231 if (nums < 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001232 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233#endif /* 0 */
1234
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001235 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001236}
1237
1238static enum match_type cmd_ipv6_prefix_match(const char *str)
1239{
1240 int state = STATE_START;
1241 int colons = 0, nums = 0, double_colon = 0;
1242 int mask;
1243 const char *sp = NULL;
1244 char *endptr = NULL;
1245
1246 if (str == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001247 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001248
1249 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001250 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001251
1252 while (*str != '\0' && state != STATE_MASK) {
1253 switch (state) {
1254 case STATE_START:
1255 if (*str == ':') {
1256 if (*(str + 1) != ':' && *(str + 1) != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001257 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001258 colons--;
1259 state = STATE_COLON;
1260 } else {
1261 sp = str;
1262 state = STATE_ADDR;
1263 }
1264
1265 continue;
1266 case STATE_COLON:
1267 colons++;
1268 if (*(str + 1) == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001269 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001270 else if (*(str + 1) == ':')
1271 state = STATE_DOUBLE;
1272 else {
1273 sp = str + 1;
1274 state = STATE_ADDR;
1275 }
1276 break;
1277 case STATE_DOUBLE:
1278 if (double_colon)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001279 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001280
1281 if (*(str + 1) == ':')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001282 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001283 else {
1284 if (*(str + 1) != '\0' && *(str + 1) != '/')
1285 colons++;
1286 sp = str + 1;
1287
1288 if (*(str + 1) == '/')
1289 state = STATE_SLASH;
1290 else
1291 state = STATE_ADDR;
1292 }
1293
1294 double_colon++;
1295 nums += 1;
1296 break;
1297 case STATE_ADDR:
1298 if (*(str + 1) == ':' || *(str + 1) == '.'
1299 || *(str + 1) == '\0' || *(str + 1) == '/') {
1300 if (str - sp > 3)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001301 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302
1303 for (; sp <= str; sp++)
1304 if (*sp == '/')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001305 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001306
1307 nums++;
1308
1309 if (*(str + 1) == ':')
1310 state = STATE_COLON;
1311 else if (*(str + 1) == '.')
1312 state = STATE_DOT;
1313 else if (*(str + 1) == '/')
1314 state = STATE_SLASH;
1315 }
1316 break;
1317 case STATE_DOT:
1318 state = STATE_ADDR;
1319 break;
1320 case STATE_SLASH:
1321 if (*(str + 1) == '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001322 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001323
1324 state = STATE_MASK;
1325 break;
1326 default:
1327 break;
1328 }
1329
1330 if (nums > 11)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001331 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001332
1333 if (colons > 7)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001334 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001335
1336 str++;
1337 }
1338
1339 if (state < STATE_MASK)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001340 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001341
1342 mask = strtol(str, &endptr, 10);
1343 if (*endptr != '\0')
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001344 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001345
1346 if (mask < 0 || mask > 128)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001347 return NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001348
1349/* I don't know why mask < 13 makes command match partly.
1350 Forgive me to make this comments. I Want to set static default route
1351 because of lack of function to originate default in ospf6d; sorry
1352 yasu
1353 if (mask < 13)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001354 return PARTLY_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001355*/
1356
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001357 return EXACT_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001358}
1359
1360#endif /* HAVE_IPV6 */
1361
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001362
1363#if ULONG_MAX == 18446744073709551615UL
1364#define DECIMAL_STRLEN_MAX_UNSIGNED 20
1365#elif ULONG_MAX == 4294967295UL
1366#define DECIMAL_STRLEN_MAX_UNSIGNED 10
1367#else
1368#error "ULONG_MAX not defined!"
1369#endif
1370
1371#if LONG_MAX == 9223372036854775807L
1372#define DECIMAL_STRLEN_MAX_SIGNED 19
1373#elif LONG_MAX == 2147483647L
1374#define DECIMAL_STRLEN_MAX_SIGNED 10
1375#else
1376#error "LONG_MAX not defined!"
1377#endif
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001378
1379static int cmd_range_match(const char *range, const char *str)
1380{
1381 char *p;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001382 char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001383 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001384
1385 if (str == NULL)
1386 return 1;
1387
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001388 if (range[1] == '-') {
1389 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001390
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001391 val = strtol(str, &endptr, 10);
1392 if (*endptr != '\0')
1393 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001394
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001395 range += 2;
1396 p = strchr(range, '-');
1397 if (p == NULL)
1398 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001399 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001400 return 0;
1401 strncpy(buf, range, p - range);
1402 buf[p - range] = '\0';
1403 min = -strtol(buf, &endptr, 10);
1404 if (*endptr != '\0')
1405 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001406
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001407 range = p + 1;
1408 p = strchr(range, '>');
1409 if (p == NULL)
1410 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001411 if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001412 return 0;
1413 strncpy(buf, range, p - range);
1414 buf[p - range] = '\0';
1415 max = strtol(buf, &endptr, 10);
1416 if (*endptr != '\0')
1417 return 0;
1418
1419 if (val < min || val > max)
1420 return 0;
1421 } else {
1422 unsigned long min, max, val;
1423
Pau Espin Pedrola1847012020-07-28 17:44:48 +02001424 if (str[0] == '-')
1425 return 0;
1426
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001427 val = strtoul(str, &endptr, 10);
1428 if (*endptr != '\0')
1429 return 0;
1430
1431 range++;
1432 p = strchr(range, '-');
1433 if (p == NULL)
1434 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001435 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001436 return 0;
1437 strncpy(buf, range, p - range);
1438 buf[p - range] = '\0';
1439 min = strtoul(buf, &endptr, 10);
1440 if (*endptr != '\0')
1441 return 0;
1442
1443 range = p + 1;
1444 p = strchr(range, '>');
1445 if (p == NULL)
1446 return 0;
Pau Espin Pedrol9fdc8712020-07-30 14:56:38 +02001447 if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001448 return 0;
1449 strncpy(buf, range, p - range);
1450 buf[p - range] = '\0';
1451 max = strtoul(buf, &endptr, 10);
1452 if (*endptr != '\0')
1453 return 0;
1454
1455 if (val < min || val > max)
1456 return 0;
1457 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001458
1459 return 1;
1460}
1461
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001462/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001463static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001464{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001465 /* we've got "[blah]". We want to strip off the []s and redo the
1466 * match check for "blah"
1467 */
1468 size_t len = strlen(str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001469
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001470 if (len < 3)
1471 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001472
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001473 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001474}
1475
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001476static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001477cmd_match(const char *str, const char *command,
1478 enum match_type min, bool recur)
1479{
1480
1481 if (recur && CMD_OPTION(str))
1482 {
1483 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001484 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001485
1486 /* this would be a bug in a command, however handle it gracefully
1487 * as it we only discover it if a user tries to run it
1488 */
1489 if (tmp == NULL)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001490 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001491
1492 ret = cmd_match(tmp, command, min, false);
1493
1494 talloc_free(tmp);
1495
1496 return ret;
1497 }
1498 else if (CMD_VARARG(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001499 return VARARG_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001500 else if (CMD_RANGE(str))
1501 {
1502 if (cmd_range_match(str, command))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001503 return RANGE_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001504 }
1505#ifdef HAVE_IPV6
1506 else if (CMD_IPV6(str))
1507 {
1508 if (cmd_ipv6_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001509 return IPV6_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001510 }
1511 else if (CMD_IPV6_PREFIX(str))
1512 {
1513 if (cmd_ipv6_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001514 return IPV6_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001515 }
1516#endif /* HAVE_IPV6 */
1517 else if (CMD_IPV4(str))
1518 {
1519 if (cmd_ipv4_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001520 return IPV4_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001521 }
1522 else if (CMD_IPV4_PREFIX(str))
1523 {
1524 if (cmd_ipv4_prefix_match(command) >= min)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001525 return IPV4_PREFIX_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001526 }
1527 else if (CMD_VARIABLE(str))
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001528 return EXTEND_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001529 else if (strncmp(command, str, strlen(command)) == 0)
1530 {
1531 if (strcmp(command, str) == 0)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001532 return EXACT_MATCH;
1533 else if (PARTLY_MATCH >= min)
1534 return PARTLY_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001535 }
1536
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001537 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001538}
1539
1540/* Filter vector at the specified index and by the given command string, to
1541 * the desired matching level (thus allowing part matches), and return match
1542 * type flag.
1543 */
1544static enum match_type
1545cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001546{
1547 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001548 struct cmd_element *cmd_element;
1549 enum match_type match_type;
1550 vector descvec;
1551 struct desc *desc;
1552
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001553 match_type = NO_MATCH;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001554
1555 /* If command and cmd_element string does not match set NULL to vector */
1556 for (i = 0; i < vector_active(v); i++)
1557 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001558 if (index >= vector_active(cmd_element->strvec))
1559 vector_slot(v, i) = NULL;
1560 else {
1561 unsigned int j;
1562 int matched = 0;
1563
1564 descvec =
1565 vector_slot(cmd_element->strvec, index);
1566
1567 for (j = 0; j < vector_active(descvec); j++)
1568 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001569 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001570
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001571 ret = cmd_match (desc->cmd, command, level, true);
1572
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001573 if (ret != NO_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001574 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001575
1576 if (match_type < ret)
1577 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001578 }
1579 if (!matched)
1580 vector_slot(v, i) = NULL;
1581 }
1582 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001583
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001584 if (match_type == NO_MATCH)
1585 return NO_MATCH;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001586
1587 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1588 * go again and filter out commands whose argument (at this index) is
1589 * 'weaker'. E.g., if we have 2 commands:
1590 *
1591 * foo bar <1-255>
1592 * foo bar BLAH
1593 *
1594 * and the command string is 'foo bar 10', then we will get here with with
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001595 * 'RANGE_MATCH' being the strongest match. However, if 'BLAH' came
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001596 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1597 *
1598 * If we don't do a 2nd pass and filter it out, the higher-layers will
1599 * consider this to be ambiguous.
1600 */
1601 for (i = 0; i < vector_active(v); i++)
1602 if ((cmd_element = vector_slot(v, i)) != NULL) {
1603 if (index >= vector_active(cmd_element->strvec))
1604 vector_slot(v, i) = NULL;
1605 else {
1606 unsigned int j;
1607 int matched = 0;
1608
1609 descvec =
1610 vector_slot(cmd_element->strvec, index);
1611
1612 for (j = 0; j < vector_active(descvec); j++)
1613 if ((desc = vector_slot(descvec, j))) {
1614 enum match_type ret;
1615
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001616 ret = cmd_match(desc->cmd, command, ANY_MATCH, true);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001617
1618 if (ret >= match_type)
1619 matched++;
1620 }
1621 if (!matched)
1622 vector_slot(v, i) = NULL;
1623 }
1624 }
1625
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001626 return match_type;
1627}
1628
1629/* Check ambiguous match */
1630static int
1631is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1632{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001633 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001634 unsigned int i;
1635 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001636 struct cmd_element *cmd_element;
1637 const char *matched = NULL;
1638 vector descvec;
1639 struct desc *desc;
1640
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001641 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1642 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1643 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1644 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1645 * that case, the string must remain allocated until this function exits or another match comes
1646 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1647 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1648 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1649 void *cmd_deopt_ctx = NULL;
1650
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001651 for (i = 0; i < vector_active(v); i++) {
1652 cmd_element = vector_slot(v, i);
1653 if (!cmd_element)
1654 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001655
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001656 int match = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001657
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001658 descvec = vector_slot(cmd_element->strvec, index);
1659
1660 for (j = 0; j < vector_active(descvec); j++) {
1661 desc = vector_slot(descvec, j);
1662 if (!desc)
1663 continue;
1664
1665 enum match_type mtype;
1666 const char *str = desc->cmd;
1667
1668 if (CMD_OPTION(str)) {
1669 if (!cmd_deopt_ctx)
1670 cmd_deopt_ctx =
1671 talloc_named_const(tall_vty_cmd_ctx, 0,
1672 __func__);
1673 str = cmd_deopt(cmd_deopt_ctx, str);
1674 if (str == NULL)
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001675 continue;
Pau Espin Pedrol47425262019-06-11 21:04:09 +02001676 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001677
1678 switch (type) {
1679 case EXACT_MATCH:
1680 if (!(CMD_VARIABLE (str))
1681 && strcmp(command, str) == 0)
1682 match++;
1683 break;
1684 case PARTLY_MATCH:
1685 if (!(CMD_VARIABLE (str))
1686 && strncmp(command, str, strlen (command)) == 0)
1687 {
1688 if (matched
1689 && strcmp(matched,
1690 str) != 0) {
1691 ret = 1; /* There is ambiguous match. */
1692 goto free_and_return;
1693 } else
1694 matched = str;
1695 match++;
1696 }
1697 break;
1698 case RANGE_MATCH:
1699 if (cmd_range_match
1700 (str, command)) {
1701 if (matched
1702 && strcmp(matched,
1703 str) != 0) {
1704 ret = 1;
1705 goto free_and_return;
1706 } else
1707 matched = str;
1708 match++;
1709 }
1710 break;
1711#ifdef HAVE_IPV6
1712 case IPV6_MATCH:
1713 if (CMD_IPV6(str))
1714 match++;
1715 break;
1716 case IPV6_PREFIX_MATCH:
1717 if ((mtype =
1718 cmd_ipv6_prefix_match
1719 (command)) != NO_MATCH) {
1720 if (mtype == PARTLY_MATCH) {
1721 ret = 2; /* There is incomplete match. */
1722 goto free_and_return;
1723 }
1724
1725 match++;
1726 }
1727 break;
1728#endif /* HAVE_IPV6 */
1729 case IPV4_MATCH:
1730 if (CMD_IPV4(str))
1731 match++;
1732 break;
1733 case IPV4_PREFIX_MATCH:
1734 if ((mtype =
1735 cmd_ipv4_prefix_match
1736 (command)) != NO_MATCH) {
1737 if (mtype == PARTLY_MATCH) {
1738 ret = 2; /* There is incomplete match. */
1739 goto free_and_return;
1740 }
1741
1742 match++;
1743 }
1744 break;
1745 case EXTEND_MATCH:
1746 if (CMD_VARIABLE (str))
1747 match++;
1748 break;
1749 case NO_MATCH:
1750 default:
1751 break;
1752 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001753 }
Pau Espin Pedrol274ac4d2019-06-12 13:06:41 +02001754 if (!match)
1755 vector_slot(v, i) = NULL;
1756 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001757
1758free_and_return:
1759 if (cmd_deopt_ctx)
1760 talloc_free(cmd_deopt_ctx);
1761 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001762}
1763
1764/* If src matches dst return dst string, otherwise return NULL */
1765static const char *cmd_entry_function(const char *src, const char *dst)
1766{
1767 /* Skip variable arguments. */
1768 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1769 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1770 return NULL;
1771
1772 /* In case of 'command \t', given src is NULL string. */
1773 if (src == NULL)
1774 return dst;
1775
1776 /* Matched with input string. */
1777 if (strncmp(src, dst, strlen(src)) == 0)
1778 return dst;
1779
1780 return NULL;
1781}
1782
1783/* If src matches dst return dst string, otherwise return NULL */
1784/* This version will return the dst string always if it is
1785 CMD_VARIABLE for '?' key processing */
1786static const char *cmd_entry_function_desc(const char *src, const char *dst)
1787{
1788 if (CMD_VARARG(dst))
1789 return dst;
1790
1791 if (CMD_RANGE(dst)) {
1792 if (cmd_range_match(dst, src))
1793 return dst;
1794 else
1795 return NULL;
1796 }
1797#ifdef HAVE_IPV6
1798 if (CMD_IPV6(dst)) {
1799 if (cmd_ipv6_match(src))
1800 return dst;
1801 else
1802 return NULL;
1803 }
1804
1805 if (CMD_IPV6_PREFIX(dst)) {
1806 if (cmd_ipv6_prefix_match(src))
1807 return dst;
1808 else
1809 return NULL;
1810 }
1811#endif /* HAVE_IPV6 */
1812
1813 if (CMD_IPV4(dst)) {
1814 if (cmd_ipv4_match(src))
1815 return dst;
1816 else
1817 return NULL;
1818 }
1819
1820 if (CMD_IPV4_PREFIX(dst)) {
1821 if (cmd_ipv4_prefix_match(src))
1822 return dst;
1823 else
1824 return NULL;
1825 }
1826
1827 /* Optional or variable commands always match on '?' */
1828 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1829 return dst;
1830
1831 /* In case of 'command \t', given src is NULL string. */
1832 if (src == NULL)
1833 return dst;
1834
1835 if (strncmp(src, dst, strlen(src)) == 0)
1836 return dst;
1837 else
1838 return NULL;
1839}
1840
1841/* Check same string element existence. If it isn't there return
1842 1. */
1843static int cmd_unique_string(vector v, const char *str)
1844{
1845 unsigned int i;
1846 char *match;
1847
1848 for (i = 0; i < vector_active(v); i++)
1849 if ((match = vector_slot(v, i)) != NULL)
1850 if (strcmp(match, str) == 0)
1851 return 0;
1852 return 1;
1853}
1854
1855/* Compare string to description vector. If there is same string
1856 return 1 else return 0. */
1857static int desc_unique_string(vector v, const char *str)
1858{
1859 unsigned int i;
1860 struct desc *desc;
1861
1862 for (i = 0; i < vector_active(v); i++)
1863 if ((desc = vector_slot(v, i)) != NULL)
1864 if (strcmp(desc->cmd, str) == 0)
1865 return 1;
1866 return 0;
1867}
1868
1869static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1870{
1871 if (first_word != NULL &&
1872 node != AUTH_NODE &&
1873 node != VIEW_NODE &&
1874 node != AUTH_ENABLE_NODE &&
1875 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1876 return 1;
1877 return 0;
1878}
1879
1880/* '?' describe command support. */
1881static vector
1882cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1883{
1884 unsigned int i;
1885 vector cmd_vector;
1886#define INIT_MATCHVEC_SIZE 10
1887 vector matchvec;
1888 struct cmd_element *cmd_element;
1889 unsigned int index;
1890 int ret;
1891 enum match_type match;
1892 char *command;
1893 static struct desc desc_cr = { "<cr>", "" };
1894
1895 /* Set index. */
1896 if (vector_active(vline) == 0) {
1897 *status = CMD_ERR_NO_MATCH;
1898 return NULL;
1899 } else
1900 index = vector_active(vline) - 1;
1901
1902 /* Make copy vector of current node's command vector. */
1903 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1904
1905 /* Prepare match vector */
1906 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1907
1908 /* Filter commands. */
1909 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001910 for (i = 0; i < index; i++) {
1911 command = vector_slot(vline, i);
1912 if (!command)
1913 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001914
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001915 match = cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001916
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001917 if (match == VARARG_MATCH) {
Harald Welte80d30fe2013-02-12 11:08:57 +01001918 struct cmd_element *cmd_element;
1919 vector descvec;
1920 unsigned int j, k;
1921
1922 for (j = 0; j < vector_active(cmd_vector); j++)
1923 if ((cmd_element =
1924 vector_slot(cmd_vector, j)) != NULL
1925 &&
1926 (vector_active(cmd_element->strvec))) {
1927 descvec =
1928 vector_slot(cmd_element->
1929 strvec,
1930 vector_active
1931 (cmd_element->
1932 strvec) - 1);
1933 for (k = 0;
1934 k < vector_active(descvec);
1935 k++) {
1936 struct desc *desc =
1937 vector_slot(descvec,
1938 k);
1939 vector_set(matchvec,
1940 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001941 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001942 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001943
Harald Welte80d30fe2013-02-12 11:08:57 +01001944 vector_set(matchvec, &desc_cr);
1945 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001946
Harald Welte80d30fe2013-02-12 11:08:57 +01001947 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001948 }
1949
Harald Welte80d30fe2013-02-12 11:08:57 +01001950 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1951 match)) == 1) {
1952 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001953 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001954 *status = CMD_ERR_AMBIGUOUS;
1955 return NULL;
1956 } else if (ret == 2) {
1957 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001958 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001959 *status = CMD_ERR_NO_MATCH;
1960 return NULL;
1961 }
1962 }
1963
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001964 /* Prepare match vector */
1965 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1966
1967 /* Make sure that cmd_vector is filtered based on current word */
1968 command = vector_slot(vline, index);
1969 if (command)
Pau Espin Pedrolde890992019-06-12 12:27:32 +02001970 cmd_filter(command, cmd_vector, index, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001971
1972 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001973 for (i = 0; i < vector_active(cmd_vector); i++) {
1974 const char *string = NULL;
1975 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001976
Harald Welte80d30fe2013-02-12 11:08:57 +01001977 cmd_element = vector_slot(cmd_vector, i);
1978 if (!cmd_element)
1979 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001980
Harald Welted17aa592013-02-12 11:11:34 +01001981 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1982 continue;
1983
Harald Welte80d30fe2013-02-12 11:08:57 +01001984 strvec = cmd_element->strvec;
1985
1986 /* if command is NULL, index may be equal to vector_active */
1987 if (command && index >= vector_active(strvec))
1988 vector_slot(cmd_vector, i) = NULL;
1989 else {
1990 /* Check if command is completed. */
1991 if (command == NULL
1992 && index == vector_active(strvec)) {
1993 string = "<cr>";
1994 if (!desc_unique_string(matchvec, string))
1995 vector_set(matchvec, &desc_cr);
1996 } else {
1997 unsigned int j;
1998 vector descvec = vector_slot(strvec, index);
1999 struct desc *desc;
2000
2001 for (j = 0; j < vector_active(descvec); j++) {
2002 desc = vector_slot(descvec, j);
2003 if (!desc)
2004 continue;
2005 string = cmd_entry_function_desc
2006 (command, desc->cmd);
2007 if (!string)
2008 continue;
2009 /* Uniqueness check */
2010 if (!desc_unique_string(matchvec, string))
2011 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002012 }
2013 }
2014 }
Harald Welte80d30fe2013-02-12 11:08:57 +01002015 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002016 vector_free(cmd_vector);
2017
2018 if (vector_slot(matchvec, 0) == NULL) {
2019 vector_free(matchvec);
2020 *status = CMD_ERR_NO_MATCH;
2021 } else
2022 *status = CMD_SUCCESS;
2023
2024 return matchvec;
2025}
2026
2027vector cmd_describe_command(vector vline, struct vty * vty, int *status)
2028{
2029 vector ret;
2030
2031 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2032 enum node_type onode;
2033 vector shifted_vline;
2034 unsigned int index;
2035
2036 onode = vty->node;
2037 vty->node = ENABLE_NODE;
2038 /* We can try it on enable node, cos' the vty is authenticated */
2039
2040 shifted_vline = vector_init(vector_count(vline));
2041 /* use memcpy? */
2042 for (index = 1; index < vector_active(vline); index++) {
2043 vector_set_index(shifted_vline, index - 1,
2044 vector_lookup(vline, index));
2045 }
2046
2047 ret = cmd_describe_command_real(shifted_vline, vty, status);
2048
2049 vector_free(shifted_vline);
2050 vty->node = onode;
2051 return ret;
2052 }
2053
2054 return cmd_describe_command_real(vline, vty, status);
2055}
2056
2057/* Check LCD of matched command. */
2058static int cmd_lcd(char **matched)
2059{
2060 int i;
2061 int j;
2062 int lcd = -1;
2063 char *s1, *s2;
2064 char c1, c2;
2065
2066 if (matched[0] == NULL || matched[1] == NULL)
2067 return 0;
2068
2069 for (i = 1; matched[i] != NULL; i++) {
2070 s1 = matched[i - 1];
2071 s2 = matched[i];
2072
2073 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2074 if (c1 != c2)
2075 break;
2076
2077 if (lcd < 0)
2078 lcd = j;
2079 else {
2080 if (lcd > j)
2081 lcd = j;
2082 }
2083 }
2084 return lcd;
2085}
2086
2087/* Command line completion support. */
2088static char **cmd_complete_command_real(vector vline, struct vty *vty,
2089 int *status)
2090{
2091 unsigned int i;
2092 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2093#define INIT_MATCHVEC_SIZE 10
2094 vector matchvec;
2095 struct cmd_element *cmd_element;
2096 unsigned int index;
2097 char **match_str;
2098 struct desc *desc;
2099 vector descvec;
2100 char *command;
2101 int lcd;
2102
2103 if (vector_active(vline) == 0) {
2104 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02002105 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002106 return NULL;
2107 } else
2108 index = vector_active(vline) - 1;
2109
2110 /* First, filter by preceeding command string */
2111 for (i = 0; i < index; i++)
2112 if ((command = vector_slot(vline, i))) {
2113 enum match_type match;
2114 int ret;
2115
2116 /* First try completion match, if there is exactly match return 1 */
2117 match =
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002118 cmd_filter(command, cmd_vector, i, ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002119
2120 /* If there is exact match then filter ambiguous match else check
2121 ambiguousness. */
2122 if ((ret =
2123 is_cmd_ambiguous(command, cmd_vector, i,
2124 match)) == 1) {
2125 vector_free(cmd_vector);
2126 *status = CMD_ERR_AMBIGUOUS;
2127 return NULL;
2128 }
2129 /*
2130 else if (ret == 2)
2131 {
2132 vector_free (cmd_vector);
2133 *status = CMD_ERR_NO_MATCH;
2134 return NULL;
2135 }
2136 */
2137 }
2138
2139 /* Prepare match vector. */
2140 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2141
2142 /* Now we got into completion */
2143 for (i = 0; i < vector_active(cmd_vector); i++)
2144 if ((cmd_element = vector_slot(cmd_vector, i))) {
2145 const char *string;
2146 vector strvec = cmd_element->strvec;
2147
2148 /* Check field length */
2149 if (index >= vector_active(strvec))
2150 vector_slot(cmd_vector, i) = NULL;
2151 else {
2152 unsigned int j;
2153
2154 descvec = vector_slot(strvec, index);
2155 for (j = 0; j < vector_active(descvec); j++)
2156 if ((desc = vector_slot(descvec, j))) {
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002157 const char *cmd = desc->cmd;
2158 char *tmp = NULL;
2159
2160 if (CMD_OPTION(desc->cmd)) {
2161 tmp = cmd_deopt(tall_vty_cmd_ctx, desc->cmd);
2162 cmd = tmp;
2163 }
2164 if ((string = cmd_entry_function(vector_slot(vline, index), cmd)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002165 if (cmd_unique_string (matchvec, string))
2166 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
Neels Hofmeyr9ea9dd02019-01-31 08:15:23 +01002167 if (tmp)
2168 talloc_free(tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002169 }
2170 }
2171 }
2172
2173 /* We don't need cmd_vector any more. */
2174 vector_free(cmd_vector);
2175
2176 /* No matched command */
2177 if (vector_slot(matchvec, 0) == NULL) {
2178 vector_free(matchvec);
2179
2180 /* In case of 'command \t' pattern. Do you need '?' command at
2181 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002182 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002183 *status = CMD_ERR_NOTHING_TODO;
2184 else
2185 *status = CMD_ERR_NO_MATCH;
2186 return NULL;
2187 }
2188
2189 /* Only one matched */
2190 if (vector_slot(matchvec, 1) == NULL) {
2191 match_str = (char **)matchvec->index;
2192 vector_only_wrapper_free(matchvec);
2193 *status = CMD_COMPLETE_FULL_MATCH;
2194 return match_str;
2195 }
2196 /* Make it sure last element is NULL. */
2197 vector_set(matchvec, NULL);
2198
2199 /* Check LCD of matched strings. */
2200 if (vector_slot(vline, index) != NULL) {
2201 lcd = cmd_lcd((char **)matchvec->index);
2202
2203 if (lcd) {
2204 int len = strlen(vector_slot(vline, index));
2205
2206 if (len < lcd) {
2207 char *lcdstr;
2208
2209 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2210 "complete-lcdstr");
2211 memcpy(lcdstr, matchvec->index[0], lcd);
2212 lcdstr[lcd] = '\0';
2213
2214 /* match_str = (char **) &lcdstr; */
2215
2216 /* Free matchvec. */
2217 for (i = 0; i < vector_active(matchvec); i++) {
2218 if (vector_slot(matchvec, i))
2219 talloc_free(vector_slot(matchvec, i));
2220 }
2221 vector_free(matchvec);
2222
2223 /* Make new matchvec. */
2224 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2225 vector_set(matchvec, lcdstr);
2226 match_str = (char **)matchvec->index;
2227 vector_only_wrapper_free(matchvec);
2228
2229 *status = CMD_COMPLETE_MATCH;
2230 return match_str;
2231 }
2232 }
2233 }
2234
2235 match_str = (char **)matchvec->index;
2236 vector_only_wrapper_free(matchvec);
2237 *status = CMD_COMPLETE_LIST_MATCH;
2238 return match_str;
2239}
2240
2241char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2242{
2243 char **ret;
2244
2245 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2246 enum node_type onode;
2247 vector shifted_vline;
2248 unsigned int index;
2249
2250 onode = vty->node;
2251 vty->node = ENABLE_NODE;
2252 /* We can try it on enable node, cos' the vty is authenticated */
2253
2254 shifted_vline = vector_init(vector_count(vline));
2255 /* use memcpy? */
2256 for (index = 1; index < vector_active(vline); index++) {
2257 vector_set_index(shifted_vline, index - 1,
2258 vector_lookup(vline, index));
2259 }
2260
2261 ret = cmd_complete_command_real(shifted_vline, vty, status);
2262
2263 vector_free(shifted_vline);
2264 vty->node = onode;
2265 return ret;
2266 }
2267
2268 return cmd_complete_command_real(vline, vty, status);
2269}
2270
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002271static struct vty_parent_node *vty_parent(struct vty *vty)
2272{
2273 return llist_first_entry_or_null(&vty->parent_nodes,
2274 struct vty_parent_node,
2275 entry);
2276}
2277
2278static bool vty_pop_parent(struct vty *vty)
2279{
2280 struct vty_parent_node *parent = vty_parent(vty);
2281 if (!parent)
2282 return false;
2283 llist_del(&parent->entry);
2284 vty->node = parent->node;
2285 vty->priv = parent->priv;
2286 if (vty->indent)
2287 talloc_free(vty->indent);
2288 vty->indent = parent->indent;
2289 talloc_free(parent);
2290 return true;
2291}
2292
2293static void vty_clear_parents(struct vty *vty)
2294{
2295 while (vty_pop_parent(vty));
2296}
2297
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002298/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002299/*
2300 * This function MUST eventually converge on a node when called repeatedly,
2301 * there must not be any cycles.
2302 * All 'config' nodes shall converge on CONFIG_NODE.
2303 * All other 'enable' nodes shall converge on ENABLE_NODE.
2304 * All 'view' only nodes shall converge on VIEW_NODE.
2305 * All other nodes shall converge on themselves or it must be ensured,
2306 * that the user's rights are not extended anyhow by calling this function.
2307 *
2308 * Note that these requirements also apply to all functions that are used
2309 * as go_parent_cb.
2310 * Note also that this function relies on the is_config_child callback to
2311 * recognize non-config nodes if go_parent_cb is not set.
2312 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002313int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002314{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002315 switch (vty->node) {
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002316 case AUTH_NODE:
2317 case VIEW_NODE:
2318 case ENABLE_NODE:
2319 case CONFIG_NODE:
2320 vty_clear_parents(vty);
2321 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002322
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002323 case AUTH_ENABLE_NODE:
2324 vty->node = VIEW_NODE;
2325 vty_clear_parents(vty);
2326 break;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002327
Vadim Yanitskiyb0bbd722020-08-15 22:59:46 +07002328 default:
2329 if (host.app_info->go_parent_cb)
2330 host.app_info->go_parent_cb(vty);
2331 vty_pop_parent(vty);
2332 break;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002333 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002334
2335 return vty->node;
2336}
2337
2338/* Execute command by argument vline vector. */
2339static int
2340cmd_execute_command_real(vector vline, struct vty *vty,
2341 struct cmd_element **cmd)
2342{
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002343 unsigned int i, j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002344 unsigned int index;
2345 vector cmd_vector;
2346 struct cmd_element *cmd_element;
2347 struct cmd_element *matched_element;
2348 unsigned int matched_count, incomplete_count;
2349 int argc;
2350 const char *argv[CMD_ARGC_MAX];
2351 enum match_type match = 0;
2352 int varflag;
2353 char *command;
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002354 int rc;
2355 /* Used for temporary storage of cmd_deopt() allocated arguments during
2356 argv[] generation */
2357 void *cmd_deopt_ctx = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002358
2359 /* Make copy of command elements. */
2360 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2361
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002362 for (index = 0; index < vector_active(vline); index++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002363 if ((command = vector_slot(vline, index))) {
2364 int ret;
2365
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002366 match = cmd_filter(command, cmd_vector, index,
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002367 ANY_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002368
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002369 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002370 break;
2371
2372 ret =
2373 is_cmd_ambiguous(command, cmd_vector, index, match);
2374
2375 if (ret == 1) {
2376 vector_free(cmd_vector);
2377 return CMD_ERR_AMBIGUOUS;
2378 } else if (ret == 2) {
2379 vector_free(cmd_vector);
2380 return CMD_ERR_NO_MATCH;
2381 }
2382 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002383 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002384
2385 /* Check matched count. */
2386 matched_element = NULL;
2387 matched_count = 0;
2388 incomplete_count = 0;
2389
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002390 for (i = 0; i < vector_active(cmd_vector); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002391 if ((cmd_element = vector_slot(cmd_vector, i))) {
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002392 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002393 || index >= cmd_element->cmdsize) {
2394 matched_element = cmd_element;
2395#if 0
2396 printf("DEBUG: %s\n", cmd_element->string);
2397#endif
2398 matched_count++;
2399 } else {
2400 incomplete_count++;
2401 }
2402 }
Vadim Yanitskiyf5781c92019-06-01 00:27:22 +07002403 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002404
2405 /* Finish of using cmd_vector. */
2406 vector_free(cmd_vector);
2407
2408 /* To execute command, matched_count must be 1. */
2409 if (matched_count == 0) {
2410 if (incomplete_count)
2411 return CMD_ERR_INCOMPLETE;
2412 else
2413 return CMD_ERR_NO_MATCH;
2414 }
2415
2416 if (matched_count > 1)
2417 return CMD_ERR_AMBIGUOUS;
2418
2419 /* Argument treatment */
2420 varflag = 0;
2421 argc = 0;
2422
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002423 cmd_deopt_ctx = talloc_named_const(tall_vty_cmd_ctx, 0, __func__);
2424
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002425 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002426 if (argc == CMD_ARGC_MAX) {
2427 rc = CMD_ERR_EXEED_ARGC_MAX;
2428 goto rc_free_deopt_ctx;
2429 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002430 if (varflag) {
2431 argv[argc++] = vector_slot(vline, i);
2432 continue;
2433 }
2434
2435 vector descvec = vector_slot(matched_element->strvec, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002436 const char *tmp_cmd;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002437
2438 if (vector_active(descvec) == 1) {
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002439 /* Single coice argument, no "(...|...)". */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002440 struct desc *desc = vector_slot(descvec, 0);
2441
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002442 if (CMD_OPTION(desc->cmd)) {
2443 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2444 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2445 } else {
2446 tmp_cmd = desc->cmd;
2447 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002448
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002449 if (CMD_VARARG(tmp_cmd))
2450 varflag = 1;
2451 if (varflag || CMD_VARIABLE(tmp_cmd))
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002452 argv[argc++] = vector_slot(vline, i);
Pau Espin Pedrol32e67102019-06-12 18:35:46 +02002453 else if (CMD_OPTION(desc->cmd))
2454 argv[argc++] = tmp_cmd;
2455 /* else : we don't want to add non-opt single-choice static args in argv[] */
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002456 } else {
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002457 /* multi choice argument. look up which choice
2458 the user meant (can only be one after
2459 filtering and checking for ambigous). For instance,
2460 if user typed "th" for "(two|three)" arg, we
2461 want to pass "three" in argv[]. */
2462 for (j = 0; j < vector_active(descvec); j++) {
2463 struct desc *desc = vector_slot(descvec, j);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002464 if (!desc)
2465 continue;
2466 if (cmd_match(desc->cmd, vector_slot(vline, i), ANY_MATCH, true) == NO_MATCH)
2467 continue;
2468 if (CMD_OPTION(desc->cmd)) {
2469 /* we need to first remove the [] chars, then check to see what's inside (var or token) */
2470 tmp_cmd = cmd_deopt(cmd_deopt_ctx, desc->cmd);
2471 } else {
2472 tmp_cmd = desc->cmd;
2473 }
2474
2475 if(CMD_VARIABLE(tmp_cmd)) {
2476 argv[argc++] = vector_slot(vline, i);
2477 } else {
2478 argv[argc++] = tmp_cmd;
2479 }
2480 break;
2481 }
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002482 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002483 }
2484
2485 /* For vtysh execution. */
2486 if (cmd)
2487 *cmd = matched_element;
2488
2489 if (matched_element->daemon)
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002490 rc = CMD_SUCCESS_DAEMON;
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002491 else {
2492 /* Execute matched command. */
2493 struct vty_parent_node this_node = {
2494 .node = vty->node,
2495 .priv = vty->priv,
2496 .indent = vty->indent,
2497 };
2498 struct vty_parent_node *parent = vty_parent(vty);
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002499 rc = (*matched_element->func) (matched_element, vty, argc, argv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002500
Neels Hofmeyrd31de232019-10-31 16:09:23 +01002501 /* If we have stepped down into a child node, push a parent frame.
2502 * The causality is such: we don't expect every single node entry implementation to push
2503 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2504 * a parent node. Hence if the node changed without the parent node changing, we must
2505 * have stepped into a child node. */
2506 if (vty->node != this_node.node && parent == vty_parent(vty)
2507 && vty->node > CONFIG_NODE) {
2508 /* Push the parent node. */
2509 parent = talloc_zero(vty, struct vty_parent_node);
2510 *parent = this_node;
2511 llist_add(&parent->entry, &vty->parent_nodes);
2512 }
2513 }
2514
Pau Espin Pedrol7e1b03f2019-06-12 14:47:48 +02002515rc_free_deopt_ctx:
2516 /* Now after we called the command func, we can free temporary strings */
2517 talloc_free(cmd_deopt_ctx);
2518 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002519}
2520
2521int
2522cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2523 int vtysh)
2524{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002525 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002526 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002527
2528 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002529
2530 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2531 vector shifted_vline;
2532 unsigned int index;
2533
2534 vty->node = ENABLE_NODE;
2535 /* We can try it on enable node, cos' the vty is authenticated */
2536
2537 shifted_vline = vector_init(vector_count(vline));
2538 /* use memcpy? */
2539 for (index = 1; index < vector_active(vline); index++) {
2540 vector_set_index(shifted_vline, index - 1,
2541 vector_lookup(vline, index));
2542 }
2543
2544 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2545
2546 vector_free(shifted_vline);
2547 vty->node = onode;
2548 return ret;
2549 }
2550
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002551 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002552}
2553
2554/* Execute command by argument readline. */
2555int
2556cmd_execute_command_strict(vector vline, struct vty *vty,
2557 struct cmd_element **cmd)
2558{
2559 unsigned int i;
2560 unsigned int index;
2561 vector cmd_vector;
2562 struct cmd_element *cmd_element;
2563 struct cmd_element *matched_element;
2564 unsigned int matched_count, incomplete_count;
2565 int argc;
2566 const char *argv[CMD_ARGC_MAX];
2567 int varflag;
2568 enum match_type match = 0;
2569 char *command;
2570
2571 /* Make copy of command element */
2572 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2573
2574 for (index = 0; index < vector_active(vline); index++)
2575 if ((command = vector_slot(vline, index))) {
2576 int ret;
2577
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002578 match = cmd_filter(vector_slot(vline, index),
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002579 cmd_vector, index, EXACT_MATCH);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002580
2581 /* If command meets '.VARARG' then finish matching. */
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002582 if (match == VARARG_MATCH)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002583 break;
2584
2585 ret =
2586 is_cmd_ambiguous(command, cmd_vector, index, match);
2587 if (ret == 1) {
2588 vector_free(cmd_vector);
2589 return CMD_ERR_AMBIGUOUS;
2590 }
2591 if (ret == 2) {
2592 vector_free(cmd_vector);
2593 return CMD_ERR_NO_MATCH;
2594 }
2595 }
2596
2597 /* Check matched count. */
2598 matched_element = NULL;
2599 matched_count = 0;
2600 incomplete_count = 0;
2601 for (i = 0; i < vector_active(cmd_vector); i++)
2602 if (vector_slot(cmd_vector, i) != NULL) {
2603 cmd_element = vector_slot(cmd_vector, i);
2604
Pau Espin Pedrolde890992019-06-12 12:27:32 +02002605 if (match == VARARG_MATCH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002606 || index >= cmd_element->cmdsize) {
2607 matched_element = cmd_element;
2608 matched_count++;
2609 } else
2610 incomplete_count++;
2611 }
2612
2613 /* Finish of using cmd_vector. */
2614 vector_free(cmd_vector);
2615
2616 /* To execute command, matched_count must be 1. */
2617 if (matched_count == 0) {
2618 if (incomplete_count)
2619 return CMD_ERR_INCOMPLETE;
2620 else
2621 return CMD_ERR_NO_MATCH;
2622 }
2623
2624 if (matched_count > 1)
2625 return CMD_ERR_AMBIGUOUS;
2626
2627 /* Argument treatment */
2628 varflag = 0;
2629 argc = 0;
2630
2631 for (i = 0; i < vector_active(vline); i++) {
Pau Espin Pedrolc17c6d62019-06-14 12:38:42 +02002632 if (argc == CMD_ARGC_MAX)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002633 return CMD_ERR_EXEED_ARGC_MAX;
Pau Espin Pedrol8930ace2019-06-12 17:51:34 +02002634 if (varflag) {
2635 argv[argc++] = vector_slot(vline, i);
2636 continue;
2637 }
2638
2639 vector descvec = vector_slot(matched_element->strvec, i);
2640
2641 if (vector_active(descvec) == 1) {
2642 struct desc *desc = vector_slot(descvec, 0);
2643
2644 if (CMD_VARARG(desc->cmd))
2645 varflag = 1;
2646
2647 if (varflag || CMD_VARIABLE(desc->cmd)
2648 || CMD_OPTION(desc->cmd))
2649 argv[argc++] = vector_slot(vline, i);
2650 } else {
2651 argv[argc++] = vector_slot(vline, i);
2652 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002653 }
2654
2655 /* For vtysh execution. */
2656 if (cmd)
2657 *cmd = matched_element;
2658
2659 if (matched_element->daemon)
2660 return CMD_SUCCESS_DAEMON;
2661
2662 /* Now execute matched command */
2663 return (*matched_element->func) (matched_element, vty, argc, argv);
2664}
2665
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002666static inline size_t len(const char *str)
2667{
2668 return str? strlen(str) : 0;
2669}
2670
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002671/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2672 * is longer than b, a must start with exactly b, and vice versa.
2673 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2674 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002675static int indent_cmp(const char *a, const char *b)
2676{
2677 size_t al, bl;
2678 al = len(a);
2679 bl = len(b);
2680 if (al > bl) {
2681 if (bl && strncmp(a, b, bl) != 0)
2682 return EINVAL;
2683 return 1;
2684 }
2685 /* al <= bl */
2686 if (al && strncmp(a, b, al) != 0)
2687 return EINVAL;
2688 return (al < bl)? -1 : 0;
2689}
2690
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002691/* Configration make from file. */
2692int config_from_file(struct vty *vty, FILE * fp)
2693{
2694 int ret;
2695 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002696 char *indent;
2697 int cmp;
2698 struct vty_parent_node this_node;
2699 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002700
2701 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002702 indent = NULL;
2703 vline = NULL;
2704 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002705
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002706 if (ret != CMD_SUCCESS)
2707 goto return_invalid_indent;
2708
2709 /* In case of comment or empty line */
2710 if (vline == NULL) {
2711 if (indent) {
2712 talloc_free(indent);
2713 indent = NULL;
2714 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002715 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002716 }
2717
Neels Hofmeyr43063632017-09-19 23:54:01 +02002718 /* We have a nonempty line. */
2719 if (!vty->indent) {
2720 /* We have just entered a node and expecting the first child to come up; but we
2721 * may also skip right back to a parent or ancestor level. */
2722 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002723
Neels Hofmeyr43063632017-09-19 23:54:01 +02002724 /* If there is no parent, record any indentation we encounter. */
2725 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2726
2727 if (cmp == EINVAL)
2728 goto return_invalid_indent;
2729
2730 if (cmp <= 0) {
2731 /* We have gone right back to the parent level or higher, we are skipping
2732 * this child node level entirely. Pop the parent to go back to a node
2733 * that was actually there (to reinstate vty->indent) and re-use below
2734 * go-parent while-loop to find an accurate match of indent in the node
2735 * ancestry. */
2736 vty_go_parent(vty);
2737 } else {
2738 /* The indent is deeper than the just entered parent, record the new
2739 * indentation characters. */
2740 vty->indent = talloc_strdup(vty, indent);
2741 /* This *is* the new indentation. */
2742 cmp = 0;
2743 }
2744 } else {
2745 /* There is a known indentation for this node level, validate and detect node
2746 * exits. */
2747 cmp = indent_cmp(indent, vty->indent);
2748 if (cmp == EINVAL)
2749 goto return_invalid_indent;
2750 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002751
2752 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2753 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2754 while (cmp < 0) {
2755 vty_go_parent(vty);
2756 cmp = indent_cmp(indent, vty->indent);
2757 if (cmp == EINVAL)
2758 goto return_invalid_indent;
2759 }
2760
2761 /* More indent without having entered a child node level? Either the parent node's indent
2762 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2763 * or the indentation increased even though the vty command didn't enter a child. */
2764 if (cmp > 0)
2765 goto return_invalid_indent;
2766
2767 /* Remember the current node before the command possibly changes it. */
2768 this_node = (struct vty_parent_node){
2769 .node = vty->node,
2770 .priv = vty->priv,
2771 .indent = vty->indent,
2772 };
2773
2774 parent = vty_parent(vty);
2775 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002776 cmd_free_strvec(vline);
2777
Pau Espin Pedrola0c81952019-10-22 18:38:01 +02002778 if (ret != CMD_SUCCESS && ret != CMD_ERR_NOTHING_TODO) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002779 if (indent) {
2780 talloc_free(indent);
2781 indent = NULL;
2782 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002783 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002784 }
2785
2786 /* If we have stepped down into a child node, push a parent frame.
2787 * The causality is such: we don't expect every single node entry implementation to push
2788 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2789 * a parent node. Hence if the node changed without the parent node changing, we must
2790 * have stepped into a child node (and now expect a deeper indent). */
2791 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2792 /* Push the parent node. */
2793 parent = talloc_zero(vty, struct vty_parent_node);
2794 *parent = this_node;
2795 llist_add(&parent->entry, &vty->parent_nodes);
2796
2797 /* The current talloc'ed vty->indent string will now be owned by this parent
2798 * struct. Indicate that we don't know what deeper indent characters the user
2799 * will choose. */
2800 vty->indent = NULL;
2801 }
2802
2803 if (indent) {
2804 talloc_free(indent);
2805 indent = NULL;
2806 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002807 }
Pau Espin Pedrol0fd0fe62019-10-11 14:10:31 +02002808 /* Make sure we call go_parent_cb for all remaining indent levels at the end of file */
2809 while (vty_parent(vty))
2810 vty_go_parent(vty);
2811
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002812 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002813
2814return_invalid_indent:
2815 if (vline)
2816 cmd_free_strvec(vline);
2817 if (indent) {
2818 talloc_free(indent);
2819 indent = NULL;
2820 }
2821 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002822}
2823
2824/* Configration from terminal */
2825DEFUN(config_terminal,
2826 config_terminal_cmd,
2827 "configure terminal",
2828 "Configuration from vty interface\n" "Configuration terminal\n")
2829{
2830 if (vty_config_lock(vty))
2831 vty->node = CONFIG_NODE;
2832 else {
2833 vty_out(vty, "VTY configuration is locked by other VTY%s",
2834 VTY_NEWLINE);
2835 return CMD_WARNING;
2836 }
2837 return CMD_SUCCESS;
2838}
2839
2840/* Enable command */
2841DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2842{
2843 /* If enable password is NULL, change to ENABLE_NODE */
2844 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2845 vty->type == VTY_SHELL_SERV)
2846 vty->node = ENABLE_NODE;
2847 else
2848 vty->node = AUTH_ENABLE_NODE;
2849
2850 return CMD_SUCCESS;
2851}
2852
2853/* Disable command */
2854DEFUN(disable,
2855 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2856{
2857 if (vty->node == ENABLE_NODE)
2858 vty->node = VIEW_NODE;
2859 return CMD_SUCCESS;
2860}
2861
2862/* Down vty node level. */
2863gDEFUN(config_exit,
2864 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2865{
2866 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002867 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002868 case VIEW_NODE:
2869 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002870 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002871 break;
2872 case CONFIG_NODE:
2873 vty->node = ENABLE_NODE;
2874 vty_config_unlock(vty);
2875 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002876 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002877 if (vty->node > CONFIG_NODE)
2878 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002879 break;
2880 }
2881 return CMD_SUCCESS;
2882}
2883
2884/* End of configuration. */
2885 gDEFUN(config_end,
2886 config_end_cmd, "end", "End current mode and change to enable mode.")
2887{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002888 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002889 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002890
2891 /* Repeatedly call go_parent until a top node is reached. */
2892 while (vty->node > CONFIG_NODE) {
2893 if (vty->node == last_node) {
2894 /* Ensure termination, this shouldn't happen. */
2895 break;
2896 }
2897 last_node = vty->node;
2898 vty_go_parent(vty);
2899 }
2900
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002901 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002902 if (vty->node > ENABLE_NODE)
2903 vty->node = ENABLE_NODE;
2904 vty->index = NULL;
2905 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002906 }
2907 return CMD_SUCCESS;
2908}
2909
2910/* Show version. */
2911DEFUN(show_version,
2912 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2913{
Harald Welte237f6242010-05-25 23:00:45 +02002914 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2915 host.app_info->version,
2916 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2917 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002918
2919 return CMD_SUCCESS;
2920}
2921
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002922DEFUN(show_online_help,
2923 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2924{
Neels Hofmeyrcf70aa02020-05-14 16:51:26 +02002925 vty_dump_xml_ref_to_vty(vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002926 return CMD_SUCCESS;
2927}
2928
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002929/* Help display function for all node. */
2930gDEFUN(config_help,
2931 config_help_cmd, "help", "Description of the interactive help system\n")
2932{
Vadim Yanitskiy1d7ae152020-08-12 16:52:35 +07002933 vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
2934 "anytime at the command line please press '?'.%s%s"
2935 "If nothing matches, the help list will be empty and you must backup%s"
2936 " until entering a '?' shows the available options.%s"
2937 "Two styles of help are provided:%s"
2938 "1. Full help is available when you are ready to enter a%s"
2939 "command argument (e.g. 'show ?') and describes each possible%s"
2940 "argument.%s"
2941 "2. Partial help is provided when an abbreviated argument is entered%s"
2942 " and you want to know what arguments match the input%s"
2943 " (e.g. 'show me?'.)%s%s",
2944 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2945 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2946 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2947 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002948 return CMD_SUCCESS;
2949}
2950
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07002951enum {
2952 ATTR_TYPE_GLOBAL = (1 << 0),
2953 ATTR_TYPE_LIB = (1 << 1),
2954 ATTR_TYPE_APP = (1 << 2),
2955};
2956
2957static void print_attr_list(struct vty *vty, unsigned int attr_mask)
2958{
2959 const char *desc;
2960 unsigned int i;
2961 bool found;
2962 char flag;
2963
2964 if (attr_mask & ATTR_TYPE_GLOBAL) {
2965 vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
2966
2967 for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
2968 desc = cmd_attr_desc[i].str;
2969 flag = '.'; /* FIXME: no flags defined */
2970 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
2971 }
2972 }
2973
2974 if (attr_mask & ATTR_TYPE_LIB) {
2975 vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
2976
2977 for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
2978 if ((desc = cmd_lib_attr_desc[i]) == NULL)
2979 continue;
2980 found = true;
2981
2982 flag = cmd_lib_attr_letters[i];
2983 if (flag == '\0')
2984 flag = '.';
2985
2986 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
2987 }
2988
2989 if (!found)
2990 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
2991 }
2992
2993 if (attr_mask & ATTR_TYPE_APP) {
2994 vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
2995
2996 for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
2997 if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
2998 continue;
2999 found = true;
3000
3001 flag = host.app_info->usr_attr_letters[i];
3002 if (flag == '\0')
3003 flag = '.';
3004
3005 vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
3006 }
3007
3008 if (!found)
3009 vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
3010 }
3011}
3012
3013gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
3014 "show vty-attributes",
3015 SHOW_STR "List of VTY attributes\n")
3016{
3017 print_attr_list(vty, 0xff);
3018 return CMD_SUCCESS;
3019}
3020
3021gDEFUN(show_vty_attr, show_vty_attr_cmd,
3022 "show vty-attributes (application|library|global)",
3023 SHOW_STR "List of VTY attributes\n"
3024 "Application specific attributes only\n"
3025 "Library specific attributes only\n"
3026 "Global attributes only\n")
3027{
3028 unsigned int attr_mask = 0;
3029
3030 if (argv[0][0] == 'g') /* global */
3031 attr_mask |= ATTR_TYPE_GLOBAL;
3032 else if (argv[0][0] == 'l') /* library */
3033 attr_mask |= ATTR_TYPE_LIB;
3034 else if (argv[0][0] == 'a') /* application */
3035 attr_mask |= ATTR_TYPE_APP;
3036
3037 print_attr_list(vty, attr_mask);
3038 return CMD_SUCCESS;
3039}
3040
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003041/* Help display function for all node. */
3042gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
3043{
3044 unsigned int i;
3045 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
3046 struct cmd_element *cmd;
3047
Vadim Yanitskiy6f50e212020-08-12 17:32:18 +07003048 for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
3049 if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
3050 continue;
3051 if (cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))
3052 continue;
3053 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
3054 }
3055
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003056 return CMD_SUCCESS;
3057}
3058
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003059static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003060{
3061 unsigned int i;
3062 int fd;
3063 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003064 char *config_file_tmp = NULL;
3065 char *config_file_sav = NULL;
3066 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003067 struct stat st;
3068
3069 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003070
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003071 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
3072 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
3073 * manually instead. */
3074
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003075 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003076 config_file_sav =
3077 _talloc_zero(tall_vty_cmd_ctx,
3078 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
3079 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003080 if (!config_file_sav)
3081 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003082 strcpy(config_file_sav, config_file);
3083 strcat(config_file_sav, CONF_BACKUP_EXT);
3084
3085 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02003086 "config_file_tmp");
3087 if (!config_file_tmp) {
3088 talloc_free(config_file_sav);
3089 return -1;
3090 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003091 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
3092
3093 /* Open file to configuration write. */
3094 fd = mkstemp(config_file_tmp);
3095 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003096 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003097 talloc_free(config_file_tmp);
3098 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003099 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003100 }
3101
3102 /* Make vty for configuration file. */
3103 file_vty = vty_new();
3104 file_vty->fd = fd;
3105 file_vty->type = VTY_FILE;
3106
3107 /* Config file header print. */
3108 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02003109 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003110 //vty_time_print (file_vty, 1);
3111 vty_out(file_vty, "!\n");
3112
3113 for (i = 0; i < vector_active(cmdvec); i++)
3114 if ((node = vector_slot(cmdvec, i)) && node->func) {
3115 if ((*node->func) (file_vty))
3116 vty_out(file_vty, "!\n");
3117 }
3118 vty_close(file_vty);
3119
3120 if (unlink(config_file_sav) != 0)
3121 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003122 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003123 talloc_free(config_file_sav);
3124 talloc_free(config_file_tmp);
3125 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003126 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003127 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003128
3129 /* Only link the .sav file if the original file exists */
3130 if (stat(config_file, &st) == 0) {
3131 if (link(config_file, config_file_sav) != 0) {
3132 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
3133 talloc_free(config_file_sav);
3134 talloc_free(config_file_tmp);
3135 unlink(config_file_tmp);
3136 return -3;
3137 }
3138 sync();
3139 if (unlink(config_file) != 0) {
3140 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3141 talloc_free(config_file_sav);
3142 talloc_free(config_file_tmp);
3143 unlink(config_file_tmp);
3144 return -4;
3145 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003146 }
3147 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003148 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003149 talloc_free(config_file_sav);
3150 talloc_free(config_file_tmp);
3151 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003152 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003153 }
3154 unlink(config_file_tmp);
3155 sync();
3156
3157 talloc_free(config_file_sav);
3158 talloc_free(config_file_tmp);
3159
3160 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003161 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
3162 return -6;
3163 }
3164
3165 return 0;
3166}
3167
3168
3169/* Write current configuration into file. */
3170DEFUN(config_write_file,
3171 config_write_file_cmd,
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003172 "write file [PATH]",
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003173 "Write running configuration to memory, network, or terminal\n"
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003174 "Write to configuration file\n"
3175 "Set file path to store the config, or replace if already exists\n")
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003176{
3177 char *failed_file;
3178 int rc;
3179
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01003180 if (host.app_info->config_is_consistent) {
3181 rc = host.app_info->config_is_consistent(vty);
3182 if (!rc) {
3183 vty_out(vty, "Configuration is not consistent%s",
3184 VTY_NEWLINE);
3185 return CMD_WARNING;
3186 }
3187 }
3188
Pau Espin Pedroleda8b7b2019-09-26 19:30:47 +02003189 if (argc == 1)
3190 host_config_set(argv[0]);
3191
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003192 if (host.config == NULL) {
3193 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
3194 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003195 return CMD_WARNING;
3196 }
3197
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003198 rc = write_config_file(host.config, &failed_file);
3199 switch (rc) {
3200 case -1:
3201 vty_out(vty, "Can't open configuration file %s.%s",
3202 failed_file, VTY_NEWLINE);
3203 rc = CMD_WARNING;
3204 break;
3205 case -2:
3206 vty_out(vty, "Can't unlink backup configuration file %s.%s",
3207 failed_file, VTY_NEWLINE);
3208 rc = CMD_WARNING;
3209 break;
3210 case -3:
3211 vty_out(vty, "Can't backup old configuration file %s.%s",
3212 failed_file, VTY_NEWLINE);
3213 rc = CMD_WARNING;
3214 break;
3215 case -4:
3216 vty_out(vty, "Can't unlink configuration file %s.%s",
3217 failed_file, VTY_NEWLINE);
3218 rc = CMD_WARNING;
3219 break;
3220 case -5:
3221 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
3222 VTY_NEWLINE);
3223 rc = CMD_WARNING;
3224 break;
3225 case -6:
3226 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
3227 failed_file, strerror(errno), errno, VTY_NEWLINE);
3228 rc = CMD_WARNING;
3229 break;
3230 default:
3231 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
3232 rc = CMD_SUCCESS;
3233 break;
3234 }
3235
3236 talloc_free(failed_file);
3237 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003238}
3239
3240ALIAS(config_write_file,
3241 config_write_cmd,
3242 "write", "Write running configuration to memory, network, or terminal\n")
3243
3244 ALIAS(config_write_file,
3245 config_write_memory_cmd,
3246 "write memory",
3247 "Write running configuration to memory, network, or terminal\n"
3248 "Write configuration to the file (same as write file)\n")
3249
3250 ALIAS(config_write_file,
3251 copy_runningconfig_startupconfig_cmd,
3252 "copy running-config startup-config",
3253 "Copy configuration\n"
3254 "Copy running config to... \n"
3255 "Copy running config to startup config (same as write file)\n")
3256
3257/* Write current configuration into the terminal. */
3258 DEFUN(config_write_terminal,
3259 config_write_terminal_cmd,
3260 "write terminal",
3261 "Write running configuration to memory, network, or terminal\n"
3262 "Write to terminal\n")
3263{
3264 unsigned int i;
3265 struct cmd_node *node;
3266
3267 if (vty->type == VTY_SHELL_SERV) {
3268 for (i = 0; i < vector_active(cmdvec); i++)
3269 if ((node = vector_slot(cmdvec, i)) && node->func
3270 && node->vtysh) {
3271 if ((*node->func) (vty))
3272 vty_out(vty, "!%s", VTY_NEWLINE);
3273 }
3274 } else {
3275 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3276 VTY_NEWLINE);
3277 vty_out(vty, "!%s", VTY_NEWLINE);
3278
3279 for (i = 0; i < vector_active(cmdvec); i++)
3280 if ((node = vector_slot(cmdvec, i)) && node->func) {
3281 if ((*node->func) (vty))
3282 vty_out(vty, "!%s", VTY_NEWLINE);
3283 }
3284 vty_out(vty, "end%s", VTY_NEWLINE);
3285 }
3286 return CMD_SUCCESS;
3287}
3288
3289/* Write current configuration into the terminal. */
3290ALIAS(config_write_terminal,
3291 show_running_config_cmd,
3292 "show running-config", SHOW_STR "running configuration\n")
3293
3294/* Write startup configuration into the terminal. */
3295 DEFUN(show_startup_config,
3296 show_startup_config_cmd,
3297 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
3298{
3299 char buf[BUFSIZ];
3300 FILE *confp;
3301
3302 confp = fopen(host.config, "r");
3303 if (confp == NULL) {
3304 vty_out(vty, "Can't open configuration file [%s]%s",
3305 host.config, VTY_NEWLINE);
3306 return CMD_WARNING;
3307 }
3308
3309 while (fgets(buf, BUFSIZ, confp)) {
3310 char *cp = buf;
3311
3312 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3313 cp++;
3314 *cp = '\0';
3315
3316 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
3317 }
3318
3319 fclose(confp);
3320
3321 return CMD_SUCCESS;
3322}
3323
3324/* Hostname configuration */
3325DEFUN(config_hostname,
3326 hostname_cmd,
3327 "hostname WORD",
3328 "Set system's network name\n" "This system's network name\n")
3329{
3330 if (!isalpha((int)*argv[0])) {
3331 vty_out(vty, "Please specify string starting with alphabet%s",
3332 VTY_NEWLINE);
3333 return CMD_WARNING;
3334 }
3335
3336 if (host.name)
3337 talloc_free(host.name);
3338
3339 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3340 return CMD_SUCCESS;
3341}
3342
3343DEFUN(config_no_hostname,
3344 no_hostname_cmd,
3345 "no hostname [HOSTNAME]",
3346 NO_STR "Reset system's network name\n" "Host name of this router\n")
3347{
3348 if (host.name)
3349 talloc_free(host.name);
3350 host.name = NULL;
3351 return CMD_SUCCESS;
3352}
3353
3354/* VTY interface password set. */
3355DEFUN(config_password, password_cmd,
3356 "password (8|) WORD",
3357 "Assign the terminal connection password\n"
3358 "Specifies a HIDDEN password will follow\n"
3359 "dummy string \n" "The HIDDEN line password string\n")
3360{
3361 /* Argument check. */
3362 if (argc == 0) {
3363 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3364 return CMD_WARNING;
3365 }
3366
3367 if (argc == 2) {
3368 if (*argv[0] == '8') {
3369 if (host.password)
3370 talloc_free(host.password);
3371 host.password = NULL;
3372 if (host.password_encrypt)
3373 talloc_free(host.password_encrypt);
3374 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3375 return CMD_SUCCESS;
3376 } else {
3377 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3378 return CMD_WARNING;
3379 }
3380 }
3381
3382 if (!isalnum((int)*argv[0])) {
3383 vty_out(vty,
3384 "Please specify string starting with alphanumeric%s",
3385 VTY_NEWLINE);
3386 return CMD_WARNING;
3387 }
3388
3389 if (host.password)
3390 talloc_free(host.password);
3391 host.password = NULL;
3392
3393#ifdef VTY_CRYPT_PW
3394 if (host.encrypt) {
3395 if (host.password_encrypt)
3396 talloc_free(host.password_encrypt);
3397 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3398 } else
3399#endif
3400 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3401
3402 return CMD_SUCCESS;
3403}
3404
3405ALIAS(config_password, password_text_cmd,
3406 "password LINE",
3407 "Assign the terminal connection password\n"
3408 "The UNENCRYPTED (cleartext) line password\n")
3409
3410/* VTY enable password set. */
3411 DEFUN(config_enable_password, enable_password_cmd,
3412 "enable password (8|) WORD",
3413 "Modify enable password parameters\n"
3414 "Assign the privileged level password\n"
3415 "Specifies a HIDDEN password will follow\n"
3416 "dummy string \n" "The HIDDEN 'enable' password string\n")
3417{
3418 /* Argument check. */
3419 if (argc == 0) {
3420 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3421 return CMD_WARNING;
3422 }
3423
3424 /* Crypt type is specified. */
3425 if (argc == 2) {
3426 if (*argv[0] == '8') {
3427 if (host.enable)
3428 talloc_free(host.enable);
3429 host.enable = NULL;
3430
3431 if (host.enable_encrypt)
3432 talloc_free(host.enable_encrypt);
3433 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3434
3435 return CMD_SUCCESS;
3436 } else {
3437 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3438 return CMD_WARNING;
3439 }
3440 }
3441
3442 if (!isalnum((int)*argv[0])) {
3443 vty_out(vty,
3444 "Please specify string starting with alphanumeric%s",
3445 VTY_NEWLINE);
3446 return CMD_WARNING;
3447 }
3448
3449 if (host.enable)
3450 talloc_free(host.enable);
3451 host.enable = NULL;
3452
3453 /* Plain password input. */
3454#ifdef VTY_CRYPT_PW
3455 if (host.encrypt) {
3456 if (host.enable_encrypt)
3457 talloc_free(host.enable_encrypt);
3458 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3459 } else
3460#endif
3461 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3462
3463 return CMD_SUCCESS;
3464}
3465
3466ALIAS(config_enable_password,
3467 enable_password_text_cmd,
3468 "enable password LINE",
3469 "Modify enable password parameters\n"
3470 "Assign the privileged level password\n"
3471 "The UNENCRYPTED (cleartext) 'enable' password\n")
3472
3473/* VTY enable password delete. */
3474 DEFUN(no_config_enable_password, no_enable_password_cmd,
3475 "no enable password",
3476 NO_STR
3477 "Modify enable password parameters\n"
3478 "Assign the privileged level password\n")
3479{
3480 if (host.enable)
3481 talloc_free(host.enable);
3482 host.enable = NULL;
3483
3484 if (host.enable_encrypt)
3485 talloc_free(host.enable_encrypt);
3486 host.enable_encrypt = NULL;
3487
3488 return CMD_SUCCESS;
3489}
3490
3491#ifdef VTY_CRYPT_PW
3492DEFUN(service_password_encrypt,
3493 service_password_encrypt_cmd,
3494 "service password-encryption",
3495 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3496{
3497 if (host.encrypt)
3498 return CMD_SUCCESS;
3499
3500 host.encrypt = 1;
3501
3502 if (host.password) {
3503 if (host.password_encrypt)
3504 talloc_free(host.password_encrypt);
3505 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3506 }
3507 if (host.enable) {
3508 if (host.enable_encrypt)
3509 talloc_free(host.enable_encrypt);
3510 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3511 }
3512
3513 return CMD_SUCCESS;
3514}
3515
3516DEFUN(no_service_password_encrypt,
3517 no_service_password_encrypt_cmd,
3518 "no service password-encryption",
3519 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3520{
3521 if (!host.encrypt)
3522 return CMD_SUCCESS;
3523
3524 host.encrypt = 0;
3525
3526 if (host.password_encrypt)
3527 talloc_free(host.password_encrypt);
3528 host.password_encrypt = NULL;
3529
3530 if (host.enable_encrypt)
3531 talloc_free(host.enable_encrypt);
3532 host.enable_encrypt = NULL;
3533
3534 return CMD_SUCCESS;
3535}
3536#endif
3537
3538DEFUN(config_terminal_length, config_terminal_length_cmd,
3539 "terminal length <0-512>",
3540 "Set terminal line parameters\n"
3541 "Set number of lines on a screen\n"
3542 "Number of lines on screen (0 for no pausing)\n")
3543{
3544 int lines;
3545 char *endptr = NULL;
3546
3547 lines = strtol(argv[0], &endptr, 10);
3548 if (lines < 0 || lines > 512 || *endptr != '\0') {
3549 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3550 return CMD_WARNING;
3551 }
3552 vty->lines = lines;
3553
3554 return CMD_SUCCESS;
3555}
3556
3557DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3558 "terminal no length",
3559 "Set terminal line parameters\n"
3560 NO_STR "Set number of lines on a screen\n")
3561{
3562 vty->lines = -1;
3563 return CMD_SUCCESS;
3564}
3565
3566DEFUN(service_terminal_length, service_terminal_length_cmd,
3567 "service terminal-length <0-512>",
3568 "Set up miscellaneous service\n"
3569 "System wide terminal length configuration\n"
3570 "Number of lines of VTY (0 means no line control)\n")
3571{
3572 int lines;
3573 char *endptr = NULL;
3574
3575 lines = strtol(argv[0], &endptr, 10);
3576 if (lines < 0 || lines > 512 || *endptr != '\0') {
3577 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3578 return CMD_WARNING;
3579 }
3580 host.lines = lines;
3581
3582 return CMD_SUCCESS;
3583}
3584
3585DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3586 "no service terminal-length [<0-512>]",
3587 NO_STR
3588 "Set up miscellaneous service\n"
3589 "System wide terminal length configuration\n"
3590 "Number of lines of VTY (0 means no line control)\n")
3591{
3592 host.lines = -1;
3593 return CMD_SUCCESS;
3594}
3595
3596DEFUN_HIDDEN(do_echo,
3597 echo_cmd,
3598 "echo .MESSAGE",
3599 "Echo a message back to the vty\n" "The message to echo\n")
3600{
3601 char *message;
3602
3603 vty_out(vty, "%s%s",
3604 ((message =
3605 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3606 if (message)
3607 talloc_free(message);
3608 return CMD_SUCCESS;
3609}
3610
3611#if 0
3612DEFUN(config_logmsg,
3613 config_logmsg_cmd,
3614 "logmsg " LOG_LEVELS " .MESSAGE",
3615 "Send a message to enabled logging destinations\n"
3616 LOG_LEVEL_DESC "The message to send\n")
3617{
3618 int level;
3619 char *message;
3620
3621 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3622 return CMD_ERR_NO_MATCH;
3623
3624 zlog(NULL, level,
3625 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3626 if (message)
3627 talloc_free(message);
3628 return CMD_SUCCESS;
3629}
3630
3631DEFUN(show_logging,
3632 show_logging_cmd,
3633 "show logging", SHOW_STR "Show current logging configuration\n")
3634{
3635 struct zlog *zl = zlog_default;
3636
3637 vty_out(vty, "Syslog logging: ");
3638 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3639 vty_out(vty, "disabled");
3640 else
3641 vty_out(vty, "level %s, facility %s, ident %s",
3642 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3643 facility_name(zl->facility), zl->ident);
3644 vty_out(vty, "%s", VTY_NEWLINE);
3645
3646 vty_out(vty, "Stdout logging: ");
3647 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3648 vty_out(vty, "disabled");
3649 else
3650 vty_out(vty, "level %s",
3651 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3652 vty_out(vty, "%s", VTY_NEWLINE);
3653
3654 vty_out(vty, "Monitor logging: ");
3655 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3656 vty_out(vty, "disabled");
3657 else
3658 vty_out(vty, "level %s",
3659 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3660 vty_out(vty, "%s", VTY_NEWLINE);
3661
3662 vty_out(vty, "File logging: ");
3663 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3664 vty_out(vty, "disabled");
3665 else
3666 vty_out(vty, "level %s, filename %s",
3667 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3668 zl->filename);
3669 vty_out(vty, "%s", VTY_NEWLINE);
3670
3671 vty_out(vty, "Protocol name: %s%s",
3672 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3673 vty_out(vty, "Record priority: %s%s",
3674 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3675
3676 return CMD_SUCCESS;
3677}
3678
3679DEFUN(config_log_stdout,
3680 config_log_stdout_cmd,
3681 "log stdout", "Logging control\n" "Set stdout logging level\n")
3682{
3683 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3684 return CMD_SUCCESS;
3685}
3686
3687DEFUN(config_log_stdout_level,
3688 config_log_stdout_level_cmd,
3689 "log stdout " LOG_LEVELS,
3690 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3691{
3692 int level;
3693
3694 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3695 return CMD_ERR_NO_MATCH;
3696 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3697 return CMD_SUCCESS;
3698}
3699
3700DEFUN(no_config_log_stdout,
3701 no_config_log_stdout_cmd,
3702 "no log stdout [LEVEL]",
3703 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3704{
3705 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3706 return CMD_SUCCESS;
3707}
3708
3709DEFUN(config_log_monitor,
3710 config_log_monitor_cmd,
3711 "log monitor",
3712 "Logging control\n" "Set terminal line (monitor) logging level\n")
3713{
3714 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3715 return CMD_SUCCESS;
3716}
3717
3718DEFUN(config_log_monitor_level,
3719 config_log_monitor_level_cmd,
3720 "log monitor " LOG_LEVELS,
3721 "Logging control\n"
3722 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3723{
3724 int level;
3725
3726 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3727 return CMD_ERR_NO_MATCH;
3728 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3729 return CMD_SUCCESS;
3730}
3731
3732DEFUN(no_config_log_monitor,
3733 no_config_log_monitor_cmd,
3734 "no log monitor [LEVEL]",
3735 NO_STR
3736 "Logging control\n"
3737 "Disable terminal line (monitor) logging\n" "Logging level\n")
3738{
3739 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3740 return CMD_SUCCESS;
3741}
3742
3743static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3744{
3745 int ret;
3746 char *p = NULL;
3747 const char *fullpath;
3748
3749 /* Path detection. */
3750 if (!IS_DIRECTORY_SEP(*fname)) {
3751 char cwd[MAXPATHLEN + 1];
3752 cwd[MAXPATHLEN] = '\0';
3753
3754 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3755 zlog_err("config_log_file: Unable to alloc mem!");
3756 return CMD_WARNING;
3757 }
3758
3759 if ((p = _talloc_zero(tall_vcmd_ctx,
3760 strlen(cwd) + strlen(fname) + 2),
3761 "set_log_file")
3762 == NULL) {
3763 zlog_err("config_log_file: Unable to alloc mem!");
3764 return CMD_WARNING;
3765 }
3766 sprintf(p, "%s/%s", cwd, fname);
3767 fullpath = p;
3768 } else
3769 fullpath = fname;
3770
3771 ret = zlog_set_file(NULL, fullpath, loglevel);
3772
3773 if (p)
3774 talloc_free(p);
3775
3776 if (!ret) {
3777 vty_out(vty, "can't open logfile %s\n", fname);
3778 return CMD_WARNING;
3779 }
3780
3781 if (host.logfile)
3782 talloc_free(host.logfile);
3783
3784 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3785
3786 return CMD_SUCCESS;
3787}
3788
3789DEFUN(config_log_file,
3790 config_log_file_cmd,
3791 "log file FILENAME",
3792 "Logging control\n" "Logging to file\n" "Logging filename\n")
3793{
3794 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3795}
3796
3797DEFUN(config_log_file_level,
3798 config_log_file_level_cmd,
3799 "log file FILENAME " LOG_LEVELS,
3800 "Logging control\n"
3801 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3802{
3803 int level;
3804
3805 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3806 return CMD_ERR_NO_MATCH;
3807 return set_log_file(vty, argv[0], level);
3808}
3809
3810DEFUN(no_config_log_file,
3811 no_config_log_file_cmd,
3812 "no log file [FILENAME]",
3813 NO_STR
3814 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3815{
3816 zlog_reset_file(NULL);
3817
3818 if (host.logfile)
3819 talloc_free(host.logfile);
3820
3821 host.logfile = NULL;
3822
3823 return CMD_SUCCESS;
3824}
3825
3826ALIAS(no_config_log_file,
3827 no_config_log_file_level_cmd,
3828 "no log file FILENAME LEVEL",
3829 NO_STR
3830 "Logging control\n"
3831 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3832
3833 DEFUN(config_log_syslog,
3834 config_log_syslog_cmd,
3835 "log syslog", "Logging control\n" "Set syslog logging level\n")
3836{
3837 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3838 return CMD_SUCCESS;
3839}
3840
3841DEFUN(config_log_syslog_level,
3842 config_log_syslog_level_cmd,
3843 "log syslog " LOG_LEVELS,
3844 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3845{
3846 int level;
3847
3848 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3849 return CMD_ERR_NO_MATCH;
3850 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3851 return CMD_SUCCESS;
3852}
3853
3854DEFUN_DEPRECATED(config_log_syslog_facility,
3855 config_log_syslog_facility_cmd,
3856 "log syslog facility " LOG_FACILITIES,
3857 "Logging control\n"
3858 "Logging goes to syslog\n"
3859 "(Deprecated) Facility parameter for syslog messages\n"
3860 LOG_FACILITY_DESC)
3861{
3862 int facility;
3863
3864 if ((facility = facility_match(argv[0])) < 0)
3865 return CMD_ERR_NO_MATCH;
3866
3867 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3868 zlog_default->facility = facility;
3869 return CMD_SUCCESS;
3870}
3871
3872DEFUN(no_config_log_syslog,
3873 no_config_log_syslog_cmd,
3874 "no log syslog [LEVEL]",
3875 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3876{
3877 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3878 return CMD_SUCCESS;
3879}
3880
3881ALIAS(no_config_log_syslog,
3882 no_config_log_syslog_facility_cmd,
3883 "no log syslog facility " LOG_FACILITIES,
3884 NO_STR
3885 "Logging control\n"
3886 "Logging goes to syslog\n"
3887 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3888
3889 DEFUN(config_log_facility,
3890 config_log_facility_cmd,
3891 "log facility " LOG_FACILITIES,
3892 "Logging control\n"
3893 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3894{
3895 int facility;
3896
3897 if ((facility = facility_match(argv[0])) < 0)
3898 return CMD_ERR_NO_MATCH;
3899 zlog_default->facility = facility;
3900 return CMD_SUCCESS;
3901}
3902
3903DEFUN(no_config_log_facility,
3904 no_config_log_facility_cmd,
3905 "no log facility [FACILITY]",
3906 NO_STR
3907 "Logging control\n"
3908 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3909{
3910 zlog_default->facility = LOG_DAEMON;
3911 return CMD_SUCCESS;
3912}
3913
3914DEFUN_DEPRECATED(config_log_trap,
3915 config_log_trap_cmd,
3916 "log trap " LOG_LEVELS,
3917 "Logging control\n"
3918 "(Deprecated) Set logging level and default for all destinations\n"
3919 LOG_LEVEL_DESC)
3920{
3921 int new_level;
3922 int i;
3923
3924 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3925 return CMD_ERR_NO_MATCH;
3926
3927 zlog_default->default_lvl = new_level;
3928 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3929 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3930 zlog_default->maxlvl[i] = new_level;
3931 return CMD_SUCCESS;
3932}
3933
3934DEFUN_DEPRECATED(no_config_log_trap,
3935 no_config_log_trap_cmd,
3936 "no log trap [LEVEL]",
3937 NO_STR
3938 "Logging control\n"
3939 "Permit all logging information\n" "Logging level\n")
3940{
3941 zlog_default->default_lvl = LOG_DEBUG;
3942 return CMD_SUCCESS;
3943}
3944
3945DEFUN(config_log_record_priority,
3946 config_log_record_priority_cmd,
3947 "log record-priority",
3948 "Logging control\n"
3949 "Log the priority of the message within the message\n")
3950{
3951 zlog_default->record_priority = 1;
3952 return CMD_SUCCESS;
3953}
3954
3955DEFUN(no_config_log_record_priority,
3956 no_config_log_record_priority_cmd,
3957 "no log record-priority",
3958 NO_STR
3959 "Logging control\n"
3960 "Do not log the priority of the message within the message\n")
3961{
3962 zlog_default->record_priority = 0;
3963 return CMD_SUCCESS;
3964}
3965#endif
3966
3967DEFUN(banner_motd_file,
3968 banner_motd_file_cmd,
3969 "banner motd file [FILE]",
3970 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3971{
3972 if (host.motdfile)
3973 talloc_free(host.motdfile);
3974 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3975
3976 return CMD_SUCCESS;
3977}
3978
3979DEFUN(banner_motd_default,
3980 banner_motd_default_cmd,
3981 "banner motd default",
3982 "Set banner string\n" "Strings for motd\n" "Default string\n")
3983{
3984 host.motd = default_motd;
3985 return CMD_SUCCESS;
3986}
3987
3988DEFUN(no_banner_motd,
3989 no_banner_motd_cmd,
3990 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3991{
3992 host.motd = NULL;
3993 if (host.motdfile)
3994 talloc_free(host.motdfile);
3995 host.motdfile = NULL;
3996 return CMD_SUCCESS;
3997}
3998
3999/* Set config filename. Called from vty.c */
4000void host_config_set(const char *filename)
4001{
4002 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
4003}
4004
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004005/*! Deprecated, now happens implicitly when calling install_node().
4006 * Users of the API may still attempt to call this function, hence
4007 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00004008void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004009{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004010}
4011
4012/*! Deprecated, now happens implicitly when calling install_node().
4013 * Users of the API may still attempt to call this function, hence
4014 * leave it here as a no-op. */
4015void vty_install_default(int node)
4016{
4017}
4018
4019/*! Install common commands like 'exit' and 'list'. */
4020static void install_basic_node_commands(int node)
4021{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004022 install_lib_element(node, &config_help_cmd);
4023 install_lib_element(node, &config_list_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004024
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004025 install_lib_element(node, &show_vty_attr_all_cmd);
4026 install_lib_element(node, &show_vty_attr_cmd);
4027
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004028 install_lib_element(node, &config_write_terminal_cmd);
4029 install_lib_element(node, &config_write_file_cmd);
4030 install_lib_element(node, &config_write_memory_cmd);
4031 install_lib_element(node, &config_write_cmd);
4032 install_lib_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004033
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004034 install_lib_element(node, &config_exit_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004035
4036 if (node >= CONFIG_NODE) {
4037 /* It's not a top node. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004038 install_lib_element(node, &config_end_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02004039 }
4040}
4041
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004042/*! Return true if a node is installed by install_basic_node_commands(), so
4043 * that we can avoid repeating them for each and every node during 'show
4044 * running-config' */
4045static bool vty_command_is_common(struct cmd_element *cmd)
4046{
4047 if (cmd == &config_help_cmd
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004048 || cmd == &show_vty_attr_all_cmd
4049 || cmd == &show_vty_attr_cmd
Neels Hofmeyr69054e22017-10-19 02:44:57 +02004050 || cmd == &config_list_cmd
4051 || cmd == &config_write_terminal_cmd
4052 || cmd == &config_write_file_cmd
4053 || cmd == &config_write_memory_cmd
4054 || cmd == &config_write_cmd
4055 || cmd == &show_running_config_cmd
4056 || cmd == &config_exit_cmd
4057 || cmd == &config_end_cmd)
4058 return true;
4059 return false;
4060}
4061
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004062/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004063 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004064 * \param[in] vty the vty of the code
4065 * \param[in] filename where to store the file
4066 * \return 0 in case of success.
4067 *
4068 * If the filename already exists create a filename.sav
4069 * version with the current code.
4070 *
4071 */
4072int osmo_vty_write_config_file(const char *filename)
4073{
4074 char *failed_file;
4075 int rc;
4076
4077 rc = write_config_file(filename, &failed_file);
4078 talloc_free(failed_file);
4079 return rc;
4080}
4081
4082/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02004083 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01004084 * \return 0 in case of success.
4085 *
4086 * If the filename already exists create a filename.sav
4087 * version with the current code.
4088 *
4089 */
4090int osmo_vty_save_config_file(void)
4091{
4092 char *failed_file;
4093 int rc;
4094
4095 if (host.config == NULL)
4096 return -7;
4097
4098 rc = write_config_file(host.config, &failed_file);
4099 talloc_free(failed_file);
4100 return rc;
4101}
4102
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004103/* Initialize command interface. Install basic nodes and commands. */
4104void cmd_init(int terminal)
4105{
4106 /* Allocate initial top vector of commands. */
4107 cmdvec = vector_init(VECTOR_MIN_SIZE);
4108
4109 /* Default host value settings. */
4110 host.name = NULL;
4111 host.password = NULL;
4112 host.enable = NULL;
4113 host.logfile = NULL;
4114 host.config = NULL;
4115 host.lines = -1;
4116 host.motd = default_motd;
4117 host.motdfile = NULL;
4118
4119 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004120 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004121 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02004122 install_node_bare(&auth_node, NULL);
4123 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004124 install_node(&config_node, config_write_host);
4125
4126 /* Each node's basic commands. */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004127 install_lib_element(VIEW_NODE, &show_version_cmd);
4128 install_lib_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004129 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004130 install_lib_element(VIEW_NODE, &config_list_cmd);
4131 install_lib_element(VIEW_NODE, &config_exit_cmd);
4132 install_lib_element(VIEW_NODE, &config_help_cmd);
Vadim Yanitskiy44c4d172020-10-05 01:30:23 +07004133 install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
4134 install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004135 install_lib_element(VIEW_NODE, &config_enable_cmd);
4136 install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
4137 install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
4138 install_lib_element(VIEW_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004139 }
4140
4141 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004142 install_lib_element(ENABLE_NODE, &config_disable_cmd);
4143 install_lib_element(ENABLE_NODE, &config_terminal_cmd);
4144 install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004145 }
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004146 install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
4147 install_lib_element(ENABLE_NODE, &show_version_cmd);
4148 install_lib_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004149
4150 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004151 install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
4152 install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
4153 install_lib_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004154 }
4155
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004156 install_lib_element(CONFIG_NODE, &hostname_cmd);
4157 install_lib_element(CONFIG_NODE, &no_hostname_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004158
4159 if (terminal) {
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004160 install_lib_element(CONFIG_NODE, &password_cmd);
4161 install_lib_element(CONFIG_NODE, &password_text_cmd);
4162 install_lib_element(CONFIG_NODE, &enable_password_cmd);
4163 install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
4164 install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004165
4166#ifdef VTY_CRYPT_PW
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004167 install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
4168 install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004169#endif
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07004170 install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
4171 install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
4172 install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
4173 install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
4174 install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004175
4176 }
4177 srand(time(NULL));
4178}
Harald Welte7acb30c2011-08-17 17:13:48 +02004179
Vadim Yanitskiyc0745eb2020-10-04 18:37:13 +07004180/* FIXME: execute this section in the unit test instead */
4181static __attribute__((constructor)) void on_dso_load(void)
4182{
4183 unsigned int i, j;
4184
4185 /* Check total number of the library specific attributes */
4186 OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
4187
4188 /* Check for duplicates in the list of library specific flags */
4189 for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
4190 if (cmd_lib_attr_letters[i] == '\0')
4191 continue;
4192
4193 /* Only upper case flag letters are allowed for libraries */
4194 OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
4195 OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
4196
4197 for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
4198 OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
4199 }
4200}
4201
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02004202/*! @} */