blob: 24d8ea415645229ac00fad3230cf7c5aeb1b0fdc [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
517 out = talloc_size(NULL, len + 1);
518 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
577 out = talloc_size(NULL, len + 1);
578 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;
682 vty_dump_element(elem, vty);
683 }
684 }
685 vty_out(vty, " </node>%s", VTY_NEWLINE);
686
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100687 for (i = 0; i < vector_active(cmdvec); ++i) {
688 struct cmd_node *cnode;
689 cnode = vector_slot(cmdvec, i);
690 if (!cnode)
691 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200692 if (vector_active(cnode->cmd_vector) < 1)
693 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100694
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200695 /* De-dup node IDs: how many times has this same name been used before? Count the first
696 * occurence as _1 and omit that first suffix, so that the first occurence is called
697 * 'name', the second becomes 'name_2', then 'name_3', ... */
698 same_name_count = 1;
699 for (j = 0; j < i; ++j) {
700 struct cmd_node *cnode2;
701 cnode2 = vector_slot(cmdvec, j);
702 if (!cnode2)
703 continue;
704 if (strcmp(cnode->name, cnode2->name) == 0)
705 same_name_count ++;
706 }
707
708 vty_out(vty, " <node id='%s", cnode->name);
709 if (same_name_count > 1 || !*cnode->name)
710 vty_out(vty, "_%d", same_name_count);
711 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200712 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100713
714 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
715 struct cmd_element *elem;
716 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200717 if (vty_command_is_common(elem))
718 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100719 vty_dump_element(elem, vty);
720 }
721
722 vty_out(vty, " </node>%s", VTY_NEWLINE);
723 }
724
725 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
726
727 return 0;
728}
729
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200730/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100731static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
732{
733 int i;
734
735 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
736 struct cmd_element *elem;
737 elem = vector_slot(cnode->cmd_vector, i);
738 if (!elem->string)
739 continue;
740 if (!strcmp(elem->string, cmdstring))
741 return 1;
742 }
743 return 0;
744}
745
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200746/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200747 * \param[in] ntype Node Type
748 * \param[cmd] element to be installed
749 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000750void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200751{
752 struct cmd_node *cnode;
753
754 cnode = vector_slot(cmdvec, ntype);
755
Harald Weltea99d45a2015-11-12 13:48:23 +0100756 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100757 /* ensure no _identical_ command has been registered at this
758 * node so far */
759 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200760
761 vector_set(cnode->cmd_vector, cmd);
762
763 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
764 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
765}
766
767/* Install a command into VIEW and ENABLE node */
768void install_element_ve(struct cmd_element *cmd)
769{
770 install_element(VIEW_NODE, cmd);
771 install_element(ENABLE_NODE, cmd);
772}
773
774#ifdef VTY_CRYPT_PW
775static unsigned char itoa64[] =
776 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
777
778static void to64(char *s, long v, int n)
779{
780 while (--n >= 0) {
781 *s++ = itoa64[v & 0x3f];
782 v >>= 6;
783 }
784}
785
786static char *zencrypt(const char *passwd)
787{
788 char salt[6];
789 struct timeval tv;
790 char *crypt(const char *, const char *);
791
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200792 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200793
794 to64(&salt[0], random(), 3);
795 to64(&salt[3], tv.tv_usec, 3);
796 salt[5] = '\0';
797
798 return crypt(passwd, salt);
799}
800#endif
801
802/* This function write configuration of this host. */
803static int config_write_host(struct vty *vty)
804{
805 if (host.name)
806 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
807
808 if (host.encrypt) {
809 if (host.password_encrypt)
810 vty_out(vty, "password 8 %s%s", host.password_encrypt,
811 VTY_NEWLINE);
812 if (host.enable_encrypt)
813 vty_out(vty, "enable password 8 %s%s",
814 host.enable_encrypt, VTY_NEWLINE);
815 } else {
816 if (host.password)
817 vty_out(vty, "password %s%s", host.password,
818 VTY_NEWLINE);
819 if (host.enable)
820 vty_out(vty, "enable password %s%s", host.enable,
821 VTY_NEWLINE);
822 }
823
824 if (host.advanced)
825 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
826
827 if (host.encrypt)
828 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
829
830 if (host.lines >= 0)
831 vty_out(vty, "service terminal-length %d%s", host.lines,
832 VTY_NEWLINE);
833
834 if (host.motdfile)
835 vty_out(vty, "banner motd file %s%s", host.motdfile,
836 VTY_NEWLINE);
837 else if (!host.motd)
838 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
839
840 return 1;
841}
842
843/* Utility function for getting command vector. */
844static vector cmd_node_vector(vector v, enum node_type ntype)
845{
846 struct cmd_node *cnode = vector_slot(v, ntype);
847 return cnode->cmd_vector;
848}
849
850/* Completion match types. */
851enum match_type {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100852 no_match = 0,
853 any_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200854 extend_match,
855 ipv4_prefix_match,
856 ipv4_match,
857 ipv6_prefix_match,
858 ipv6_match,
859 range_match,
860 vararg_match,
861 partly_match,
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100862 exact_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200863};
864
865static enum match_type cmd_ipv4_match(const char *str)
866{
867 const char *sp;
868 int dots = 0, nums = 0;
869 char buf[4];
870
871 if (str == NULL)
872 return partly_match;
873
874 for (;;) {
875 memset(buf, 0, sizeof(buf));
876 sp = str;
877 while (*str != '\0') {
878 if (*str == '.') {
879 if (dots >= 3)
880 return no_match;
881
882 if (*(str + 1) == '.')
883 return no_match;
884
885 if (*(str + 1) == '\0')
886 return partly_match;
887
888 dots++;
889 break;
890 }
891 if (!isdigit((int)*str))
892 return no_match;
893
894 str++;
895 }
896
897 if (str - sp > 3)
898 return no_match;
899
900 strncpy(buf, sp, str - sp);
901 if (atoi(buf) > 255)
902 return no_match;
903
904 nums++;
905
906 if (*str == '\0')
907 break;
908
909 str++;
910 }
911
912 if (nums < 4)
913 return partly_match;
914
915 return exact_match;
916}
917
918static enum match_type cmd_ipv4_prefix_match(const char *str)
919{
920 const char *sp;
921 int dots = 0;
922 char buf[4];
923
924 if (str == NULL)
925 return partly_match;
926
927 for (;;) {
928 memset(buf, 0, sizeof(buf));
929 sp = str;
930 while (*str != '\0' && *str != '/') {
931 if (*str == '.') {
932 if (dots == 3)
933 return no_match;
934
935 if (*(str + 1) == '.' || *(str + 1) == '/')
936 return no_match;
937
938 if (*(str + 1) == '\0')
939 return partly_match;
940
941 dots++;
942 break;
943 }
944
945 if (!isdigit((int)*str))
946 return no_match;
947
948 str++;
949 }
950
951 if (str - sp > 3)
952 return no_match;
953
954 strncpy(buf, sp, str - sp);
955 if (atoi(buf) > 255)
956 return no_match;
957
958 if (dots == 3) {
959 if (*str == '/') {
960 if (*(str + 1) == '\0')
961 return partly_match;
962
963 str++;
964 break;
965 } else if (*str == '\0')
966 return partly_match;
967 }
968
969 if (*str == '\0')
970 return partly_match;
971
972 str++;
973 }
974
975 sp = str;
976 while (*str != '\0') {
977 if (!isdigit((int)*str))
978 return no_match;
979
980 str++;
981 }
982
983 if (atoi(sp) > 32)
984 return no_match;
985
986 return exact_match;
987}
988
989#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
990#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
991#define STATE_START 1
992#define STATE_COLON 2
993#define STATE_DOUBLE 3
994#define STATE_ADDR 4
995#define STATE_DOT 5
996#define STATE_SLASH 6
997#define STATE_MASK 7
998
999#ifdef HAVE_IPV6
1000
1001static enum match_type cmd_ipv6_match(const char *str)
1002{
1003 int state = STATE_START;
1004 int colons = 0, nums = 0, double_colon = 0;
1005 const char *sp = NULL;
1006 struct sockaddr_in6 sin6_dummy;
1007 int ret;
1008
1009 if (str == NULL)
1010 return partly_match;
1011
1012 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
1013 return no_match;
1014
1015 /* use inet_pton that has a better support,
1016 * for example inet_pton can support the automatic addresses:
1017 * ::1.2.3.4
1018 */
1019 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1020
1021 if (ret == 1)
1022 return exact_match;
1023
1024 while (*str != '\0') {
1025 switch (state) {
1026 case STATE_START:
1027 if (*str == ':') {
1028 if (*(str + 1) != ':' && *(str + 1) != '\0')
1029 return no_match;
1030 colons--;
1031 state = STATE_COLON;
1032 } else {
1033 sp = str;
1034 state = STATE_ADDR;
1035 }
1036
1037 continue;
1038 case STATE_COLON:
1039 colons++;
1040 if (*(str + 1) == ':')
1041 state = STATE_DOUBLE;
1042 else {
1043 sp = str + 1;
1044 state = STATE_ADDR;
1045 }
1046 break;
1047 case STATE_DOUBLE:
1048 if (double_colon)
1049 return no_match;
1050
1051 if (*(str + 1) == ':')
1052 return no_match;
1053 else {
1054 if (*(str + 1) != '\0')
1055 colons++;
1056 sp = str + 1;
1057 state = STATE_ADDR;
1058 }
1059
1060 double_colon++;
1061 nums++;
1062 break;
1063 case STATE_ADDR:
1064 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1065 if (str - sp > 3)
1066 return no_match;
1067
1068 nums++;
1069 state = STATE_COLON;
1070 }
1071 if (*(str + 1) == '.')
1072 state = STATE_DOT;
1073 break;
1074 case STATE_DOT:
1075 state = STATE_ADDR;
1076 break;
1077 default:
1078 break;
1079 }
1080
1081 if (nums > 8)
1082 return no_match;
1083
1084 if (colons > 7)
1085 return no_match;
1086
1087 str++;
1088 }
1089
1090#if 0
1091 if (nums < 11)
1092 return partly_match;
1093#endif /* 0 */
1094
1095 return exact_match;
1096}
1097
1098static enum match_type cmd_ipv6_prefix_match(const char *str)
1099{
1100 int state = STATE_START;
1101 int colons = 0, nums = 0, double_colon = 0;
1102 int mask;
1103 const char *sp = NULL;
1104 char *endptr = NULL;
1105
1106 if (str == NULL)
1107 return partly_match;
1108
1109 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
1110 return no_match;
1111
1112 while (*str != '\0' && state != STATE_MASK) {
1113 switch (state) {
1114 case STATE_START:
1115 if (*str == ':') {
1116 if (*(str + 1) != ':' && *(str + 1) != '\0')
1117 return no_match;
1118 colons--;
1119 state = STATE_COLON;
1120 } else {
1121 sp = str;
1122 state = STATE_ADDR;
1123 }
1124
1125 continue;
1126 case STATE_COLON:
1127 colons++;
1128 if (*(str + 1) == '/')
1129 return no_match;
1130 else if (*(str + 1) == ':')
1131 state = STATE_DOUBLE;
1132 else {
1133 sp = str + 1;
1134 state = STATE_ADDR;
1135 }
1136 break;
1137 case STATE_DOUBLE:
1138 if (double_colon)
1139 return no_match;
1140
1141 if (*(str + 1) == ':')
1142 return no_match;
1143 else {
1144 if (*(str + 1) != '\0' && *(str + 1) != '/')
1145 colons++;
1146 sp = str + 1;
1147
1148 if (*(str + 1) == '/')
1149 state = STATE_SLASH;
1150 else
1151 state = STATE_ADDR;
1152 }
1153
1154 double_colon++;
1155 nums += 1;
1156 break;
1157 case STATE_ADDR:
1158 if (*(str + 1) == ':' || *(str + 1) == '.'
1159 || *(str + 1) == '\0' || *(str + 1) == '/') {
1160 if (str - sp > 3)
1161 return no_match;
1162
1163 for (; sp <= str; sp++)
1164 if (*sp == '/')
1165 return no_match;
1166
1167 nums++;
1168
1169 if (*(str + 1) == ':')
1170 state = STATE_COLON;
1171 else if (*(str + 1) == '.')
1172 state = STATE_DOT;
1173 else if (*(str + 1) == '/')
1174 state = STATE_SLASH;
1175 }
1176 break;
1177 case STATE_DOT:
1178 state = STATE_ADDR;
1179 break;
1180 case STATE_SLASH:
1181 if (*(str + 1) == '\0')
1182 return partly_match;
1183
1184 state = STATE_MASK;
1185 break;
1186 default:
1187 break;
1188 }
1189
1190 if (nums > 11)
1191 return no_match;
1192
1193 if (colons > 7)
1194 return no_match;
1195
1196 str++;
1197 }
1198
1199 if (state < STATE_MASK)
1200 return partly_match;
1201
1202 mask = strtol(str, &endptr, 10);
1203 if (*endptr != '\0')
1204 return no_match;
1205
1206 if (mask < 0 || mask > 128)
1207 return no_match;
1208
1209/* I don't know why mask < 13 makes command match partly.
1210 Forgive me to make this comments. I Want to set static default route
1211 because of lack of function to originate default in ospf6d; sorry
1212 yasu
1213 if (mask < 13)
1214 return partly_match;
1215*/
1216
1217 return exact_match;
1218}
1219
1220#endif /* HAVE_IPV6 */
1221
1222#define DECIMAL_STRLEN_MAX 10
1223
1224static int cmd_range_match(const char *range, const char *str)
1225{
1226 char *p;
1227 char buf[DECIMAL_STRLEN_MAX + 1];
1228 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001229
1230 if (str == NULL)
1231 return 1;
1232
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001233 if (range[1] == '-') {
1234 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001236 val = strtol(str, &endptr, 10);
1237 if (*endptr != '\0')
1238 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001239
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001240 range += 2;
1241 p = strchr(range, '-');
1242 if (p == NULL)
1243 return 0;
1244 if (p - range > DECIMAL_STRLEN_MAX)
1245 return 0;
1246 strncpy(buf, range, p - range);
1247 buf[p - range] = '\0';
1248 min = -strtol(buf, &endptr, 10);
1249 if (*endptr != '\0')
1250 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001251
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001252 range = p + 1;
1253 p = strchr(range, '>');
1254 if (p == NULL)
1255 return 0;
1256 if (p - range > DECIMAL_STRLEN_MAX)
1257 return 0;
1258 strncpy(buf, range, p - range);
1259 buf[p - range] = '\0';
1260 max = strtol(buf, &endptr, 10);
1261 if (*endptr != '\0')
1262 return 0;
1263
1264 if (val < min || val > max)
1265 return 0;
1266 } else {
1267 unsigned long min, max, val;
1268
1269 val = strtoul(str, &endptr, 10);
1270 if (*endptr != '\0')
1271 return 0;
1272
1273 range++;
1274 p = strchr(range, '-');
1275 if (p == NULL)
1276 return 0;
1277 if (p - range > DECIMAL_STRLEN_MAX)
1278 return 0;
1279 strncpy(buf, range, p - range);
1280 buf[p - range] = '\0';
1281 min = strtoul(buf, &endptr, 10);
1282 if (*endptr != '\0')
1283 return 0;
1284
1285 range = p + 1;
1286 p = strchr(range, '>');
1287 if (p == NULL)
1288 return 0;
1289 if (p - range > DECIMAL_STRLEN_MAX)
1290 return 0;
1291 strncpy(buf, range, p - range);
1292 buf[p - range] = '\0';
1293 max = strtoul(buf, &endptr, 10);
1294 if (*endptr != '\0')
1295 return 0;
1296
1297 if (val < min || val > max)
1298 return 0;
1299 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001300
1301 return 1;
1302}
1303
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001304/* helper to retrieve the 'real' argument string from an optional argument */
1305static char *
1306cmd_deopt(const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001307{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001308 /* we've got "[blah]". We want to strip off the []s and redo the
1309 * match check for "blah"
1310 */
1311 size_t len = strlen(str);
1312 char *tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001313
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001314 if (len < 3)
1315 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001316
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001317 /* tmp will hold a string of len-2 chars, so 'len' size is fine */
1318 tmp = talloc_size(NULL, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001319
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001320 memcpy(tmp, (str + 1), len - 2);
1321 tmp[len - 2] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001322
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001323 return tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001324}
1325
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001326static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001327cmd_match(const char *str, const char *command,
1328 enum match_type min, bool recur)
1329{
1330
1331 if (recur && CMD_OPTION(str))
1332 {
1333 enum match_type ret;
1334 char *tmp = cmd_deopt(str);
1335
1336 /* this would be a bug in a command, however handle it gracefully
1337 * as it we only discover it if a user tries to run it
1338 */
1339 if (tmp == NULL)
1340 return no_match;
1341
1342 ret = cmd_match(tmp, command, min, false);
1343
1344 talloc_free(tmp);
1345
1346 return ret;
1347 }
1348 else if (CMD_VARARG(str))
1349 return vararg_match;
1350 else if (CMD_RANGE(str))
1351 {
1352 if (cmd_range_match(str, command))
1353 return range_match;
1354 }
1355#ifdef HAVE_IPV6
1356 else if (CMD_IPV6(str))
1357 {
1358 if (cmd_ipv6_match(command) >= min)
1359 return ipv6_match;
1360 }
1361 else if (CMD_IPV6_PREFIX(str))
1362 {
1363 if (cmd_ipv6_prefix_match(command) >= min)
1364 return ipv6_prefix_match;
1365 }
1366#endif /* HAVE_IPV6 */
1367 else if (CMD_IPV4(str))
1368 {
1369 if (cmd_ipv4_match(command) >= min)
1370 return ipv4_match;
1371 }
1372 else if (CMD_IPV4_PREFIX(str))
1373 {
1374 if (cmd_ipv4_prefix_match(command) >= min)
1375 return ipv4_prefix_match;
1376 }
1377 else if (CMD_VARIABLE(str))
1378 return extend_match;
1379 else if (strncmp(command, str, strlen(command)) == 0)
1380 {
1381 if (strcmp(command, str) == 0)
1382 return exact_match;
1383 else if (partly_match >= min)
1384 return partly_match;
1385 }
1386
1387 return no_match;
1388}
1389
1390/* Filter vector at the specified index and by the given command string, to
1391 * the desired matching level (thus allowing part matches), and return match
1392 * type flag.
1393 */
1394static enum match_type
1395cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001396{
1397 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001398 struct cmd_element *cmd_element;
1399 enum match_type match_type;
1400 vector descvec;
1401 struct desc *desc;
1402
1403 match_type = no_match;
1404
1405 /* If command and cmd_element string does not match set NULL to vector */
1406 for (i = 0; i < vector_active(v); i++)
1407 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001408 if (index >= vector_active(cmd_element->strvec))
1409 vector_slot(v, i) = NULL;
1410 else {
1411 unsigned int j;
1412 int matched = 0;
1413
1414 descvec =
1415 vector_slot(cmd_element->strvec, index);
1416
1417 for (j = 0; j < vector_active(descvec); j++)
1418 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001419 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001420
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001421 ret = cmd_match (desc->cmd, command, level, true);
1422
1423 if (ret != no_match)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001424 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001425
1426 if (match_type < ret)
1427 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428 }
1429 if (!matched)
1430 vector_slot(v, i) = NULL;
1431 }
1432 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001433
1434 if (match_type == no_match)
1435 return no_match;
1436
1437 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1438 * go again and filter out commands whose argument (at this index) is
1439 * 'weaker'. E.g., if we have 2 commands:
1440 *
1441 * foo bar <1-255>
1442 * foo bar BLAH
1443 *
1444 * and the command string is 'foo bar 10', then we will get here with with
1445 * 'range_match' being the strongest match. However, if 'BLAH' came
1446 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1447 *
1448 * If we don't do a 2nd pass and filter it out, the higher-layers will
1449 * consider this to be ambiguous.
1450 */
1451 for (i = 0; i < vector_active(v); i++)
1452 if ((cmd_element = vector_slot(v, i)) != NULL) {
1453 if (index >= vector_active(cmd_element->strvec))
1454 vector_slot(v, i) = NULL;
1455 else {
1456 unsigned int j;
1457 int matched = 0;
1458
1459 descvec =
1460 vector_slot(cmd_element->strvec, index);
1461
1462 for (j = 0; j < vector_active(descvec); j++)
1463 if ((desc = vector_slot(descvec, j))) {
1464 enum match_type ret;
1465
1466 ret = cmd_match(desc->cmd, command, any_match, true);
1467
1468 if (ret >= match_type)
1469 matched++;
1470 }
1471 if (!matched)
1472 vector_slot(v, i) = NULL;
1473 }
1474 }
1475
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001476 return match_type;
1477}
1478
1479/* Check ambiguous match */
1480static int
1481is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1482{
1483 unsigned int i;
1484 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001485 struct cmd_element *cmd_element;
1486 const char *matched = NULL;
1487 vector descvec;
1488 struct desc *desc;
1489
1490 for (i = 0; i < vector_active(v); i++)
1491 if ((cmd_element = vector_slot(v, i)) != NULL) {
1492 int match = 0;
1493
1494 descvec = vector_slot(cmd_element->strvec, index);
1495
1496 for (j = 0; j < vector_active(descvec); j++)
1497 if ((desc = vector_slot(descvec, j))) {
1498 enum match_type ret;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001499 const char *str = desc->cmd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001500
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001501 if (CMD_OPTION(str))
1502 if ((str = cmd_deopt(str)) == NULL)
1503 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001504
1505 switch (type) {
1506 case exact_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001507 if (!(CMD_VARIABLE (str))
1508 && strcmp(command, str) == 0)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001509 match++;
1510 break;
1511 case partly_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001512 if (!(CMD_VARIABLE (str))
1513 && strncmp(command, str, strlen (command)) == 0)
1514 {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001515 if (matched
1516 && strcmp(matched,
1517 str) != 0)
1518 return 1; /* There is ambiguous match. */
1519 else
1520 matched = str;
1521 match++;
1522 }
1523 break;
1524 case range_match:
1525 if (cmd_range_match
1526 (str, command)) {
1527 if (matched
1528 && strcmp(matched,
1529 str) != 0)
1530 return 1;
1531 else
1532 matched = str;
1533 match++;
1534 }
1535 break;
1536#ifdef HAVE_IPV6
1537 case ipv6_match:
1538 if (CMD_IPV6(str))
1539 match++;
1540 break;
1541 case ipv6_prefix_match:
1542 if ((ret =
1543 cmd_ipv6_prefix_match
1544 (command)) != no_match) {
1545 if (ret == partly_match)
1546 return 2; /* There is incomplete match. */
1547
1548 match++;
1549 }
1550 break;
1551#endif /* HAVE_IPV6 */
1552 case ipv4_match:
1553 if (CMD_IPV4(str))
1554 match++;
1555 break;
1556 case ipv4_prefix_match:
1557 if ((ret =
1558 cmd_ipv4_prefix_match
1559 (command)) != no_match) {
1560 if (ret == partly_match)
1561 return 2; /* There is incomplete match. */
1562
1563 match++;
1564 }
1565 break;
1566 case extend_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001567 if (CMD_VARIABLE (str))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001568 match++;
1569 break;
1570 case no_match:
1571 default:
1572 break;
1573 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001574
1575 if (CMD_OPTION(desc->cmd))
1576 talloc_free((void*)str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577 }
1578 if (!match)
1579 vector_slot(v, i) = NULL;
1580 }
1581 return 0;
1582}
1583
1584/* If src matches dst return dst string, otherwise return NULL */
1585static const char *cmd_entry_function(const char *src, const char *dst)
1586{
1587 /* Skip variable arguments. */
1588 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1589 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1590 return NULL;
1591
1592 /* In case of 'command \t', given src is NULL string. */
1593 if (src == NULL)
1594 return dst;
1595
1596 /* Matched with input string. */
1597 if (strncmp(src, dst, strlen(src)) == 0)
1598 return dst;
1599
1600 return NULL;
1601}
1602
1603/* If src matches dst return dst string, otherwise return NULL */
1604/* This version will return the dst string always if it is
1605 CMD_VARIABLE for '?' key processing */
1606static const char *cmd_entry_function_desc(const char *src, const char *dst)
1607{
1608 if (CMD_VARARG(dst))
1609 return dst;
1610
1611 if (CMD_RANGE(dst)) {
1612 if (cmd_range_match(dst, src))
1613 return dst;
1614 else
1615 return NULL;
1616 }
1617#ifdef HAVE_IPV6
1618 if (CMD_IPV6(dst)) {
1619 if (cmd_ipv6_match(src))
1620 return dst;
1621 else
1622 return NULL;
1623 }
1624
1625 if (CMD_IPV6_PREFIX(dst)) {
1626 if (cmd_ipv6_prefix_match(src))
1627 return dst;
1628 else
1629 return NULL;
1630 }
1631#endif /* HAVE_IPV6 */
1632
1633 if (CMD_IPV4(dst)) {
1634 if (cmd_ipv4_match(src))
1635 return dst;
1636 else
1637 return NULL;
1638 }
1639
1640 if (CMD_IPV4_PREFIX(dst)) {
1641 if (cmd_ipv4_prefix_match(src))
1642 return dst;
1643 else
1644 return NULL;
1645 }
1646
1647 /* Optional or variable commands always match on '?' */
1648 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1649 return dst;
1650
1651 /* In case of 'command \t', given src is NULL string. */
1652 if (src == NULL)
1653 return dst;
1654
1655 if (strncmp(src, dst, strlen(src)) == 0)
1656 return dst;
1657 else
1658 return NULL;
1659}
1660
1661/* Check same string element existence. If it isn't there return
1662 1. */
1663static int cmd_unique_string(vector v, const char *str)
1664{
1665 unsigned int i;
1666 char *match;
1667
1668 for (i = 0; i < vector_active(v); i++)
1669 if ((match = vector_slot(v, i)) != NULL)
1670 if (strcmp(match, str) == 0)
1671 return 0;
1672 return 1;
1673}
1674
1675/* Compare string to description vector. If there is same string
1676 return 1 else return 0. */
1677static int desc_unique_string(vector v, const char *str)
1678{
1679 unsigned int i;
1680 struct desc *desc;
1681
1682 for (i = 0; i < vector_active(v); i++)
1683 if ((desc = vector_slot(v, i)) != NULL)
1684 if (strcmp(desc->cmd, str) == 0)
1685 return 1;
1686 return 0;
1687}
1688
1689static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1690{
1691 if (first_word != NULL &&
1692 node != AUTH_NODE &&
1693 node != VIEW_NODE &&
1694 node != AUTH_ENABLE_NODE &&
1695 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1696 return 1;
1697 return 0;
1698}
1699
1700/* '?' describe command support. */
1701static vector
1702cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1703{
1704 unsigned int i;
1705 vector cmd_vector;
1706#define INIT_MATCHVEC_SIZE 10
1707 vector matchvec;
1708 struct cmd_element *cmd_element;
1709 unsigned int index;
1710 int ret;
1711 enum match_type match;
1712 char *command;
1713 static struct desc desc_cr = { "<cr>", "" };
1714
1715 /* Set index. */
1716 if (vector_active(vline) == 0) {
1717 *status = CMD_ERR_NO_MATCH;
1718 return NULL;
1719 } else
1720 index = vector_active(vline) - 1;
1721
1722 /* Make copy vector of current node's command vector. */
1723 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1724
1725 /* Prepare match vector */
1726 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1727
1728 /* Filter commands. */
1729 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001730 for (i = 0; i < index; i++) {
1731 command = vector_slot(vline, i);
1732 if (!command)
1733 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001734
Harald Welte80d30fe2013-02-12 11:08:57 +01001735 match = cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001736
Harald Welte80d30fe2013-02-12 11:08:57 +01001737 if (match == vararg_match) {
1738 struct cmd_element *cmd_element;
1739 vector descvec;
1740 unsigned int j, k;
1741
1742 for (j = 0; j < vector_active(cmd_vector); j++)
1743 if ((cmd_element =
1744 vector_slot(cmd_vector, j)) != NULL
1745 &&
1746 (vector_active(cmd_element->strvec))) {
1747 descvec =
1748 vector_slot(cmd_element->
1749 strvec,
1750 vector_active
1751 (cmd_element->
1752 strvec) - 1);
1753 for (k = 0;
1754 k < vector_active(descvec);
1755 k++) {
1756 struct desc *desc =
1757 vector_slot(descvec,
1758 k);
1759 vector_set(matchvec,
1760 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001761 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001762 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001763
Harald Welte80d30fe2013-02-12 11:08:57 +01001764 vector_set(matchvec, &desc_cr);
1765 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001766
Harald Welte80d30fe2013-02-12 11:08:57 +01001767 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001768 }
1769
Harald Welte80d30fe2013-02-12 11:08:57 +01001770 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1771 match)) == 1) {
1772 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001773 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001774 *status = CMD_ERR_AMBIGUOUS;
1775 return NULL;
1776 } else if (ret == 2) {
1777 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001778 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001779 *status = CMD_ERR_NO_MATCH;
1780 return NULL;
1781 }
1782 }
1783
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001784 /* Prepare match vector */
1785 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1786
1787 /* Make sure that cmd_vector is filtered based on current word */
1788 command = vector_slot(vline, index);
1789 if (command)
Vadim Yanitskiy49a0dec2017-06-12 03:49:38 +07001790 cmd_filter(command, cmd_vector, index, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001791
1792 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001793 for (i = 0; i < vector_active(cmd_vector); i++) {
1794 const char *string = NULL;
1795 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001796
Harald Welte80d30fe2013-02-12 11:08:57 +01001797 cmd_element = vector_slot(cmd_vector, i);
1798 if (!cmd_element)
1799 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001800
Harald Welted17aa592013-02-12 11:11:34 +01001801 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1802 continue;
1803
Harald Welte80d30fe2013-02-12 11:08:57 +01001804 strvec = cmd_element->strvec;
1805
1806 /* if command is NULL, index may be equal to vector_active */
1807 if (command && index >= vector_active(strvec))
1808 vector_slot(cmd_vector, i) = NULL;
1809 else {
1810 /* Check if command is completed. */
1811 if (command == NULL
1812 && index == vector_active(strvec)) {
1813 string = "<cr>";
1814 if (!desc_unique_string(matchvec, string))
1815 vector_set(matchvec, &desc_cr);
1816 } else {
1817 unsigned int j;
1818 vector descvec = vector_slot(strvec, index);
1819 struct desc *desc;
1820
1821 for (j = 0; j < vector_active(descvec); j++) {
1822 desc = vector_slot(descvec, j);
1823 if (!desc)
1824 continue;
1825 string = cmd_entry_function_desc
1826 (command, desc->cmd);
1827 if (!string)
1828 continue;
1829 /* Uniqueness check */
1830 if (!desc_unique_string(matchvec, string))
1831 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001832 }
1833 }
1834 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001835 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001836 vector_free(cmd_vector);
1837
1838 if (vector_slot(matchvec, 0) == NULL) {
1839 vector_free(matchvec);
1840 *status = CMD_ERR_NO_MATCH;
1841 } else
1842 *status = CMD_SUCCESS;
1843
1844 return matchvec;
1845}
1846
1847vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1848{
1849 vector ret;
1850
1851 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1852 enum node_type onode;
1853 vector shifted_vline;
1854 unsigned int index;
1855
1856 onode = vty->node;
1857 vty->node = ENABLE_NODE;
1858 /* We can try it on enable node, cos' the vty is authenticated */
1859
1860 shifted_vline = vector_init(vector_count(vline));
1861 /* use memcpy? */
1862 for (index = 1; index < vector_active(vline); index++) {
1863 vector_set_index(shifted_vline, index - 1,
1864 vector_lookup(vline, index));
1865 }
1866
1867 ret = cmd_describe_command_real(shifted_vline, vty, status);
1868
1869 vector_free(shifted_vline);
1870 vty->node = onode;
1871 return ret;
1872 }
1873
1874 return cmd_describe_command_real(vline, vty, status);
1875}
1876
1877/* Check LCD of matched command. */
1878static int cmd_lcd(char **matched)
1879{
1880 int i;
1881 int j;
1882 int lcd = -1;
1883 char *s1, *s2;
1884 char c1, c2;
1885
1886 if (matched[0] == NULL || matched[1] == NULL)
1887 return 0;
1888
1889 for (i = 1; matched[i] != NULL; i++) {
1890 s1 = matched[i - 1];
1891 s2 = matched[i];
1892
1893 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1894 if (c1 != c2)
1895 break;
1896
1897 if (lcd < 0)
1898 lcd = j;
1899 else {
1900 if (lcd > j)
1901 lcd = j;
1902 }
1903 }
1904 return lcd;
1905}
1906
1907/* Command line completion support. */
1908static char **cmd_complete_command_real(vector vline, struct vty *vty,
1909 int *status)
1910{
1911 unsigned int i;
1912 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1913#define INIT_MATCHVEC_SIZE 10
1914 vector matchvec;
1915 struct cmd_element *cmd_element;
1916 unsigned int index;
1917 char **match_str;
1918 struct desc *desc;
1919 vector descvec;
1920 char *command;
1921 int lcd;
1922
1923 if (vector_active(vline) == 0) {
1924 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001925 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001926 return NULL;
1927 } else
1928 index = vector_active(vline) - 1;
1929
1930 /* First, filter by preceeding command string */
1931 for (i = 0; i < index; i++)
1932 if ((command = vector_slot(vline, i))) {
1933 enum match_type match;
1934 int ret;
1935
1936 /* First try completion match, if there is exactly match return 1 */
1937 match =
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001938 cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001939
1940 /* If there is exact match then filter ambiguous match else check
1941 ambiguousness. */
1942 if ((ret =
1943 is_cmd_ambiguous(command, cmd_vector, i,
1944 match)) == 1) {
1945 vector_free(cmd_vector);
1946 *status = CMD_ERR_AMBIGUOUS;
1947 return NULL;
1948 }
1949 /*
1950 else if (ret == 2)
1951 {
1952 vector_free (cmd_vector);
1953 *status = CMD_ERR_NO_MATCH;
1954 return NULL;
1955 }
1956 */
1957 }
1958
1959 /* Prepare match vector. */
1960 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1961
1962 /* Now we got into completion */
1963 for (i = 0; i < vector_active(cmd_vector); i++)
1964 if ((cmd_element = vector_slot(cmd_vector, i))) {
1965 const char *string;
1966 vector strvec = cmd_element->strvec;
1967
1968 /* Check field length */
1969 if (index >= vector_active(strvec))
1970 vector_slot(cmd_vector, i) = NULL;
1971 else {
1972 unsigned int j;
1973
1974 descvec = vector_slot(strvec, index);
1975 for (j = 0; j < vector_active(descvec); j++)
1976 if ((desc = vector_slot(descvec, j))) {
1977 if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
1978 if (cmd_unique_string (matchvec, string))
1979 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
1980 }
1981 }
1982 }
1983
1984 /* We don't need cmd_vector any more. */
1985 vector_free(cmd_vector);
1986
1987 /* No matched command */
1988 if (vector_slot(matchvec, 0) == NULL) {
1989 vector_free(matchvec);
1990
1991 /* In case of 'command \t' pattern. Do you need '?' command at
1992 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01001993 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001994 *status = CMD_ERR_NOTHING_TODO;
1995 else
1996 *status = CMD_ERR_NO_MATCH;
1997 return NULL;
1998 }
1999
2000 /* Only one matched */
2001 if (vector_slot(matchvec, 1) == NULL) {
2002 match_str = (char **)matchvec->index;
2003 vector_only_wrapper_free(matchvec);
2004 *status = CMD_COMPLETE_FULL_MATCH;
2005 return match_str;
2006 }
2007 /* Make it sure last element is NULL. */
2008 vector_set(matchvec, NULL);
2009
2010 /* Check LCD of matched strings. */
2011 if (vector_slot(vline, index) != NULL) {
2012 lcd = cmd_lcd((char **)matchvec->index);
2013
2014 if (lcd) {
2015 int len = strlen(vector_slot(vline, index));
2016
2017 if (len < lcd) {
2018 char *lcdstr;
2019
2020 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2021 "complete-lcdstr");
2022 memcpy(lcdstr, matchvec->index[0], lcd);
2023 lcdstr[lcd] = '\0';
2024
2025 /* match_str = (char **) &lcdstr; */
2026
2027 /* Free matchvec. */
2028 for (i = 0; i < vector_active(matchvec); i++) {
2029 if (vector_slot(matchvec, i))
2030 talloc_free(vector_slot(matchvec, i));
2031 }
2032 vector_free(matchvec);
2033
2034 /* Make new matchvec. */
2035 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2036 vector_set(matchvec, lcdstr);
2037 match_str = (char **)matchvec->index;
2038 vector_only_wrapper_free(matchvec);
2039
2040 *status = CMD_COMPLETE_MATCH;
2041 return match_str;
2042 }
2043 }
2044 }
2045
2046 match_str = (char **)matchvec->index;
2047 vector_only_wrapper_free(matchvec);
2048 *status = CMD_COMPLETE_LIST_MATCH;
2049 return match_str;
2050}
2051
2052char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2053{
2054 char **ret;
2055
2056 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2057 enum node_type onode;
2058 vector shifted_vline;
2059 unsigned int index;
2060
2061 onode = vty->node;
2062 vty->node = ENABLE_NODE;
2063 /* We can try it on enable node, cos' the vty is authenticated */
2064
2065 shifted_vline = vector_init(vector_count(vline));
2066 /* use memcpy? */
2067 for (index = 1; index < vector_active(vline); index++) {
2068 vector_set_index(shifted_vline, index - 1,
2069 vector_lookup(vline, index));
2070 }
2071
2072 ret = cmd_complete_command_real(shifted_vline, vty, status);
2073
2074 vector_free(shifted_vline);
2075 vty->node = onode;
2076 return ret;
2077 }
2078
2079 return cmd_complete_command_real(vline, vty, status);
2080}
2081
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002082static struct vty_parent_node *vty_parent(struct vty *vty)
2083{
2084 return llist_first_entry_or_null(&vty->parent_nodes,
2085 struct vty_parent_node,
2086 entry);
2087}
2088
2089static bool vty_pop_parent(struct vty *vty)
2090{
2091 struct vty_parent_node *parent = vty_parent(vty);
2092 if (!parent)
2093 return false;
2094 llist_del(&parent->entry);
2095 vty->node = parent->node;
2096 vty->priv = parent->priv;
2097 if (vty->indent)
2098 talloc_free(vty->indent);
2099 vty->indent = parent->indent;
2100 talloc_free(parent);
2101 return true;
2102}
2103
2104static void vty_clear_parents(struct vty *vty)
2105{
2106 while (vty_pop_parent(vty));
2107}
2108
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002109/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002110/*
2111 * This function MUST eventually converge on a node when called repeatedly,
2112 * there must not be any cycles.
2113 * All 'config' nodes shall converge on CONFIG_NODE.
2114 * All other 'enable' nodes shall converge on ENABLE_NODE.
2115 * All 'view' only nodes shall converge on VIEW_NODE.
2116 * All other nodes shall converge on themselves or it must be ensured,
2117 * that the user's rights are not extended anyhow by calling this function.
2118 *
2119 * Note that these requirements also apply to all functions that are used
2120 * as go_parent_cb.
2121 * Note also that this function relies on the is_config_child callback to
2122 * recognize non-config nodes if go_parent_cb is not set.
2123 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002124int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002125{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002126 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002127 case AUTH_NODE:
2128 case VIEW_NODE:
2129 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002130 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002131 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002132 break;
2133
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002134 case AUTH_ENABLE_NODE:
2135 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002136 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002137 break;
2138
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002139 case CFG_LOG_NODE:
2140 case VTY_NODE:
2141 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002142 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002143 break;
2144
2145 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002146 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002147 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002148 vty_pop_parent(vty);
2149 }
2150 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002151 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002152 vty_clear_parents(vty);
2153 }
2154 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002155 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002156 vty_clear_parents(vty);
2157 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002158 break;
2159 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002160
2161 return vty->node;
2162}
2163
2164/* Execute command by argument vline vector. */
2165static int
2166cmd_execute_command_real(vector vline, struct vty *vty,
2167 struct cmd_element **cmd)
2168{
2169 unsigned int i;
2170 unsigned int index;
2171 vector cmd_vector;
2172 struct cmd_element *cmd_element;
2173 struct cmd_element *matched_element;
2174 unsigned int matched_count, incomplete_count;
2175 int argc;
2176 const char *argv[CMD_ARGC_MAX];
2177 enum match_type match = 0;
2178 int varflag;
2179 char *command;
2180
2181 /* Make copy of command elements. */
2182 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2183
2184 for (index = 0; index < vector_active(vline); index++)
2185 if ((command = vector_slot(vline, index))) {
2186 int ret;
2187
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002188 match = cmd_filter(command, cmd_vector, index,
2189 any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002190
2191 if (match == vararg_match)
2192 break;
2193
2194 ret =
2195 is_cmd_ambiguous(command, cmd_vector, index, match);
2196
2197 if (ret == 1) {
2198 vector_free(cmd_vector);
2199 return CMD_ERR_AMBIGUOUS;
2200 } else if (ret == 2) {
2201 vector_free(cmd_vector);
2202 return CMD_ERR_NO_MATCH;
2203 }
2204 }
2205
2206 /* Check matched count. */
2207 matched_element = NULL;
2208 matched_count = 0;
2209 incomplete_count = 0;
2210
2211 for (i = 0; i < vector_active(cmd_vector); i++)
2212 if ((cmd_element = vector_slot(cmd_vector, i))) {
2213 if (match == vararg_match
2214 || index >= cmd_element->cmdsize) {
2215 matched_element = cmd_element;
2216#if 0
2217 printf("DEBUG: %s\n", cmd_element->string);
2218#endif
2219 matched_count++;
2220 } else {
2221 incomplete_count++;
2222 }
2223 }
2224
2225 /* Finish of using cmd_vector. */
2226 vector_free(cmd_vector);
2227
2228 /* To execute command, matched_count must be 1. */
2229 if (matched_count == 0) {
2230 if (incomplete_count)
2231 return CMD_ERR_INCOMPLETE;
2232 else
2233 return CMD_ERR_NO_MATCH;
2234 }
2235
2236 if (matched_count > 1)
2237 return CMD_ERR_AMBIGUOUS;
2238
2239 /* Argument treatment */
2240 varflag = 0;
2241 argc = 0;
2242
2243 for (i = 0; i < vector_active(vline); i++) {
2244 if (varflag)
2245 argv[argc++] = vector_slot(vline, i);
2246 else {
2247 vector descvec =
2248 vector_slot(matched_element->strvec, i);
2249
2250 if (vector_active(descvec) == 1) {
2251 struct desc *desc = vector_slot(descvec, 0);
2252
2253 if (CMD_VARARG(desc->cmd))
2254 varflag = 1;
2255
2256 if (varflag || CMD_VARIABLE(desc->cmd)
2257 || CMD_OPTION(desc->cmd))
2258 argv[argc++] = vector_slot(vline, i);
2259 } else
2260 argv[argc++] = vector_slot(vline, i);
2261 }
2262
2263 if (argc >= CMD_ARGC_MAX)
2264 return CMD_ERR_EXEED_ARGC_MAX;
2265 }
2266
2267 /* For vtysh execution. */
2268 if (cmd)
2269 *cmd = matched_element;
2270
2271 if (matched_element->daemon)
2272 return CMD_SUCCESS_DAEMON;
2273
2274 /* Execute matched command. */
2275 return (*matched_element->func) (matched_element, vty, argc, argv);
2276}
2277
2278int
2279cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2280 int vtysh)
2281{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002282 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002283 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002284
2285 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002286
2287 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2288 vector shifted_vline;
2289 unsigned int index;
2290
2291 vty->node = ENABLE_NODE;
2292 /* We can try it on enable node, cos' the vty is authenticated */
2293
2294 shifted_vline = vector_init(vector_count(vline));
2295 /* use memcpy? */
2296 for (index = 1; index < vector_active(vline); index++) {
2297 vector_set_index(shifted_vline, index - 1,
2298 vector_lookup(vline, index));
2299 }
2300
2301 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2302
2303 vector_free(shifted_vline);
2304 vty->node = onode;
2305 return ret;
2306 }
2307
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002308 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002309}
2310
2311/* Execute command by argument readline. */
2312int
2313cmd_execute_command_strict(vector vline, struct vty *vty,
2314 struct cmd_element **cmd)
2315{
2316 unsigned int i;
2317 unsigned int index;
2318 vector cmd_vector;
2319 struct cmd_element *cmd_element;
2320 struct cmd_element *matched_element;
2321 unsigned int matched_count, incomplete_count;
2322 int argc;
2323 const char *argv[CMD_ARGC_MAX];
2324 int varflag;
2325 enum match_type match = 0;
2326 char *command;
2327
2328 /* Make copy of command element */
2329 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2330
2331 for (index = 0; index < vector_active(vline); index++)
2332 if ((command = vector_slot(vline, index))) {
2333 int ret;
2334
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002335 match = cmd_filter(vector_slot(vline, index),
2336 cmd_vector, index, exact_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002337
2338 /* If command meets '.VARARG' then finish matching. */
2339 if (match == vararg_match)
2340 break;
2341
2342 ret =
2343 is_cmd_ambiguous(command, cmd_vector, index, match);
2344 if (ret == 1) {
2345 vector_free(cmd_vector);
2346 return CMD_ERR_AMBIGUOUS;
2347 }
2348 if (ret == 2) {
2349 vector_free(cmd_vector);
2350 return CMD_ERR_NO_MATCH;
2351 }
2352 }
2353
2354 /* Check matched count. */
2355 matched_element = NULL;
2356 matched_count = 0;
2357 incomplete_count = 0;
2358 for (i = 0; i < vector_active(cmd_vector); i++)
2359 if (vector_slot(cmd_vector, i) != NULL) {
2360 cmd_element = vector_slot(cmd_vector, i);
2361
2362 if (match == vararg_match
2363 || index >= cmd_element->cmdsize) {
2364 matched_element = cmd_element;
2365 matched_count++;
2366 } else
2367 incomplete_count++;
2368 }
2369
2370 /* Finish of using cmd_vector. */
2371 vector_free(cmd_vector);
2372
2373 /* To execute command, matched_count must be 1. */
2374 if (matched_count == 0) {
2375 if (incomplete_count)
2376 return CMD_ERR_INCOMPLETE;
2377 else
2378 return CMD_ERR_NO_MATCH;
2379 }
2380
2381 if (matched_count > 1)
2382 return CMD_ERR_AMBIGUOUS;
2383
2384 /* Argument treatment */
2385 varflag = 0;
2386 argc = 0;
2387
2388 for (i = 0; i < vector_active(vline); i++) {
2389 if (varflag)
2390 argv[argc++] = vector_slot(vline, i);
2391 else {
2392 vector descvec =
2393 vector_slot(matched_element->strvec, i);
2394
2395 if (vector_active(descvec) == 1) {
2396 struct desc *desc = vector_slot(descvec, 0);
2397
2398 if (CMD_VARARG(desc->cmd))
2399 varflag = 1;
2400
2401 if (varflag || CMD_VARIABLE(desc->cmd)
2402 || CMD_OPTION(desc->cmd))
2403 argv[argc++] = vector_slot(vline, i);
2404 } else
2405 argv[argc++] = vector_slot(vline, i);
2406 }
2407
2408 if (argc >= CMD_ARGC_MAX)
2409 return CMD_ERR_EXEED_ARGC_MAX;
2410 }
2411
2412 /* For vtysh execution. */
2413 if (cmd)
2414 *cmd = matched_element;
2415
2416 if (matched_element->daemon)
2417 return CMD_SUCCESS_DAEMON;
2418
2419 /* Now execute matched command */
2420 return (*matched_element->func) (matched_element, vty, argc, argv);
2421}
2422
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002423static inline size_t len(const char *str)
2424{
2425 return str? strlen(str) : 0;
2426}
2427
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002428/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2429 * is longer than b, a must start with exactly b, and vice versa.
2430 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2431 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002432static int indent_cmp(const char *a, const char *b)
2433{
2434 size_t al, bl;
2435 al = len(a);
2436 bl = len(b);
2437 if (al > bl) {
2438 if (bl && strncmp(a, b, bl) != 0)
2439 return EINVAL;
2440 return 1;
2441 }
2442 /* al <= bl */
2443 if (al && strncmp(a, b, al) != 0)
2444 return EINVAL;
2445 return (al < bl)? -1 : 0;
2446}
2447
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002448/* Configration make from file. */
2449int config_from_file(struct vty *vty, FILE * fp)
2450{
2451 int ret;
2452 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002453 char *indent;
2454 int cmp;
2455 struct vty_parent_node this_node;
2456 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002457
2458 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002459 indent = NULL;
2460 vline = NULL;
2461 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002462
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002463 if (ret != CMD_SUCCESS)
2464 goto return_invalid_indent;
2465
2466 /* In case of comment or empty line */
2467 if (vline == NULL) {
2468 if (indent) {
2469 talloc_free(indent);
2470 indent = NULL;
2471 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002472 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002473 }
2474
Neels Hofmeyr43063632017-09-19 23:54:01 +02002475 /* We have a nonempty line. */
2476 if (!vty->indent) {
2477 /* We have just entered a node and expecting the first child to come up; but we
2478 * may also skip right back to a parent or ancestor level. */
2479 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002480
Neels Hofmeyr43063632017-09-19 23:54:01 +02002481 /* If there is no parent, record any indentation we encounter. */
2482 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2483
2484 if (cmp == EINVAL)
2485 goto return_invalid_indent;
2486
2487 if (cmp <= 0) {
2488 /* We have gone right back to the parent level or higher, we are skipping
2489 * this child node level entirely. Pop the parent to go back to a node
2490 * that was actually there (to reinstate vty->indent) and re-use below
2491 * go-parent while-loop to find an accurate match of indent in the node
2492 * ancestry. */
2493 vty_go_parent(vty);
2494 } else {
2495 /* The indent is deeper than the just entered parent, record the new
2496 * indentation characters. */
2497 vty->indent = talloc_strdup(vty, indent);
2498 /* This *is* the new indentation. */
2499 cmp = 0;
2500 }
2501 } else {
2502 /* There is a known indentation for this node level, validate and detect node
2503 * exits. */
2504 cmp = indent_cmp(indent, vty->indent);
2505 if (cmp == EINVAL)
2506 goto return_invalid_indent;
2507 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002508
2509 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2510 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2511 while (cmp < 0) {
2512 vty_go_parent(vty);
2513 cmp = indent_cmp(indent, vty->indent);
2514 if (cmp == EINVAL)
2515 goto return_invalid_indent;
2516 }
2517
2518 /* More indent without having entered a child node level? Either the parent node's indent
2519 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2520 * or the indentation increased even though the vty command didn't enter a child. */
2521 if (cmp > 0)
2522 goto return_invalid_indent;
2523
2524 /* Remember the current node before the command possibly changes it. */
2525 this_node = (struct vty_parent_node){
2526 .node = vty->node,
2527 .priv = vty->priv,
2528 .indent = vty->indent,
2529 };
2530
2531 parent = vty_parent(vty);
2532 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002533 cmd_free_strvec(vline);
2534
2535 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002536 && ret != CMD_ERR_NOTHING_TODO) {
2537 if (indent) {
2538 talloc_free(indent);
2539 indent = NULL;
2540 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002541 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002542 }
2543
2544 /* If we have stepped down into a child node, push a parent frame.
2545 * The causality is such: we don't expect every single node entry implementation to push
2546 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2547 * a parent node. Hence if the node changed without the parent node changing, we must
2548 * have stepped into a child node (and now expect a deeper indent). */
2549 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2550 /* Push the parent node. */
2551 parent = talloc_zero(vty, struct vty_parent_node);
2552 *parent = this_node;
2553 llist_add(&parent->entry, &vty->parent_nodes);
2554
2555 /* The current talloc'ed vty->indent string will now be owned by this parent
2556 * struct. Indicate that we don't know what deeper indent characters the user
2557 * will choose. */
2558 vty->indent = NULL;
2559 }
2560
2561 if (indent) {
2562 talloc_free(indent);
2563 indent = NULL;
2564 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002565 }
2566 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002567
2568return_invalid_indent:
2569 if (vline)
2570 cmd_free_strvec(vline);
2571 if (indent) {
2572 talloc_free(indent);
2573 indent = NULL;
2574 }
2575 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002576}
2577
2578/* Configration from terminal */
2579DEFUN(config_terminal,
2580 config_terminal_cmd,
2581 "configure terminal",
2582 "Configuration from vty interface\n" "Configuration terminal\n")
2583{
2584 if (vty_config_lock(vty))
2585 vty->node = CONFIG_NODE;
2586 else {
2587 vty_out(vty, "VTY configuration is locked by other VTY%s",
2588 VTY_NEWLINE);
2589 return CMD_WARNING;
2590 }
2591 return CMD_SUCCESS;
2592}
2593
2594/* Enable command */
2595DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2596{
2597 /* If enable password is NULL, change to ENABLE_NODE */
2598 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2599 vty->type == VTY_SHELL_SERV)
2600 vty->node = ENABLE_NODE;
2601 else
2602 vty->node = AUTH_ENABLE_NODE;
2603
2604 return CMD_SUCCESS;
2605}
2606
2607/* Disable command */
2608DEFUN(disable,
2609 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2610{
2611 if (vty->node == ENABLE_NODE)
2612 vty->node = VIEW_NODE;
2613 return CMD_SUCCESS;
2614}
2615
2616/* Down vty node level. */
2617gDEFUN(config_exit,
2618 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2619{
2620 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002621 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002622 case VIEW_NODE:
2623 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002624 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002625 break;
2626 case CONFIG_NODE:
2627 vty->node = ENABLE_NODE;
2628 vty_config_unlock(vty);
2629 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002630 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002631 if (vty->node > CONFIG_NODE)
2632 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002633 break;
2634 }
2635 return CMD_SUCCESS;
2636}
2637
2638/* End of configuration. */
2639 gDEFUN(config_end,
2640 config_end_cmd, "end", "End current mode and change to enable mode.")
2641{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002642 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002643 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002644
2645 /* Repeatedly call go_parent until a top node is reached. */
2646 while (vty->node > CONFIG_NODE) {
2647 if (vty->node == last_node) {
2648 /* Ensure termination, this shouldn't happen. */
2649 break;
2650 }
2651 last_node = vty->node;
2652 vty_go_parent(vty);
2653 }
2654
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002655 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002656 if (vty->node > ENABLE_NODE)
2657 vty->node = ENABLE_NODE;
2658 vty->index = NULL;
2659 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002660 }
2661 return CMD_SUCCESS;
2662}
2663
2664/* Show version. */
2665DEFUN(show_version,
2666 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2667{
Harald Welte237f6242010-05-25 23:00:45 +02002668 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2669 host.app_info->version,
2670 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2671 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002672
2673 return CMD_SUCCESS;
2674}
2675
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002676DEFUN(show_online_help,
2677 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2678{
2679 vty_dump_nodes(vty);
2680 return CMD_SUCCESS;
2681}
2682
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002683/* Help display function for all node. */
2684gDEFUN(config_help,
2685 config_help_cmd, "help", "Description of the interactive help system\n")
2686{
2687 vty_out(vty,
2688 "This VTY provides advanced help features. When you need help,%s\
2689anytime at the command line please press '?'.%s\
2690%s\
2691If nothing matches, the help list will be empty and you must backup%s\
2692 until entering a '?' shows the available options.%s\
2693Two styles of help are provided:%s\
26941. Full help is available when you are ready to enter a%s\
2695command argument (e.g. 'show ?') and describes each possible%s\
2696argument.%s\
26972. Partial help is provided when an abbreviated argument is entered%s\
2698 and you want to know what arguments match the input%s\
2699 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2700 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2701 return CMD_SUCCESS;
2702}
2703
2704/* Help display function for all node. */
2705gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2706{
2707 unsigned int i;
2708 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2709 struct cmd_element *cmd;
2710
2711 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2712 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
2713 && !(cmd->attr == CMD_ATTR_DEPRECATED
2714 || cmd->attr == CMD_ATTR_HIDDEN))
2715 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2716 return CMD_SUCCESS;
2717}
2718
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002719static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002720{
2721 unsigned int i;
2722 int fd;
2723 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002724 char *config_file_tmp = NULL;
2725 char *config_file_sav = NULL;
2726 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002727 struct stat st;
2728
2729 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002730
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002731 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2732 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2733 * manually instead. */
2734
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002735 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002736 config_file_sav =
2737 _talloc_zero(tall_vty_cmd_ctx,
2738 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2739 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002740 if (!config_file_sav)
2741 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002742 strcpy(config_file_sav, config_file);
2743 strcat(config_file_sav, CONF_BACKUP_EXT);
2744
2745 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002746 "config_file_tmp");
2747 if (!config_file_tmp) {
2748 talloc_free(config_file_sav);
2749 return -1;
2750 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002751 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2752
2753 /* Open file to configuration write. */
2754 fd = mkstemp(config_file_tmp);
2755 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002756 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002757 talloc_free(config_file_tmp);
2758 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002759 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002760 }
2761
2762 /* Make vty for configuration file. */
2763 file_vty = vty_new();
2764 file_vty->fd = fd;
2765 file_vty->type = VTY_FILE;
2766
2767 /* Config file header print. */
2768 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002769 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002770 //vty_time_print (file_vty, 1);
2771 vty_out(file_vty, "!\n");
2772
2773 for (i = 0; i < vector_active(cmdvec); i++)
2774 if ((node = vector_slot(cmdvec, i)) && node->func) {
2775 if ((*node->func) (file_vty))
2776 vty_out(file_vty, "!\n");
2777 }
2778 vty_close(file_vty);
2779
2780 if (unlink(config_file_sav) != 0)
2781 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002782 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002783 talloc_free(config_file_sav);
2784 talloc_free(config_file_tmp);
2785 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002786 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002787 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002788
2789 /* Only link the .sav file if the original file exists */
2790 if (stat(config_file, &st) == 0) {
2791 if (link(config_file, config_file_sav) != 0) {
2792 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2793 talloc_free(config_file_sav);
2794 talloc_free(config_file_tmp);
2795 unlink(config_file_tmp);
2796 return -3;
2797 }
2798 sync();
2799 if (unlink(config_file) != 0) {
2800 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2801 talloc_free(config_file_sav);
2802 talloc_free(config_file_tmp);
2803 unlink(config_file_tmp);
2804 return -4;
2805 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002806 }
2807 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002808 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002809 talloc_free(config_file_sav);
2810 talloc_free(config_file_tmp);
2811 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002812 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002813 }
2814 unlink(config_file_tmp);
2815 sync();
2816
2817 talloc_free(config_file_sav);
2818 talloc_free(config_file_tmp);
2819
2820 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002821 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2822 return -6;
2823 }
2824
2825 return 0;
2826}
2827
2828
2829/* Write current configuration into file. */
2830DEFUN(config_write_file,
2831 config_write_file_cmd,
2832 "write file",
2833 "Write running configuration to memory, network, or terminal\n"
2834 "Write to configuration file\n")
2835{
2836 char *failed_file;
2837 int rc;
2838
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002839 if (host.app_info->config_is_consistent) {
2840 rc = host.app_info->config_is_consistent(vty);
2841 if (!rc) {
2842 vty_out(vty, "Configuration is not consistent%s",
2843 VTY_NEWLINE);
2844 return CMD_WARNING;
2845 }
2846 }
2847
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002848 if (host.config == NULL) {
2849 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2850 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002851 return CMD_WARNING;
2852 }
2853
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002854 rc = write_config_file(host.config, &failed_file);
2855 switch (rc) {
2856 case -1:
2857 vty_out(vty, "Can't open configuration file %s.%s",
2858 failed_file, VTY_NEWLINE);
2859 rc = CMD_WARNING;
2860 break;
2861 case -2:
2862 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2863 failed_file, VTY_NEWLINE);
2864 rc = CMD_WARNING;
2865 break;
2866 case -3:
2867 vty_out(vty, "Can't backup old configuration file %s.%s",
2868 failed_file, VTY_NEWLINE);
2869 rc = CMD_WARNING;
2870 break;
2871 case -4:
2872 vty_out(vty, "Can't unlink configuration file %s.%s",
2873 failed_file, VTY_NEWLINE);
2874 rc = CMD_WARNING;
2875 break;
2876 case -5:
2877 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2878 VTY_NEWLINE);
2879 rc = CMD_WARNING;
2880 break;
2881 case -6:
2882 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2883 failed_file, strerror(errno), errno, VTY_NEWLINE);
2884 rc = CMD_WARNING;
2885 break;
2886 default:
2887 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2888 rc = CMD_SUCCESS;
2889 break;
2890 }
2891
2892 talloc_free(failed_file);
2893 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002894}
2895
2896ALIAS(config_write_file,
2897 config_write_cmd,
2898 "write", "Write running configuration to memory, network, or terminal\n")
2899
2900 ALIAS(config_write_file,
2901 config_write_memory_cmd,
2902 "write memory",
2903 "Write running configuration to memory, network, or terminal\n"
2904 "Write configuration to the file (same as write file)\n")
2905
2906 ALIAS(config_write_file,
2907 copy_runningconfig_startupconfig_cmd,
2908 "copy running-config startup-config",
2909 "Copy configuration\n"
2910 "Copy running config to... \n"
2911 "Copy running config to startup config (same as write file)\n")
2912
2913/* Write current configuration into the terminal. */
2914 DEFUN(config_write_terminal,
2915 config_write_terminal_cmd,
2916 "write terminal",
2917 "Write running configuration to memory, network, or terminal\n"
2918 "Write to terminal\n")
2919{
2920 unsigned int i;
2921 struct cmd_node *node;
2922
2923 if (vty->type == VTY_SHELL_SERV) {
2924 for (i = 0; i < vector_active(cmdvec); i++)
2925 if ((node = vector_slot(cmdvec, i)) && node->func
2926 && node->vtysh) {
2927 if ((*node->func) (vty))
2928 vty_out(vty, "!%s", VTY_NEWLINE);
2929 }
2930 } else {
2931 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2932 VTY_NEWLINE);
2933 vty_out(vty, "!%s", VTY_NEWLINE);
2934
2935 for (i = 0; i < vector_active(cmdvec); i++)
2936 if ((node = vector_slot(cmdvec, i)) && node->func) {
2937 if ((*node->func) (vty))
2938 vty_out(vty, "!%s", VTY_NEWLINE);
2939 }
2940 vty_out(vty, "end%s", VTY_NEWLINE);
2941 }
2942 return CMD_SUCCESS;
2943}
2944
2945/* Write current configuration into the terminal. */
2946ALIAS(config_write_terminal,
2947 show_running_config_cmd,
2948 "show running-config", SHOW_STR "running configuration\n")
2949
2950/* Write startup configuration into the terminal. */
2951 DEFUN(show_startup_config,
2952 show_startup_config_cmd,
2953 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2954{
2955 char buf[BUFSIZ];
2956 FILE *confp;
2957
2958 confp = fopen(host.config, "r");
2959 if (confp == NULL) {
2960 vty_out(vty, "Can't open configuration file [%s]%s",
2961 host.config, VTY_NEWLINE);
2962 return CMD_WARNING;
2963 }
2964
2965 while (fgets(buf, BUFSIZ, confp)) {
2966 char *cp = buf;
2967
2968 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
2969 cp++;
2970 *cp = '\0';
2971
2972 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
2973 }
2974
2975 fclose(confp);
2976
2977 return CMD_SUCCESS;
2978}
2979
2980/* Hostname configuration */
2981DEFUN(config_hostname,
2982 hostname_cmd,
2983 "hostname WORD",
2984 "Set system's network name\n" "This system's network name\n")
2985{
2986 if (!isalpha((int)*argv[0])) {
2987 vty_out(vty, "Please specify string starting with alphabet%s",
2988 VTY_NEWLINE);
2989 return CMD_WARNING;
2990 }
2991
2992 if (host.name)
2993 talloc_free(host.name);
2994
2995 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2996 return CMD_SUCCESS;
2997}
2998
2999DEFUN(config_no_hostname,
3000 no_hostname_cmd,
3001 "no hostname [HOSTNAME]",
3002 NO_STR "Reset system's network name\n" "Host name of this router\n")
3003{
3004 if (host.name)
3005 talloc_free(host.name);
3006 host.name = NULL;
3007 return CMD_SUCCESS;
3008}
3009
3010/* VTY interface password set. */
3011DEFUN(config_password, password_cmd,
3012 "password (8|) WORD",
3013 "Assign the terminal connection password\n"
3014 "Specifies a HIDDEN password will follow\n"
3015 "dummy string \n" "The HIDDEN line password string\n")
3016{
3017 /* Argument check. */
3018 if (argc == 0) {
3019 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3020 return CMD_WARNING;
3021 }
3022
3023 if (argc == 2) {
3024 if (*argv[0] == '8') {
3025 if (host.password)
3026 talloc_free(host.password);
3027 host.password = NULL;
3028 if (host.password_encrypt)
3029 talloc_free(host.password_encrypt);
3030 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3031 return CMD_SUCCESS;
3032 } else {
3033 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3034 return CMD_WARNING;
3035 }
3036 }
3037
3038 if (!isalnum((int)*argv[0])) {
3039 vty_out(vty,
3040 "Please specify string starting with alphanumeric%s",
3041 VTY_NEWLINE);
3042 return CMD_WARNING;
3043 }
3044
3045 if (host.password)
3046 talloc_free(host.password);
3047 host.password = NULL;
3048
3049#ifdef VTY_CRYPT_PW
3050 if (host.encrypt) {
3051 if (host.password_encrypt)
3052 talloc_free(host.password_encrypt);
3053 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3054 } else
3055#endif
3056 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3057
3058 return CMD_SUCCESS;
3059}
3060
3061ALIAS(config_password, password_text_cmd,
3062 "password LINE",
3063 "Assign the terminal connection password\n"
3064 "The UNENCRYPTED (cleartext) line password\n")
3065
3066/* VTY enable password set. */
3067 DEFUN(config_enable_password, enable_password_cmd,
3068 "enable password (8|) WORD",
3069 "Modify enable password parameters\n"
3070 "Assign the privileged level password\n"
3071 "Specifies a HIDDEN password will follow\n"
3072 "dummy string \n" "The HIDDEN 'enable' password string\n")
3073{
3074 /* Argument check. */
3075 if (argc == 0) {
3076 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3077 return CMD_WARNING;
3078 }
3079
3080 /* Crypt type is specified. */
3081 if (argc == 2) {
3082 if (*argv[0] == '8') {
3083 if (host.enable)
3084 talloc_free(host.enable);
3085 host.enable = NULL;
3086
3087 if (host.enable_encrypt)
3088 talloc_free(host.enable_encrypt);
3089 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3090
3091 return CMD_SUCCESS;
3092 } else {
3093 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3094 return CMD_WARNING;
3095 }
3096 }
3097
3098 if (!isalnum((int)*argv[0])) {
3099 vty_out(vty,
3100 "Please specify string starting with alphanumeric%s",
3101 VTY_NEWLINE);
3102 return CMD_WARNING;
3103 }
3104
3105 if (host.enable)
3106 talloc_free(host.enable);
3107 host.enable = NULL;
3108
3109 /* Plain password input. */
3110#ifdef VTY_CRYPT_PW
3111 if (host.encrypt) {
3112 if (host.enable_encrypt)
3113 talloc_free(host.enable_encrypt);
3114 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3115 } else
3116#endif
3117 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3118
3119 return CMD_SUCCESS;
3120}
3121
3122ALIAS(config_enable_password,
3123 enable_password_text_cmd,
3124 "enable password LINE",
3125 "Modify enable password parameters\n"
3126 "Assign the privileged level password\n"
3127 "The UNENCRYPTED (cleartext) 'enable' password\n")
3128
3129/* VTY enable password delete. */
3130 DEFUN(no_config_enable_password, no_enable_password_cmd,
3131 "no enable password",
3132 NO_STR
3133 "Modify enable password parameters\n"
3134 "Assign the privileged level password\n")
3135{
3136 if (host.enable)
3137 talloc_free(host.enable);
3138 host.enable = NULL;
3139
3140 if (host.enable_encrypt)
3141 talloc_free(host.enable_encrypt);
3142 host.enable_encrypt = NULL;
3143
3144 return CMD_SUCCESS;
3145}
3146
3147#ifdef VTY_CRYPT_PW
3148DEFUN(service_password_encrypt,
3149 service_password_encrypt_cmd,
3150 "service password-encryption",
3151 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3152{
3153 if (host.encrypt)
3154 return CMD_SUCCESS;
3155
3156 host.encrypt = 1;
3157
3158 if (host.password) {
3159 if (host.password_encrypt)
3160 talloc_free(host.password_encrypt);
3161 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3162 }
3163 if (host.enable) {
3164 if (host.enable_encrypt)
3165 talloc_free(host.enable_encrypt);
3166 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3167 }
3168
3169 return CMD_SUCCESS;
3170}
3171
3172DEFUN(no_service_password_encrypt,
3173 no_service_password_encrypt_cmd,
3174 "no service password-encryption",
3175 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3176{
3177 if (!host.encrypt)
3178 return CMD_SUCCESS;
3179
3180 host.encrypt = 0;
3181
3182 if (host.password_encrypt)
3183 talloc_free(host.password_encrypt);
3184 host.password_encrypt = NULL;
3185
3186 if (host.enable_encrypt)
3187 talloc_free(host.enable_encrypt);
3188 host.enable_encrypt = NULL;
3189
3190 return CMD_SUCCESS;
3191}
3192#endif
3193
3194DEFUN(config_terminal_length, config_terminal_length_cmd,
3195 "terminal length <0-512>",
3196 "Set terminal line parameters\n"
3197 "Set number of lines on a screen\n"
3198 "Number of lines on screen (0 for no pausing)\n")
3199{
3200 int lines;
3201 char *endptr = NULL;
3202
3203 lines = strtol(argv[0], &endptr, 10);
3204 if (lines < 0 || lines > 512 || *endptr != '\0') {
3205 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3206 return CMD_WARNING;
3207 }
3208 vty->lines = lines;
3209
3210 return CMD_SUCCESS;
3211}
3212
3213DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3214 "terminal no length",
3215 "Set terminal line parameters\n"
3216 NO_STR "Set number of lines on a screen\n")
3217{
3218 vty->lines = -1;
3219 return CMD_SUCCESS;
3220}
3221
3222DEFUN(service_terminal_length, service_terminal_length_cmd,
3223 "service terminal-length <0-512>",
3224 "Set up miscellaneous service\n"
3225 "System wide terminal length configuration\n"
3226 "Number of lines of VTY (0 means no line control)\n")
3227{
3228 int lines;
3229 char *endptr = NULL;
3230
3231 lines = strtol(argv[0], &endptr, 10);
3232 if (lines < 0 || lines > 512 || *endptr != '\0') {
3233 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3234 return CMD_WARNING;
3235 }
3236 host.lines = lines;
3237
3238 return CMD_SUCCESS;
3239}
3240
3241DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3242 "no service terminal-length [<0-512>]",
3243 NO_STR
3244 "Set up miscellaneous service\n"
3245 "System wide terminal length configuration\n"
3246 "Number of lines of VTY (0 means no line control)\n")
3247{
3248 host.lines = -1;
3249 return CMD_SUCCESS;
3250}
3251
3252DEFUN_HIDDEN(do_echo,
3253 echo_cmd,
3254 "echo .MESSAGE",
3255 "Echo a message back to the vty\n" "The message to echo\n")
3256{
3257 char *message;
3258
3259 vty_out(vty, "%s%s",
3260 ((message =
3261 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3262 if (message)
3263 talloc_free(message);
3264 return CMD_SUCCESS;
3265}
3266
3267#if 0
3268DEFUN(config_logmsg,
3269 config_logmsg_cmd,
3270 "logmsg " LOG_LEVELS " .MESSAGE",
3271 "Send a message to enabled logging destinations\n"
3272 LOG_LEVEL_DESC "The message to send\n")
3273{
3274 int level;
3275 char *message;
3276
3277 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3278 return CMD_ERR_NO_MATCH;
3279
3280 zlog(NULL, level,
3281 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3282 if (message)
3283 talloc_free(message);
3284 return CMD_SUCCESS;
3285}
3286
3287DEFUN(show_logging,
3288 show_logging_cmd,
3289 "show logging", SHOW_STR "Show current logging configuration\n")
3290{
3291 struct zlog *zl = zlog_default;
3292
3293 vty_out(vty, "Syslog logging: ");
3294 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3295 vty_out(vty, "disabled");
3296 else
3297 vty_out(vty, "level %s, facility %s, ident %s",
3298 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3299 facility_name(zl->facility), zl->ident);
3300 vty_out(vty, "%s", VTY_NEWLINE);
3301
3302 vty_out(vty, "Stdout logging: ");
3303 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3304 vty_out(vty, "disabled");
3305 else
3306 vty_out(vty, "level %s",
3307 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3308 vty_out(vty, "%s", VTY_NEWLINE);
3309
3310 vty_out(vty, "Monitor logging: ");
3311 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3312 vty_out(vty, "disabled");
3313 else
3314 vty_out(vty, "level %s",
3315 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3316 vty_out(vty, "%s", VTY_NEWLINE);
3317
3318 vty_out(vty, "File logging: ");
3319 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3320 vty_out(vty, "disabled");
3321 else
3322 vty_out(vty, "level %s, filename %s",
3323 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3324 zl->filename);
3325 vty_out(vty, "%s", VTY_NEWLINE);
3326
3327 vty_out(vty, "Protocol name: %s%s",
3328 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3329 vty_out(vty, "Record priority: %s%s",
3330 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3331
3332 return CMD_SUCCESS;
3333}
3334
3335DEFUN(config_log_stdout,
3336 config_log_stdout_cmd,
3337 "log stdout", "Logging control\n" "Set stdout logging level\n")
3338{
3339 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3340 return CMD_SUCCESS;
3341}
3342
3343DEFUN(config_log_stdout_level,
3344 config_log_stdout_level_cmd,
3345 "log stdout " LOG_LEVELS,
3346 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3347{
3348 int level;
3349
3350 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3351 return CMD_ERR_NO_MATCH;
3352 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3353 return CMD_SUCCESS;
3354}
3355
3356DEFUN(no_config_log_stdout,
3357 no_config_log_stdout_cmd,
3358 "no log stdout [LEVEL]",
3359 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3360{
3361 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3362 return CMD_SUCCESS;
3363}
3364
3365DEFUN(config_log_monitor,
3366 config_log_monitor_cmd,
3367 "log monitor",
3368 "Logging control\n" "Set terminal line (monitor) logging level\n")
3369{
3370 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3371 return CMD_SUCCESS;
3372}
3373
3374DEFUN(config_log_monitor_level,
3375 config_log_monitor_level_cmd,
3376 "log monitor " LOG_LEVELS,
3377 "Logging control\n"
3378 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3379{
3380 int level;
3381
3382 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3383 return CMD_ERR_NO_MATCH;
3384 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3385 return CMD_SUCCESS;
3386}
3387
3388DEFUN(no_config_log_monitor,
3389 no_config_log_monitor_cmd,
3390 "no log monitor [LEVEL]",
3391 NO_STR
3392 "Logging control\n"
3393 "Disable terminal line (monitor) logging\n" "Logging level\n")
3394{
3395 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3396 return CMD_SUCCESS;
3397}
3398
3399static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3400{
3401 int ret;
3402 char *p = NULL;
3403 const char *fullpath;
3404
3405 /* Path detection. */
3406 if (!IS_DIRECTORY_SEP(*fname)) {
3407 char cwd[MAXPATHLEN + 1];
3408 cwd[MAXPATHLEN] = '\0';
3409
3410 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3411 zlog_err("config_log_file: Unable to alloc mem!");
3412 return CMD_WARNING;
3413 }
3414
3415 if ((p = _talloc_zero(tall_vcmd_ctx,
3416 strlen(cwd) + strlen(fname) + 2),
3417 "set_log_file")
3418 == NULL) {
3419 zlog_err("config_log_file: Unable to alloc mem!");
3420 return CMD_WARNING;
3421 }
3422 sprintf(p, "%s/%s", cwd, fname);
3423 fullpath = p;
3424 } else
3425 fullpath = fname;
3426
3427 ret = zlog_set_file(NULL, fullpath, loglevel);
3428
3429 if (p)
3430 talloc_free(p);
3431
3432 if (!ret) {
3433 vty_out(vty, "can't open logfile %s\n", fname);
3434 return CMD_WARNING;
3435 }
3436
3437 if (host.logfile)
3438 talloc_free(host.logfile);
3439
3440 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3441
3442 return CMD_SUCCESS;
3443}
3444
3445DEFUN(config_log_file,
3446 config_log_file_cmd,
3447 "log file FILENAME",
3448 "Logging control\n" "Logging to file\n" "Logging filename\n")
3449{
3450 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3451}
3452
3453DEFUN(config_log_file_level,
3454 config_log_file_level_cmd,
3455 "log file FILENAME " LOG_LEVELS,
3456 "Logging control\n"
3457 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3458{
3459 int level;
3460
3461 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3462 return CMD_ERR_NO_MATCH;
3463 return set_log_file(vty, argv[0], level);
3464}
3465
3466DEFUN(no_config_log_file,
3467 no_config_log_file_cmd,
3468 "no log file [FILENAME]",
3469 NO_STR
3470 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3471{
3472 zlog_reset_file(NULL);
3473
3474 if (host.logfile)
3475 talloc_free(host.logfile);
3476
3477 host.logfile = NULL;
3478
3479 return CMD_SUCCESS;
3480}
3481
3482ALIAS(no_config_log_file,
3483 no_config_log_file_level_cmd,
3484 "no log file FILENAME LEVEL",
3485 NO_STR
3486 "Logging control\n"
3487 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3488
3489 DEFUN(config_log_syslog,
3490 config_log_syslog_cmd,
3491 "log syslog", "Logging control\n" "Set syslog logging level\n")
3492{
3493 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3494 return CMD_SUCCESS;
3495}
3496
3497DEFUN(config_log_syslog_level,
3498 config_log_syslog_level_cmd,
3499 "log syslog " LOG_LEVELS,
3500 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3501{
3502 int level;
3503
3504 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3505 return CMD_ERR_NO_MATCH;
3506 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3507 return CMD_SUCCESS;
3508}
3509
3510DEFUN_DEPRECATED(config_log_syslog_facility,
3511 config_log_syslog_facility_cmd,
3512 "log syslog facility " LOG_FACILITIES,
3513 "Logging control\n"
3514 "Logging goes to syslog\n"
3515 "(Deprecated) Facility parameter for syslog messages\n"
3516 LOG_FACILITY_DESC)
3517{
3518 int facility;
3519
3520 if ((facility = facility_match(argv[0])) < 0)
3521 return CMD_ERR_NO_MATCH;
3522
3523 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3524 zlog_default->facility = facility;
3525 return CMD_SUCCESS;
3526}
3527
3528DEFUN(no_config_log_syslog,
3529 no_config_log_syslog_cmd,
3530 "no log syslog [LEVEL]",
3531 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3532{
3533 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3534 return CMD_SUCCESS;
3535}
3536
3537ALIAS(no_config_log_syslog,
3538 no_config_log_syslog_facility_cmd,
3539 "no log syslog facility " LOG_FACILITIES,
3540 NO_STR
3541 "Logging control\n"
3542 "Logging goes to syslog\n"
3543 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3544
3545 DEFUN(config_log_facility,
3546 config_log_facility_cmd,
3547 "log facility " LOG_FACILITIES,
3548 "Logging control\n"
3549 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3550{
3551 int facility;
3552
3553 if ((facility = facility_match(argv[0])) < 0)
3554 return CMD_ERR_NO_MATCH;
3555 zlog_default->facility = facility;
3556 return CMD_SUCCESS;
3557}
3558
3559DEFUN(no_config_log_facility,
3560 no_config_log_facility_cmd,
3561 "no log facility [FACILITY]",
3562 NO_STR
3563 "Logging control\n"
3564 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3565{
3566 zlog_default->facility = LOG_DAEMON;
3567 return CMD_SUCCESS;
3568}
3569
3570DEFUN_DEPRECATED(config_log_trap,
3571 config_log_trap_cmd,
3572 "log trap " LOG_LEVELS,
3573 "Logging control\n"
3574 "(Deprecated) Set logging level and default for all destinations\n"
3575 LOG_LEVEL_DESC)
3576{
3577 int new_level;
3578 int i;
3579
3580 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3581 return CMD_ERR_NO_MATCH;
3582
3583 zlog_default->default_lvl = new_level;
3584 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3585 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3586 zlog_default->maxlvl[i] = new_level;
3587 return CMD_SUCCESS;
3588}
3589
3590DEFUN_DEPRECATED(no_config_log_trap,
3591 no_config_log_trap_cmd,
3592 "no log trap [LEVEL]",
3593 NO_STR
3594 "Logging control\n"
3595 "Permit all logging information\n" "Logging level\n")
3596{
3597 zlog_default->default_lvl = LOG_DEBUG;
3598 return CMD_SUCCESS;
3599}
3600
3601DEFUN(config_log_record_priority,
3602 config_log_record_priority_cmd,
3603 "log record-priority",
3604 "Logging control\n"
3605 "Log the priority of the message within the message\n")
3606{
3607 zlog_default->record_priority = 1;
3608 return CMD_SUCCESS;
3609}
3610
3611DEFUN(no_config_log_record_priority,
3612 no_config_log_record_priority_cmd,
3613 "no log record-priority",
3614 NO_STR
3615 "Logging control\n"
3616 "Do not log the priority of the message within the message\n")
3617{
3618 zlog_default->record_priority = 0;
3619 return CMD_SUCCESS;
3620}
3621#endif
3622
3623DEFUN(banner_motd_file,
3624 banner_motd_file_cmd,
3625 "banner motd file [FILE]",
3626 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3627{
3628 if (host.motdfile)
3629 talloc_free(host.motdfile);
3630 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3631
3632 return CMD_SUCCESS;
3633}
3634
3635DEFUN(banner_motd_default,
3636 banner_motd_default_cmd,
3637 "banner motd default",
3638 "Set banner string\n" "Strings for motd\n" "Default string\n")
3639{
3640 host.motd = default_motd;
3641 return CMD_SUCCESS;
3642}
3643
3644DEFUN(no_banner_motd,
3645 no_banner_motd_cmd,
3646 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3647{
3648 host.motd = NULL;
3649 if (host.motdfile)
3650 talloc_free(host.motdfile);
3651 host.motdfile = NULL;
3652 return CMD_SUCCESS;
3653}
3654
3655/* Set config filename. Called from vty.c */
3656void host_config_set(const char *filename)
3657{
3658 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3659}
3660
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003661/*! Deprecated, now happens implicitly when calling install_node().
3662 * Users of the API may still attempt to call this function, hence
3663 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003664void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003665{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003666}
3667
3668/*! Deprecated, now happens implicitly when calling install_node().
3669 * Users of the API may still attempt to call this function, hence
3670 * leave it here as a no-op. */
3671void vty_install_default(int node)
3672{
3673}
3674
3675/*! Install common commands like 'exit' and 'list'. */
3676static void install_basic_node_commands(int node)
3677{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003678 install_element(node, &config_help_cmd);
3679 install_element(node, &config_list_cmd);
3680
3681 install_element(node, &config_write_terminal_cmd);
3682 install_element(node, &config_write_file_cmd);
3683 install_element(node, &config_write_memory_cmd);
3684 install_element(node, &config_write_cmd);
3685 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003686
3687 install_element(node, &config_exit_cmd);
3688
3689 if (node >= CONFIG_NODE) {
3690 /* It's not a top node. */
3691 install_element(node, &config_end_cmd);
3692 }
3693}
3694
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003695/*! Return true if a node is installed by install_basic_node_commands(), so
3696 * that we can avoid repeating them for each and every node during 'show
3697 * running-config' */
3698static bool vty_command_is_common(struct cmd_element *cmd)
3699{
3700 if (cmd == &config_help_cmd
3701 || cmd == &config_list_cmd
3702 || cmd == &config_write_terminal_cmd
3703 || cmd == &config_write_file_cmd
3704 || cmd == &config_write_memory_cmd
3705 || cmd == &config_write_cmd
3706 || cmd == &show_running_config_cmd
3707 || cmd == &config_exit_cmd
3708 || cmd == &config_end_cmd)
3709 return true;
3710 return false;
3711}
3712
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003713/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003714 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003715 * \param[in] vty the vty of the code
3716 * \param[in] filename where to store the file
3717 * \return 0 in case of success.
3718 *
3719 * If the filename already exists create a filename.sav
3720 * version with the current code.
3721 *
3722 */
3723int osmo_vty_write_config_file(const char *filename)
3724{
3725 char *failed_file;
3726 int rc;
3727
3728 rc = write_config_file(filename, &failed_file);
3729 talloc_free(failed_file);
3730 return rc;
3731}
3732
3733/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003734 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003735 * \return 0 in case of success.
3736 *
3737 * If the filename already exists create a filename.sav
3738 * version with the current code.
3739 *
3740 */
3741int osmo_vty_save_config_file(void)
3742{
3743 char *failed_file;
3744 int rc;
3745
3746 if (host.config == NULL)
3747 return -7;
3748
3749 rc = write_config_file(host.config, &failed_file);
3750 talloc_free(failed_file);
3751 return rc;
3752}
3753
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003754/* Initialize command interface. Install basic nodes and commands. */
3755void cmd_init(int terminal)
3756{
3757 /* Allocate initial top vector of commands. */
3758 cmdvec = vector_init(VECTOR_MIN_SIZE);
3759
3760 /* Default host value settings. */
3761 host.name = NULL;
3762 host.password = NULL;
3763 host.enable = NULL;
3764 host.logfile = NULL;
3765 host.config = NULL;
3766 host.lines = -1;
3767 host.motd = default_motd;
3768 host.motdfile = NULL;
3769
3770 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003771 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003772 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003773 install_node_bare(&auth_node, NULL);
3774 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003775 install_node(&config_node, config_write_host);
3776
3777 /* Each node's basic commands. */
3778 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003779 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003780 if (terminal) {
3781 install_element(VIEW_NODE, &config_list_cmd);
3782 install_element(VIEW_NODE, &config_exit_cmd);
3783 install_element(VIEW_NODE, &config_help_cmd);
3784 install_element(VIEW_NODE, &config_enable_cmd);
3785 install_element(VIEW_NODE, &config_terminal_length_cmd);
3786 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3787 install_element(VIEW_NODE, &echo_cmd);
3788 }
3789
3790 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003791 install_element(ENABLE_NODE, &config_disable_cmd);
3792 install_element(ENABLE_NODE, &config_terminal_cmd);
3793 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3794 }
3795 install_element (ENABLE_NODE, &show_startup_config_cmd);
3796 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003797 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003798
3799 if (terminal) {
3800 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3801 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3802 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003803 }
3804
3805 install_element(CONFIG_NODE, &hostname_cmd);
3806 install_element(CONFIG_NODE, &no_hostname_cmd);
3807
3808 if (terminal) {
3809 install_element(CONFIG_NODE, &password_cmd);
3810 install_element(CONFIG_NODE, &password_text_cmd);
3811 install_element(CONFIG_NODE, &enable_password_cmd);
3812 install_element(CONFIG_NODE, &enable_password_text_cmd);
3813 install_element(CONFIG_NODE, &no_enable_password_cmd);
3814
3815#ifdef VTY_CRYPT_PW
3816 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3817 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3818#endif
3819 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3820 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3821 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3822 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3823 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3824
3825 }
3826 srand(time(NULL));
3827}
Harald Welte7acb30c2011-08-17 17:13:48 +02003828
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003829/*! @} */