blob: 71f6a71ef80cdfbd59dd6b215bd790c43ced73be [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
2731 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002732 config_file_sav =
2733 _talloc_zero(tall_vty_cmd_ctx,
2734 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2735 "config_file_sav");
2736 strcpy(config_file_sav, config_file);
2737 strcat(config_file_sav, CONF_BACKUP_EXT);
2738
2739 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
2740 "config_file_tmp");
2741 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2742
2743 /* Open file to configuration write. */
2744 fd = mkstemp(config_file_tmp);
2745 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002746 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002747 talloc_free(config_file_tmp);
2748 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002749 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002750 }
2751
2752 /* Make vty for configuration file. */
2753 file_vty = vty_new();
2754 file_vty->fd = fd;
2755 file_vty->type = VTY_FILE;
2756
2757 /* Config file header print. */
2758 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002759 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002760 //vty_time_print (file_vty, 1);
2761 vty_out(file_vty, "!\n");
2762
2763 for (i = 0; i < vector_active(cmdvec); i++)
2764 if ((node = vector_slot(cmdvec, i)) && node->func) {
2765 if ((*node->func) (file_vty))
2766 vty_out(file_vty, "!\n");
2767 }
2768 vty_close(file_vty);
2769
2770 if (unlink(config_file_sav) != 0)
2771 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002772 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002773 talloc_free(config_file_sav);
2774 talloc_free(config_file_tmp);
2775 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002776 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002777 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002778
2779 /* Only link the .sav file if the original file exists */
2780 if (stat(config_file, &st) == 0) {
2781 if (link(config_file, config_file_sav) != 0) {
2782 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2783 talloc_free(config_file_sav);
2784 talloc_free(config_file_tmp);
2785 unlink(config_file_tmp);
2786 return -3;
2787 }
2788 sync();
2789 if (unlink(config_file) != 0) {
2790 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2791 talloc_free(config_file_sav);
2792 talloc_free(config_file_tmp);
2793 unlink(config_file_tmp);
2794 return -4;
2795 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002796 }
2797 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002798 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002799 talloc_free(config_file_sav);
2800 talloc_free(config_file_tmp);
2801 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002802 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002803 }
2804 unlink(config_file_tmp);
2805 sync();
2806
2807 talloc_free(config_file_sav);
2808 talloc_free(config_file_tmp);
2809
2810 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002811 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2812 return -6;
2813 }
2814
2815 return 0;
2816}
2817
2818
2819/* Write current configuration into file. */
2820DEFUN(config_write_file,
2821 config_write_file_cmd,
2822 "write file",
2823 "Write running configuration to memory, network, or terminal\n"
2824 "Write to configuration file\n")
2825{
2826 char *failed_file;
2827 int rc;
2828
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002829 if (host.app_info->config_is_consistent) {
2830 rc = host.app_info->config_is_consistent(vty);
2831 if (!rc) {
2832 vty_out(vty, "Configuration is not consistent%s",
2833 VTY_NEWLINE);
2834 return CMD_WARNING;
2835 }
2836 }
2837
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002838 if (host.config == NULL) {
2839 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2840 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002841 return CMD_WARNING;
2842 }
2843
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002844 rc = write_config_file(host.config, &failed_file);
2845 switch (rc) {
2846 case -1:
2847 vty_out(vty, "Can't open configuration file %s.%s",
2848 failed_file, VTY_NEWLINE);
2849 rc = CMD_WARNING;
2850 break;
2851 case -2:
2852 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2853 failed_file, VTY_NEWLINE);
2854 rc = CMD_WARNING;
2855 break;
2856 case -3:
2857 vty_out(vty, "Can't backup old configuration file %s.%s",
2858 failed_file, VTY_NEWLINE);
2859 rc = CMD_WARNING;
2860 break;
2861 case -4:
2862 vty_out(vty, "Can't unlink configuration file %s.%s",
2863 failed_file, VTY_NEWLINE);
2864 rc = CMD_WARNING;
2865 break;
2866 case -5:
2867 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2868 VTY_NEWLINE);
2869 rc = CMD_WARNING;
2870 break;
2871 case -6:
2872 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2873 failed_file, strerror(errno), errno, VTY_NEWLINE);
2874 rc = CMD_WARNING;
2875 break;
2876 default:
2877 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2878 rc = CMD_SUCCESS;
2879 break;
2880 }
2881
2882 talloc_free(failed_file);
2883 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002884}
2885
2886ALIAS(config_write_file,
2887 config_write_cmd,
2888 "write", "Write running configuration to memory, network, or terminal\n")
2889
2890 ALIAS(config_write_file,
2891 config_write_memory_cmd,
2892 "write memory",
2893 "Write running configuration to memory, network, or terminal\n"
2894 "Write configuration to the file (same as write file)\n")
2895
2896 ALIAS(config_write_file,
2897 copy_runningconfig_startupconfig_cmd,
2898 "copy running-config startup-config",
2899 "Copy configuration\n"
2900 "Copy running config to... \n"
2901 "Copy running config to startup config (same as write file)\n")
2902
2903/* Write current configuration into the terminal. */
2904 DEFUN(config_write_terminal,
2905 config_write_terminal_cmd,
2906 "write terminal",
2907 "Write running configuration to memory, network, or terminal\n"
2908 "Write to terminal\n")
2909{
2910 unsigned int i;
2911 struct cmd_node *node;
2912
2913 if (vty->type == VTY_SHELL_SERV) {
2914 for (i = 0; i < vector_active(cmdvec); i++)
2915 if ((node = vector_slot(cmdvec, i)) && node->func
2916 && node->vtysh) {
2917 if ((*node->func) (vty))
2918 vty_out(vty, "!%s", VTY_NEWLINE);
2919 }
2920 } else {
2921 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2922 VTY_NEWLINE);
2923 vty_out(vty, "!%s", VTY_NEWLINE);
2924
2925 for (i = 0; i < vector_active(cmdvec); i++)
2926 if ((node = vector_slot(cmdvec, i)) && node->func) {
2927 if ((*node->func) (vty))
2928 vty_out(vty, "!%s", VTY_NEWLINE);
2929 }
2930 vty_out(vty, "end%s", VTY_NEWLINE);
2931 }
2932 return CMD_SUCCESS;
2933}
2934
2935/* Write current configuration into the terminal. */
2936ALIAS(config_write_terminal,
2937 show_running_config_cmd,
2938 "show running-config", SHOW_STR "running configuration\n")
2939
2940/* Write startup configuration into the terminal. */
2941 DEFUN(show_startup_config,
2942 show_startup_config_cmd,
2943 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2944{
2945 char buf[BUFSIZ];
2946 FILE *confp;
2947
2948 confp = fopen(host.config, "r");
2949 if (confp == NULL) {
2950 vty_out(vty, "Can't open configuration file [%s]%s",
2951 host.config, VTY_NEWLINE);
2952 return CMD_WARNING;
2953 }
2954
2955 while (fgets(buf, BUFSIZ, confp)) {
2956 char *cp = buf;
2957
2958 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
2959 cp++;
2960 *cp = '\0';
2961
2962 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
2963 }
2964
2965 fclose(confp);
2966
2967 return CMD_SUCCESS;
2968}
2969
2970/* Hostname configuration */
2971DEFUN(config_hostname,
2972 hostname_cmd,
2973 "hostname WORD",
2974 "Set system's network name\n" "This system's network name\n")
2975{
2976 if (!isalpha((int)*argv[0])) {
2977 vty_out(vty, "Please specify string starting with alphabet%s",
2978 VTY_NEWLINE);
2979 return CMD_WARNING;
2980 }
2981
2982 if (host.name)
2983 talloc_free(host.name);
2984
2985 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2986 return CMD_SUCCESS;
2987}
2988
2989DEFUN(config_no_hostname,
2990 no_hostname_cmd,
2991 "no hostname [HOSTNAME]",
2992 NO_STR "Reset system's network name\n" "Host name of this router\n")
2993{
2994 if (host.name)
2995 talloc_free(host.name);
2996 host.name = NULL;
2997 return CMD_SUCCESS;
2998}
2999
3000/* VTY interface password set. */
3001DEFUN(config_password, password_cmd,
3002 "password (8|) WORD",
3003 "Assign the terminal connection password\n"
3004 "Specifies a HIDDEN password will follow\n"
3005 "dummy string \n" "The HIDDEN line password string\n")
3006{
3007 /* Argument check. */
3008 if (argc == 0) {
3009 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3010 return CMD_WARNING;
3011 }
3012
3013 if (argc == 2) {
3014 if (*argv[0] == '8') {
3015 if (host.password)
3016 talloc_free(host.password);
3017 host.password = NULL;
3018 if (host.password_encrypt)
3019 talloc_free(host.password_encrypt);
3020 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3021 return CMD_SUCCESS;
3022 } else {
3023 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3024 return CMD_WARNING;
3025 }
3026 }
3027
3028 if (!isalnum((int)*argv[0])) {
3029 vty_out(vty,
3030 "Please specify string starting with alphanumeric%s",
3031 VTY_NEWLINE);
3032 return CMD_WARNING;
3033 }
3034
3035 if (host.password)
3036 talloc_free(host.password);
3037 host.password = NULL;
3038
3039#ifdef VTY_CRYPT_PW
3040 if (host.encrypt) {
3041 if (host.password_encrypt)
3042 talloc_free(host.password_encrypt);
3043 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3044 } else
3045#endif
3046 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3047
3048 return CMD_SUCCESS;
3049}
3050
3051ALIAS(config_password, password_text_cmd,
3052 "password LINE",
3053 "Assign the terminal connection password\n"
3054 "The UNENCRYPTED (cleartext) line password\n")
3055
3056/* VTY enable password set. */
3057 DEFUN(config_enable_password, enable_password_cmd,
3058 "enable password (8|) WORD",
3059 "Modify enable password parameters\n"
3060 "Assign the privileged level password\n"
3061 "Specifies a HIDDEN password will follow\n"
3062 "dummy string \n" "The HIDDEN 'enable' password string\n")
3063{
3064 /* Argument check. */
3065 if (argc == 0) {
3066 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3067 return CMD_WARNING;
3068 }
3069
3070 /* Crypt type is specified. */
3071 if (argc == 2) {
3072 if (*argv[0] == '8') {
3073 if (host.enable)
3074 talloc_free(host.enable);
3075 host.enable = NULL;
3076
3077 if (host.enable_encrypt)
3078 talloc_free(host.enable_encrypt);
3079 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3080
3081 return CMD_SUCCESS;
3082 } else {
3083 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3084 return CMD_WARNING;
3085 }
3086 }
3087
3088 if (!isalnum((int)*argv[0])) {
3089 vty_out(vty,
3090 "Please specify string starting with alphanumeric%s",
3091 VTY_NEWLINE);
3092 return CMD_WARNING;
3093 }
3094
3095 if (host.enable)
3096 talloc_free(host.enable);
3097 host.enable = NULL;
3098
3099 /* Plain password input. */
3100#ifdef VTY_CRYPT_PW
3101 if (host.encrypt) {
3102 if (host.enable_encrypt)
3103 talloc_free(host.enable_encrypt);
3104 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3105 } else
3106#endif
3107 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3108
3109 return CMD_SUCCESS;
3110}
3111
3112ALIAS(config_enable_password,
3113 enable_password_text_cmd,
3114 "enable password LINE",
3115 "Modify enable password parameters\n"
3116 "Assign the privileged level password\n"
3117 "The UNENCRYPTED (cleartext) 'enable' password\n")
3118
3119/* VTY enable password delete. */
3120 DEFUN(no_config_enable_password, no_enable_password_cmd,
3121 "no enable password",
3122 NO_STR
3123 "Modify enable password parameters\n"
3124 "Assign the privileged level password\n")
3125{
3126 if (host.enable)
3127 talloc_free(host.enable);
3128 host.enable = NULL;
3129
3130 if (host.enable_encrypt)
3131 talloc_free(host.enable_encrypt);
3132 host.enable_encrypt = NULL;
3133
3134 return CMD_SUCCESS;
3135}
3136
3137#ifdef VTY_CRYPT_PW
3138DEFUN(service_password_encrypt,
3139 service_password_encrypt_cmd,
3140 "service password-encryption",
3141 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3142{
3143 if (host.encrypt)
3144 return CMD_SUCCESS;
3145
3146 host.encrypt = 1;
3147
3148 if (host.password) {
3149 if (host.password_encrypt)
3150 talloc_free(host.password_encrypt);
3151 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3152 }
3153 if (host.enable) {
3154 if (host.enable_encrypt)
3155 talloc_free(host.enable_encrypt);
3156 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3157 }
3158
3159 return CMD_SUCCESS;
3160}
3161
3162DEFUN(no_service_password_encrypt,
3163 no_service_password_encrypt_cmd,
3164 "no service password-encryption",
3165 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3166{
3167 if (!host.encrypt)
3168 return CMD_SUCCESS;
3169
3170 host.encrypt = 0;
3171
3172 if (host.password_encrypt)
3173 talloc_free(host.password_encrypt);
3174 host.password_encrypt = NULL;
3175
3176 if (host.enable_encrypt)
3177 talloc_free(host.enable_encrypt);
3178 host.enable_encrypt = NULL;
3179
3180 return CMD_SUCCESS;
3181}
3182#endif
3183
3184DEFUN(config_terminal_length, config_terminal_length_cmd,
3185 "terminal length <0-512>",
3186 "Set terminal line parameters\n"
3187 "Set number of lines on a screen\n"
3188 "Number of lines on screen (0 for no pausing)\n")
3189{
3190 int lines;
3191 char *endptr = NULL;
3192
3193 lines = strtol(argv[0], &endptr, 10);
3194 if (lines < 0 || lines > 512 || *endptr != '\0') {
3195 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3196 return CMD_WARNING;
3197 }
3198 vty->lines = lines;
3199
3200 return CMD_SUCCESS;
3201}
3202
3203DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3204 "terminal no length",
3205 "Set terminal line parameters\n"
3206 NO_STR "Set number of lines on a screen\n")
3207{
3208 vty->lines = -1;
3209 return CMD_SUCCESS;
3210}
3211
3212DEFUN(service_terminal_length, service_terminal_length_cmd,
3213 "service terminal-length <0-512>",
3214 "Set up miscellaneous service\n"
3215 "System wide terminal length configuration\n"
3216 "Number of lines of VTY (0 means no line control)\n")
3217{
3218 int lines;
3219 char *endptr = NULL;
3220
3221 lines = strtol(argv[0], &endptr, 10);
3222 if (lines < 0 || lines > 512 || *endptr != '\0') {
3223 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3224 return CMD_WARNING;
3225 }
3226 host.lines = lines;
3227
3228 return CMD_SUCCESS;
3229}
3230
3231DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3232 "no service terminal-length [<0-512>]",
3233 NO_STR
3234 "Set up miscellaneous service\n"
3235 "System wide terminal length configuration\n"
3236 "Number of lines of VTY (0 means no line control)\n")
3237{
3238 host.lines = -1;
3239 return CMD_SUCCESS;
3240}
3241
3242DEFUN_HIDDEN(do_echo,
3243 echo_cmd,
3244 "echo .MESSAGE",
3245 "Echo a message back to the vty\n" "The message to echo\n")
3246{
3247 char *message;
3248
3249 vty_out(vty, "%s%s",
3250 ((message =
3251 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3252 if (message)
3253 talloc_free(message);
3254 return CMD_SUCCESS;
3255}
3256
3257#if 0
3258DEFUN(config_logmsg,
3259 config_logmsg_cmd,
3260 "logmsg " LOG_LEVELS " .MESSAGE",
3261 "Send a message to enabled logging destinations\n"
3262 LOG_LEVEL_DESC "The message to send\n")
3263{
3264 int level;
3265 char *message;
3266
3267 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3268 return CMD_ERR_NO_MATCH;
3269
3270 zlog(NULL, level,
3271 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3272 if (message)
3273 talloc_free(message);
3274 return CMD_SUCCESS;
3275}
3276
3277DEFUN(show_logging,
3278 show_logging_cmd,
3279 "show logging", SHOW_STR "Show current logging configuration\n")
3280{
3281 struct zlog *zl = zlog_default;
3282
3283 vty_out(vty, "Syslog logging: ");
3284 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3285 vty_out(vty, "disabled");
3286 else
3287 vty_out(vty, "level %s, facility %s, ident %s",
3288 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3289 facility_name(zl->facility), zl->ident);
3290 vty_out(vty, "%s", VTY_NEWLINE);
3291
3292 vty_out(vty, "Stdout logging: ");
3293 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3294 vty_out(vty, "disabled");
3295 else
3296 vty_out(vty, "level %s",
3297 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3298 vty_out(vty, "%s", VTY_NEWLINE);
3299
3300 vty_out(vty, "Monitor logging: ");
3301 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3302 vty_out(vty, "disabled");
3303 else
3304 vty_out(vty, "level %s",
3305 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3306 vty_out(vty, "%s", VTY_NEWLINE);
3307
3308 vty_out(vty, "File logging: ");
3309 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3310 vty_out(vty, "disabled");
3311 else
3312 vty_out(vty, "level %s, filename %s",
3313 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3314 zl->filename);
3315 vty_out(vty, "%s", VTY_NEWLINE);
3316
3317 vty_out(vty, "Protocol name: %s%s",
3318 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3319 vty_out(vty, "Record priority: %s%s",
3320 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3321
3322 return CMD_SUCCESS;
3323}
3324
3325DEFUN(config_log_stdout,
3326 config_log_stdout_cmd,
3327 "log stdout", "Logging control\n" "Set stdout logging level\n")
3328{
3329 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3330 return CMD_SUCCESS;
3331}
3332
3333DEFUN(config_log_stdout_level,
3334 config_log_stdout_level_cmd,
3335 "log stdout " LOG_LEVELS,
3336 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3337{
3338 int level;
3339
3340 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3341 return CMD_ERR_NO_MATCH;
3342 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3343 return CMD_SUCCESS;
3344}
3345
3346DEFUN(no_config_log_stdout,
3347 no_config_log_stdout_cmd,
3348 "no log stdout [LEVEL]",
3349 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3350{
3351 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3352 return CMD_SUCCESS;
3353}
3354
3355DEFUN(config_log_monitor,
3356 config_log_monitor_cmd,
3357 "log monitor",
3358 "Logging control\n" "Set terminal line (monitor) logging level\n")
3359{
3360 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3361 return CMD_SUCCESS;
3362}
3363
3364DEFUN(config_log_monitor_level,
3365 config_log_monitor_level_cmd,
3366 "log monitor " LOG_LEVELS,
3367 "Logging control\n"
3368 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3369{
3370 int level;
3371
3372 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3373 return CMD_ERR_NO_MATCH;
3374 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3375 return CMD_SUCCESS;
3376}
3377
3378DEFUN(no_config_log_monitor,
3379 no_config_log_monitor_cmd,
3380 "no log monitor [LEVEL]",
3381 NO_STR
3382 "Logging control\n"
3383 "Disable terminal line (monitor) logging\n" "Logging level\n")
3384{
3385 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3386 return CMD_SUCCESS;
3387}
3388
3389static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3390{
3391 int ret;
3392 char *p = NULL;
3393 const char *fullpath;
3394
3395 /* Path detection. */
3396 if (!IS_DIRECTORY_SEP(*fname)) {
3397 char cwd[MAXPATHLEN + 1];
3398 cwd[MAXPATHLEN] = '\0';
3399
3400 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3401 zlog_err("config_log_file: Unable to alloc mem!");
3402 return CMD_WARNING;
3403 }
3404
3405 if ((p = _talloc_zero(tall_vcmd_ctx,
3406 strlen(cwd) + strlen(fname) + 2),
3407 "set_log_file")
3408 == NULL) {
3409 zlog_err("config_log_file: Unable to alloc mem!");
3410 return CMD_WARNING;
3411 }
3412 sprintf(p, "%s/%s", cwd, fname);
3413 fullpath = p;
3414 } else
3415 fullpath = fname;
3416
3417 ret = zlog_set_file(NULL, fullpath, loglevel);
3418
3419 if (p)
3420 talloc_free(p);
3421
3422 if (!ret) {
3423 vty_out(vty, "can't open logfile %s\n", fname);
3424 return CMD_WARNING;
3425 }
3426
3427 if (host.logfile)
3428 talloc_free(host.logfile);
3429
3430 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3431
3432 return CMD_SUCCESS;
3433}
3434
3435DEFUN(config_log_file,
3436 config_log_file_cmd,
3437 "log file FILENAME",
3438 "Logging control\n" "Logging to file\n" "Logging filename\n")
3439{
3440 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3441}
3442
3443DEFUN(config_log_file_level,
3444 config_log_file_level_cmd,
3445 "log file FILENAME " LOG_LEVELS,
3446 "Logging control\n"
3447 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3448{
3449 int level;
3450
3451 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3452 return CMD_ERR_NO_MATCH;
3453 return set_log_file(vty, argv[0], level);
3454}
3455
3456DEFUN(no_config_log_file,
3457 no_config_log_file_cmd,
3458 "no log file [FILENAME]",
3459 NO_STR
3460 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3461{
3462 zlog_reset_file(NULL);
3463
3464 if (host.logfile)
3465 talloc_free(host.logfile);
3466
3467 host.logfile = NULL;
3468
3469 return CMD_SUCCESS;
3470}
3471
3472ALIAS(no_config_log_file,
3473 no_config_log_file_level_cmd,
3474 "no log file FILENAME LEVEL",
3475 NO_STR
3476 "Logging control\n"
3477 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3478
3479 DEFUN(config_log_syslog,
3480 config_log_syslog_cmd,
3481 "log syslog", "Logging control\n" "Set syslog logging level\n")
3482{
3483 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3484 return CMD_SUCCESS;
3485}
3486
3487DEFUN(config_log_syslog_level,
3488 config_log_syslog_level_cmd,
3489 "log syslog " LOG_LEVELS,
3490 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3491{
3492 int level;
3493
3494 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3495 return CMD_ERR_NO_MATCH;
3496 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3497 return CMD_SUCCESS;
3498}
3499
3500DEFUN_DEPRECATED(config_log_syslog_facility,
3501 config_log_syslog_facility_cmd,
3502 "log syslog facility " LOG_FACILITIES,
3503 "Logging control\n"
3504 "Logging goes to syslog\n"
3505 "(Deprecated) Facility parameter for syslog messages\n"
3506 LOG_FACILITY_DESC)
3507{
3508 int facility;
3509
3510 if ((facility = facility_match(argv[0])) < 0)
3511 return CMD_ERR_NO_MATCH;
3512
3513 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3514 zlog_default->facility = facility;
3515 return CMD_SUCCESS;
3516}
3517
3518DEFUN(no_config_log_syslog,
3519 no_config_log_syslog_cmd,
3520 "no log syslog [LEVEL]",
3521 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3522{
3523 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3524 return CMD_SUCCESS;
3525}
3526
3527ALIAS(no_config_log_syslog,
3528 no_config_log_syslog_facility_cmd,
3529 "no log syslog facility " LOG_FACILITIES,
3530 NO_STR
3531 "Logging control\n"
3532 "Logging goes to syslog\n"
3533 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3534
3535 DEFUN(config_log_facility,
3536 config_log_facility_cmd,
3537 "log facility " LOG_FACILITIES,
3538 "Logging control\n"
3539 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3540{
3541 int facility;
3542
3543 if ((facility = facility_match(argv[0])) < 0)
3544 return CMD_ERR_NO_MATCH;
3545 zlog_default->facility = facility;
3546 return CMD_SUCCESS;
3547}
3548
3549DEFUN(no_config_log_facility,
3550 no_config_log_facility_cmd,
3551 "no log facility [FACILITY]",
3552 NO_STR
3553 "Logging control\n"
3554 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3555{
3556 zlog_default->facility = LOG_DAEMON;
3557 return CMD_SUCCESS;
3558}
3559
3560DEFUN_DEPRECATED(config_log_trap,
3561 config_log_trap_cmd,
3562 "log trap " LOG_LEVELS,
3563 "Logging control\n"
3564 "(Deprecated) Set logging level and default for all destinations\n"
3565 LOG_LEVEL_DESC)
3566{
3567 int new_level;
3568 int i;
3569
3570 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3571 return CMD_ERR_NO_MATCH;
3572
3573 zlog_default->default_lvl = new_level;
3574 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3575 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3576 zlog_default->maxlvl[i] = new_level;
3577 return CMD_SUCCESS;
3578}
3579
3580DEFUN_DEPRECATED(no_config_log_trap,
3581 no_config_log_trap_cmd,
3582 "no log trap [LEVEL]",
3583 NO_STR
3584 "Logging control\n"
3585 "Permit all logging information\n" "Logging level\n")
3586{
3587 zlog_default->default_lvl = LOG_DEBUG;
3588 return CMD_SUCCESS;
3589}
3590
3591DEFUN(config_log_record_priority,
3592 config_log_record_priority_cmd,
3593 "log record-priority",
3594 "Logging control\n"
3595 "Log the priority of the message within the message\n")
3596{
3597 zlog_default->record_priority = 1;
3598 return CMD_SUCCESS;
3599}
3600
3601DEFUN(no_config_log_record_priority,
3602 no_config_log_record_priority_cmd,
3603 "no log record-priority",
3604 NO_STR
3605 "Logging control\n"
3606 "Do not log the priority of the message within the message\n")
3607{
3608 zlog_default->record_priority = 0;
3609 return CMD_SUCCESS;
3610}
3611#endif
3612
3613DEFUN(banner_motd_file,
3614 banner_motd_file_cmd,
3615 "banner motd file [FILE]",
3616 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3617{
3618 if (host.motdfile)
3619 talloc_free(host.motdfile);
3620 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3621
3622 return CMD_SUCCESS;
3623}
3624
3625DEFUN(banner_motd_default,
3626 banner_motd_default_cmd,
3627 "banner motd default",
3628 "Set banner string\n" "Strings for motd\n" "Default string\n")
3629{
3630 host.motd = default_motd;
3631 return CMD_SUCCESS;
3632}
3633
3634DEFUN(no_banner_motd,
3635 no_banner_motd_cmd,
3636 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3637{
3638 host.motd = NULL;
3639 if (host.motdfile)
3640 talloc_free(host.motdfile);
3641 host.motdfile = NULL;
3642 return CMD_SUCCESS;
3643}
3644
3645/* Set config filename. Called from vty.c */
3646void host_config_set(const char *filename)
3647{
3648 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3649}
3650
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003651/*! Deprecated, now happens implicitly when calling install_node().
3652 * Users of the API may still attempt to call this function, hence
3653 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003654void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003655{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003656}
3657
3658/*! Deprecated, now happens implicitly when calling install_node().
3659 * Users of the API may still attempt to call this function, hence
3660 * leave it here as a no-op. */
3661void vty_install_default(int node)
3662{
3663}
3664
3665/*! Install common commands like 'exit' and 'list'. */
3666static void install_basic_node_commands(int node)
3667{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003668 install_element(node, &config_help_cmd);
3669 install_element(node, &config_list_cmd);
3670
3671 install_element(node, &config_write_terminal_cmd);
3672 install_element(node, &config_write_file_cmd);
3673 install_element(node, &config_write_memory_cmd);
3674 install_element(node, &config_write_cmd);
3675 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003676
3677 install_element(node, &config_exit_cmd);
3678
3679 if (node >= CONFIG_NODE) {
3680 /* It's not a top node. */
3681 install_element(node, &config_end_cmd);
3682 }
3683}
3684
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003685/*! Return true if a node is installed by install_basic_node_commands(), so
3686 * that we can avoid repeating them for each and every node during 'show
3687 * running-config' */
3688static bool vty_command_is_common(struct cmd_element *cmd)
3689{
3690 if (cmd == &config_help_cmd
3691 || cmd == &config_list_cmd
3692 || cmd == &config_write_terminal_cmd
3693 || cmd == &config_write_file_cmd
3694 || cmd == &config_write_memory_cmd
3695 || cmd == &config_write_cmd
3696 || cmd == &show_running_config_cmd
3697 || cmd == &config_exit_cmd
3698 || cmd == &config_end_cmd)
3699 return true;
3700 return false;
3701}
3702
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003703/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003704 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003705 * \param[in] vty the vty of the code
3706 * \param[in] filename where to store the file
3707 * \return 0 in case of success.
3708 *
3709 * If the filename already exists create a filename.sav
3710 * version with the current code.
3711 *
3712 */
3713int osmo_vty_write_config_file(const char *filename)
3714{
3715 char *failed_file;
3716 int rc;
3717
3718 rc = write_config_file(filename, &failed_file);
3719 talloc_free(failed_file);
3720 return rc;
3721}
3722
3723/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003724 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003725 * \return 0 in case of success.
3726 *
3727 * If the filename already exists create a filename.sav
3728 * version with the current code.
3729 *
3730 */
3731int osmo_vty_save_config_file(void)
3732{
3733 char *failed_file;
3734 int rc;
3735
3736 if (host.config == NULL)
3737 return -7;
3738
3739 rc = write_config_file(host.config, &failed_file);
3740 talloc_free(failed_file);
3741 return rc;
3742}
3743
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003744/* Initialize command interface. Install basic nodes and commands. */
3745void cmd_init(int terminal)
3746{
3747 /* Allocate initial top vector of commands. */
3748 cmdvec = vector_init(VECTOR_MIN_SIZE);
3749
3750 /* Default host value settings. */
3751 host.name = NULL;
3752 host.password = NULL;
3753 host.enable = NULL;
3754 host.logfile = NULL;
3755 host.config = NULL;
3756 host.lines = -1;
3757 host.motd = default_motd;
3758 host.motdfile = NULL;
3759
3760 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003761 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003762 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003763 install_node_bare(&auth_node, NULL);
3764 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003765 install_node(&config_node, config_write_host);
3766
3767 /* Each node's basic commands. */
3768 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003769 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003770 if (terminal) {
3771 install_element(VIEW_NODE, &config_list_cmd);
3772 install_element(VIEW_NODE, &config_exit_cmd);
3773 install_element(VIEW_NODE, &config_help_cmd);
3774 install_element(VIEW_NODE, &config_enable_cmd);
3775 install_element(VIEW_NODE, &config_terminal_length_cmd);
3776 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3777 install_element(VIEW_NODE, &echo_cmd);
3778 }
3779
3780 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003781 install_element(ENABLE_NODE, &config_disable_cmd);
3782 install_element(ENABLE_NODE, &config_terminal_cmd);
3783 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3784 }
3785 install_element (ENABLE_NODE, &show_startup_config_cmd);
3786 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003787 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003788
3789 if (terminal) {
3790 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3791 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3792 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003793 }
3794
3795 install_element(CONFIG_NODE, &hostname_cmd);
3796 install_element(CONFIG_NODE, &no_hostname_cmd);
3797
3798 if (terminal) {
3799 install_element(CONFIG_NODE, &password_cmd);
3800 install_element(CONFIG_NODE, &password_text_cmd);
3801 install_element(CONFIG_NODE, &enable_password_cmd);
3802 install_element(CONFIG_NODE, &enable_password_text_cmd);
3803 install_element(CONFIG_NODE, &no_enable_password_cmd);
3804
3805#ifdef VTY_CRYPT_PW
3806 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3807 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3808#endif
3809 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3810 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3811 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3812 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3813 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3814
3815 }
3816 srand(time(NULL));
3817}
Harald Welte7acb30c2011-08-17 17:13:48 +02003818
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003819/*! @} */