blob: 689609c0664b4ae46c70b4f12534222c74ac8757 [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>
40#include <sys/time.h>
41#include <sys/stat.h>
42
43#include <osmocom/vty/vector.h>
44#include <osmocom/vty/vty.h>
45#include <osmocom/vty/command.h>
46
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010047#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010048#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020049
Harald Weltee881b1b2011-08-17 18:52:30 +020050/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020051 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020052 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020053 *
54 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020055
Harald Welte3fb0b6f2010-05-19 19:02:52 +020056#define CONFIGFILE_MASK 022
57
58void *tall_vty_cmd_ctx;
59
60/* Command vector which includes some level of command lists. Normally
61 each daemon maintains each own cmdvec. */
62vector cmdvec;
63
64/* Host information structure. */
65struct host host;
66
67/* Standard command node structures. */
68struct cmd_node auth_node = {
69 AUTH_NODE,
70 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010071 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020072};
73
74struct cmd_node view_node = {
75 VIEW_NODE,
76 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node auth_enable_node = {
81 AUTH_ENABLE_NODE,
82 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node enable_node = {
87 ENABLE_NODE,
88 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node config_node = {
93 CONFIG_NODE,
94 "%s(config)# ",
95 1
96};
97
98/* Default motd string. */
99const char *default_motd = "";
100
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200101/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200102 *
103 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200104void print_version(int print_copyright)
105{
Harald Welte237f6242010-05-25 23:00:45 +0200106 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200107 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200108 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200109}
110
111/* Utility function to concatenate argv argument into a single string
112 with inserting ' ' character between each argument. */
113char *argv_concat(const char **argv, int argc, int shift)
114{
115 int i;
116 size_t len;
117 char *str;
118 char *p;
119
120 len = 0;
121 for (i = shift; i < argc; i++)
122 len += strlen(argv[i]) + 1;
123 if (!len)
124 return NULL;
125 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
126 for (i = shift; i < argc; i++) {
127 size_t arglen;
128 memcpy(p, argv[i], (arglen = strlen(argv[i])));
129 p += arglen;
130 *p++ = ' ';
131 }
132 *(p - 1) = '\0';
133 return str;
134}
135
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200136/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
137 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
138 * in turn, this name us used for XML IDs in 'show online-help'. */
139static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
140{
141 const char *pos;
142 int dest = 0;
143
144 if (!prompt || !*prompt)
145 return "";
146
147 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
148 if (pos[0] == '%' && pos[1]) {
149 /* skip "%s"; loop pos++ does the second one. */
150 pos++;
151 continue;
152 }
153 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
154 continue;
155 name_buf[dest] = pos[0];
156 dest++;
157 }
158 name_buf[dest] = '\0';
159 return name_buf;
160}
161
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200162static void install_basic_node_commands(int node);
163
164/*! Install top node of command vector, without adding basic node commands. */
165static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200166{
167 vector_set_index(cmdvec, node->node, node);
168 node->func = func;
169 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200170 if (!*node->name)
171 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172}
173
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200174/*! Install top node of command vector. */
175void install_node(struct cmd_node *node, int (*func) (struct vty *))
176{
177 install_node_bare(node, func);
178 install_basic_node_commands(node->node);
179}
180
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200181/* Compare two command's string. Used in sort_node (). */
182static int cmp_node(const void *p, const void *q)
183{
184 struct cmd_element *a = *(struct cmd_element **)p;
185 struct cmd_element *b = *(struct cmd_element **)q;
186
187 return strcmp(a->string, b->string);
188}
189
190static int cmp_desc(const void *p, const void *q)
191{
192 struct desc *a = *(struct desc **)p;
193 struct desc *b = *(struct desc **)q;
194
195 return strcmp(a->cmd, b->cmd);
196}
197
Jacob Erlbeck2442e092013-09-06 16:51:58 +0200198static int is_config_child(struct vty *vty)
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800199{
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800200 if (vty->node <= CONFIG_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800201 return 0;
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800202 else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800203 return 1;
204 else if (host.app_info->is_config_node)
Holger Hans Peter Freyther8f09f012010-08-25 17:34:56 +0800205 return host.app_info->is_config_node(vty, vty->node);
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800206 else
207 return vty->node > CONFIG_NODE;
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200211void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200212{
213 unsigned int i, j;
214 struct cmd_node *cnode;
215 vector descvec;
216 struct cmd_element *cmd_element;
217
218 for (i = 0; i < vector_active(cmdvec); i++)
219 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
220 vector cmd_vector = cnode->cmd_vector;
221 qsort(cmd_vector->index, vector_active(cmd_vector),
222 sizeof(void *), cmp_node);
223
224 for (j = 0; j < vector_active(cmd_vector); j++)
225 if ((cmd_element =
226 vector_slot(cmd_vector, j)) != NULL
227 && vector_active(cmd_element->strvec)) {
228 descvec =
229 vector_slot(cmd_element->strvec,
230 vector_active
231 (cmd_element->strvec) -
232 1);
233 qsort(descvec->index,
234 vector_active(descvec),
235 sizeof(void *), cmp_desc);
236 }
237 }
238}
239
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200240/*! Break up string in command tokens. Return leading indents.
241 * \param[in] string String to split.
242 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
243 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
244 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
245 *
246 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
247 * so that \a indent can simply return the count of leading spaces.
248 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
249 */
250int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200251{
252 const char *cp, *start;
253 char *token;
254 int strlen;
255 vector strvec;
256
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200257 *strvec_p = NULL;
258 if (indent)
259 *indent = 0;
260
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200262 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200263
264 cp = string;
265
266 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200267 while (isspace((int)*cp) && *cp != '\0') {
268 /* if we're counting indents, we need to be strict about them */
269 if (indent && (*cp != ' ') && (*cp != '\t')) {
270 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
271 if (*cp == '\n' || *cp == '\r') {
272 cp++;
273 string = cp;
274 continue;
275 }
276 return CMD_ERR_INVALID_INDENT;
277 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200278 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200279 }
280
281 if (indent)
282 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200283
284 /* Return if there is only white spaces */
285 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200286 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200287
288 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200289 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200290
291 /* Prepare return vector. */
292 strvec = vector_init(VECTOR_MIN_SIZE);
293
294 /* Copy each command piece and set into vector. */
295 while (1) {
296 start = cp;
297 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
298 *cp != '\0')
299 cp++;
300 strlen = cp - start;
301 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
302 memcpy(token, start, strlen);
303 *(token + strlen) = '\0';
304 vector_set(strvec, token);
305
306 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
307 *cp != '\0')
308 cp++;
309
310 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200311 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200312 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200313
314 *strvec_p = strvec;
315 return CMD_SUCCESS;
316}
317
318/*! Breaking up string into each command piece. I assume given
319 character is separated by a space character. Return value is a
320 vector which includes char ** data element. */
321vector cmd_make_strvec(const char *string)
322{
323 vector strvec;
324 cmd_make_strvec2(string, NULL, &strvec);
325 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200326}
327
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200328/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200329void cmd_free_strvec(vector v)
330{
331 unsigned int i;
332 char *cp;
333
334 if (!v)
335 return;
336
337 for (i = 0; i < vector_active(v); i++)
338 if ((cp = vector_slot(v, i)) != NULL)
339 talloc_free(cp);
340
341 vector_free(v);
342}
343
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200344/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200345static char *cmd_desc_str(const char **string)
346{
347 const char *cp, *start;
348 char *token;
349 int strlen;
350
351 cp = *string;
352
353 if (cp == NULL)
354 return NULL;
355
356 /* Skip white spaces. */
357 while (isspace((int)*cp) && *cp != '\0')
358 cp++;
359
360 /* Return if there is only white spaces */
361 if (*cp == '\0')
362 return NULL;
363
364 start = cp;
365
366 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
367 cp++;
368
369 strlen = cp - start;
370 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
371 memcpy(token, start, strlen);
372 *(token + strlen) = '\0';
373
374 *string = cp;
375
376 return token;
377}
378
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200379/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200380static vector cmd_make_descvec(const char *string, const char *descstr)
381{
382 int multiple = 0;
383 const char *sp;
384 char *token;
385 int len;
386 const char *cp;
387 const char *dp;
388 vector allvec;
389 vector strvec = NULL;
390 struct desc *desc;
391
392 cp = string;
393 dp = descstr;
394
395 if (cp == NULL)
396 return NULL;
397
398 allvec = vector_init(VECTOR_MIN_SIZE);
399
400 while (1) {
401 while (isspace((int)*cp) && *cp != '\0')
402 cp++;
403
404 if (*cp == '(') {
405 multiple = 1;
406 cp++;
407 }
408 if (*cp == ')') {
409 multiple = 0;
410 cp++;
411 }
412 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100413 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200414 cp++;
415 }
416
417 while (isspace((int)*cp) && *cp != '\0')
418 cp++;
419
420 if (*cp == '(') {
421 multiple = 1;
422 cp++;
423 }
424
425 if (*cp == '\0')
426 return allvec;
427
428 sp = cp;
429
430 while (!
431 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
432 || *cp == ')' || *cp == '|') && *cp != '\0')
433 cp++;
434
435 len = cp - sp;
436
437 token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
438 memcpy(token, sp, len);
439 *(token + len) = '\0';
440
441 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
442 desc->cmd = token;
443 desc->str = cmd_desc_str(&dp);
444
445 if (multiple) {
446 if (multiple == 1) {
447 strvec = vector_init(VECTOR_MIN_SIZE);
448 vector_set(allvec, strvec);
449 }
450 multiple++;
451 } else {
452 strvec = vector_init(VECTOR_MIN_SIZE);
453 vector_set(allvec, strvec);
454 }
455 vector_set(strvec, desc);
456 }
457}
458
459/* Count mandantory string vector size. This is to determine inputed
460 command has enough command length. */
461static int cmd_cmdsize(vector strvec)
462{
463 unsigned int i;
464 int size = 0;
465 vector descvec;
466 struct desc *desc;
467
468 for (i = 0; i < vector_active(strvec); i++)
469 if ((descvec = vector_slot(strvec, i)) != NULL) {
470 if ((vector_active(descvec)) == 1
471 && (desc = vector_slot(descvec, 0)) != NULL) {
472 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
473 return size;
474 else
475 size++;
476 } else
477 size++;
478 }
479 return size;
480}
481
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200482/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483const char *cmd_prompt(enum node_type node)
484{
485 struct cmd_node *cnode;
486
487 cnode = vector_slot(cmdvec, node);
488 return cnode->prompt;
489}
490
Alexander Couzensad580ba2016-05-16 16:01:45 +0200491/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200492 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200493 * \param unsafe string
494 * \return a new talloc char *
495 */
496char *osmo_asciidoc_escape(const char *inp)
497{
498 int _strlen;
499 char *out, *out_ptr;
500 int len = 0, i, j;
501
502 if (!inp)
503 return NULL;
504 _strlen = strlen(inp);
505
506 for (i = 0; i < _strlen; ++i) {
507 switch (inp[i]) {
508 case '|':
509 len += 2;
510 break;
511 default:
512 len += 1;
513 break;
514 }
515 }
516
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200517 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200518 if (!out)
519 return NULL;
520
521 out_ptr = out;
522
523#define ADD(out, str) \
524 for (j = 0; j < strlen(str); ++j) \
525 *(out++) = str[j];
526
527 for (i = 0; i < _strlen; ++i) {
528 switch (inp[i]) {
529 case '|':
530 ADD(out_ptr, "\\|");
531 break;
532 default:
533 *(out_ptr++) = inp[i];
534 break;
535 }
536 }
537
538#undef ADD
539
540 out_ptr[0] = '\0';
541 return out;
542}
543
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100544static char *xml_escape(const char *inp)
545{
546 int _strlen;
547 char *out, *out_ptr;
548 int len = 0, i, j;
549
550 if (!inp)
551 return NULL;
552 _strlen = strlen(inp);
553
554 for (i = 0; i < _strlen; ++i) {
555 switch (inp[i]) {
556 case '"':
557 len += 6;
558 break;
559 case '\'':
560 len += 6;
561 break;
562 case '<':
563 len += 4;
564 break;
565 case '>':
566 len += 4;
567 break;
568 case '&':
569 len += 5;
570 break;
571 default:
572 len += 1;
573 break;
574 }
575 }
576
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200577 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100578 if (!out)
579 return NULL;
580
581 out_ptr = out;
582
583#define ADD(out, str) \
584 for (j = 0; j < strlen(str); ++j) \
585 *(out++) = str[j];
586
587 for (i = 0; i < _strlen; ++i) {
588 switch (inp[i]) {
589 case '"':
590 ADD(out_ptr, "&quot;");
591 break;
592 case '\'':
593 ADD(out_ptr, "&apos;");
594 break;
595 case '<':
596 ADD(out_ptr, "&lt;");
597 break;
598 case '>':
599 ADD(out_ptr, "&gt;");
600 break;
601 case '&':
602 ADD(out_ptr, "&amp;");
603 break;
604 default:
605 *(out_ptr++) = inp[i];
606 break;
607 }
608 }
609
610#undef ADD
611
612 out_ptr[0] = '\0';
613 return out;
614}
615
616/*
617 * Write one cmd_element as XML to the given VTY.
618 */
619static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
620{
621 char *xml_string = xml_escape(cmd->string);
622
623 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
624 vty_out(vty, " <params>%s", VTY_NEWLINE);
625
626 int j;
627 for (j = 0; j < vector_count(cmd->strvec); ++j) {
628 vector descvec = vector_slot(cmd->strvec, j);
629 int i;
630 for (i = 0; i < vector_active(descvec); ++i) {
631 char *xml_param, *xml_doc;
632 struct desc *desc = vector_slot(descvec, i);
633 if (desc == NULL)
634 continue;
635
636 xml_param = xml_escape(desc->cmd);
637 xml_doc = xml_escape(desc->str);
638 vty_out(vty, " <param name='%s' doc='%s' />%s",
639 xml_param, xml_doc, VTY_NEWLINE);
640 talloc_free(xml_param);
641 talloc_free(xml_doc);
642 }
643 }
644
645 vty_out(vty, " </params>%s", VTY_NEWLINE);
646 vty_out(vty, " </command>%s", VTY_NEWLINE);
647
648 talloc_free(xml_string);
649 return 0;
650}
651
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200652static bool vty_command_is_common(struct cmd_element *cmd);
653
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100654/*
655 * Dump all nodes and commands associated with a given node as XML to the VTY.
656 */
657static int vty_dump_nodes(struct vty *vty)
658{
659 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200660 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100661
662 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
663
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200664 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
665 vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
666 vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
667 vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
668 " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
669 for (i = 0; i < vector_active(cmdvec); ++i) {
670 struct cmd_node *cnode;
671 cnode = vector_slot(cmdvec, i);
672 if (!cnode)
673 continue;
674 if (cnode->node != CONFIG_NODE)
675 continue;
676
677 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
678 struct cmd_element *elem;
679 elem = vector_slot(cnode->cmd_vector, j);
680 if (!vty_command_is_common(elem))
681 continue;
Harald Welte23a299f2018-06-09 17:40:52 +0200682 if (!elem->attr & CMD_ATTR_DEPRECATED)
683 vty_dump_element(elem, vty);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200684 }
685 }
686 vty_out(vty, " </node>%s", VTY_NEWLINE);
687
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100688 for (i = 0; i < vector_active(cmdvec); ++i) {
689 struct cmd_node *cnode;
690 cnode = vector_slot(cmdvec, i);
691 if (!cnode)
692 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200693 if (vector_active(cnode->cmd_vector) < 1)
694 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100695
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200696 /* De-dup node IDs: how many times has this same name been used before? Count the first
697 * occurence as _1 and omit that first suffix, so that the first occurence is called
698 * 'name', the second becomes 'name_2', then 'name_3', ... */
699 same_name_count = 1;
700 for (j = 0; j < i; ++j) {
701 struct cmd_node *cnode2;
702 cnode2 = vector_slot(cmdvec, j);
703 if (!cnode2)
704 continue;
705 if (strcmp(cnode->name, cnode2->name) == 0)
706 same_name_count ++;
707 }
708
709 vty_out(vty, " <node id='%s", cnode->name);
710 if (same_name_count > 1 || !*cnode->name)
711 vty_out(vty, "_%d", same_name_count);
712 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200713 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100714
715 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
716 struct cmd_element *elem;
717 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200718 if (vty_command_is_common(elem))
719 continue;
Harald Welte23a299f2018-06-09 17:40:52 +0200720 if (!elem->attr & CMD_ATTR_DEPRECATED)
721 vty_dump_element(elem, vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100722 }
723
724 vty_out(vty, " </node>%s", VTY_NEWLINE);
725 }
726
727 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
728
729 return 0;
730}
731
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200732/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100733static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
734{
735 int i;
736
737 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
738 struct cmd_element *elem;
739 elem = vector_slot(cnode->cmd_vector, i);
740 if (!elem->string)
741 continue;
742 if (!strcmp(elem->string, cmdstring))
743 return 1;
744 }
745 return 0;
746}
747
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200748/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200749 * \param[in] ntype Node Type
750 * \param[cmd] element to be installed
751 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000752void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200753{
754 struct cmd_node *cnode;
755
756 cnode = vector_slot(cmdvec, ntype);
757
Harald Weltea99d45a2015-11-12 13:48:23 +0100758 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100759 /* ensure no _identical_ command has been registered at this
760 * node so far */
761 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200762
763 vector_set(cnode->cmd_vector, cmd);
764
765 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
766 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
767}
768
769/* Install a command into VIEW and ENABLE node */
770void install_element_ve(struct cmd_element *cmd)
771{
772 install_element(VIEW_NODE, cmd);
773 install_element(ENABLE_NODE, cmd);
774}
775
776#ifdef VTY_CRYPT_PW
777static unsigned char itoa64[] =
778 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
779
780static void to64(char *s, long v, int n)
781{
782 while (--n >= 0) {
783 *s++ = itoa64[v & 0x3f];
784 v >>= 6;
785 }
786}
787
788static char *zencrypt(const char *passwd)
789{
790 char salt[6];
791 struct timeval tv;
792 char *crypt(const char *, const char *);
793
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200794 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200795
796 to64(&salt[0], random(), 3);
797 to64(&salt[3], tv.tv_usec, 3);
798 salt[5] = '\0';
799
800 return crypt(passwd, salt);
801}
802#endif
803
804/* This function write configuration of this host. */
805static int config_write_host(struct vty *vty)
806{
807 if (host.name)
808 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
809
810 if (host.encrypt) {
811 if (host.password_encrypt)
812 vty_out(vty, "password 8 %s%s", host.password_encrypt,
813 VTY_NEWLINE);
814 if (host.enable_encrypt)
815 vty_out(vty, "enable password 8 %s%s",
816 host.enable_encrypt, VTY_NEWLINE);
817 } else {
818 if (host.password)
819 vty_out(vty, "password %s%s", host.password,
820 VTY_NEWLINE);
821 if (host.enable)
822 vty_out(vty, "enable password %s%s", host.enable,
823 VTY_NEWLINE);
824 }
825
826 if (host.advanced)
827 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
828
829 if (host.encrypt)
830 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
831
832 if (host.lines >= 0)
833 vty_out(vty, "service terminal-length %d%s", host.lines,
834 VTY_NEWLINE);
835
836 if (host.motdfile)
837 vty_out(vty, "banner motd file %s%s", host.motdfile,
838 VTY_NEWLINE);
839 else if (!host.motd)
840 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
841
842 return 1;
843}
844
845/* Utility function for getting command vector. */
846static vector cmd_node_vector(vector v, enum node_type ntype)
847{
848 struct cmd_node *cnode = vector_slot(v, ntype);
849 return cnode->cmd_vector;
850}
851
852/* Completion match types. */
853enum match_type {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100854 no_match = 0,
855 any_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200856 extend_match,
857 ipv4_prefix_match,
858 ipv4_match,
859 ipv6_prefix_match,
860 ipv6_match,
861 range_match,
862 vararg_match,
863 partly_match,
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100864 exact_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200865};
866
867static enum match_type cmd_ipv4_match(const char *str)
868{
869 const char *sp;
870 int dots = 0, nums = 0;
871 char buf[4];
872
873 if (str == NULL)
874 return partly_match;
875
876 for (;;) {
877 memset(buf, 0, sizeof(buf));
878 sp = str;
879 while (*str != '\0') {
880 if (*str == '.') {
881 if (dots >= 3)
882 return no_match;
883
884 if (*(str + 1) == '.')
885 return no_match;
886
887 if (*(str + 1) == '\0')
888 return partly_match;
889
890 dots++;
891 break;
892 }
893 if (!isdigit((int)*str))
894 return no_match;
895
896 str++;
897 }
898
899 if (str - sp > 3)
900 return no_match;
901
902 strncpy(buf, sp, str - sp);
903 if (atoi(buf) > 255)
904 return no_match;
905
906 nums++;
907
908 if (*str == '\0')
909 break;
910
911 str++;
912 }
913
914 if (nums < 4)
915 return partly_match;
916
917 return exact_match;
918}
919
920static enum match_type cmd_ipv4_prefix_match(const char *str)
921{
922 const char *sp;
923 int dots = 0;
924 char buf[4];
925
926 if (str == NULL)
927 return partly_match;
928
929 for (;;) {
930 memset(buf, 0, sizeof(buf));
931 sp = str;
932 while (*str != '\0' && *str != '/') {
933 if (*str == '.') {
934 if (dots == 3)
935 return no_match;
936
937 if (*(str + 1) == '.' || *(str + 1) == '/')
938 return no_match;
939
940 if (*(str + 1) == '\0')
941 return partly_match;
942
943 dots++;
944 break;
945 }
946
947 if (!isdigit((int)*str))
948 return no_match;
949
950 str++;
951 }
952
953 if (str - sp > 3)
954 return no_match;
955
956 strncpy(buf, sp, str - sp);
957 if (atoi(buf) > 255)
958 return no_match;
959
960 if (dots == 3) {
961 if (*str == '/') {
962 if (*(str + 1) == '\0')
963 return partly_match;
964
965 str++;
966 break;
967 } else if (*str == '\0')
968 return partly_match;
969 }
970
971 if (*str == '\0')
972 return partly_match;
973
974 str++;
975 }
976
977 sp = str;
978 while (*str != '\0') {
979 if (!isdigit((int)*str))
980 return no_match;
981
982 str++;
983 }
984
985 if (atoi(sp) > 32)
986 return no_match;
987
988 return exact_match;
989}
990
991#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
992#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
993#define STATE_START 1
994#define STATE_COLON 2
995#define STATE_DOUBLE 3
996#define STATE_ADDR 4
997#define STATE_DOT 5
998#define STATE_SLASH 6
999#define STATE_MASK 7
1000
1001#ifdef HAVE_IPV6
1002
1003static enum match_type cmd_ipv6_match(const char *str)
1004{
1005 int state = STATE_START;
1006 int colons = 0, nums = 0, double_colon = 0;
1007 const char *sp = NULL;
1008 struct sockaddr_in6 sin6_dummy;
1009 int ret;
1010
1011 if (str == NULL)
1012 return partly_match;
1013
1014 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
1015 return no_match;
1016
1017 /* use inet_pton that has a better support,
1018 * for example inet_pton can support the automatic addresses:
1019 * ::1.2.3.4
1020 */
1021 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1022
1023 if (ret == 1)
1024 return exact_match;
1025
1026 while (*str != '\0') {
1027 switch (state) {
1028 case STATE_START:
1029 if (*str == ':') {
1030 if (*(str + 1) != ':' && *(str + 1) != '\0')
1031 return no_match;
1032 colons--;
1033 state = STATE_COLON;
1034 } else {
1035 sp = str;
1036 state = STATE_ADDR;
1037 }
1038
1039 continue;
1040 case STATE_COLON:
1041 colons++;
1042 if (*(str + 1) == ':')
1043 state = STATE_DOUBLE;
1044 else {
1045 sp = str + 1;
1046 state = STATE_ADDR;
1047 }
1048 break;
1049 case STATE_DOUBLE:
1050 if (double_colon)
1051 return no_match;
1052
1053 if (*(str + 1) == ':')
1054 return no_match;
1055 else {
1056 if (*(str + 1) != '\0')
1057 colons++;
1058 sp = str + 1;
1059 state = STATE_ADDR;
1060 }
1061
1062 double_colon++;
1063 nums++;
1064 break;
1065 case STATE_ADDR:
1066 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1067 if (str - sp > 3)
1068 return no_match;
1069
1070 nums++;
1071 state = STATE_COLON;
1072 }
1073 if (*(str + 1) == '.')
1074 state = STATE_DOT;
1075 break;
1076 case STATE_DOT:
1077 state = STATE_ADDR;
1078 break;
1079 default:
1080 break;
1081 }
1082
1083 if (nums > 8)
1084 return no_match;
1085
1086 if (colons > 7)
1087 return no_match;
1088
1089 str++;
1090 }
1091
1092#if 0
1093 if (nums < 11)
1094 return partly_match;
1095#endif /* 0 */
1096
1097 return exact_match;
1098}
1099
1100static enum match_type cmd_ipv6_prefix_match(const char *str)
1101{
1102 int state = STATE_START;
1103 int colons = 0, nums = 0, double_colon = 0;
1104 int mask;
1105 const char *sp = NULL;
1106 char *endptr = NULL;
1107
1108 if (str == NULL)
1109 return partly_match;
1110
1111 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
1112 return no_match;
1113
1114 while (*str != '\0' && state != STATE_MASK) {
1115 switch (state) {
1116 case STATE_START:
1117 if (*str == ':') {
1118 if (*(str + 1) != ':' && *(str + 1) != '\0')
1119 return no_match;
1120 colons--;
1121 state = STATE_COLON;
1122 } else {
1123 sp = str;
1124 state = STATE_ADDR;
1125 }
1126
1127 continue;
1128 case STATE_COLON:
1129 colons++;
1130 if (*(str + 1) == '/')
1131 return no_match;
1132 else if (*(str + 1) == ':')
1133 state = STATE_DOUBLE;
1134 else {
1135 sp = str + 1;
1136 state = STATE_ADDR;
1137 }
1138 break;
1139 case STATE_DOUBLE:
1140 if (double_colon)
1141 return no_match;
1142
1143 if (*(str + 1) == ':')
1144 return no_match;
1145 else {
1146 if (*(str + 1) != '\0' && *(str + 1) != '/')
1147 colons++;
1148 sp = str + 1;
1149
1150 if (*(str + 1) == '/')
1151 state = STATE_SLASH;
1152 else
1153 state = STATE_ADDR;
1154 }
1155
1156 double_colon++;
1157 nums += 1;
1158 break;
1159 case STATE_ADDR:
1160 if (*(str + 1) == ':' || *(str + 1) == '.'
1161 || *(str + 1) == '\0' || *(str + 1) == '/') {
1162 if (str - sp > 3)
1163 return no_match;
1164
1165 for (; sp <= str; sp++)
1166 if (*sp == '/')
1167 return no_match;
1168
1169 nums++;
1170
1171 if (*(str + 1) == ':')
1172 state = STATE_COLON;
1173 else if (*(str + 1) == '.')
1174 state = STATE_DOT;
1175 else if (*(str + 1) == '/')
1176 state = STATE_SLASH;
1177 }
1178 break;
1179 case STATE_DOT:
1180 state = STATE_ADDR;
1181 break;
1182 case STATE_SLASH:
1183 if (*(str + 1) == '\0')
1184 return partly_match;
1185
1186 state = STATE_MASK;
1187 break;
1188 default:
1189 break;
1190 }
1191
1192 if (nums > 11)
1193 return no_match;
1194
1195 if (colons > 7)
1196 return no_match;
1197
1198 str++;
1199 }
1200
1201 if (state < STATE_MASK)
1202 return partly_match;
1203
1204 mask = strtol(str, &endptr, 10);
1205 if (*endptr != '\0')
1206 return no_match;
1207
1208 if (mask < 0 || mask > 128)
1209 return no_match;
1210
1211/* I don't know why mask < 13 makes command match partly.
1212 Forgive me to make this comments. I Want to set static default route
1213 because of lack of function to originate default in ospf6d; sorry
1214 yasu
1215 if (mask < 13)
1216 return partly_match;
1217*/
1218
1219 return exact_match;
1220}
1221
1222#endif /* HAVE_IPV6 */
1223
1224#define DECIMAL_STRLEN_MAX 10
1225
1226static int cmd_range_match(const char *range, const char *str)
1227{
1228 char *p;
1229 char buf[DECIMAL_STRLEN_MAX + 1];
1230 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001231
1232 if (str == NULL)
1233 return 1;
1234
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001235 if (range[1] == '-') {
1236 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001237
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001238 val = strtol(str, &endptr, 10);
1239 if (*endptr != '\0')
1240 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001241
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001242 range += 2;
1243 p = strchr(range, '-');
1244 if (p == NULL)
1245 return 0;
1246 if (p - range > DECIMAL_STRLEN_MAX)
1247 return 0;
1248 strncpy(buf, range, p - range);
1249 buf[p - range] = '\0';
1250 min = -strtol(buf, &endptr, 10);
1251 if (*endptr != '\0')
1252 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001253
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001254 range = p + 1;
1255 p = strchr(range, '>');
1256 if (p == NULL)
1257 return 0;
1258 if (p - range > DECIMAL_STRLEN_MAX)
1259 return 0;
1260 strncpy(buf, range, p - range);
1261 buf[p - range] = '\0';
1262 max = strtol(buf, &endptr, 10);
1263 if (*endptr != '\0')
1264 return 0;
1265
1266 if (val < min || val > max)
1267 return 0;
1268 } else {
1269 unsigned long min, max, val;
1270
1271 val = strtoul(str, &endptr, 10);
1272 if (*endptr != '\0')
1273 return 0;
1274
1275 range++;
1276 p = strchr(range, '-');
1277 if (p == NULL)
1278 return 0;
1279 if (p - range > DECIMAL_STRLEN_MAX)
1280 return 0;
1281 strncpy(buf, range, p - range);
1282 buf[p - range] = '\0';
1283 min = strtoul(buf, &endptr, 10);
1284 if (*endptr != '\0')
1285 return 0;
1286
1287 range = p + 1;
1288 p = strchr(range, '>');
1289 if (p == NULL)
1290 return 0;
1291 if (p - range > DECIMAL_STRLEN_MAX)
1292 return 0;
1293 strncpy(buf, range, p - range);
1294 buf[p - range] = '\0';
1295 max = strtoul(buf, &endptr, 10);
1296 if (*endptr != '\0')
1297 return 0;
1298
1299 if (val < min || val > max)
1300 return 0;
1301 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302
1303 return 1;
1304}
1305
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001306/* helper to retrieve the 'real' argument string from an optional argument */
1307static char *
1308cmd_deopt(const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001309{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001310 /* we've got "[blah]". We want to strip off the []s and redo the
1311 * match check for "blah"
1312 */
1313 size_t len = strlen(str);
1314 char *tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001315
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001316 if (len < 3)
1317 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001318
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001319 /* tmp will hold a string of len-2 chars, so 'len' size is fine */
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +02001320 tmp = talloc_size(tall_vty_cmd_ctx, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001321
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001322 memcpy(tmp, (str + 1), len - 2);
1323 tmp[len - 2] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001324
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001325 return tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001326}
1327
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001328static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001329cmd_match(const char *str, const char *command,
1330 enum match_type min, bool recur)
1331{
1332
1333 if (recur && CMD_OPTION(str))
1334 {
1335 enum match_type ret;
1336 char *tmp = cmd_deopt(str);
1337
1338 /* this would be a bug in a command, however handle it gracefully
1339 * as it we only discover it if a user tries to run it
1340 */
1341 if (tmp == NULL)
1342 return no_match;
1343
1344 ret = cmd_match(tmp, command, min, false);
1345
1346 talloc_free(tmp);
1347
1348 return ret;
1349 }
1350 else if (CMD_VARARG(str))
1351 return vararg_match;
1352 else if (CMD_RANGE(str))
1353 {
1354 if (cmd_range_match(str, command))
1355 return range_match;
1356 }
1357#ifdef HAVE_IPV6
1358 else if (CMD_IPV6(str))
1359 {
1360 if (cmd_ipv6_match(command) >= min)
1361 return ipv6_match;
1362 }
1363 else if (CMD_IPV6_PREFIX(str))
1364 {
1365 if (cmd_ipv6_prefix_match(command) >= min)
1366 return ipv6_prefix_match;
1367 }
1368#endif /* HAVE_IPV6 */
1369 else if (CMD_IPV4(str))
1370 {
1371 if (cmd_ipv4_match(command) >= min)
1372 return ipv4_match;
1373 }
1374 else if (CMD_IPV4_PREFIX(str))
1375 {
1376 if (cmd_ipv4_prefix_match(command) >= min)
1377 return ipv4_prefix_match;
1378 }
1379 else if (CMD_VARIABLE(str))
1380 return extend_match;
1381 else if (strncmp(command, str, strlen(command)) == 0)
1382 {
1383 if (strcmp(command, str) == 0)
1384 return exact_match;
1385 else if (partly_match >= min)
1386 return partly_match;
1387 }
1388
1389 return no_match;
1390}
1391
1392/* Filter vector at the specified index and by the given command string, to
1393 * the desired matching level (thus allowing part matches), and return match
1394 * type flag.
1395 */
1396static enum match_type
1397cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001398{
1399 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001400 struct cmd_element *cmd_element;
1401 enum match_type match_type;
1402 vector descvec;
1403 struct desc *desc;
1404
1405 match_type = no_match;
1406
1407 /* If command and cmd_element string does not match set NULL to vector */
1408 for (i = 0; i < vector_active(v); i++)
1409 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001410 if (index >= vector_active(cmd_element->strvec))
1411 vector_slot(v, i) = NULL;
1412 else {
1413 unsigned int j;
1414 int matched = 0;
1415
1416 descvec =
1417 vector_slot(cmd_element->strvec, index);
1418
1419 for (j = 0; j < vector_active(descvec); j++)
1420 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001421 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001422
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001423 ret = cmd_match (desc->cmd, command, level, true);
1424
1425 if (ret != no_match)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001426 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001427
1428 if (match_type < ret)
1429 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001430 }
1431 if (!matched)
1432 vector_slot(v, i) = NULL;
1433 }
1434 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001435
1436 if (match_type == no_match)
1437 return no_match;
1438
1439 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1440 * go again and filter out commands whose argument (at this index) is
1441 * 'weaker'. E.g., if we have 2 commands:
1442 *
1443 * foo bar <1-255>
1444 * foo bar BLAH
1445 *
1446 * and the command string is 'foo bar 10', then we will get here with with
1447 * 'range_match' being the strongest match. However, if 'BLAH' came
1448 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1449 *
1450 * If we don't do a 2nd pass and filter it out, the higher-layers will
1451 * consider this to be ambiguous.
1452 */
1453 for (i = 0; i < vector_active(v); i++)
1454 if ((cmd_element = vector_slot(v, i)) != NULL) {
1455 if (index >= vector_active(cmd_element->strvec))
1456 vector_slot(v, i) = NULL;
1457 else {
1458 unsigned int j;
1459 int matched = 0;
1460
1461 descvec =
1462 vector_slot(cmd_element->strvec, index);
1463
1464 for (j = 0; j < vector_active(descvec); j++)
1465 if ((desc = vector_slot(descvec, j))) {
1466 enum match_type ret;
1467
1468 ret = cmd_match(desc->cmd, command, any_match, true);
1469
1470 if (ret >= match_type)
1471 matched++;
1472 }
1473 if (!matched)
1474 vector_slot(v, i) = NULL;
1475 }
1476 }
1477
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001478 return match_type;
1479}
1480
1481/* Check ambiguous match */
1482static int
1483is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1484{
1485 unsigned int i;
1486 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487 struct cmd_element *cmd_element;
1488 const char *matched = NULL;
1489 vector descvec;
1490 struct desc *desc;
1491
1492 for (i = 0; i < vector_active(v); i++)
1493 if ((cmd_element = vector_slot(v, i)) != NULL) {
1494 int match = 0;
1495
1496 descvec = vector_slot(cmd_element->strvec, index);
1497
1498 for (j = 0; j < vector_active(descvec); j++)
1499 if ((desc = vector_slot(descvec, j))) {
1500 enum match_type ret;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001501 const char *str = desc->cmd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001502
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001503 if (CMD_OPTION(str))
1504 if ((str = cmd_deopt(str)) == NULL)
1505 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001506
1507 switch (type) {
1508 case exact_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001509 if (!(CMD_VARIABLE (str))
1510 && strcmp(command, str) == 0)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001511 match++;
1512 break;
1513 case partly_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001514 if (!(CMD_VARIABLE (str))
1515 && strncmp(command, str, strlen (command)) == 0)
1516 {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001517 if (matched
1518 && strcmp(matched,
1519 str) != 0)
1520 return 1; /* There is ambiguous match. */
1521 else
1522 matched = str;
1523 match++;
1524 }
1525 break;
1526 case range_match:
1527 if (cmd_range_match
1528 (str, command)) {
1529 if (matched
1530 && strcmp(matched,
1531 str) != 0)
1532 return 1;
1533 else
1534 matched = str;
1535 match++;
1536 }
1537 break;
1538#ifdef HAVE_IPV6
1539 case ipv6_match:
1540 if (CMD_IPV6(str))
1541 match++;
1542 break;
1543 case ipv6_prefix_match:
1544 if ((ret =
1545 cmd_ipv6_prefix_match
1546 (command)) != no_match) {
1547 if (ret == partly_match)
1548 return 2; /* There is incomplete match. */
1549
1550 match++;
1551 }
1552 break;
1553#endif /* HAVE_IPV6 */
1554 case ipv4_match:
1555 if (CMD_IPV4(str))
1556 match++;
1557 break;
1558 case ipv4_prefix_match:
1559 if ((ret =
1560 cmd_ipv4_prefix_match
1561 (command)) != no_match) {
1562 if (ret == partly_match)
1563 return 2; /* There is incomplete match. */
1564
1565 match++;
1566 }
1567 break;
1568 case extend_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001569 if (CMD_VARIABLE (str))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001570 match++;
1571 break;
1572 case no_match:
1573 default:
1574 break;
1575 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001576
1577 if (CMD_OPTION(desc->cmd))
1578 talloc_free((void*)str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001579 }
1580 if (!match)
1581 vector_slot(v, i) = NULL;
1582 }
1583 return 0;
1584}
1585
1586/* If src matches dst return dst string, otherwise return NULL */
1587static const char *cmd_entry_function(const char *src, const char *dst)
1588{
1589 /* Skip variable arguments. */
1590 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1591 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1592 return NULL;
1593
1594 /* In case of 'command \t', given src is NULL string. */
1595 if (src == NULL)
1596 return dst;
1597
1598 /* Matched with input string. */
1599 if (strncmp(src, dst, strlen(src)) == 0)
1600 return dst;
1601
1602 return NULL;
1603}
1604
1605/* If src matches dst return dst string, otherwise return NULL */
1606/* This version will return the dst string always if it is
1607 CMD_VARIABLE for '?' key processing */
1608static const char *cmd_entry_function_desc(const char *src, const char *dst)
1609{
1610 if (CMD_VARARG(dst))
1611 return dst;
1612
1613 if (CMD_RANGE(dst)) {
1614 if (cmd_range_match(dst, src))
1615 return dst;
1616 else
1617 return NULL;
1618 }
1619#ifdef HAVE_IPV6
1620 if (CMD_IPV6(dst)) {
1621 if (cmd_ipv6_match(src))
1622 return dst;
1623 else
1624 return NULL;
1625 }
1626
1627 if (CMD_IPV6_PREFIX(dst)) {
1628 if (cmd_ipv6_prefix_match(src))
1629 return dst;
1630 else
1631 return NULL;
1632 }
1633#endif /* HAVE_IPV6 */
1634
1635 if (CMD_IPV4(dst)) {
1636 if (cmd_ipv4_match(src))
1637 return dst;
1638 else
1639 return NULL;
1640 }
1641
1642 if (CMD_IPV4_PREFIX(dst)) {
1643 if (cmd_ipv4_prefix_match(src))
1644 return dst;
1645 else
1646 return NULL;
1647 }
1648
1649 /* Optional or variable commands always match on '?' */
1650 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1651 return dst;
1652
1653 /* In case of 'command \t', given src is NULL string. */
1654 if (src == NULL)
1655 return dst;
1656
1657 if (strncmp(src, dst, strlen(src)) == 0)
1658 return dst;
1659 else
1660 return NULL;
1661}
1662
1663/* Check same string element existence. If it isn't there return
1664 1. */
1665static int cmd_unique_string(vector v, const char *str)
1666{
1667 unsigned int i;
1668 char *match;
1669
1670 for (i = 0; i < vector_active(v); i++)
1671 if ((match = vector_slot(v, i)) != NULL)
1672 if (strcmp(match, str) == 0)
1673 return 0;
1674 return 1;
1675}
1676
1677/* Compare string to description vector. If there is same string
1678 return 1 else return 0. */
1679static int desc_unique_string(vector v, const char *str)
1680{
1681 unsigned int i;
1682 struct desc *desc;
1683
1684 for (i = 0; i < vector_active(v); i++)
1685 if ((desc = vector_slot(v, i)) != NULL)
1686 if (strcmp(desc->cmd, str) == 0)
1687 return 1;
1688 return 0;
1689}
1690
1691static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1692{
1693 if (first_word != NULL &&
1694 node != AUTH_NODE &&
1695 node != VIEW_NODE &&
1696 node != AUTH_ENABLE_NODE &&
1697 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1698 return 1;
1699 return 0;
1700}
1701
1702/* '?' describe command support. */
1703static vector
1704cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1705{
1706 unsigned int i;
1707 vector cmd_vector;
1708#define INIT_MATCHVEC_SIZE 10
1709 vector matchvec;
1710 struct cmd_element *cmd_element;
1711 unsigned int index;
1712 int ret;
1713 enum match_type match;
1714 char *command;
1715 static struct desc desc_cr = { "<cr>", "" };
1716
1717 /* Set index. */
1718 if (vector_active(vline) == 0) {
1719 *status = CMD_ERR_NO_MATCH;
1720 return NULL;
1721 } else
1722 index = vector_active(vline) - 1;
1723
1724 /* Make copy vector of current node's command vector. */
1725 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1726
1727 /* Prepare match vector */
1728 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1729
1730 /* Filter commands. */
1731 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001732 for (i = 0; i < index; i++) {
1733 command = vector_slot(vline, i);
1734 if (!command)
1735 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001736
Harald Welte80d30fe2013-02-12 11:08:57 +01001737 match = cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001738
Harald Welte80d30fe2013-02-12 11:08:57 +01001739 if (match == vararg_match) {
1740 struct cmd_element *cmd_element;
1741 vector descvec;
1742 unsigned int j, k;
1743
1744 for (j = 0; j < vector_active(cmd_vector); j++)
1745 if ((cmd_element =
1746 vector_slot(cmd_vector, j)) != NULL
1747 &&
1748 (vector_active(cmd_element->strvec))) {
1749 descvec =
1750 vector_slot(cmd_element->
1751 strvec,
1752 vector_active
1753 (cmd_element->
1754 strvec) - 1);
1755 for (k = 0;
1756 k < vector_active(descvec);
1757 k++) {
1758 struct desc *desc =
1759 vector_slot(descvec,
1760 k);
1761 vector_set(matchvec,
1762 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001763 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001764 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001765
Harald Welte80d30fe2013-02-12 11:08:57 +01001766 vector_set(matchvec, &desc_cr);
1767 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001768
Harald Welte80d30fe2013-02-12 11:08:57 +01001769 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001770 }
1771
Harald Welte80d30fe2013-02-12 11:08:57 +01001772 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1773 match)) == 1) {
1774 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001775 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001776 *status = CMD_ERR_AMBIGUOUS;
1777 return NULL;
1778 } else if (ret == 2) {
1779 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001780 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001781 *status = CMD_ERR_NO_MATCH;
1782 return NULL;
1783 }
1784 }
1785
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001786 /* Prepare match vector */
1787 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1788
1789 /* Make sure that cmd_vector is filtered based on current word */
1790 command = vector_slot(vline, index);
1791 if (command)
Vadim Yanitskiy49a0dec2017-06-12 03:49:38 +07001792 cmd_filter(command, cmd_vector, index, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001793
1794 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001795 for (i = 0; i < vector_active(cmd_vector); i++) {
1796 const char *string = NULL;
1797 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001798
Harald Welte80d30fe2013-02-12 11:08:57 +01001799 cmd_element = vector_slot(cmd_vector, i);
1800 if (!cmd_element)
1801 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001802
Harald Welted17aa592013-02-12 11:11:34 +01001803 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1804 continue;
1805
Harald Welte80d30fe2013-02-12 11:08:57 +01001806 strvec = cmd_element->strvec;
1807
1808 /* if command is NULL, index may be equal to vector_active */
1809 if (command && index >= vector_active(strvec))
1810 vector_slot(cmd_vector, i) = NULL;
1811 else {
1812 /* Check if command is completed. */
1813 if (command == NULL
1814 && index == vector_active(strvec)) {
1815 string = "<cr>";
1816 if (!desc_unique_string(matchvec, string))
1817 vector_set(matchvec, &desc_cr);
1818 } else {
1819 unsigned int j;
1820 vector descvec = vector_slot(strvec, index);
1821 struct desc *desc;
1822
1823 for (j = 0; j < vector_active(descvec); j++) {
1824 desc = vector_slot(descvec, j);
1825 if (!desc)
1826 continue;
1827 string = cmd_entry_function_desc
1828 (command, desc->cmd);
1829 if (!string)
1830 continue;
1831 /* Uniqueness check */
1832 if (!desc_unique_string(matchvec, string))
1833 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001834 }
1835 }
1836 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001837 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001838 vector_free(cmd_vector);
1839
1840 if (vector_slot(matchvec, 0) == NULL) {
1841 vector_free(matchvec);
1842 *status = CMD_ERR_NO_MATCH;
1843 } else
1844 *status = CMD_SUCCESS;
1845
1846 return matchvec;
1847}
1848
1849vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1850{
1851 vector ret;
1852
1853 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1854 enum node_type onode;
1855 vector shifted_vline;
1856 unsigned int index;
1857
1858 onode = vty->node;
1859 vty->node = ENABLE_NODE;
1860 /* We can try it on enable node, cos' the vty is authenticated */
1861
1862 shifted_vline = vector_init(vector_count(vline));
1863 /* use memcpy? */
1864 for (index = 1; index < vector_active(vline); index++) {
1865 vector_set_index(shifted_vline, index - 1,
1866 vector_lookup(vline, index));
1867 }
1868
1869 ret = cmd_describe_command_real(shifted_vline, vty, status);
1870
1871 vector_free(shifted_vline);
1872 vty->node = onode;
1873 return ret;
1874 }
1875
1876 return cmd_describe_command_real(vline, vty, status);
1877}
1878
1879/* Check LCD of matched command. */
1880static int cmd_lcd(char **matched)
1881{
1882 int i;
1883 int j;
1884 int lcd = -1;
1885 char *s1, *s2;
1886 char c1, c2;
1887
1888 if (matched[0] == NULL || matched[1] == NULL)
1889 return 0;
1890
1891 for (i = 1; matched[i] != NULL; i++) {
1892 s1 = matched[i - 1];
1893 s2 = matched[i];
1894
1895 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1896 if (c1 != c2)
1897 break;
1898
1899 if (lcd < 0)
1900 lcd = j;
1901 else {
1902 if (lcd > j)
1903 lcd = j;
1904 }
1905 }
1906 return lcd;
1907}
1908
1909/* Command line completion support. */
1910static char **cmd_complete_command_real(vector vline, struct vty *vty,
1911 int *status)
1912{
1913 unsigned int i;
1914 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1915#define INIT_MATCHVEC_SIZE 10
1916 vector matchvec;
1917 struct cmd_element *cmd_element;
1918 unsigned int index;
1919 char **match_str;
1920 struct desc *desc;
1921 vector descvec;
1922 char *command;
1923 int lcd;
1924
1925 if (vector_active(vline) == 0) {
1926 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001927 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001928 return NULL;
1929 } else
1930 index = vector_active(vline) - 1;
1931
1932 /* First, filter by preceeding command string */
1933 for (i = 0; i < index; i++)
1934 if ((command = vector_slot(vline, i))) {
1935 enum match_type match;
1936 int ret;
1937
1938 /* First try completion match, if there is exactly match return 1 */
1939 match =
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001940 cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001941
1942 /* If there is exact match then filter ambiguous match else check
1943 ambiguousness. */
1944 if ((ret =
1945 is_cmd_ambiguous(command, cmd_vector, i,
1946 match)) == 1) {
1947 vector_free(cmd_vector);
1948 *status = CMD_ERR_AMBIGUOUS;
1949 return NULL;
1950 }
1951 /*
1952 else if (ret == 2)
1953 {
1954 vector_free (cmd_vector);
1955 *status = CMD_ERR_NO_MATCH;
1956 return NULL;
1957 }
1958 */
1959 }
1960
1961 /* Prepare match vector. */
1962 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1963
1964 /* Now we got into completion */
1965 for (i = 0; i < vector_active(cmd_vector); i++)
1966 if ((cmd_element = vector_slot(cmd_vector, i))) {
1967 const char *string;
1968 vector strvec = cmd_element->strvec;
1969
1970 /* Check field length */
1971 if (index >= vector_active(strvec))
1972 vector_slot(cmd_vector, i) = NULL;
1973 else {
1974 unsigned int j;
1975
1976 descvec = vector_slot(strvec, index);
1977 for (j = 0; j < vector_active(descvec); j++)
1978 if ((desc = vector_slot(descvec, j))) {
1979 if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
1980 if (cmd_unique_string (matchvec, string))
1981 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
1982 }
1983 }
1984 }
1985
1986 /* We don't need cmd_vector any more. */
1987 vector_free(cmd_vector);
1988
1989 /* No matched command */
1990 if (vector_slot(matchvec, 0) == NULL) {
1991 vector_free(matchvec);
1992
1993 /* In case of 'command \t' pattern. Do you need '?' command at
1994 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01001995 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001996 *status = CMD_ERR_NOTHING_TODO;
1997 else
1998 *status = CMD_ERR_NO_MATCH;
1999 return NULL;
2000 }
2001
2002 /* Only one matched */
2003 if (vector_slot(matchvec, 1) == NULL) {
2004 match_str = (char **)matchvec->index;
2005 vector_only_wrapper_free(matchvec);
2006 *status = CMD_COMPLETE_FULL_MATCH;
2007 return match_str;
2008 }
2009 /* Make it sure last element is NULL. */
2010 vector_set(matchvec, NULL);
2011
2012 /* Check LCD of matched strings. */
2013 if (vector_slot(vline, index) != NULL) {
2014 lcd = cmd_lcd((char **)matchvec->index);
2015
2016 if (lcd) {
2017 int len = strlen(vector_slot(vline, index));
2018
2019 if (len < lcd) {
2020 char *lcdstr;
2021
2022 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2023 "complete-lcdstr");
2024 memcpy(lcdstr, matchvec->index[0], lcd);
2025 lcdstr[lcd] = '\0';
2026
2027 /* match_str = (char **) &lcdstr; */
2028
2029 /* Free matchvec. */
2030 for (i = 0; i < vector_active(matchvec); i++) {
2031 if (vector_slot(matchvec, i))
2032 talloc_free(vector_slot(matchvec, i));
2033 }
2034 vector_free(matchvec);
2035
2036 /* Make new matchvec. */
2037 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2038 vector_set(matchvec, lcdstr);
2039 match_str = (char **)matchvec->index;
2040 vector_only_wrapper_free(matchvec);
2041
2042 *status = CMD_COMPLETE_MATCH;
2043 return match_str;
2044 }
2045 }
2046 }
2047
2048 match_str = (char **)matchvec->index;
2049 vector_only_wrapper_free(matchvec);
2050 *status = CMD_COMPLETE_LIST_MATCH;
2051 return match_str;
2052}
2053
2054char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2055{
2056 char **ret;
2057
2058 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2059 enum node_type onode;
2060 vector shifted_vline;
2061 unsigned int index;
2062
2063 onode = vty->node;
2064 vty->node = ENABLE_NODE;
2065 /* We can try it on enable node, cos' the vty is authenticated */
2066
2067 shifted_vline = vector_init(vector_count(vline));
2068 /* use memcpy? */
2069 for (index = 1; index < vector_active(vline); index++) {
2070 vector_set_index(shifted_vline, index - 1,
2071 vector_lookup(vline, index));
2072 }
2073
2074 ret = cmd_complete_command_real(shifted_vline, vty, status);
2075
2076 vector_free(shifted_vline);
2077 vty->node = onode;
2078 return ret;
2079 }
2080
2081 return cmd_complete_command_real(vline, vty, status);
2082}
2083
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002084static struct vty_parent_node *vty_parent(struct vty *vty)
2085{
2086 return llist_first_entry_or_null(&vty->parent_nodes,
2087 struct vty_parent_node,
2088 entry);
2089}
2090
2091static bool vty_pop_parent(struct vty *vty)
2092{
2093 struct vty_parent_node *parent = vty_parent(vty);
2094 if (!parent)
2095 return false;
2096 llist_del(&parent->entry);
2097 vty->node = parent->node;
2098 vty->priv = parent->priv;
2099 if (vty->indent)
2100 talloc_free(vty->indent);
2101 vty->indent = parent->indent;
2102 talloc_free(parent);
2103 return true;
2104}
2105
2106static void vty_clear_parents(struct vty *vty)
2107{
2108 while (vty_pop_parent(vty));
2109}
2110
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002111/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002112/*
2113 * This function MUST eventually converge on a node when called repeatedly,
2114 * there must not be any cycles.
2115 * All 'config' nodes shall converge on CONFIG_NODE.
2116 * All other 'enable' nodes shall converge on ENABLE_NODE.
2117 * All 'view' only nodes shall converge on VIEW_NODE.
2118 * All other nodes shall converge on themselves or it must be ensured,
2119 * that the user's rights are not extended anyhow by calling this function.
2120 *
2121 * Note that these requirements also apply to all functions that are used
2122 * as go_parent_cb.
2123 * Note also that this function relies on the is_config_child callback to
2124 * recognize non-config nodes if go_parent_cb is not set.
2125 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002126int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002127{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002128 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002129 case AUTH_NODE:
2130 case VIEW_NODE:
2131 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002132 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002133 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002134 break;
2135
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002136 case AUTH_ENABLE_NODE:
2137 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002138 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002139 break;
2140
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002141 case CFG_LOG_NODE:
2142 case VTY_NODE:
2143 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002144 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002145 break;
2146
2147 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002148 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002149 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002150 vty_pop_parent(vty);
2151 }
2152 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002153 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002154 vty_clear_parents(vty);
2155 }
2156 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002157 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002158 vty_clear_parents(vty);
2159 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002160 break;
2161 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002162
2163 return vty->node;
2164}
2165
2166/* Execute command by argument vline vector. */
2167static int
2168cmd_execute_command_real(vector vline, struct vty *vty,
2169 struct cmd_element **cmd)
2170{
2171 unsigned int i;
2172 unsigned int index;
2173 vector cmd_vector;
2174 struct cmd_element *cmd_element;
2175 struct cmd_element *matched_element;
2176 unsigned int matched_count, incomplete_count;
2177 int argc;
2178 const char *argv[CMD_ARGC_MAX];
2179 enum match_type match = 0;
2180 int varflag;
2181 char *command;
2182
2183 /* Make copy of command elements. */
2184 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2185
2186 for (index = 0; index < vector_active(vline); index++)
2187 if ((command = vector_slot(vline, index))) {
2188 int ret;
2189
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002190 match = cmd_filter(command, cmd_vector, index,
2191 any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002192
2193 if (match == vararg_match)
2194 break;
2195
2196 ret =
2197 is_cmd_ambiguous(command, cmd_vector, index, match);
2198
2199 if (ret == 1) {
2200 vector_free(cmd_vector);
2201 return CMD_ERR_AMBIGUOUS;
2202 } else if (ret == 2) {
2203 vector_free(cmd_vector);
2204 return CMD_ERR_NO_MATCH;
2205 }
2206 }
2207
2208 /* Check matched count. */
2209 matched_element = NULL;
2210 matched_count = 0;
2211 incomplete_count = 0;
2212
2213 for (i = 0; i < vector_active(cmd_vector); i++)
2214 if ((cmd_element = vector_slot(cmd_vector, i))) {
2215 if (match == vararg_match
2216 || index >= cmd_element->cmdsize) {
2217 matched_element = cmd_element;
2218#if 0
2219 printf("DEBUG: %s\n", cmd_element->string);
2220#endif
2221 matched_count++;
2222 } else {
2223 incomplete_count++;
2224 }
2225 }
2226
2227 /* Finish of using cmd_vector. */
2228 vector_free(cmd_vector);
2229
2230 /* To execute command, matched_count must be 1. */
2231 if (matched_count == 0) {
2232 if (incomplete_count)
2233 return CMD_ERR_INCOMPLETE;
2234 else
2235 return CMD_ERR_NO_MATCH;
2236 }
2237
2238 if (matched_count > 1)
2239 return CMD_ERR_AMBIGUOUS;
2240
2241 /* Argument treatment */
2242 varflag = 0;
2243 argc = 0;
2244
2245 for (i = 0; i < vector_active(vline); i++) {
2246 if (varflag)
2247 argv[argc++] = vector_slot(vline, i);
2248 else {
2249 vector descvec =
2250 vector_slot(matched_element->strvec, i);
2251
2252 if (vector_active(descvec) == 1) {
2253 struct desc *desc = vector_slot(descvec, 0);
2254
2255 if (CMD_VARARG(desc->cmd))
2256 varflag = 1;
2257
2258 if (varflag || CMD_VARIABLE(desc->cmd)
2259 || CMD_OPTION(desc->cmd))
2260 argv[argc++] = vector_slot(vline, i);
2261 } else
2262 argv[argc++] = vector_slot(vline, i);
2263 }
2264
2265 if (argc >= CMD_ARGC_MAX)
2266 return CMD_ERR_EXEED_ARGC_MAX;
2267 }
2268
2269 /* For vtysh execution. */
2270 if (cmd)
2271 *cmd = matched_element;
2272
2273 if (matched_element->daemon)
2274 return CMD_SUCCESS_DAEMON;
2275
2276 /* Execute matched command. */
2277 return (*matched_element->func) (matched_element, vty, argc, argv);
2278}
2279
2280int
2281cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2282 int vtysh)
2283{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002284 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002285 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002286
2287 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002288
2289 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2290 vector shifted_vline;
2291 unsigned int index;
2292
2293 vty->node = ENABLE_NODE;
2294 /* We can try it on enable node, cos' the vty is authenticated */
2295
2296 shifted_vline = vector_init(vector_count(vline));
2297 /* use memcpy? */
2298 for (index = 1; index < vector_active(vline); index++) {
2299 vector_set_index(shifted_vline, index - 1,
2300 vector_lookup(vline, index));
2301 }
2302
2303 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2304
2305 vector_free(shifted_vline);
2306 vty->node = onode;
2307 return ret;
2308 }
2309
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002310 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002311}
2312
2313/* Execute command by argument readline. */
2314int
2315cmd_execute_command_strict(vector vline, struct vty *vty,
2316 struct cmd_element **cmd)
2317{
2318 unsigned int i;
2319 unsigned int index;
2320 vector cmd_vector;
2321 struct cmd_element *cmd_element;
2322 struct cmd_element *matched_element;
2323 unsigned int matched_count, incomplete_count;
2324 int argc;
2325 const char *argv[CMD_ARGC_MAX];
2326 int varflag;
2327 enum match_type match = 0;
2328 char *command;
2329
2330 /* Make copy of command element */
2331 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2332
2333 for (index = 0; index < vector_active(vline); index++)
2334 if ((command = vector_slot(vline, index))) {
2335 int ret;
2336
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002337 match = cmd_filter(vector_slot(vline, index),
2338 cmd_vector, index, exact_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002339
2340 /* If command meets '.VARARG' then finish matching. */
2341 if (match == vararg_match)
2342 break;
2343
2344 ret =
2345 is_cmd_ambiguous(command, cmd_vector, index, match);
2346 if (ret == 1) {
2347 vector_free(cmd_vector);
2348 return CMD_ERR_AMBIGUOUS;
2349 }
2350 if (ret == 2) {
2351 vector_free(cmd_vector);
2352 return CMD_ERR_NO_MATCH;
2353 }
2354 }
2355
2356 /* Check matched count. */
2357 matched_element = NULL;
2358 matched_count = 0;
2359 incomplete_count = 0;
2360 for (i = 0; i < vector_active(cmd_vector); i++)
2361 if (vector_slot(cmd_vector, i) != NULL) {
2362 cmd_element = vector_slot(cmd_vector, i);
2363
2364 if (match == vararg_match
2365 || index >= cmd_element->cmdsize) {
2366 matched_element = cmd_element;
2367 matched_count++;
2368 } else
2369 incomplete_count++;
2370 }
2371
2372 /* Finish of using cmd_vector. */
2373 vector_free(cmd_vector);
2374
2375 /* To execute command, matched_count must be 1. */
2376 if (matched_count == 0) {
2377 if (incomplete_count)
2378 return CMD_ERR_INCOMPLETE;
2379 else
2380 return CMD_ERR_NO_MATCH;
2381 }
2382
2383 if (matched_count > 1)
2384 return CMD_ERR_AMBIGUOUS;
2385
2386 /* Argument treatment */
2387 varflag = 0;
2388 argc = 0;
2389
2390 for (i = 0; i < vector_active(vline); i++) {
2391 if (varflag)
2392 argv[argc++] = vector_slot(vline, i);
2393 else {
2394 vector descvec =
2395 vector_slot(matched_element->strvec, i);
2396
2397 if (vector_active(descvec) == 1) {
2398 struct desc *desc = vector_slot(descvec, 0);
2399
2400 if (CMD_VARARG(desc->cmd))
2401 varflag = 1;
2402
2403 if (varflag || CMD_VARIABLE(desc->cmd)
2404 || CMD_OPTION(desc->cmd))
2405 argv[argc++] = vector_slot(vline, i);
2406 } else
2407 argv[argc++] = vector_slot(vline, i);
2408 }
2409
2410 if (argc >= CMD_ARGC_MAX)
2411 return CMD_ERR_EXEED_ARGC_MAX;
2412 }
2413
2414 /* For vtysh execution. */
2415 if (cmd)
2416 *cmd = matched_element;
2417
2418 if (matched_element->daemon)
2419 return CMD_SUCCESS_DAEMON;
2420
2421 /* Now execute matched command */
2422 return (*matched_element->func) (matched_element, vty, argc, argv);
2423}
2424
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002425static inline size_t len(const char *str)
2426{
2427 return str? strlen(str) : 0;
2428}
2429
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002430/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2431 * is longer than b, a must start with exactly b, and vice versa.
2432 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2433 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002434static int indent_cmp(const char *a, const char *b)
2435{
2436 size_t al, bl;
2437 al = len(a);
2438 bl = len(b);
2439 if (al > bl) {
2440 if (bl && strncmp(a, b, bl) != 0)
2441 return EINVAL;
2442 return 1;
2443 }
2444 /* al <= bl */
2445 if (al && strncmp(a, b, al) != 0)
2446 return EINVAL;
2447 return (al < bl)? -1 : 0;
2448}
2449
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002450/* Configration make from file. */
2451int config_from_file(struct vty *vty, FILE * fp)
2452{
2453 int ret;
2454 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002455 char *indent;
2456 int cmp;
2457 struct vty_parent_node this_node;
2458 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002459
2460 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002461 indent = NULL;
2462 vline = NULL;
2463 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002464
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002465 if (ret != CMD_SUCCESS)
2466 goto return_invalid_indent;
2467
2468 /* In case of comment or empty line */
2469 if (vline == NULL) {
2470 if (indent) {
2471 talloc_free(indent);
2472 indent = NULL;
2473 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002474 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002475 }
2476
Neels Hofmeyr43063632017-09-19 23:54:01 +02002477 /* We have a nonempty line. */
2478 if (!vty->indent) {
2479 /* We have just entered a node and expecting the first child to come up; but we
2480 * may also skip right back to a parent or ancestor level. */
2481 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002482
Neels Hofmeyr43063632017-09-19 23:54:01 +02002483 /* If there is no parent, record any indentation we encounter. */
2484 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2485
2486 if (cmp == EINVAL)
2487 goto return_invalid_indent;
2488
2489 if (cmp <= 0) {
2490 /* We have gone right back to the parent level or higher, we are skipping
2491 * this child node level entirely. Pop the parent to go back to a node
2492 * that was actually there (to reinstate vty->indent) and re-use below
2493 * go-parent while-loop to find an accurate match of indent in the node
2494 * ancestry. */
2495 vty_go_parent(vty);
2496 } else {
2497 /* The indent is deeper than the just entered parent, record the new
2498 * indentation characters. */
2499 vty->indent = talloc_strdup(vty, indent);
2500 /* This *is* the new indentation. */
2501 cmp = 0;
2502 }
2503 } else {
2504 /* There is a known indentation for this node level, validate and detect node
2505 * exits. */
2506 cmp = indent_cmp(indent, vty->indent);
2507 if (cmp == EINVAL)
2508 goto return_invalid_indent;
2509 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002510
2511 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2512 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2513 while (cmp < 0) {
2514 vty_go_parent(vty);
2515 cmp = indent_cmp(indent, vty->indent);
2516 if (cmp == EINVAL)
2517 goto return_invalid_indent;
2518 }
2519
2520 /* More indent without having entered a child node level? Either the parent node's indent
2521 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2522 * or the indentation increased even though the vty command didn't enter a child. */
2523 if (cmp > 0)
2524 goto return_invalid_indent;
2525
2526 /* Remember the current node before the command possibly changes it. */
2527 this_node = (struct vty_parent_node){
2528 .node = vty->node,
2529 .priv = vty->priv,
2530 .indent = vty->indent,
2531 };
2532
2533 parent = vty_parent(vty);
2534 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002535 cmd_free_strvec(vline);
2536
2537 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002538 && ret != CMD_ERR_NOTHING_TODO) {
2539 if (indent) {
2540 talloc_free(indent);
2541 indent = NULL;
2542 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002543 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002544 }
2545
2546 /* If we have stepped down into a child node, push a parent frame.
2547 * The causality is such: we don't expect every single node entry implementation to push
2548 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2549 * a parent node. Hence if the node changed without the parent node changing, we must
2550 * have stepped into a child node (and now expect a deeper indent). */
2551 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2552 /* Push the parent node. */
2553 parent = talloc_zero(vty, struct vty_parent_node);
2554 *parent = this_node;
2555 llist_add(&parent->entry, &vty->parent_nodes);
2556
2557 /* The current talloc'ed vty->indent string will now be owned by this parent
2558 * struct. Indicate that we don't know what deeper indent characters the user
2559 * will choose. */
2560 vty->indent = NULL;
2561 }
2562
2563 if (indent) {
2564 talloc_free(indent);
2565 indent = NULL;
2566 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002567 }
2568 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002569
2570return_invalid_indent:
2571 if (vline)
2572 cmd_free_strvec(vline);
2573 if (indent) {
2574 talloc_free(indent);
2575 indent = NULL;
2576 }
2577 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002578}
2579
2580/* Configration from terminal */
2581DEFUN(config_terminal,
2582 config_terminal_cmd,
2583 "configure terminal",
2584 "Configuration from vty interface\n" "Configuration terminal\n")
2585{
2586 if (vty_config_lock(vty))
2587 vty->node = CONFIG_NODE;
2588 else {
2589 vty_out(vty, "VTY configuration is locked by other VTY%s",
2590 VTY_NEWLINE);
2591 return CMD_WARNING;
2592 }
2593 return CMD_SUCCESS;
2594}
2595
2596/* Enable command */
2597DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2598{
2599 /* If enable password is NULL, change to ENABLE_NODE */
2600 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2601 vty->type == VTY_SHELL_SERV)
2602 vty->node = ENABLE_NODE;
2603 else
2604 vty->node = AUTH_ENABLE_NODE;
2605
2606 return CMD_SUCCESS;
2607}
2608
2609/* Disable command */
2610DEFUN(disable,
2611 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2612{
2613 if (vty->node == ENABLE_NODE)
2614 vty->node = VIEW_NODE;
2615 return CMD_SUCCESS;
2616}
2617
2618/* Down vty node level. */
2619gDEFUN(config_exit,
2620 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2621{
2622 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002623 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002624 case VIEW_NODE:
2625 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002626 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002627 break;
2628 case CONFIG_NODE:
2629 vty->node = ENABLE_NODE;
2630 vty_config_unlock(vty);
2631 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002632 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002633 if (vty->node > CONFIG_NODE)
2634 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002635 break;
2636 }
2637 return CMD_SUCCESS;
2638}
2639
2640/* End of configuration. */
2641 gDEFUN(config_end,
2642 config_end_cmd, "end", "End current mode and change to enable mode.")
2643{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002644 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002645 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002646
2647 /* Repeatedly call go_parent until a top node is reached. */
2648 while (vty->node > CONFIG_NODE) {
2649 if (vty->node == last_node) {
2650 /* Ensure termination, this shouldn't happen. */
2651 break;
2652 }
2653 last_node = vty->node;
2654 vty_go_parent(vty);
2655 }
2656
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002657 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002658 if (vty->node > ENABLE_NODE)
2659 vty->node = ENABLE_NODE;
2660 vty->index = NULL;
2661 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002662 }
2663 return CMD_SUCCESS;
2664}
2665
2666/* Show version. */
2667DEFUN(show_version,
2668 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2669{
Harald Welte237f6242010-05-25 23:00:45 +02002670 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2671 host.app_info->version,
2672 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2673 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002674
2675 return CMD_SUCCESS;
2676}
2677
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002678DEFUN(show_online_help,
2679 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2680{
2681 vty_dump_nodes(vty);
2682 return CMD_SUCCESS;
2683}
2684
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002685/* Help display function for all node. */
2686gDEFUN(config_help,
2687 config_help_cmd, "help", "Description of the interactive help system\n")
2688{
2689 vty_out(vty,
2690 "This VTY provides advanced help features. When you need help,%s\
2691anytime at the command line please press '?'.%s\
2692%s\
2693If nothing matches, the help list will be empty and you must backup%s\
2694 until entering a '?' shows the available options.%s\
2695Two styles of help are provided:%s\
26961. Full help is available when you are ready to enter a%s\
2697command argument (e.g. 'show ?') and describes each possible%s\
2698argument.%s\
26992. Partial help is provided when an abbreviated argument is entered%s\
2700 and you want to know what arguments match the input%s\
2701 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2702 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2703 return CMD_SUCCESS;
2704}
2705
2706/* Help display function for all node. */
2707gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2708{
2709 unsigned int i;
2710 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2711 struct cmd_element *cmd;
2712
2713 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2714 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
2715 && !(cmd->attr == CMD_ATTR_DEPRECATED
2716 || cmd->attr == CMD_ATTR_HIDDEN))
2717 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2718 return CMD_SUCCESS;
2719}
2720
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002721static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002722{
2723 unsigned int i;
2724 int fd;
2725 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002726 char *config_file_tmp = NULL;
2727 char *config_file_sav = NULL;
2728 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002729 struct stat st;
2730
2731 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002732
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002733 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2734 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2735 * manually instead. */
2736
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002737 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002738 config_file_sav =
2739 _talloc_zero(tall_vty_cmd_ctx,
2740 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2741 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002742 if (!config_file_sav)
2743 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002744 strcpy(config_file_sav, config_file);
2745 strcat(config_file_sav, CONF_BACKUP_EXT);
2746
2747 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002748 "config_file_tmp");
2749 if (!config_file_tmp) {
2750 talloc_free(config_file_sav);
2751 return -1;
2752 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002753 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2754
2755 /* Open file to configuration write. */
2756 fd = mkstemp(config_file_tmp);
2757 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002758 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002759 talloc_free(config_file_tmp);
2760 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002761 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002762 }
2763
2764 /* Make vty for configuration file. */
2765 file_vty = vty_new();
2766 file_vty->fd = fd;
2767 file_vty->type = VTY_FILE;
2768
2769 /* Config file header print. */
2770 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002771 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002772 //vty_time_print (file_vty, 1);
2773 vty_out(file_vty, "!\n");
2774
2775 for (i = 0; i < vector_active(cmdvec); i++)
2776 if ((node = vector_slot(cmdvec, i)) && node->func) {
2777 if ((*node->func) (file_vty))
2778 vty_out(file_vty, "!\n");
2779 }
2780 vty_close(file_vty);
2781
2782 if (unlink(config_file_sav) != 0)
2783 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002784 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002785 talloc_free(config_file_sav);
2786 talloc_free(config_file_tmp);
2787 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002788 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002789 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002790
2791 /* Only link the .sav file if the original file exists */
2792 if (stat(config_file, &st) == 0) {
2793 if (link(config_file, config_file_sav) != 0) {
2794 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2795 talloc_free(config_file_sav);
2796 talloc_free(config_file_tmp);
2797 unlink(config_file_tmp);
2798 return -3;
2799 }
2800 sync();
2801 if (unlink(config_file) != 0) {
2802 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2803 talloc_free(config_file_sav);
2804 talloc_free(config_file_tmp);
2805 unlink(config_file_tmp);
2806 return -4;
2807 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002808 }
2809 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002810 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002811 talloc_free(config_file_sav);
2812 talloc_free(config_file_tmp);
2813 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002814 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002815 }
2816 unlink(config_file_tmp);
2817 sync();
2818
2819 talloc_free(config_file_sav);
2820 talloc_free(config_file_tmp);
2821
2822 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002823 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2824 return -6;
2825 }
2826
2827 return 0;
2828}
2829
2830
2831/* Write current configuration into file. */
2832DEFUN(config_write_file,
2833 config_write_file_cmd,
2834 "write file",
2835 "Write running configuration to memory, network, or terminal\n"
2836 "Write to configuration file\n")
2837{
2838 char *failed_file;
2839 int rc;
2840
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002841 if (host.app_info->config_is_consistent) {
2842 rc = host.app_info->config_is_consistent(vty);
2843 if (!rc) {
2844 vty_out(vty, "Configuration is not consistent%s",
2845 VTY_NEWLINE);
2846 return CMD_WARNING;
2847 }
2848 }
2849
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002850 if (host.config == NULL) {
2851 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2852 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002853 return CMD_WARNING;
2854 }
2855
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002856 rc = write_config_file(host.config, &failed_file);
2857 switch (rc) {
2858 case -1:
2859 vty_out(vty, "Can't open configuration file %s.%s",
2860 failed_file, VTY_NEWLINE);
2861 rc = CMD_WARNING;
2862 break;
2863 case -2:
2864 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2865 failed_file, VTY_NEWLINE);
2866 rc = CMD_WARNING;
2867 break;
2868 case -3:
2869 vty_out(vty, "Can't backup old configuration file %s.%s",
2870 failed_file, VTY_NEWLINE);
2871 rc = CMD_WARNING;
2872 break;
2873 case -4:
2874 vty_out(vty, "Can't unlink configuration file %s.%s",
2875 failed_file, VTY_NEWLINE);
2876 rc = CMD_WARNING;
2877 break;
2878 case -5:
2879 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2880 VTY_NEWLINE);
2881 rc = CMD_WARNING;
2882 break;
2883 case -6:
2884 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2885 failed_file, strerror(errno), errno, VTY_NEWLINE);
2886 rc = CMD_WARNING;
2887 break;
2888 default:
2889 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2890 rc = CMD_SUCCESS;
2891 break;
2892 }
2893
2894 talloc_free(failed_file);
2895 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002896}
2897
2898ALIAS(config_write_file,
2899 config_write_cmd,
2900 "write", "Write running configuration to memory, network, or terminal\n")
2901
2902 ALIAS(config_write_file,
2903 config_write_memory_cmd,
2904 "write memory",
2905 "Write running configuration to memory, network, or terminal\n"
2906 "Write configuration to the file (same as write file)\n")
2907
2908 ALIAS(config_write_file,
2909 copy_runningconfig_startupconfig_cmd,
2910 "copy running-config startup-config",
2911 "Copy configuration\n"
2912 "Copy running config to... \n"
2913 "Copy running config to startup config (same as write file)\n")
2914
2915/* Write current configuration into the terminal. */
2916 DEFUN(config_write_terminal,
2917 config_write_terminal_cmd,
2918 "write terminal",
2919 "Write running configuration to memory, network, or terminal\n"
2920 "Write to terminal\n")
2921{
2922 unsigned int i;
2923 struct cmd_node *node;
2924
2925 if (vty->type == VTY_SHELL_SERV) {
2926 for (i = 0; i < vector_active(cmdvec); i++)
2927 if ((node = vector_slot(cmdvec, i)) && node->func
2928 && node->vtysh) {
2929 if ((*node->func) (vty))
2930 vty_out(vty, "!%s", VTY_NEWLINE);
2931 }
2932 } else {
2933 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2934 VTY_NEWLINE);
2935 vty_out(vty, "!%s", VTY_NEWLINE);
2936
2937 for (i = 0; i < vector_active(cmdvec); i++)
2938 if ((node = vector_slot(cmdvec, i)) && node->func) {
2939 if ((*node->func) (vty))
2940 vty_out(vty, "!%s", VTY_NEWLINE);
2941 }
2942 vty_out(vty, "end%s", VTY_NEWLINE);
2943 }
2944 return CMD_SUCCESS;
2945}
2946
2947/* Write current configuration into the terminal. */
2948ALIAS(config_write_terminal,
2949 show_running_config_cmd,
2950 "show running-config", SHOW_STR "running configuration\n")
2951
2952/* Write startup configuration into the terminal. */
2953 DEFUN(show_startup_config,
2954 show_startup_config_cmd,
2955 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2956{
2957 char buf[BUFSIZ];
2958 FILE *confp;
2959
2960 confp = fopen(host.config, "r");
2961 if (confp == NULL) {
2962 vty_out(vty, "Can't open configuration file [%s]%s",
2963 host.config, VTY_NEWLINE);
2964 return CMD_WARNING;
2965 }
2966
2967 while (fgets(buf, BUFSIZ, confp)) {
2968 char *cp = buf;
2969
2970 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
2971 cp++;
2972 *cp = '\0';
2973
2974 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
2975 }
2976
2977 fclose(confp);
2978
2979 return CMD_SUCCESS;
2980}
2981
2982/* Hostname configuration */
2983DEFUN(config_hostname,
2984 hostname_cmd,
2985 "hostname WORD",
2986 "Set system's network name\n" "This system's network name\n")
2987{
2988 if (!isalpha((int)*argv[0])) {
2989 vty_out(vty, "Please specify string starting with alphabet%s",
2990 VTY_NEWLINE);
2991 return CMD_WARNING;
2992 }
2993
2994 if (host.name)
2995 talloc_free(host.name);
2996
2997 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2998 return CMD_SUCCESS;
2999}
3000
3001DEFUN(config_no_hostname,
3002 no_hostname_cmd,
3003 "no hostname [HOSTNAME]",
3004 NO_STR "Reset system's network name\n" "Host name of this router\n")
3005{
3006 if (host.name)
3007 talloc_free(host.name);
3008 host.name = NULL;
3009 return CMD_SUCCESS;
3010}
3011
3012/* VTY interface password set. */
3013DEFUN(config_password, password_cmd,
3014 "password (8|) WORD",
3015 "Assign the terminal connection password\n"
3016 "Specifies a HIDDEN password will follow\n"
3017 "dummy string \n" "The HIDDEN line password string\n")
3018{
3019 /* Argument check. */
3020 if (argc == 0) {
3021 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3022 return CMD_WARNING;
3023 }
3024
3025 if (argc == 2) {
3026 if (*argv[0] == '8') {
3027 if (host.password)
3028 talloc_free(host.password);
3029 host.password = NULL;
3030 if (host.password_encrypt)
3031 talloc_free(host.password_encrypt);
3032 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3033 return CMD_SUCCESS;
3034 } else {
3035 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3036 return CMD_WARNING;
3037 }
3038 }
3039
3040 if (!isalnum((int)*argv[0])) {
3041 vty_out(vty,
3042 "Please specify string starting with alphanumeric%s",
3043 VTY_NEWLINE);
3044 return CMD_WARNING;
3045 }
3046
3047 if (host.password)
3048 talloc_free(host.password);
3049 host.password = NULL;
3050
3051#ifdef VTY_CRYPT_PW
3052 if (host.encrypt) {
3053 if (host.password_encrypt)
3054 talloc_free(host.password_encrypt);
3055 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3056 } else
3057#endif
3058 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3059
3060 return CMD_SUCCESS;
3061}
3062
3063ALIAS(config_password, password_text_cmd,
3064 "password LINE",
3065 "Assign the terminal connection password\n"
3066 "The UNENCRYPTED (cleartext) line password\n")
3067
3068/* VTY enable password set. */
3069 DEFUN(config_enable_password, enable_password_cmd,
3070 "enable password (8|) WORD",
3071 "Modify enable password parameters\n"
3072 "Assign the privileged level password\n"
3073 "Specifies a HIDDEN password will follow\n"
3074 "dummy string \n" "The HIDDEN 'enable' password string\n")
3075{
3076 /* Argument check. */
3077 if (argc == 0) {
3078 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3079 return CMD_WARNING;
3080 }
3081
3082 /* Crypt type is specified. */
3083 if (argc == 2) {
3084 if (*argv[0] == '8') {
3085 if (host.enable)
3086 talloc_free(host.enable);
3087 host.enable = NULL;
3088
3089 if (host.enable_encrypt)
3090 talloc_free(host.enable_encrypt);
3091 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3092
3093 return CMD_SUCCESS;
3094 } else {
3095 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3096 return CMD_WARNING;
3097 }
3098 }
3099
3100 if (!isalnum((int)*argv[0])) {
3101 vty_out(vty,
3102 "Please specify string starting with alphanumeric%s",
3103 VTY_NEWLINE);
3104 return CMD_WARNING;
3105 }
3106
3107 if (host.enable)
3108 talloc_free(host.enable);
3109 host.enable = NULL;
3110
3111 /* Plain password input. */
3112#ifdef VTY_CRYPT_PW
3113 if (host.encrypt) {
3114 if (host.enable_encrypt)
3115 talloc_free(host.enable_encrypt);
3116 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3117 } else
3118#endif
3119 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3120
3121 return CMD_SUCCESS;
3122}
3123
3124ALIAS(config_enable_password,
3125 enable_password_text_cmd,
3126 "enable password LINE",
3127 "Modify enable password parameters\n"
3128 "Assign the privileged level password\n"
3129 "The UNENCRYPTED (cleartext) 'enable' password\n")
3130
3131/* VTY enable password delete. */
3132 DEFUN(no_config_enable_password, no_enable_password_cmd,
3133 "no enable password",
3134 NO_STR
3135 "Modify enable password parameters\n"
3136 "Assign the privileged level password\n")
3137{
3138 if (host.enable)
3139 talloc_free(host.enable);
3140 host.enable = NULL;
3141
3142 if (host.enable_encrypt)
3143 talloc_free(host.enable_encrypt);
3144 host.enable_encrypt = NULL;
3145
3146 return CMD_SUCCESS;
3147}
3148
3149#ifdef VTY_CRYPT_PW
3150DEFUN(service_password_encrypt,
3151 service_password_encrypt_cmd,
3152 "service password-encryption",
3153 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3154{
3155 if (host.encrypt)
3156 return CMD_SUCCESS;
3157
3158 host.encrypt = 1;
3159
3160 if (host.password) {
3161 if (host.password_encrypt)
3162 talloc_free(host.password_encrypt);
3163 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3164 }
3165 if (host.enable) {
3166 if (host.enable_encrypt)
3167 talloc_free(host.enable_encrypt);
3168 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3169 }
3170
3171 return CMD_SUCCESS;
3172}
3173
3174DEFUN(no_service_password_encrypt,
3175 no_service_password_encrypt_cmd,
3176 "no service password-encryption",
3177 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3178{
3179 if (!host.encrypt)
3180 return CMD_SUCCESS;
3181
3182 host.encrypt = 0;
3183
3184 if (host.password_encrypt)
3185 talloc_free(host.password_encrypt);
3186 host.password_encrypt = NULL;
3187
3188 if (host.enable_encrypt)
3189 talloc_free(host.enable_encrypt);
3190 host.enable_encrypt = NULL;
3191
3192 return CMD_SUCCESS;
3193}
3194#endif
3195
3196DEFUN(config_terminal_length, config_terminal_length_cmd,
3197 "terminal length <0-512>",
3198 "Set terminal line parameters\n"
3199 "Set number of lines on a screen\n"
3200 "Number of lines on screen (0 for no pausing)\n")
3201{
3202 int lines;
3203 char *endptr = NULL;
3204
3205 lines = strtol(argv[0], &endptr, 10);
3206 if (lines < 0 || lines > 512 || *endptr != '\0') {
3207 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3208 return CMD_WARNING;
3209 }
3210 vty->lines = lines;
3211
3212 return CMD_SUCCESS;
3213}
3214
3215DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3216 "terminal no length",
3217 "Set terminal line parameters\n"
3218 NO_STR "Set number of lines on a screen\n")
3219{
3220 vty->lines = -1;
3221 return CMD_SUCCESS;
3222}
3223
3224DEFUN(service_terminal_length, service_terminal_length_cmd,
3225 "service terminal-length <0-512>",
3226 "Set up miscellaneous service\n"
3227 "System wide terminal length configuration\n"
3228 "Number of lines of VTY (0 means no line control)\n")
3229{
3230 int lines;
3231 char *endptr = NULL;
3232
3233 lines = strtol(argv[0], &endptr, 10);
3234 if (lines < 0 || lines > 512 || *endptr != '\0') {
3235 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3236 return CMD_WARNING;
3237 }
3238 host.lines = lines;
3239
3240 return CMD_SUCCESS;
3241}
3242
3243DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3244 "no service terminal-length [<0-512>]",
3245 NO_STR
3246 "Set up miscellaneous service\n"
3247 "System wide terminal length configuration\n"
3248 "Number of lines of VTY (0 means no line control)\n")
3249{
3250 host.lines = -1;
3251 return CMD_SUCCESS;
3252}
3253
3254DEFUN_HIDDEN(do_echo,
3255 echo_cmd,
3256 "echo .MESSAGE",
3257 "Echo a message back to the vty\n" "The message to echo\n")
3258{
3259 char *message;
3260
3261 vty_out(vty, "%s%s",
3262 ((message =
3263 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3264 if (message)
3265 talloc_free(message);
3266 return CMD_SUCCESS;
3267}
3268
3269#if 0
3270DEFUN(config_logmsg,
3271 config_logmsg_cmd,
3272 "logmsg " LOG_LEVELS " .MESSAGE",
3273 "Send a message to enabled logging destinations\n"
3274 LOG_LEVEL_DESC "The message to send\n")
3275{
3276 int level;
3277 char *message;
3278
3279 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3280 return CMD_ERR_NO_MATCH;
3281
3282 zlog(NULL, level,
3283 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3284 if (message)
3285 talloc_free(message);
3286 return CMD_SUCCESS;
3287}
3288
3289DEFUN(show_logging,
3290 show_logging_cmd,
3291 "show logging", SHOW_STR "Show current logging configuration\n")
3292{
3293 struct zlog *zl = zlog_default;
3294
3295 vty_out(vty, "Syslog logging: ");
3296 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3297 vty_out(vty, "disabled");
3298 else
3299 vty_out(vty, "level %s, facility %s, ident %s",
3300 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3301 facility_name(zl->facility), zl->ident);
3302 vty_out(vty, "%s", VTY_NEWLINE);
3303
3304 vty_out(vty, "Stdout logging: ");
3305 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3306 vty_out(vty, "disabled");
3307 else
3308 vty_out(vty, "level %s",
3309 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3310 vty_out(vty, "%s", VTY_NEWLINE);
3311
3312 vty_out(vty, "Monitor logging: ");
3313 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3314 vty_out(vty, "disabled");
3315 else
3316 vty_out(vty, "level %s",
3317 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3318 vty_out(vty, "%s", VTY_NEWLINE);
3319
3320 vty_out(vty, "File logging: ");
3321 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3322 vty_out(vty, "disabled");
3323 else
3324 vty_out(vty, "level %s, filename %s",
3325 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3326 zl->filename);
3327 vty_out(vty, "%s", VTY_NEWLINE);
3328
3329 vty_out(vty, "Protocol name: %s%s",
3330 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3331 vty_out(vty, "Record priority: %s%s",
3332 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3333
3334 return CMD_SUCCESS;
3335}
3336
3337DEFUN(config_log_stdout,
3338 config_log_stdout_cmd,
3339 "log stdout", "Logging control\n" "Set stdout logging level\n")
3340{
3341 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3342 return CMD_SUCCESS;
3343}
3344
3345DEFUN(config_log_stdout_level,
3346 config_log_stdout_level_cmd,
3347 "log stdout " LOG_LEVELS,
3348 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3349{
3350 int level;
3351
3352 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3353 return CMD_ERR_NO_MATCH;
3354 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3355 return CMD_SUCCESS;
3356}
3357
3358DEFUN(no_config_log_stdout,
3359 no_config_log_stdout_cmd,
3360 "no log stdout [LEVEL]",
3361 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3362{
3363 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3364 return CMD_SUCCESS;
3365}
3366
3367DEFUN(config_log_monitor,
3368 config_log_monitor_cmd,
3369 "log monitor",
3370 "Logging control\n" "Set terminal line (monitor) logging level\n")
3371{
3372 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3373 return CMD_SUCCESS;
3374}
3375
3376DEFUN(config_log_monitor_level,
3377 config_log_monitor_level_cmd,
3378 "log monitor " LOG_LEVELS,
3379 "Logging control\n"
3380 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3381{
3382 int level;
3383
3384 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3385 return CMD_ERR_NO_MATCH;
3386 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3387 return CMD_SUCCESS;
3388}
3389
3390DEFUN(no_config_log_monitor,
3391 no_config_log_monitor_cmd,
3392 "no log monitor [LEVEL]",
3393 NO_STR
3394 "Logging control\n"
3395 "Disable terminal line (monitor) logging\n" "Logging level\n")
3396{
3397 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3398 return CMD_SUCCESS;
3399}
3400
3401static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3402{
3403 int ret;
3404 char *p = NULL;
3405 const char *fullpath;
3406
3407 /* Path detection. */
3408 if (!IS_DIRECTORY_SEP(*fname)) {
3409 char cwd[MAXPATHLEN + 1];
3410 cwd[MAXPATHLEN] = '\0';
3411
3412 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3413 zlog_err("config_log_file: Unable to alloc mem!");
3414 return CMD_WARNING;
3415 }
3416
3417 if ((p = _talloc_zero(tall_vcmd_ctx,
3418 strlen(cwd) + strlen(fname) + 2),
3419 "set_log_file")
3420 == NULL) {
3421 zlog_err("config_log_file: Unable to alloc mem!");
3422 return CMD_WARNING;
3423 }
3424 sprintf(p, "%s/%s", cwd, fname);
3425 fullpath = p;
3426 } else
3427 fullpath = fname;
3428
3429 ret = zlog_set_file(NULL, fullpath, loglevel);
3430
3431 if (p)
3432 talloc_free(p);
3433
3434 if (!ret) {
3435 vty_out(vty, "can't open logfile %s\n", fname);
3436 return CMD_WARNING;
3437 }
3438
3439 if (host.logfile)
3440 talloc_free(host.logfile);
3441
3442 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3443
3444 return CMD_SUCCESS;
3445}
3446
3447DEFUN(config_log_file,
3448 config_log_file_cmd,
3449 "log file FILENAME",
3450 "Logging control\n" "Logging to file\n" "Logging filename\n")
3451{
3452 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3453}
3454
3455DEFUN(config_log_file_level,
3456 config_log_file_level_cmd,
3457 "log file FILENAME " LOG_LEVELS,
3458 "Logging control\n"
3459 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3460{
3461 int level;
3462
3463 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3464 return CMD_ERR_NO_MATCH;
3465 return set_log_file(vty, argv[0], level);
3466}
3467
3468DEFUN(no_config_log_file,
3469 no_config_log_file_cmd,
3470 "no log file [FILENAME]",
3471 NO_STR
3472 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3473{
3474 zlog_reset_file(NULL);
3475
3476 if (host.logfile)
3477 talloc_free(host.logfile);
3478
3479 host.logfile = NULL;
3480
3481 return CMD_SUCCESS;
3482}
3483
3484ALIAS(no_config_log_file,
3485 no_config_log_file_level_cmd,
3486 "no log file FILENAME LEVEL",
3487 NO_STR
3488 "Logging control\n"
3489 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3490
3491 DEFUN(config_log_syslog,
3492 config_log_syslog_cmd,
3493 "log syslog", "Logging control\n" "Set syslog logging level\n")
3494{
3495 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3496 return CMD_SUCCESS;
3497}
3498
3499DEFUN(config_log_syslog_level,
3500 config_log_syslog_level_cmd,
3501 "log syslog " LOG_LEVELS,
3502 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3503{
3504 int level;
3505
3506 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3507 return CMD_ERR_NO_MATCH;
3508 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3509 return CMD_SUCCESS;
3510}
3511
3512DEFUN_DEPRECATED(config_log_syslog_facility,
3513 config_log_syslog_facility_cmd,
3514 "log syslog facility " LOG_FACILITIES,
3515 "Logging control\n"
3516 "Logging goes to syslog\n"
3517 "(Deprecated) Facility parameter for syslog messages\n"
3518 LOG_FACILITY_DESC)
3519{
3520 int facility;
3521
3522 if ((facility = facility_match(argv[0])) < 0)
3523 return CMD_ERR_NO_MATCH;
3524
3525 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3526 zlog_default->facility = facility;
3527 return CMD_SUCCESS;
3528}
3529
3530DEFUN(no_config_log_syslog,
3531 no_config_log_syslog_cmd,
3532 "no log syslog [LEVEL]",
3533 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3534{
3535 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3536 return CMD_SUCCESS;
3537}
3538
3539ALIAS(no_config_log_syslog,
3540 no_config_log_syslog_facility_cmd,
3541 "no log syslog facility " LOG_FACILITIES,
3542 NO_STR
3543 "Logging control\n"
3544 "Logging goes to syslog\n"
3545 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3546
3547 DEFUN(config_log_facility,
3548 config_log_facility_cmd,
3549 "log facility " LOG_FACILITIES,
3550 "Logging control\n"
3551 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3552{
3553 int facility;
3554
3555 if ((facility = facility_match(argv[0])) < 0)
3556 return CMD_ERR_NO_MATCH;
3557 zlog_default->facility = facility;
3558 return CMD_SUCCESS;
3559}
3560
3561DEFUN(no_config_log_facility,
3562 no_config_log_facility_cmd,
3563 "no log facility [FACILITY]",
3564 NO_STR
3565 "Logging control\n"
3566 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3567{
3568 zlog_default->facility = LOG_DAEMON;
3569 return CMD_SUCCESS;
3570}
3571
3572DEFUN_DEPRECATED(config_log_trap,
3573 config_log_trap_cmd,
3574 "log trap " LOG_LEVELS,
3575 "Logging control\n"
3576 "(Deprecated) Set logging level and default for all destinations\n"
3577 LOG_LEVEL_DESC)
3578{
3579 int new_level;
3580 int i;
3581
3582 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3583 return CMD_ERR_NO_MATCH;
3584
3585 zlog_default->default_lvl = new_level;
3586 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3587 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3588 zlog_default->maxlvl[i] = new_level;
3589 return CMD_SUCCESS;
3590}
3591
3592DEFUN_DEPRECATED(no_config_log_trap,
3593 no_config_log_trap_cmd,
3594 "no log trap [LEVEL]",
3595 NO_STR
3596 "Logging control\n"
3597 "Permit all logging information\n" "Logging level\n")
3598{
3599 zlog_default->default_lvl = LOG_DEBUG;
3600 return CMD_SUCCESS;
3601}
3602
3603DEFUN(config_log_record_priority,
3604 config_log_record_priority_cmd,
3605 "log record-priority",
3606 "Logging control\n"
3607 "Log the priority of the message within the message\n")
3608{
3609 zlog_default->record_priority = 1;
3610 return CMD_SUCCESS;
3611}
3612
3613DEFUN(no_config_log_record_priority,
3614 no_config_log_record_priority_cmd,
3615 "no log record-priority",
3616 NO_STR
3617 "Logging control\n"
3618 "Do not log the priority of the message within the message\n")
3619{
3620 zlog_default->record_priority = 0;
3621 return CMD_SUCCESS;
3622}
3623#endif
3624
3625DEFUN(banner_motd_file,
3626 banner_motd_file_cmd,
3627 "banner motd file [FILE]",
3628 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3629{
3630 if (host.motdfile)
3631 talloc_free(host.motdfile);
3632 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3633
3634 return CMD_SUCCESS;
3635}
3636
3637DEFUN(banner_motd_default,
3638 banner_motd_default_cmd,
3639 "banner motd default",
3640 "Set banner string\n" "Strings for motd\n" "Default string\n")
3641{
3642 host.motd = default_motd;
3643 return CMD_SUCCESS;
3644}
3645
3646DEFUN(no_banner_motd,
3647 no_banner_motd_cmd,
3648 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3649{
3650 host.motd = NULL;
3651 if (host.motdfile)
3652 talloc_free(host.motdfile);
3653 host.motdfile = NULL;
3654 return CMD_SUCCESS;
3655}
3656
3657/* Set config filename. Called from vty.c */
3658void host_config_set(const char *filename)
3659{
3660 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3661}
3662
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003663/*! Deprecated, now happens implicitly when calling install_node().
3664 * Users of the API may still attempt to call this function, hence
3665 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003666void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003667{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003668}
3669
3670/*! Deprecated, now happens implicitly when calling install_node().
3671 * Users of the API may still attempt to call this function, hence
3672 * leave it here as a no-op. */
3673void vty_install_default(int node)
3674{
3675}
3676
3677/*! Install common commands like 'exit' and 'list'. */
3678static void install_basic_node_commands(int node)
3679{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003680 install_element(node, &config_help_cmd);
3681 install_element(node, &config_list_cmd);
3682
3683 install_element(node, &config_write_terminal_cmd);
3684 install_element(node, &config_write_file_cmd);
3685 install_element(node, &config_write_memory_cmd);
3686 install_element(node, &config_write_cmd);
3687 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003688
3689 install_element(node, &config_exit_cmd);
3690
3691 if (node >= CONFIG_NODE) {
3692 /* It's not a top node. */
3693 install_element(node, &config_end_cmd);
3694 }
3695}
3696
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003697/*! Return true if a node is installed by install_basic_node_commands(), so
3698 * that we can avoid repeating them for each and every node during 'show
3699 * running-config' */
3700static bool vty_command_is_common(struct cmd_element *cmd)
3701{
3702 if (cmd == &config_help_cmd
3703 || cmd == &config_list_cmd
3704 || cmd == &config_write_terminal_cmd
3705 || cmd == &config_write_file_cmd
3706 || cmd == &config_write_memory_cmd
3707 || cmd == &config_write_cmd
3708 || cmd == &show_running_config_cmd
3709 || cmd == &config_exit_cmd
3710 || cmd == &config_end_cmd)
3711 return true;
3712 return false;
3713}
3714
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003715/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003716 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003717 * \param[in] vty the vty of the code
3718 * \param[in] filename where to store the file
3719 * \return 0 in case of success.
3720 *
3721 * If the filename already exists create a filename.sav
3722 * version with the current code.
3723 *
3724 */
3725int osmo_vty_write_config_file(const char *filename)
3726{
3727 char *failed_file;
3728 int rc;
3729
3730 rc = write_config_file(filename, &failed_file);
3731 talloc_free(failed_file);
3732 return rc;
3733}
3734
3735/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003736 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003737 * \return 0 in case of success.
3738 *
3739 * If the filename already exists create a filename.sav
3740 * version with the current code.
3741 *
3742 */
3743int osmo_vty_save_config_file(void)
3744{
3745 char *failed_file;
3746 int rc;
3747
3748 if (host.config == NULL)
3749 return -7;
3750
3751 rc = write_config_file(host.config, &failed_file);
3752 talloc_free(failed_file);
3753 return rc;
3754}
3755
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003756/* Initialize command interface. Install basic nodes and commands. */
3757void cmd_init(int terminal)
3758{
3759 /* Allocate initial top vector of commands. */
3760 cmdvec = vector_init(VECTOR_MIN_SIZE);
3761
3762 /* Default host value settings. */
3763 host.name = NULL;
3764 host.password = NULL;
3765 host.enable = NULL;
3766 host.logfile = NULL;
3767 host.config = NULL;
3768 host.lines = -1;
3769 host.motd = default_motd;
3770 host.motdfile = NULL;
3771
3772 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003773 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003774 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003775 install_node_bare(&auth_node, NULL);
3776 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003777 install_node(&config_node, config_write_host);
3778
3779 /* Each node's basic commands. */
3780 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003781 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003782 if (terminal) {
3783 install_element(VIEW_NODE, &config_list_cmd);
3784 install_element(VIEW_NODE, &config_exit_cmd);
3785 install_element(VIEW_NODE, &config_help_cmd);
3786 install_element(VIEW_NODE, &config_enable_cmd);
3787 install_element(VIEW_NODE, &config_terminal_length_cmd);
3788 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3789 install_element(VIEW_NODE, &echo_cmd);
3790 }
3791
3792 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003793 install_element(ENABLE_NODE, &config_disable_cmd);
3794 install_element(ENABLE_NODE, &config_terminal_cmd);
3795 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3796 }
3797 install_element (ENABLE_NODE, &show_startup_config_cmd);
3798 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003799 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003800
3801 if (terminal) {
3802 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3803 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3804 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003805 }
3806
3807 install_element(CONFIG_NODE, &hostname_cmd);
3808 install_element(CONFIG_NODE, &no_hostname_cmd);
3809
3810 if (terminal) {
3811 install_element(CONFIG_NODE, &password_cmd);
3812 install_element(CONFIG_NODE, &password_text_cmd);
3813 install_element(CONFIG_NODE, &enable_password_cmd);
3814 install_element(CONFIG_NODE, &enable_password_text_cmd);
3815 install_element(CONFIG_NODE, &no_enable_password_cmd);
3816
3817#ifdef VTY_CRYPT_PW
3818 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3819 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3820#endif
3821 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3822 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3823 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3824 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3825 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3826
3827 }
3828 srand(time(NULL));
3829}
Harald Welte7acb30c2011-08-17 17:13:48 +02003830
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003831/*! @} */