blob: 9d02d696a610781ba5dcc74649c0efbdeaad1dce [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) {
Neels Hofmeyrc1978092019-01-31 08:14:26 +0100470 if ((vector_active(descvec)) >= 1
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200471 && (desc = vector_slot(descvec, 0)) != NULL) {
472 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
473 return size;
474 else
475 size++;
476 } else
477 size++;
478 }
479 return size;
480}
481
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200482/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483const char *cmd_prompt(enum node_type node)
484{
485 struct cmd_node *cnode;
486
487 cnode = vector_slot(cmdvec, node);
488 return cnode->prompt;
489}
490
Alexander Couzensad580ba2016-05-16 16:01:45 +0200491/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200492 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200493 * \param unsafe string
494 * \return a new talloc char *
495 */
496char *osmo_asciidoc_escape(const char *inp)
497{
498 int _strlen;
499 char *out, *out_ptr;
500 int len = 0, i, j;
501
502 if (!inp)
503 return NULL;
504 _strlen = strlen(inp);
505
506 for (i = 0; i < _strlen; ++i) {
507 switch (inp[i]) {
508 case '|':
509 len += 2;
510 break;
511 default:
512 len += 1;
513 break;
514 }
515 }
516
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200517 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200518 if (!out)
519 return NULL;
520
521 out_ptr = out;
522
523#define ADD(out, str) \
524 for (j = 0; j < strlen(str); ++j) \
525 *(out++) = str[j];
526
527 for (i = 0; i < _strlen; ++i) {
528 switch (inp[i]) {
529 case '|':
530 ADD(out_ptr, "\\|");
531 break;
532 default:
533 *(out_ptr++) = inp[i];
534 break;
535 }
536 }
537
538#undef ADD
539
540 out_ptr[0] = '\0';
541 return out;
542}
543
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100544static char *xml_escape(const char *inp)
545{
546 int _strlen;
547 char *out, *out_ptr;
548 int len = 0, i, j;
549
550 if (!inp)
551 return NULL;
552 _strlen = strlen(inp);
553
554 for (i = 0; i < _strlen; ++i) {
555 switch (inp[i]) {
556 case '"':
557 len += 6;
558 break;
559 case '\'':
560 len += 6;
561 break;
562 case '<':
563 len += 4;
564 break;
565 case '>':
566 len += 4;
567 break;
568 case '&':
569 len += 5;
570 break;
571 default:
572 len += 1;
573 break;
574 }
575 }
576
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200577 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100578 if (!out)
579 return NULL;
580
581 out_ptr = out;
582
583#define ADD(out, str) \
584 for (j = 0; j < strlen(str); ++j) \
585 *(out++) = str[j];
586
587 for (i = 0; i < _strlen; ++i) {
588 switch (inp[i]) {
589 case '"':
590 ADD(out_ptr, "&quot;");
591 break;
592 case '\'':
593 ADD(out_ptr, "&apos;");
594 break;
595 case '<':
596 ADD(out_ptr, "&lt;");
597 break;
598 case '>':
599 ADD(out_ptr, "&gt;");
600 break;
601 case '&':
602 ADD(out_ptr, "&amp;");
603 break;
604 default:
605 *(out_ptr++) = inp[i];
606 break;
607 }
608 }
609
610#undef ADD
611
612 out_ptr[0] = '\0';
613 return out;
614}
615
616/*
617 * Write one cmd_element as XML to the given VTY.
618 */
619static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
620{
621 char *xml_string = xml_escape(cmd->string);
622
623 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
624 vty_out(vty, " <params>%s", VTY_NEWLINE);
625
626 int j;
627 for (j = 0; j < vector_count(cmd->strvec); ++j) {
628 vector descvec = vector_slot(cmd->strvec, j);
629 int i;
630 for (i = 0; i < vector_active(descvec); ++i) {
631 char *xml_param, *xml_doc;
632 struct desc *desc = vector_slot(descvec, i);
633 if (desc == NULL)
634 continue;
635
636 xml_param = xml_escape(desc->cmd);
637 xml_doc = xml_escape(desc->str);
638 vty_out(vty, " <param name='%s' doc='%s' />%s",
639 xml_param, xml_doc, VTY_NEWLINE);
640 talloc_free(xml_param);
641 talloc_free(xml_doc);
642 }
643 }
644
645 vty_out(vty, " </params>%s", VTY_NEWLINE);
646 vty_out(vty, " </command>%s", VTY_NEWLINE);
647
648 talloc_free(xml_string);
649 return 0;
650}
651
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200652static bool vty_command_is_common(struct cmd_element *cmd);
653
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100654/*
655 * Dump all nodes and commands associated with a given node as XML to the VTY.
656 */
657static int vty_dump_nodes(struct vty *vty)
658{
659 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200660 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100661
662 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
663
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200664 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
665 vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
666 vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
667 vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
668 " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
669 for (i = 0; i < vector_active(cmdvec); ++i) {
670 struct cmd_node *cnode;
671 cnode = vector_slot(cmdvec, i);
672 if (!cnode)
673 continue;
674 if (cnode->node != CONFIG_NODE)
675 continue;
676
677 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
678 struct cmd_element *elem;
679 elem = vector_slot(cnode->cmd_vector, j);
680 if (!vty_command_is_common(elem))
681 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200682 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200683 vty_dump_element(elem, vty);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200684 }
685 }
686 vty_out(vty, " </node>%s", VTY_NEWLINE);
687
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100688 for (i = 0; i < vector_active(cmdvec); ++i) {
689 struct cmd_node *cnode;
690 cnode = vector_slot(cmdvec, i);
691 if (!cnode)
692 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200693 if (vector_active(cnode->cmd_vector) < 1)
694 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100695
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200696 /* De-dup node IDs: how many times has this same name been used before? Count the first
697 * occurence as _1 and omit that first suffix, so that the first occurence is called
698 * 'name', the second becomes 'name_2', then 'name_3', ... */
699 same_name_count = 1;
700 for (j = 0; j < i; ++j) {
701 struct cmd_node *cnode2;
702 cnode2 = vector_slot(cmdvec, j);
703 if (!cnode2)
704 continue;
705 if (strcmp(cnode->name, cnode2->name) == 0)
706 same_name_count ++;
707 }
708
709 vty_out(vty, " <node id='%s", cnode->name);
710 if (same_name_count > 1 || !*cnode->name)
711 vty_out(vty, "_%d", same_name_count);
712 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200713 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100714
715 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
716 struct cmd_element *elem;
717 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200718 if (vty_command_is_common(elem))
719 continue;
Neels Hofmeyr53989112018-09-24 18:39:00 +0200720 if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte23a299f2018-06-09 17:40:52 +0200721 vty_dump_element(elem, vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100722 }
723
724 vty_out(vty, " </node>%s", VTY_NEWLINE);
725 }
726
727 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
728
729 return 0;
730}
731
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200732/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100733static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
734{
735 int i;
736
737 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
738 struct cmd_element *elem;
739 elem = vector_slot(cnode->cmd_vector, i);
740 if (!elem->string)
741 continue;
742 if (!strcmp(elem->string, cmdstring))
743 return 1;
744 }
745 return 0;
746}
747
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200748/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200749 * \param[in] ntype Node Type
750 * \param[cmd] element to be installed
751 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000752void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200753{
754 struct cmd_node *cnode;
755
756 cnode = vector_slot(cmdvec, ntype);
757
Harald Weltea99d45a2015-11-12 13:48:23 +0100758 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100759 /* ensure no _identical_ command has been registered at this
760 * node so far */
761 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200762
763 vector_set(cnode->cmd_vector, cmd);
764
765 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
766 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
767}
768
769/* Install a command into VIEW and ENABLE node */
770void install_element_ve(struct cmd_element *cmd)
771{
772 install_element(VIEW_NODE, cmd);
773 install_element(ENABLE_NODE, cmd);
774}
775
776#ifdef VTY_CRYPT_PW
777static unsigned char itoa64[] =
778 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
779
780static void to64(char *s, long v, int n)
781{
782 while (--n >= 0) {
783 *s++ = itoa64[v & 0x3f];
784 v >>= 6;
785 }
786}
787
788static char *zencrypt(const char *passwd)
789{
790 char salt[6];
791 struct timeval tv;
792 char *crypt(const char *, const char *);
793
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200794 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200795
796 to64(&salt[0], random(), 3);
797 to64(&salt[3], tv.tv_usec, 3);
798 salt[5] = '\0';
799
800 return crypt(passwd, salt);
801}
802#endif
803
804/* This function write configuration of this host. */
805static int config_write_host(struct vty *vty)
806{
807 if (host.name)
808 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
809
810 if (host.encrypt) {
811 if (host.password_encrypt)
812 vty_out(vty, "password 8 %s%s", host.password_encrypt,
813 VTY_NEWLINE);
814 if (host.enable_encrypt)
815 vty_out(vty, "enable password 8 %s%s",
816 host.enable_encrypt, VTY_NEWLINE);
817 } else {
818 if (host.password)
819 vty_out(vty, "password %s%s", host.password,
820 VTY_NEWLINE);
821 if (host.enable)
822 vty_out(vty, "enable password %s%s", host.enable,
823 VTY_NEWLINE);
824 }
825
826 if (host.advanced)
827 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
828
829 if (host.encrypt)
830 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
831
832 if (host.lines >= 0)
833 vty_out(vty, "service terminal-length %d%s", host.lines,
834 VTY_NEWLINE);
835
836 if (host.motdfile)
837 vty_out(vty, "banner motd file %s%s", host.motdfile,
838 VTY_NEWLINE);
839 else if (!host.motd)
840 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
841
842 return 1;
843}
844
845/* Utility function for getting command vector. */
846static vector cmd_node_vector(vector v, enum node_type ntype)
847{
848 struct cmd_node *cnode = vector_slot(v, ntype);
849 return cnode->cmd_vector;
850}
851
852/* Completion match types. */
853enum match_type {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100854 no_match = 0,
855 any_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200856 extend_match,
857 ipv4_prefix_match,
858 ipv4_match,
859 ipv6_prefix_match,
860 ipv6_match,
861 range_match,
862 vararg_match,
863 partly_match,
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100864 exact_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200865};
866
867static enum match_type cmd_ipv4_match(const char *str)
868{
869 const char *sp;
870 int dots = 0, nums = 0;
871 char buf[4];
872
873 if (str == NULL)
874 return partly_match;
875
876 for (;;) {
877 memset(buf, 0, sizeof(buf));
878 sp = str;
879 while (*str != '\0') {
880 if (*str == '.') {
881 if (dots >= 3)
882 return no_match;
883
884 if (*(str + 1) == '.')
885 return no_match;
886
887 if (*(str + 1) == '\0')
888 return partly_match;
889
890 dots++;
891 break;
892 }
893 if (!isdigit((int)*str))
894 return no_match;
895
896 str++;
897 }
898
899 if (str - sp > 3)
900 return no_match;
901
902 strncpy(buf, sp, str - sp);
903 if (atoi(buf) > 255)
904 return no_match;
905
906 nums++;
907
908 if (*str == '\0')
909 break;
910
911 str++;
912 }
913
914 if (nums < 4)
915 return partly_match;
916
917 return exact_match;
918}
919
920static enum match_type cmd_ipv4_prefix_match(const char *str)
921{
922 const char *sp;
923 int dots = 0;
924 char buf[4];
925
926 if (str == NULL)
927 return partly_match;
928
929 for (;;) {
930 memset(buf, 0, sizeof(buf));
931 sp = str;
932 while (*str != '\0' && *str != '/') {
933 if (*str == '.') {
934 if (dots == 3)
935 return no_match;
936
937 if (*(str + 1) == '.' || *(str + 1) == '/')
938 return no_match;
939
940 if (*(str + 1) == '\0')
941 return partly_match;
942
943 dots++;
944 break;
945 }
946
947 if (!isdigit((int)*str))
948 return no_match;
949
950 str++;
951 }
952
953 if (str - sp > 3)
954 return no_match;
955
956 strncpy(buf, sp, str - sp);
957 if (atoi(buf) > 255)
958 return no_match;
959
960 if (dots == 3) {
961 if (*str == '/') {
962 if (*(str + 1) == '\0')
963 return partly_match;
964
965 str++;
966 break;
967 } else if (*str == '\0')
968 return partly_match;
969 }
970
971 if (*str == '\0')
972 return partly_match;
973
974 str++;
975 }
976
977 sp = str;
978 while (*str != '\0') {
979 if (!isdigit((int)*str))
980 return no_match;
981
982 str++;
983 }
984
985 if (atoi(sp) > 32)
986 return no_match;
987
988 return exact_match;
989}
990
991#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
992#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
993#define STATE_START 1
994#define STATE_COLON 2
995#define STATE_DOUBLE 3
996#define STATE_ADDR 4
997#define STATE_DOT 5
998#define STATE_SLASH 6
999#define STATE_MASK 7
1000
1001#ifdef HAVE_IPV6
1002
1003static enum match_type cmd_ipv6_match(const char *str)
1004{
1005 int state = STATE_START;
1006 int colons = 0, nums = 0, double_colon = 0;
1007 const char *sp = NULL;
1008 struct sockaddr_in6 sin6_dummy;
1009 int ret;
1010
1011 if (str == NULL)
1012 return partly_match;
1013
1014 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
1015 return no_match;
1016
1017 /* use inet_pton that has a better support,
1018 * for example inet_pton can support the automatic addresses:
1019 * ::1.2.3.4
1020 */
1021 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1022
1023 if (ret == 1)
1024 return exact_match;
1025
1026 while (*str != '\0') {
1027 switch (state) {
1028 case STATE_START:
1029 if (*str == ':') {
1030 if (*(str + 1) != ':' && *(str + 1) != '\0')
1031 return no_match;
1032 colons--;
1033 state = STATE_COLON;
1034 } else {
1035 sp = str;
1036 state = STATE_ADDR;
1037 }
1038
1039 continue;
1040 case STATE_COLON:
1041 colons++;
1042 if (*(str + 1) == ':')
1043 state = STATE_DOUBLE;
1044 else {
1045 sp = str + 1;
1046 state = STATE_ADDR;
1047 }
1048 break;
1049 case STATE_DOUBLE:
1050 if (double_colon)
1051 return no_match;
1052
1053 if (*(str + 1) == ':')
1054 return no_match;
1055 else {
1056 if (*(str + 1) != '\0')
1057 colons++;
1058 sp = str + 1;
1059 state = STATE_ADDR;
1060 }
1061
1062 double_colon++;
1063 nums++;
1064 break;
1065 case STATE_ADDR:
1066 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1067 if (str - sp > 3)
1068 return no_match;
1069
1070 nums++;
1071 state = STATE_COLON;
1072 }
1073 if (*(str + 1) == '.')
1074 state = STATE_DOT;
1075 break;
1076 case STATE_DOT:
1077 state = STATE_ADDR;
1078 break;
1079 default:
1080 break;
1081 }
1082
1083 if (nums > 8)
1084 return no_match;
1085
1086 if (colons > 7)
1087 return no_match;
1088
1089 str++;
1090 }
1091
1092#if 0
1093 if (nums < 11)
1094 return partly_match;
1095#endif /* 0 */
1096
1097 return exact_match;
1098}
1099
1100static enum match_type cmd_ipv6_prefix_match(const char *str)
1101{
1102 int state = STATE_START;
1103 int colons = 0, nums = 0, double_colon = 0;
1104 int mask;
1105 const char *sp = NULL;
1106 char *endptr = NULL;
1107
1108 if (str == NULL)
1109 return partly_match;
1110
1111 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
1112 return no_match;
1113
1114 while (*str != '\0' && state != STATE_MASK) {
1115 switch (state) {
1116 case STATE_START:
1117 if (*str == ':') {
1118 if (*(str + 1) != ':' && *(str + 1) != '\0')
1119 return no_match;
1120 colons--;
1121 state = STATE_COLON;
1122 } else {
1123 sp = str;
1124 state = STATE_ADDR;
1125 }
1126
1127 continue;
1128 case STATE_COLON:
1129 colons++;
1130 if (*(str + 1) == '/')
1131 return no_match;
1132 else if (*(str + 1) == ':')
1133 state = STATE_DOUBLE;
1134 else {
1135 sp = str + 1;
1136 state = STATE_ADDR;
1137 }
1138 break;
1139 case STATE_DOUBLE:
1140 if (double_colon)
1141 return no_match;
1142
1143 if (*(str + 1) == ':')
1144 return no_match;
1145 else {
1146 if (*(str + 1) != '\0' && *(str + 1) != '/')
1147 colons++;
1148 sp = str + 1;
1149
1150 if (*(str + 1) == '/')
1151 state = STATE_SLASH;
1152 else
1153 state = STATE_ADDR;
1154 }
1155
1156 double_colon++;
1157 nums += 1;
1158 break;
1159 case STATE_ADDR:
1160 if (*(str + 1) == ':' || *(str + 1) == '.'
1161 || *(str + 1) == '\0' || *(str + 1) == '/') {
1162 if (str - sp > 3)
1163 return no_match;
1164
1165 for (; sp <= str; sp++)
1166 if (*sp == '/')
1167 return no_match;
1168
1169 nums++;
1170
1171 if (*(str + 1) == ':')
1172 state = STATE_COLON;
1173 else if (*(str + 1) == '.')
1174 state = STATE_DOT;
1175 else if (*(str + 1) == '/')
1176 state = STATE_SLASH;
1177 }
1178 break;
1179 case STATE_DOT:
1180 state = STATE_ADDR;
1181 break;
1182 case STATE_SLASH:
1183 if (*(str + 1) == '\0')
1184 return partly_match;
1185
1186 state = STATE_MASK;
1187 break;
1188 default:
1189 break;
1190 }
1191
1192 if (nums > 11)
1193 return no_match;
1194
1195 if (colons > 7)
1196 return no_match;
1197
1198 str++;
1199 }
1200
1201 if (state < STATE_MASK)
1202 return partly_match;
1203
1204 mask = strtol(str, &endptr, 10);
1205 if (*endptr != '\0')
1206 return no_match;
1207
1208 if (mask < 0 || mask > 128)
1209 return no_match;
1210
1211/* I don't know why mask < 13 makes command match partly.
1212 Forgive me to make this comments. I Want to set static default route
1213 because of lack of function to originate default in ospf6d; sorry
1214 yasu
1215 if (mask < 13)
1216 return partly_match;
1217*/
1218
1219 return exact_match;
1220}
1221
1222#endif /* HAVE_IPV6 */
1223
1224#define DECIMAL_STRLEN_MAX 10
1225
1226static int cmd_range_match(const char *range, const char *str)
1227{
1228 char *p;
1229 char buf[DECIMAL_STRLEN_MAX + 1];
1230 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001231
1232 if (str == NULL)
1233 return 1;
1234
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001235 if (range[1] == '-') {
1236 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001237
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001238 val = strtol(str, &endptr, 10);
1239 if (*endptr != '\0')
1240 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001241
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001242 range += 2;
1243 p = strchr(range, '-');
1244 if (p == NULL)
1245 return 0;
1246 if (p - range > DECIMAL_STRLEN_MAX)
1247 return 0;
1248 strncpy(buf, range, p - range);
1249 buf[p - range] = '\0';
1250 min = -strtol(buf, &endptr, 10);
1251 if (*endptr != '\0')
1252 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001253
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001254 range = p + 1;
1255 p = strchr(range, '>');
1256 if (p == NULL)
1257 return 0;
1258 if (p - range > DECIMAL_STRLEN_MAX)
1259 return 0;
1260 strncpy(buf, range, p - range);
1261 buf[p - range] = '\0';
1262 max = strtol(buf, &endptr, 10);
1263 if (*endptr != '\0')
1264 return 0;
1265
1266 if (val < min || val > max)
1267 return 0;
1268 } else {
1269 unsigned long min, max, val;
1270
1271 val = strtoul(str, &endptr, 10);
1272 if (*endptr != '\0')
1273 return 0;
1274
1275 range++;
1276 p = strchr(range, '-');
1277 if (p == NULL)
1278 return 0;
1279 if (p - range > DECIMAL_STRLEN_MAX)
1280 return 0;
1281 strncpy(buf, range, p - range);
1282 buf[p - range] = '\0';
1283 min = strtoul(buf, &endptr, 10);
1284 if (*endptr != '\0')
1285 return 0;
1286
1287 range = p + 1;
1288 p = strchr(range, '>');
1289 if (p == NULL)
1290 return 0;
1291 if (p - range > DECIMAL_STRLEN_MAX)
1292 return 0;
1293 strncpy(buf, range, p - range);
1294 buf[p - range] = '\0';
1295 max = strtoul(buf, &endptr, 10);
1296 if (*endptr != '\0')
1297 return 0;
1298
1299 if (val < min || val > max)
1300 return 0;
1301 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302
1303 return 1;
1304}
1305
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001306/* helper to retrieve the 'real' argument string from an optional argument */
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001307static char *cmd_deopt(void *ctx, const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001308{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001309 /* we've got "[blah]". We want to strip off the []s and redo the
1310 * match check for "blah"
1311 */
1312 size_t len = strlen(str);
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
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001317 return talloc_strndup(ctx, str + 1, len - 2);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001318}
1319
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001320static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001321cmd_match(const char *str, const char *command,
1322 enum match_type min, bool recur)
1323{
1324
1325 if (recur && CMD_OPTION(str))
1326 {
1327 enum match_type ret;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001328 char *tmp = cmd_deopt(tall_vty_cmd_ctx, str);
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001329
1330 /* this would be a bug in a command, however handle it gracefully
1331 * as it we only discover it if a user tries to run it
1332 */
1333 if (tmp == NULL)
1334 return no_match;
1335
1336 ret = cmd_match(tmp, command, min, false);
1337
1338 talloc_free(tmp);
1339
1340 return ret;
1341 }
1342 else if (CMD_VARARG(str))
1343 return vararg_match;
1344 else if (CMD_RANGE(str))
1345 {
1346 if (cmd_range_match(str, command))
1347 return range_match;
1348 }
1349#ifdef HAVE_IPV6
1350 else if (CMD_IPV6(str))
1351 {
1352 if (cmd_ipv6_match(command) >= min)
1353 return ipv6_match;
1354 }
1355 else if (CMD_IPV6_PREFIX(str))
1356 {
1357 if (cmd_ipv6_prefix_match(command) >= min)
1358 return ipv6_prefix_match;
1359 }
1360#endif /* HAVE_IPV6 */
1361 else if (CMD_IPV4(str))
1362 {
1363 if (cmd_ipv4_match(command) >= min)
1364 return ipv4_match;
1365 }
1366 else if (CMD_IPV4_PREFIX(str))
1367 {
1368 if (cmd_ipv4_prefix_match(command) >= min)
1369 return ipv4_prefix_match;
1370 }
1371 else if (CMD_VARIABLE(str))
1372 return extend_match;
1373 else if (strncmp(command, str, strlen(command)) == 0)
1374 {
1375 if (strcmp(command, str) == 0)
1376 return exact_match;
1377 else if (partly_match >= min)
1378 return partly_match;
1379 }
1380
1381 return no_match;
1382}
1383
1384/* Filter vector at the specified index and by the given command string, to
1385 * the desired matching level (thus allowing part matches), and return match
1386 * type flag.
1387 */
1388static enum match_type
1389cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001390{
1391 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001392 struct cmd_element *cmd_element;
1393 enum match_type match_type;
1394 vector descvec;
1395 struct desc *desc;
1396
1397 match_type = no_match;
1398
1399 /* If command and cmd_element string does not match set NULL to vector */
1400 for (i = 0; i < vector_active(v); i++)
1401 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001402 if (index >= vector_active(cmd_element->strvec))
1403 vector_slot(v, i) = NULL;
1404 else {
1405 unsigned int j;
1406 int matched = 0;
1407
1408 descvec =
1409 vector_slot(cmd_element->strvec, index);
1410
1411 for (j = 0; j < vector_active(descvec); j++)
1412 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001413 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001414
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001415 ret = cmd_match (desc->cmd, command, level, true);
1416
1417 if (ret != no_match)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001418 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001419
1420 if (match_type < ret)
1421 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001422 }
1423 if (!matched)
1424 vector_slot(v, i) = NULL;
1425 }
1426 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001427
1428 if (match_type == no_match)
1429 return no_match;
1430
1431 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1432 * go again and filter out commands whose argument (at this index) is
1433 * 'weaker'. E.g., if we have 2 commands:
1434 *
1435 * foo bar <1-255>
1436 * foo bar BLAH
1437 *
1438 * and the command string is 'foo bar 10', then we will get here with with
1439 * 'range_match' being the strongest match. However, if 'BLAH' came
1440 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1441 *
1442 * If we don't do a 2nd pass and filter it out, the higher-layers will
1443 * consider this to be ambiguous.
1444 */
1445 for (i = 0; i < vector_active(v); i++)
1446 if ((cmd_element = vector_slot(v, i)) != NULL) {
1447 if (index >= vector_active(cmd_element->strvec))
1448 vector_slot(v, i) = NULL;
1449 else {
1450 unsigned int j;
1451 int matched = 0;
1452
1453 descvec =
1454 vector_slot(cmd_element->strvec, index);
1455
1456 for (j = 0; j < vector_active(descvec); j++)
1457 if ((desc = vector_slot(descvec, j))) {
1458 enum match_type ret;
1459
1460 ret = cmd_match(desc->cmd, command, any_match, true);
1461
1462 if (ret >= match_type)
1463 matched++;
1464 }
1465 if (!matched)
1466 vector_slot(v, i) = NULL;
1467 }
1468 }
1469
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001470 return match_type;
1471}
1472
1473/* Check ambiguous match */
1474static int
1475is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1476{
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001477 int ret = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001478 unsigned int i;
1479 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001480 struct cmd_element *cmd_element;
1481 const char *matched = NULL;
1482 vector descvec;
1483 struct desc *desc;
1484
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001485 /* In this loop, when a match is found, 'matched' points to it. If on a later iteration, an
1486 * identical match is found, the command is ambiguous. The trickiness is that a string may be
1487 * enclosed in '[str]' square brackets, which get removed by a talloc_strndup(), via cmd_deopt().
1488 * Such a string is usually needed for one loop iteration, except when 'matched' points to it. In
1489 * that case, the string must remain allocated until this function exits or another match comes
1490 * around. This is sufficiently confusing to justify a separate talloc tree to store all of the
1491 * odd allocations, and to free them all at the end. We are not expecting too many optional args
1492 * or ambiguities to cause a noticeable memory footprint from keeping all allocations. */
1493 void *cmd_deopt_ctx = NULL;
1494
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001495 for (i = 0; i < vector_active(v); i++)
1496 if ((cmd_element = vector_slot(v, i)) != NULL) {
1497 int match = 0;
1498
1499 descvec = vector_slot(cmd_element->strvec, index);
1500
1501 for (j = 0; j < vector_active(descvec); j++)
1502 if ((desc = vector_slot(descvec, j))) {
1503 enum match_type ret;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001504 const char *str = desc->cmd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001505
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001506 if (CMD_OPTION(str)) {
1507 if (!cmd_deopt_ctx)
1508 cmd_deopt_ctx =
1509 talloc_named_const(tall_vty_cmd_ctx, 0,
1510 __func__);
1511 str = cmd_deopt(cmd_deopt_ctx, str);
1512 if (str == NULL)
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001513 continue;
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001514 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001515
1516 switch (type) {
1517 case exact_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001518 if (!(CMD_VARIABLE (str))
1519 && strcmp(command, str) == 0)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001520 match++;
1521 break;
1522 case partly_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001523 if (!(CMD_VARIABLE (str))
1524 && strncmp(command, str, strlen (command)) == 0)
1525 {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001526 if (matched
1527 && strcmp(matched,
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001528 str) != 0) {
1529 ret = 1; /* There is ambiguous match. */
1530 goto free_and_return;
1531 } else
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001532 matched = str;
1533 match++;
1534 }
1535 break;
1536 case range_match:
1537 if (cmd_range_match
1538 (str, command)) {
1539 if (matched
1540 && strcmp(matched,
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001541 str) != 0) {
1542 ret = 1;
1543 goto free_and_return;
1544 } else
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001545 matched = str;
1546 match++;
1547 }
1548 break;
1549#ifdef HAVE_IPV6
1550 case ipv6_match:
1551 if (CMD_IPV6(str))
1552 match++;
1553 break;
1554 case ipv6_prefix_match:
1555 if ((ret =
1556 cmd_ipv6_prefix_match
1557 (command)) != no_match) {
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001558 if (ret == partly_match) {
1559 ret = 2; /* There is incomplete match. */
1560 goto free_and_return;
1561 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001562
1563 match++;
1564 }
1565 break;
1566#endif /* HAVE_IPV6 */
1567 case ipv4_match:
1568 if (CMD_IPV4(str))
1569 match++;
1570 break;
1571 case ipv4_prefix_match:
1572 if ((ret =
1573 cmd_ipv4_prefix_match
1574 (command)) != no_match) {
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001575 if (ret == partly_match) {
1576 ret = 2; /* There is incomplete match. */
1577 goto free_and_return;
1578 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001579
1580 match++;
1581 }
1582 break;
1583 case extend_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001584 if (CMD_VARIABLE (str))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001585 match++;
1586 break;
1587 case no_match:
1588 default:
1589 break;
1590 }
1591 }
1592 if (!match)
1593 vector_slot(v, i) = NULL;
1594 }
Neels Hofmeyr5314c512018-07-09 23:22:21 +02001595
1596free_and_return:
1597 if (cmd_deopt_ctx)
1598 talloc_free(cmd_deopt_ctx);
1599 return ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001600}
1601
1602/* If src matches dst return dst string, otherwise return NULL */
1603static const char *cmd_entry_function(const char *src, const char *dst)
1604{
1605 /* Skip variable arguments. */
1606 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1607 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1608 return NULL;
1609
1610 /* In case of 'command \t', given src is NULL string. */
1611 if (src == NULL)
1612 return dst;
1613
1614 /* Matched with input string. */
1615 if (strncmp(src, dst, strlen(src)) == 0)
1616 return dst;
1617
1618 return NULL;
1619}
1620
1621/* If src matches dst return dst string, otherwise return NULL */
1622/* This version will return the dst string always if it is
1623 CMD_VARIABLE for '?' key processing */
1624static const char *cmd_entry_function_desc(const char *src, const char *dst)
1625{
1626 if (CMD_VARARG(dst))
1627 return dst;
1628
1629 if (CMD_RANGE(dst)) {
1630 if (cmd_range_match(dst, src))
1631 return dst;
1632 else
1633 return NULL;
1634 }
1635#ifdef HAVE_IPV6
1636 if (CMD_IPV6(dst)) {
1637 if (cmd_ipv6_match(src))
1638 return dst;
1639 else
1640 return NULL;
1641 }
1642
1643 if (CMD_IPV6_PREFIX(dst)) {
1644 if (cmd_ipv6_prefix_match(src))
1645 return dst;
1646 else
1647 return NULL;
1648 }
1649#endif /* HAVE_IPV6 */
1650
1651 if (CMD_IPV4(dst)) {
1652 if (cmd_ipv4_match(src))
1653 return dst;
1654 else
1655 return NULL;
1656 }
1657
1658 if (CMD_IPV4_PREFIX(dst)) {
1659 if (cmd_ipv4_prefix_match(src))
1660 return dst;
1661 else
1662 return NULL;
1663 }
1664
1665 /* Optional or variable commands always match on '?' */
1666 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1667 return dst;
1668
1669 /* In case of 'command \t', given src is NULL string. */
1670 if (src == NULL)
1671 return dst;
1672
1673 if (strncmp(src, dst, strlen(src)) == 0)
1674 return dst;
1675 else
1676 return NULL;
1677}
1678
1679/* Check same string element existence. If it isn't there return
1680 1. */
1681static int cmd_unique_string(vector v, const char *str)
1682{
1683 unsigned int i;
1684 char *match;
1685
1686 for (i = 0; i < vector_active(v); i++)
1687 if ((match = vector_slot(v, i)) != NULL)
1688 if (strcmp(match, str) == 0)
1689 return 0;
1690 return 1;
1691}
1692
1693/* Compare string to description vector. If there is same string
1694 return 1 else return 0. */
1695static int desc_unique_string(vector v, const char *str)
1696{
1697 unsigned int i;
1698 struct desc *desc;
1699
1700 for (i = 0; i < vector_active(v); i++)
1701 if ((desc = vector_slot(v, i)) != NULL)
1702 if (strcmp(desc->cmd, str) == 0)
1703 return 1;
1704 return 0;
1705}
1706
1707static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1708{
1709 if (first_word != NULL &&
1710 node != AUTH_NODE &&
1711 node != VIEW_NODE &&
1712 node != AUTH_ENABLE_NODE &&
1713 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1714 return 1;
1715 return 0;
1716}
1717
1718/* '?' describe command support. */
1719static vector
1720cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1721{
1722 unsigned int i;
1723 vector cmd_vector;
1724#define INIT_MATCHVEC_SIZE 10
1725 vector matchvec;
1726 struct cmd_element *cmd_element;
1727 unsigned int index;
1728 int ret;
1729 enum match_type match;
1730 char *command;
1731 static struct desc desc_cr = { "<cr>", "" };
1732
1733 /* Set index. */
1734 if (vector_active(vline) == 0) {
1735 *status = CMD_ERR_NO_MATCH;
1736 return NULL;
1737 } else
1738 index = vector_active(vline) - 1;
1739
1740 /* Make copy vector of current node's command vector. */
1741 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1742
1743 /* Prepare match vector */
1744 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1745
1746 /* Filter commands. */
1747 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001748 for (i = 0; i < index; i++) {
1749 command = vector_slot(vline, i);
1750 if (!command)
1751 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001752
Harald Welte80d30fe2013-02-12 11:08:57 +01001753 match = cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001754
Harald Welte80d30fe2013-02-12 11:08:57 +01001755 if (match == vararg_match) {
1756 struct cmd_element *cmd_element;
1757 vector descvec;
1758 unsigned int j, k;
1759
1760 for (j = 0; j < vector_active(cmd_vector); j++)
1761 if ((cmd_element =
1762 vector_slot(cmd_vector, j)) != NULL
1763 &&
1764 (vector_active(cmd_element->strvec))) {
1765 descvec =
1766 vector_slot(cmd_element->
1767 strvec,
1768 vector_active
1769 (cmd_element->
1770 strvec) - 1);
1771 for (k = 0;
1772 k < vector_active(descvec);
1773 k++) {
1774 struct desc *desc =
1775 vector_slot(descvec,
1776 k);
1777 vector_set(matchvec,
1778 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001779 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001780 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001781
Harald Welte80d30fe2013-02-12 11:08:57 +01001782 vector_set(matchvec, &desc_cr);
1783 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001784
Harald Welte80d30fe2013-02-12 11:08:57 +01001785 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001786 }
1787
Harald Welte80d30fe2013-02-12 11:08:57 +01001788 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1789 match)) == 1) {
1790 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001791 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001792 *status = CMD_ERR_AMBIGUOUS;
1793 return NULL;
1794 } else if (ret == 2) {
1795 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001796 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001797 *status = CMD_ERR_NO_MATCH;
1798 return NULL;
1799 }
1800 }
1801
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001802 /* Prepare match vector */
1803 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1804
1805 /* Make sure that cmd_vector is filtered based on current word */
1806 command = vector_slot(vline, index);
1807 if (command)
Vadim Yanitskiy49a0dec2017-06-12 03:49:38 +07001808 cmd_filter(command, cmd_vector, index, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001809
1810 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001811 for (i = 0; i < vector_active(cmd_vector); i++) {
1812 const char *string = NULL;
1813 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001814
Harald Welte80d30fe2013-02-12 11:08:57 +01001815 cmd_element = vector_slot(cmd_vector, i);
1816 if (!cmd_element)
1817 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001818
Harald Welted17aa592013-02-12 11:11:34 +01001819 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1820 continue;
1821
Harald Welte80d30fe2013-02-12 11:08:57 +01001822 strvec = cmd_element->strvec;
1823
1824 /* if command is NULL, index may be equal to vector_active */
1825 if (command && index >= vector_active(strvec))
1826 vector_slot(cmd_vector, i) = NULL;
1827 else {
1828 /* Check if command is completed. */
1829 if (command == NULL
1830 && index == vector_active(strvec)) {
1831 string = "<cr>";
1832 if (!desc_unique_string(matchvec, string))
1833 vector_set(matchvec, &desc_cr);
1834 } else {
1835 unsigned int j;
1836 vector descvec = vector_slot(strvec, index);
1837 struct desc *desc;
1838
1839 for (j = 0; j < vector_active(descvec); j++) {
1840 desc = vector_slot(descvec, j);
1841 if (!desc)
1842 continue;
1843 string = cmd_entry_function_desc
1844 (command, desc->cmd);
1845 if (!string)
1846 continue;
1847 /* Uniqueness check */
1848 if (!desc_unique_string(matchvec, string))
1849 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001850 }
1851 }
1852 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001853 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001854 vector_free(cmd_vector);
1855
1856 if (vector_slot(matchvec, 0) == NULL) {
1857 vector_free(matchvec);
1858 *status = CMD_ERR_NO_MATCH;
1859 } else
1860 *status = CMD_SUCCESS;
1861
1862 return matchvec;
1863}
1864
1865vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1866{
1867 vector ret;
1868
1869 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1870 enum node_type onode;
1871 vector shifted_vline;
1872 unsigned int index;
1873
1874 onode = vty->node;
1875 vty->node = ENABLE_NODE;
1876 /* We can try it on enable node, cos' the vty is authenticated */
1877
1878 shifted_vline = vector_init(vector_count(vline));
1879 /* use memcpy? */
1880 for (index = 1; index < vector_active(vline); index++) {
1881 vector_set_index(shifted_vline, index - 1,
1882 vector_lookup(vline, index));
1883 }
1884
1885 ret = cmd_describe_command_real(shifted_vline, vty, status);
1886
1887 vector_free(shifted_vline);
1888 vty->node = onode;
1889 return ret;
1890 }
1891
1892 return cmd_describe_command_real(vline, vty, status);
1893}
1894
1895/* Check LCD of matched command. */
1896static int cmd_lcd(char **matched)
1897{
1898 int i;
1899 int j;
1900 int lcd = -1;
1901 char *s1, *s2;
1902 char c1, c2;
1903
1904 if (matched[0] == NULL || matched[1] == NULL)
1905 return 0;
1906
1907 for (i = 1; matched[i] != NULL; i++) {
1908 s1 = matched[i - 1];
1909 s2 = matched[i];
1910
1911 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1912 if (c1 != c2)
1913 break;
1914
1915 if (lcd < 0)
1916 lcd = j;
1917 else {
1918 if (lcd > j)
1919 lcd = j;
1920 }
1921 }
1922 return lcd;
1923}
1924
1925/* Command line completion support. */
1926static char **cmd_complete_command_real(vector vline, struct vty *vty,
1927 int *status)
1928{
1929 unsigned int i;
1930 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1931#define INIT_MATCHVEC_SIZE 10
1932 vector matchvec;
1933 struct cmd_element *cmd_element;
1934 unsigned int index;
1935 char **match_str;
1936 struct desc *desc;
1937 vector descvec;
1938 char *command;
1939 int lcd;
1940
1941 if (vector_active(vline) == 0) {
1942 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001943 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001944 return NULL;
1945 } else
1946 index = vector_active(vline) - 1;
1947
1948 /* First, filter by preceeding command string */
1949 for (i = 0; i < index; i++)
1950 if ((command = vector_slot(vline, i))) {
1951 enum match_type match;
1952 int ret;
1953
1954 /* First try completion match, if there is exactly match return 1 */
1955 match =
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001956 cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001957
1958 /* If there is exact match then filter ambiguous match else check
1959 ambiguousness. */
1960 if ((ret =
1961 is_cmd_ambiguous(command, cmd_vector, i,
1962 match)) == 1) {
1963 vector_free(cmd_vector);
1964 *status = CMD_ERR_AMBIGUOUS;
1965 return NULL;
1966 }
1967 /*
1968 else if (ret == 2)
1969 {
1970 vector_free (cmd_vector);
1971 *status = CMD_ERR_NO_MATCH;
1972 return NULL;
1973 }
1974 */
1975 }
1976
1977 /* Prepare match vector. */
1978 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1979
1980 /* Now we got into completion */
1981 for (i = 0; i < vector_active(cmd_vector); i++)
1982 if ((cmd_element = vector_slot(cmd_vector, i))) {
1983 const char *string;
1984 vector strvec = cmd_element->strvec;
1985
1986 /* Check field length */
1987 if (index >= vector_active(strvec))
1988 vector_slot(cmd_vector, i) = NULL;
1989 else {
1990 unsigned int j;
1991
1992 descvec = vector_slot(strvec, index);
1993 for (j = 0; j < vector_active(descvec); j++)
1994 if ((desc = vector_slot(descvec, j))) {
1995 if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
1996 if (cmd_unique_string (matchvec, string))
1997 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
1998 }
1999 }
2000 }
2001
2002 /* We don't need cmd_vector any more. */
2003 vector_free(cmd_vector);
2004
2005 /* No matched command */
2006 if (vector_slot(matchvec, 0) == NULL) {
2007 vector_free(matchvec);
2008
2009 /* In case of 'command \t' pattern. Do you need '?' command at
2010 the end of the line. */
Pau Espin Pedrol0f7bcb52017-11-02 19:08:14 +01002011 if (vector_slot(vline, index) == NULL)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002012 *status = CMD_ERR_NOTHING_TODO;
2013 else
2014 *status = CMD_ERR_NO_MATCH;
2015 return NULL;
2016 }
2017
2018 /* Only one matched */
2019 if (vector_slot(matchvec, 1) == NULL) {
2020 match_str = (char **)matchvec->index;
2021 vector_only_wrapper_free(matchvec);
2022 *status = CMD_COMPLETE_FULL_MATCH;
2023 return match_str;
2024 }
2025 /* Make it sure last element is NULL. */
2026 vector_set(matchvec, NULL);
2027
2028 /* Check LCD of matched strings. */
2029 if (vector_slot(vline, index) != NULL) {
2030 lcd = cmd_lcd((char **)matchvec->index);
2031
2032 if (lcd) {
2033 int len = strlen(vector_slot(vline, index));
2034
2035 if (len < lcd) {
2036 char *lcdstr;
2037
2038 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
2039 "complete-lcdstr");
2040 memcpy(lcdstr, matchvec->index[0], lcd);
2041 lcdstr[lcd] = '\0';
2042
2043 /* match_str = (char **) &lcdstr; */
2044
2045 /* Free matchvec. */
2046 for (i = 0; i < vector_active(matchvec); i++) {
2047 if (vector_slot(matchvec, i))
2048 talloc_free(vector_slot(matchvec, i));
2049 }
2050 vector_free(matchvec);
2051
2052 /* Make new matchvec. */
2053 matchvec = vector_init(INIT_MATCHVEC_SIZE);
2054 vector_set(matchvec, lcdstr);
2055 match_str = (char **)matchvec->index;
2056 vector_only_wrapper_free(matchvec);
2057
2058 *status = CMD_COMPLETE_MATCH;
2059 return match_str;
2060 }
2061 }
2062 }
2063
2064 match_str = (char **)matchvec->index;
2065 vector_only_wrapper_free(matchvec);
2066 *status = CMD_COMPLETE_LIST_MATCH;
2067 return match_str;
2068}
2069
2070char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2071{
2072 char **ret;
2073
2074 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2075 enum node_type onode;
2076 vector shifted_vline;
2077 unsigned int index;
2078
2079 onode = vty->node;
2080 vty->node = ENABLE_NODE;
2081 /* We can try it on enable node, cos' the vty is authenticated */
2082
2083 shifted_vline = vector_init(vector_count(vline));
2084 /* use memcpy? */
2085 for (index = 1; index < vector_active(vline); index++) {
2086 vector_set_index(shifted_vline, index - 1,
2087 vector_lookup(vline, index));
2088 }
2089
2090 ret = cmd_complete_command_real(shifted_vline, vty, status);
2091
2092 vector_free(shifted_vline);
2093 vty->node = onode;
2094 return ret;
2095 }
2096
2097 return cmd_complete_command_real(vline, vty, status);
2098}
2099
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002100static struct vty_parent_node *vty_parent(struct vty *vty)
2101{
2102 return llist_first_entry_or_null(&vty->parent_nodes,
2103 struct vty_parent_node,
2104 entry);
2105}
2106
2107static bool vty_pop_parent(struct vty *vty)
2108{
2109 struct vty_parent_node *parent = vty_parent(vty);
2110 if (!parent)
2111 return false;
2112 llist_del(&parent->entry);
2113 vty->node = parent->node;
2114 vty->priv = parent->priv;
2115 if (vty->indent)
2116 talloc_free(vty->indent);
2117 vty->indent = parent->indent;
2118 talloc_free(parent);
2119 return true;
2120}
2121
2122static void vty_clear_parents(struct vty *vty)
2123{
2124 while (vty_pop_parent(vty));
2125}
2126
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002127/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002128/*
2129 * This function MUST eventually converge on a node when called repeatedly,
2130 * there must not be any cycles.
2131 * All 'config' nodes shall converge on CONFIG_NODE.
2132 * All other 'enable' nodes shall converge on ENABLE_NODE.
2133 * All 'view' only nodes shall converge on VIEW_NODE.
2134 * All other nodes shall converge on themselves or it must be ensured,
2135 * that the user's rights are not extended anyhow by calling this function.
2136 *
2137 * Note that these requirements also apply to all functions that are used
2138 * as go_parent_cb.
2139 * Note also that this function relies on the is_config_child callback to
2140 * recognize non-config nodes if go_parent_cb is not set.
2141 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002142int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002143{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002144 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002145 case AUTH_NODE:
2146 case VIEW_NODE:
2147 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002148 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002149 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002150 break;
2151
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002152 case AUTH_ENABLE_NODE:
2153 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002154 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002155 break;
2156
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002157 case CFG_LOG_NODE:
2158 case VTY_NODE:
2159 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002160 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002161 break;
2162
2163 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002164 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002165 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002166 vty_pop_parent(vty);
2167 }
2168 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002169 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002170 vty_clear_parents(vty);
2171 }
2172 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002173 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002174 vty_clear_parents(vty);
2175 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002176 break;
2177 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002178
2179 return vty->node;
2180}
2181
2182/* Execute command by argument vline vector. */
2183static int
2184cmd_execute_command_real(vector vline, struct vty *vty,
2185 struct cmd_element **cmd)
2186{
2187 unsigned int i;
2188 unsigned int index;
2189 vector cmd_vector;
2190 struct cmd_element *cmd_element;
2191 struct cmd_element *matched_element;
2192 unsigned int matched_count, incomplete_count;
2193 int argc;
2194 const char *argv[CMD_ARGC_MAX];
2195 enum match_type match = 0;
2196 int varflag;
2197 char *command;
2198
2199 /* Make copy of command elements. */
2200 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2201
2202 for (index = 0; index < vector_active(vline); index++)
2203 if ((command = vector_slot(vline, index))) {
2204 int ret;
2205
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002206 match = cmd_filter(command, cmd_vector, index,
2207 any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002208
2209 if (match == vararg_match)
2210 break;
2211
2212 ret =
2213 is_cmd_ambiguous(command, cmd_vector, index, match);
2214
2215 if (ret == 1) {
2216 vector_free(cmd_vector);
2217 return CMD_ERR_AMBIGUOUS;
2218 } else if (ret == 2) {
2219 vector_free(cmd_vector);
2220 return CMD_ERR_NO_MATCH;
2221 }
2222 }
2223
2224 /* Check matched count. */
2225 matched_element = NULL;
2226 matched_count = 0;
2227 incomplete_count = 0;
2228
2229 for (i = 0; i < vector_active(cmd_vector); i++)
2230 if ((cmd_element = vector_slot(cmd_vector, i))) {
2231 if (match == vararg_match
2232 || index >= cmd_element->cmdsize) {
2233 matched_element = cmd_element;
2234#if 0
2235 printf("DEBUG: %s\n", cmd_element->string);
2236#endif
2237 matched_count++;
2238 } else {
2239 incomplete_count++;
2240 }
2241 }
2242
2243 /* Finish of using cmd_vector. */
2244 vector_free(cmd_vector);
2245
2246 /* To execute command, matched_count must be 1. */
2247 if (matched_count == 0) {
2248 if (incomplete_count)
2249 return CMD_ERR_INCOMPLETE;
2250 else
2251 return CMD_ERR_NO_MATCH;
2252 }
2253
2254 if (matched_count > 1)
2255 return CMD_ERR_AMBIGUOUS;
2256
2257 /* Argument treatment */
2258 varflag = 0;
2259 argc = 0;
2260
2261 for (i = 0; i < vector_active(vline); i++) {
2262 if (varflag)
2263 argv[argc++] = vector_slot(vline, i);
2264 else {
2265 vector descvec =
2266 vector_slot(matched_element->strvec, i);
2267
2268 if (vector_active(descvec) == 1) {
2269 struct desc *desc = vector_slot(descvec, 0);
2270
2271 if (CMD_VARARG(desc->cmd))
2272 varflag = 1;
2273
2274 if (varflag || CMD_VARIABLE(desc->cmd)
2275 || CMD_OPTION(desc->cmd))
2276 argv[argc++] = vector_slot(vline, i);
2277 } else
2278 argv[argc++] = vector_slot(vline, i);
2279 }
2280
2281 if (argc >= CMD_ARGC_MAX)
2282 return CMD_ERR_EXEED_ARGC_MAX;
2283 }
2284
2285 /* For vtysh execution. */
2286 if (cmd)
2287 *cmd = matched_element;
2288
2289 if (matched_element->daemon)
2290 return CMD_SUCCESS_DAEMON;
2291
2292 /* Execute matched command. */
2293 return (*matched_element->func) (matched_element, vty, argc, argv);
2294}
2295
2296int
2297cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2298 int vtysh)
2299{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002300 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002301 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002302
2303 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002304
2305 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2306 vector shifted_vline;
2307 unsigned int index;
2308
2309 vty->node = ENABLE_NODE;
2310 /* We can try it on enable node, cos' the vty is authenticated */
2311
2312 shifted_vline = vector_init(vector_count(vline));
2313 /* use memcpy? */
2314 for (index = 1; index < vector_active(vline); index++) {
2315 vector_set_index(shifted_vline, index - 1,
2316 vector_lookup(vline, index));
2317 }
2318
2319 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2320
2321 vector_free(shifted_vline);
2322 vty->node = onode;
2323 return ret;
2324 }
2325
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002326 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002327}
2328
2329/* Execute command by argument readline. */
2330int
2331cmd_execute_command_strict(vector vline, struct vty *vty,
2332 struct cmd_element **cmd)
2333{
2334 unsigned int i;
2335 unsigned int index;
2336 vector cmd_vector;
2337 struct cmd_element *cmd_element;
2338 struct cmd_element *matched_element;
2339 unsigned int matched_count, incomplete_count;
2340 int argc;
2341 const char *argv[CMD_ARGC_MAX];
2342 int varflag;
2343 enum match_type match = 0;
2344 char *command;
2345
2346 /* Make copy of command element */
2347 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2348
2349 for (index = 0; index < vector_active(vline); index++)
2350 if ((command = vector_slot(vline, index))) {
2351 int ret;
2352
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002353 match = cmd_filter(vector_slot(vline, index),
2354 cmd_vector, index, exact_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002355
2356 /* If command meets '.VARARG' then finish matching. */
2357 if (match == vararg_match)
2358 break;
2359
2360 ret =
2361 is_cmd_ambiguous(command, cmd_vector, index, match);
2362 if (ret == 1) {
2363 vector_free(cmd_vector);
2364 return CMD_ERR_AMBIGUOUS;
2365 }
2366 if (ret == 2) {
2367 vector_free(cmd_vector);
2368 return CMD_ERR_NO_MATCH;
2369 }
2370 }
2371
2372 /* Check matched count. */
2373 matched_element = NULL;
2374 matched_count = 0;
2375 incomplete_count = 0;
2376 for (i = 0; i < vector_active(cmd_vector); i++)
2377 if (vector_slot(cmd_vector, i) != NULL) {
2378 cmd_element = vector_slot(cmd_vector, i);
2379
2380 if (match == vararg_match
2381 || index >= cmd_element->cmdsize) {
2382 matched_element = cmd_element;
2383 matched_count++;
2384 } else
2385 incomplete_count++;
2386 }
2387
2388 /* Finish of using cmd_vector. */
2389 vector_free(cmd_vector);
2390
2391 /* To execute command, matched_count must be 1. */
2392 if (matched_count == 0) {
2393 if (incomplete_count)
2394 return CMD_ERR_INCOMPLETE;
2395 else
2396 return CMD_ERR_NO_MATCH;
2397 }
2398
2399 if (matched_count > 1)
2400 return CMD_ERR_AMBIGUOUS;
2401
2402 /* Argument treatment */
2403 varflag = 0;
2404 argc = 0;
2405
2406 for (i = 0; i < vector_active(vline); i++) {
2407 if (varflag)
2408 argv[argc++] = vector_slot(vline, i);
2409 else {
2410 vector descvec =
2411 vector_slot(matched_element->strvec, i);
2412
2413 if (vector_active(descvec) == 1) {
2414 struct desc *desc = vector_slot(descvec, 0);
2415
2416 if (CMD_VARARG(desc->cmd))
2417 varflag = 1;
2418
2419 if (varflag || CMD_VARIABLE(desc->cmd)
2420 || CMD_OPTION(desc->cmd))
2421 argv[argc++] = vector_slot(vline, i);
2422 } else
2423 argv[argc++] = vector_slot(vline, i);
2424 }
2425
2426 if (argc >= CMD_ARGC_MAX)
2427 return CMD_ERR_EXEED_ARGC_MAX;
2428 }
2429
2430 /* For vtysh execution. */
2431 if (cmd)
2432 *cmd = matched_element;
2433
2434 if (matched_element->daemon)
2435 return CMD_SUCCESS_DAEMON;
2436
2437 /* Now execute matched command */
2438 return (*matched_element->func) (matched_element, vty, argc, argv);
2439}
2440
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002441static inline size_t len(const char *str)
2442{
2443 return str? strlen(str) : 0;
2444}
2445
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002446/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2447 * is longer than b, a must start with exactly b, and vice versa.
2448 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2449 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002450static int indent_cmp(const char *a, const char *b)
2451{
2452 size_t al, bl;
2453 al = len(a);
2454 bl = len(b);
2455 if (al > bl) {
2456 if (bl && strncmp(a, b, bl) != 0)
2457 return EINVAL;
2458 return 1;
2459 }
2460 /* al <= bl */
2461 if (al && strncmp(a, b, al) != 0)
2462 return EINVAL;
2463 return (al < bl)? -1 : 0;
2464}
2465
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002466/* Configration make from file. */
2467int config_from_file(struct vty *vty, FILE * fp)
2468{
2469 int ret;
2470 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002471 char *indent;
2472 int cmp;
2473 struct vty_parent_node this_node;
2474 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002475
2476 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002477 indent = NULL;
2478 vline = NULL;
2479 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002480
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002481 if (ret != CMD_SUCCESS)
2482 goto return_invalid_indent;
2483
2484 /* In case of comment or empty line */
2485 if (vline == NULL) {
2486 if (indent) {
2487 talloc_free(indent);
2488 indent = NULL;
2489 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002490 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002491 }
2492
Neels Hofmeyr43063632017-09-19 23:54:01 +02002493 /* We have a nonempty line. */
2494 if (!vty->indent) {
2495 /* We have just entered a node and expecting the first child to come up; but we
2496 * may also skip right back to a parent or ancestor level. */
2497 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002498
Neels Hofmeyr43063632017-09-19 23:54:01 +02002499 /* If there is no parent, record any indentation we encounter. */
2500 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2501
2502 if (cmp == EINVAL)
2503 goto return_invalid_indent;
2504
2505 if (cmp <= 0) {
2506 /* We have gone right back to the parent level or higher, we are skipping
2507 * this child node level entirely. Pop the parent to go back to a node
2508 * that was actually there (to reinstate vty->indent) and re-use below
2509 * go-parent while-loop to find an accurate match of indent in the node
2510 * ancestry. */
2511 vty_go_parent(vty);
2512 } else {
2513 /* The indent is deeper than the just entered parent, record the new
2514 * indentation characters. */
2515 vty->indent = talloc_strdup(vty, indent);
2516 /* This *is* the new indentation. */
2517 cmp = 0;
2518 }
2519 } else {
2520 /* There is a known indentation for this node level, validate and detect node
2521 * exits. */
2522 cmp = indent_cmp(indent, vty->indent);
2523 if (cmp == EINVAL)
2524 goto return_invalid_indent;
2525 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002526
2527 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2528 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2529 while (cmp < 0) {
2530 vty_go_parent(vty);
2531 cmp = indent_cmp(indent, vty->indent);
2532 if (cmp == EINVAL)
2533 goto return_invalid_indent;
2534 }
2535
2536 /* More indent without having entered a child node level? Either the parent node's indent
2537 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2538 * or the indentation increased even though the vty command didn't enter a child. */
2539 if (cmp > 0)
2540 goto return_invalid_indent;
2541
2542 /* Remember the current node before the command possibly changes it. */
2543 this_node = (struct vty_parent_node){
2544 .node = vty->node,
2545 .priv = vty->priv,
2546 .indent = vty->indent,
2547 };
2548
2549 parent = vty_parent(vty);
2550 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002551 cmd_free_strvec(vline);
2552
2553 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002554 && ret != CMD_ERR_NOTHING_TODO) {
2555 if (indent) {
2556 talloc_free(indent);
2557 indent = NULL;
2558 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002559 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002560 }
2561
2562 /* If we have stepped down into a child node, push a parent frame.
2563 * The causality is such: we don't expect every single node entry implementation to push
2564 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2565 * a parent node. Hence if the node changed without the parent node changing, we must
2566 * have stepped into a child node (and now expect a deeper indent). */
2567 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2568 /* Push the parent node. */
2569 parent = talloc_zero(vty, struct vty_parent_node);
2570 *parent = this_node;
2571 llist_add(&parent->entry, &vty->parent_nodes);
2572
2573 /* The current talloc'ed vty->indent string will now be owned by this parent
2574 * struct. Indicate that we don't know what deeper indent characters the user
2575 * will choose. */
2576 vty->indent = NULL;
2577 }
2578
2579 if (indent) {
2580 talloc_free(indent);
2581 indent = NULL;
2582 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002583 }
2584 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002585
2586return_invalid_indent:
2587 if (vline)
2588 cmd_free_strvec(vline);
2589 if (indent) {
2590 talloc_free(indent);
2591 indent = NULL;
2592 }
2593 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002594}
2595
2596/* Configration from terminal */
2597DEFUN(config_terminal,
2598 config_terminal_cmd,
2599 "configure terminal",
2600 "Configuration from vty interface\n" "Configuration terminal\n")
2601{
2602 if (vty_config_lock(vty))
2603 vty->node = CONFIG_NODE;
2604 else {
2605 vty_out(vty, "VTY configuration is locked by other VTY%s",
2606 VTY_NEWLINE);
2607 return CMD_WARNING;
2608 }
2609 return CMD_SUCCESS;
2610}
2611
2612/* Enable command */
2613DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2614{
2615 /* If enable password is NULL, change to ENABLE_NODE */
2616 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2617 vty->type == VTY_SHELL_SERV)
2618 vty->node = ENABLE_NODE;
2619 else
2620 vty->node = AUTH_ENABLE_NODE;
2621
2622 return CMD_SUCCESS;
2623}
2624
2625/* Disable command */
2626DEFUN(disable,
2627 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2628{
2629 if (vty->node == ENABLE_NODE)
2630 vty->node = VIEW_NODE;
2631 return CMD_SUCCESS;
2632}
2633
2634/* Down vty node level. */
2635gDEFUN(config_exit,
2636 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2637{
2638 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002639 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002640 case VIEW_NODE:
2641 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002642 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002643 break;
2644 case CONFIG_NODE:
2645 vty->node = ENABLE_NODE;
2646 vty_config_unlock(vty);
2647 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002648 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002649 if (vty->node > CONFIG_NODE)
2650 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002651 break;
2652 }
2653 return CMD_SUCCESS;
2654}
2655
2656/* End of configuration. */
2657 gDEFUN(config_end,
2658 config_end_cmd, "end", "End current mode and change to enable mode.")
2659{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002660 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002661 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002662
2663 /* Repeatedly call go_parent until a top node is reached. */
2664 while (vty->node > CONFIG_NODE) {
2665 if (vty->node == last_node) {
2666 /* Ensure termination, this shouldn't happen. */
2667 break;
2668 }
2669 last_node = vty->node;
2670 vty_go_parent(vty);
2671 }
2672
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002673 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002674 if (vty->node > ENABLE_NODE)
2675 vty->node = ENABLE_NODE;
2676 vty->index = NULL;
2677 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002678 }
2679 return CMD_SUCCESS;
2680}
2681
2682/* Show version. */
2683DEFUN(show_version,
2684 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2685{
Harald Welte237f6242010-05-25 23:00:45 +02002686 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2687 host.app_info->version,
2688 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2689 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002690
2691 return CMD_SUCCESS;
2692}
2693
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002694DEFUN(show_online_help,
2695 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2696{
2697 vty_dump_nodes(vty);
2698 return CMD_SUCCESS;
2699}
2700
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002701/* Help display function for all node. */
2702gDEFUN(config_help,
2703 config_help_cmd, "help", "Description of the interactive help system\n")
2704{
2705 vty_out(vty,
2706 "This VTY provides advanced help features. When you need help,%s\
2707anytime at the command line please press '?'.%s\
2708%s\
2709If nothing matches, the help list will be empty and you must backup%s\
2710 until entering a '?' shows the available options.%s\
2711Two styles of help are provided:%s\
27121. Full help is available when you are ready to enter a%s\
2713command argument (e.g. 'show ?') and describes each possible%s\
2714argument.%s\
27152. Partial help is provided when an abbreviated argument is entered%s\
2716 and you want to know what arguments match the input%s\
2717 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2718 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2719 return CMD_SUCCESS;
2720}
2721
2722/* Help display function for all node. */
2723gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2724{
2725 unsigned int i;
2726 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2727 struct cmd_element *cmd;
2728
2729 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2730 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
Neels Hofmeyra7557fe2018-09-24 04:16:42 +02002731 && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002732 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2733 return CMD_SUCCESS;
2734}
2735
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002736static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002737{
2738 unsigned int i;
2739 int fd;
2740 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002741 char *config_file_tmp = NULL;
2742 char *config_file_sav = NULL;
2743 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002744 struct stat st;
2745
2746 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002747
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002748 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2749 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2750 * manually instead. */
2751
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002752 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002753 config_file_sav =
2754 _talloc_zero(tall_vty_cmd_ctx,
2755 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2756 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002757 if (!config_file_sav)
2758 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002759 strcpy(config_file_sav, config_file);
2760 strcat(config_file_sav, CONF_BACKUP_EXT);
2761
2762 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002763 "config_file_tmp");
2764 if (!config_file_tmp) {
2765 talloc_free(config_file_sav);
2766 return -1;
2767 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002768 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2769
2770 /* Open file to configuration write. */
2771 fd = mkstemp(config_file_tmp);
2772 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002773 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002774 talloc_free(config_file_tmp);
2775 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002776 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002777 }
2778
2779 /* Make vty for configuration file. */
2780 file_vty = vty_new();
2781 file_vty->fd = fd;
2782 file_vty->type = VTY_FILE;
2783
2784 /* Config file header print. */
2785 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002786 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002787 //vty_time_print (file_vty, 1);
2788 vty_out(file_vty, "!\n");
2789
2790 for (i = 0; i < vector_active(cmdvec); i++)
2791 if ((node = vector_slot(cmdvec, i)) && node->func) {
2792 if ((*node->func) (file_vty))
2793 vty_out(file_vty, "!\n");
2794 }
2795 vty_close(file_vty);
2796
2797 if (unlink(config_file_sav) != 0)
2798 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002799 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002800 talloc_free(config_file_sav);
2801 talloc_free(config_file_tmp);
2802 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002803 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002804 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002805
2806 /* Only link the .sav file if the original file exists */
2807 if (stat(config_file, &st) == 0) {
2808 if (link(config_file, config_file_sav) != 0) {
2809 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2810 talloc_free(config_file_sav);
2811 talloc_free(config_file_tmp);
2812 unlink(config_file_tmp);
2813 return -3;
2814 }
2815 sync();
2816 if (unlink(config_file) != 0) {
2817 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2818 talloc_free(config_file_sav);
2819 talloc_free(config_file_tmp);
2820 unlink(config_file_tmp);
2821 return -4;
2822 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002823 }
2824 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002825 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002826 talloc_free(config_file_sav);
2827 talloc_free(config_file_tmp);
2828 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002829 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002830 }
2831 unlink(config_file_tmp);
2832 sync();
2833
2834 talloc_free(config_file_sav);
2835 talloc_free(config_file_tmp);
2836
2837 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002838 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2839 return -6;
2840 }
2841
2842 return 0;
2843}
2844
2845
2846/* Write current configuration into file. */
2847DEFUN(config_write_file,
2848 config_write_file_cmd,
2849 "write file",
2850 "Write running configuration to memory, network, or terminal\n"
2851 "Write to configuration file\n")
2852{
2853 char *failed_file;
2854 int rc;
2855
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002856 if (host.app_info->config_is_consistent) {
2857 rc = host.app_info->config_is_consistent(vty);
2858 if (!rc) {
2859 vty_out(vty, "Configuration is not consistent%s",
2860 VTY_NEWLINE);
2861 return CMD_WARNING;
2862 }
2863 }
2864
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002865 if (host.config == NULL) {
2866 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2867 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002868 return CMD_WARNING;
2869 }
2870
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002871 rc = write_config_file(host.config, &failed_file);
2872 switch (rc) {
2873 case -1:
2874 vty_out(vty, "Can't open configuration file %s.%s",
2875 failed_file, VTY_NEWLINE);
2876 rc = CMD_WARNING;
2877 break;
2878 case -2:
2879 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2880 failed_file, VTY_NEWLINE);
2881 rc = CMD_WARNING;
2882 break;
2883 case -3:
2884 vty_out(vty, "Can't backup old configuration file %s.%s",
2885 failed_file, VTY_NEWLINE);
2886 rc = CMD_WARNING;
2887 break;
2888 case -4:
2889 vty_out(vty, "Can't unlink configuration file %s.%s",
2890 failed_file, VTY_NEWLINE);
2891 rc = CMD_WARNING;
2892 break;
2893 case -5:
2894 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2895 VTY_NEWLINE);
2896 rc = CMD_WARNING;
2897 break;
2898 case -6:
2899 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2900 failed_file, strerror(errno), errno, VTY_NEWLINE);
2901 rc = CMD_WARNING;
2902 break;
2903 default:
2904 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2905 rc = CMD_SUCCESS;
2906 break;
2907 }
2908
2909 talloc_free(failed_file);
2910 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002911}
2912
2913ALIAS(config_write_file,
2914 config_write_cmd,
2915 "write", "Write running configuration to memory, network, or terminal\n")
2916
2917 ALIAS(config_write_file,
2918 config_write_memory_cmd,
2919 "write memory",
2920 "Write running configuration to memory, network, or terminal\n"
2921 "Write configuration to the file (same as write file)\n")
2922
2923 ALIAS(config_write_file,
2924 copy_runningconfig_startupconfig_cmd,
2925 "copy running-config startup-config",
2926 "Copy configuration\n"
2927 "Copy running config to... \n"
2928 "Copy running config to startup config (same as write file)\n")
2929
2930/* Write current configuration into the terminal. */
2931 DEFUN(config_write_terminal,
2932 config_write_terminal_cmd,
2933 "write terminal",
2934 "Write running configuration to memory, network, or terminal\n"
2935 "Write to terminal\n")
2936{
2937 unsigned int i;
2938 struct cmd_node *node;
2939
2940 if (vty->type == VTY_SHELL_SERV) {
2941 for (i = 0; i < vector_active(cmdvec); i++)
2942 if ((node = vector_slot(cmdvec, i)) && node->func
2943 && node->vtysh) {
2944 if ((*node->func) (vty))
2945 vty_out(vty, "!%s", VTY_NEWLINE);
2946 }
2947 } else {
2948 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2949 VTY_NEWLINE);
2950 vty_out(vty, "!%s", VTY_NEWLINE);
2951
2952 for (i = 0; i < vector_active(cmdvec); i++)
2953 if ((node = vector_slot(cmdvec, i)) && node->func) {
2954 if ((*node->func) (vty))
2955 vty_out(vty, "!%s", VTY_NEWLINE);
2956 }
2957 vty_out(vty, "end%s", VTY_NEWLINE);
2958 }
2959 return CMD_SUCCESS;
2960}
2961
2962/* Write current configuration into the terminal. */
2963ALIAS(config_write_terminal,
2964 show_running_config_cmd,
2965 "show running-config", SHOW_STR "running configuration\n")
2966
2967/* Write startup configuration into the terminal. */
2968 DEFUN(show_startup_config,
2969 show_startup_config_cmd,
2970 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2971{
2972 char buf[BUFSIZ];
2973 FILE *confp;
2974
2975 confp = fopen(host.config, "r");
2976 if (confp == NULL) {
2977 vty_out(vty, "Can't open configuration file [%s]%s",
2978 host.config, VTY_NEWLINE);
2979 return CMD_WARNING;
2980 }
2981
2982 while (fgets(buf, BUFSIZ, confp)) {
2983 char *cp = buf;
2984
2985 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
2986 cp++;
2987 *cp = '\0';
2988
2989 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
2990 }
2991
2992 fclose(confp);
2993
2994 return CMD_SUCCESS;
2995}
2996
2997/* Hostname configuration */
2998DEFUN(config_hostname,
2999 hostname_cmd,
3000 "hostname WORD",
3001 "Set system's network name\n" "This system's network name\n")
3002{
3003 if (!isalpha((int)*argv[0])) {
3004 vty_out(vty, "Please specify string starting with alphabet%s",
3005 VTY_NEWLINE);
3006 return CMD_WARNING;
3007 }
3008
3009 if (host.name)
3010 talloc_free(host.name);
3011
3012 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3013 return CMD_SUCCESS;
3014}
3015
3016DEFUN(config_no_hostname,
3017 no_hostname_cmd,
3018 "no hostname [HOSTNAME]",
3019 NO_STR "Reset system's network name\n" "Host name of this router\n")
3020{
3021 if (host.name)
3022 talloc_free(host.name);
3023 host.name = NULL;
3024 return CMD_SUCCESS;
3025}
3026
3027/* VTY interface password set. */
3028DEFUN(config_password, password_cmd,
3029 "password (8|) WORD",
3030 "Assign the terminal connection password\n"
3031 "Specifies a HIDDEN password will follow\n"
3032 "dummy string \n" "The HIDDEN line password string\n")
3033{
3034 /* Argument check. */
3035 if (argc == 0) {
3036 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3037 return CMD_WARNING;
3038 }
3039
3040 if (argc == 2) {
3041 if (*argv[0] == '8') {
3042 if (host.password)
3043 talloc_free(host.password);
3044 host.password = NULL;
3045 if (host.password_encrypt)
3046 talloc_free(host.password_encrypt);
3047 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3048 return CMD_SUCCESS;
3049 } else {
3050 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3051 return CMD_WARNING;
3052 }
3053 }
3054
3055 if (!isalnum((int)*argv[0])) {
3056 vty_out(vty,
3057 "Please specify string starting with alphanumeric%s",
3058 VTY_NEWLINE);
3059 return CMD_WARNING;
3060 }
3061
3062 if (host.password)
3063 talloc_free(host.password);
3064 host.password = NULL;
3065
3066#ifdef VTY_CRYPT_PW
3067 if (host.encrypt) {
3068 if (host.password_encrypt)
3069 talloc_free(host.password_encrypt);
3070 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3071 } else
3072#endif
3073 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3074
3075 return CMD_SUCCESS;
3076}
3077
3078ALIAS(config_password, password_text_cmd,
3079 "password LINE",
3080 "Assign the terminal connection password\n"
3081 "The UNENCRYPTED (cleartext) line password\n")
3082
3083/* VTY enable password set. */
3084 DEFUN(config_enable_password, enable_password_cmd,
3085 "enable password (8|) WORD",
3086 "Modify enable password parameters\n"
3087 "Assign the privileged level password\n"
3088 "Specifies a HIDDEN password will follow\n"
3089 "dummy string \n" "The HIDDEN 'enable' password string\n")
3090{
3091 /* Argument check. */
3092 if (argc == 0) {
3093 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3094 return CMD_WARNING;
3095 }
3096
3097 /* Crypt type is specified. */
3098 if (argc == 2) {
3099 if (*argv[0] == '8') {
3100 if (host.enable)
3101 talloc_free(host.enable);
3102 host.enable = NULL;
3103
3104 if (host.enable_encrypt)
3105 talloc_free(host.enable_encrypt);
3106 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3107
3108 return CMD_SUCCESS;
3109 } else {
3110 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3111 return CMD_WARNING;
3112 }
3113 }
3114
3115 if (!isalnum((int)*argv[0])) {
3116 vty_out(vty,
3117 "Please specify string starting with alphanumeric%s",
3118 VTY_NEWLINE);
3119 return CMD_WARNING;
3120 }
3121
3122 if (host.enable)
3123 talloc_free(host.enable);
3124 host.enable = NULL;
3125
3126 /* Plain password input. */
3127#ifdef VTY_CRYPT_PW
3128 if (host.encrypt) {
3129 if (host.enable_encrypt)
3130 talloc_free(host.enable_encrypt);
3131 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3132 } else
3133#endif
3134 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3135
3136 return CMD_SUCCESS;
3137}
3138
3139ALIAS(config_enable_password,
3140 enable_password_text_cmd,
3141 "enable password LINE",
3142 "Modify enable password parameters\n"
3143 "Assign the privileged level password\n"
3144 "The UNENCRYPTED (cleartext) 'enable' password\n")
3145
3146/* VTY enable password delete. */
3147 DEFUN(no_config_enable_password, no_enable_password_cmd,
3148 "no enable password",
3149 NO_STR
3150 "Modify enable password parameters\n"
3151 "Assign the privileged level password\n")
3152{
3153 if (host.enable)
3154 talloc_free(host.enable);
3155 host.enable = NULL;
3156
3157 if (host.enable_encrypt)
3158 talloc_free(host.enable_encrypt);
3159 host.enable_encrypt = NULL;
3160
3161 return CMD_SUCCESS;
3162}
3163
3164#ifdef VTY_CRYPT_PW
3165DEFUN(service_password_encrypt,
3166 service_password_encrypt_cmd,
3167 "service password-encryption",
3168 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3169{
3170 if (host.encrypt)
3171 return CMD_SUCCESS;
3172
3173 host.encrypt = 1;
3174
3175 if (host.password) {
3176 if (host.password_encrypt)
3177 talloc_free(host.password_encrypt);
3178 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3179 }
3180 if (host.enable) {
3181 if (host.enable_encrypt)
3182 talloc_free(host.enable_encrypt);
3183 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3184 }
3185
3186 return CMD_SUCCESS;
3187}
3188
3189DEFUN(no_service_password_encrypt,
3190 no_service_password_encrypt_cmd,
3191 "no service password-encryption",
3192 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3193{
3194 if (!host.encrypt)
3195 return CMD_SUCCESS;
3196
3197 host.encrypt = 0;
3198
3199 if (host.password_encrypt)
3200 talloc_free(host.password_encrypt);
3201 host.password_encrypt = NULL;
3202
3203 if (host.enable_encrypt)
3204 talloc_free(host.enable_encrypt);
3205 host.enable_encrypt = NULL;
3206
3207 return CMD_SUCCESS;
3208}
3209#endif
3210
3211DEFUN(config_terminal_length, config_terminal_length_cmd,
3212 "terminal length <0-512>",
3213 "Set terminal line parameters\n"
3214 "Set number of lines on a screen\n"
3215 "Number of lines on screen (0 for no pausing)\n")
3216{
3217 int lines;
3218 char *endptr = NULL;
3219
3220 lines = strtol(argv[0], &endptr, 10);
3221 if (lines < 0 || lines > 512 || *endptr != '\0') {
3222 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3223 return CMD_WARNING;
3224 }
3225 vty->lines = lines;
3226
3227 return CMD_SUCCESS;
3228}
3229
3230DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3231 "terminal no length",
3232 "Set terminal line parameters\n"
3233 NO_STR "Set number of lines on a screen\n")
3234{
3235 vty->lines = -1;
3236 return CMD_SUCCESS;
3237}
3238
3239DEFUN(service_terminal_length, service_terminal_length_cmd,
3240 "service terminal-length <0-512>",
3241 "Set up miscellaneous service\n"
3242 "System wide terminal length configuration\n"
3243 "Number of lines of VTY (0 means no line control)\n")
3244{
3245 int lines;
3246 char *endptr = NULL;
3247
3248 lines = strtol(argv[0], &endptr, 10);
3249 if (lines < 0 || lines > 512 || *endptr != '\0') {
3250 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3251 return CMD_WARNING;
3252 }
3253 host.lines = lines;
3254
3255 return CMD_SUCCESS;
3256}
3257
3258DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3259 "no service terminal-length [<0-512>]",
3260 NO_STR
3261 "Set up miscellaneous service\n"
3262 "System wide terminal length configuration\n"
3263 "Number of lines of VTY (0 means no line control)\n")
3264{
3265 host.lines = -1;
3266 return CMD_SUCCESS;
3267}
3268
3269DEFUN_HIDDEN(do_echo,
3270 echo_cmd,
3271 "echo .MESSAGE",
3272 "Echo a message back to the vty\n" "The message to echo\n")
3273{
3274 char *message;
3275
3276 vty_out(vty, "%s%s",
3277 ((message =
3278 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3279 if (message)
3280 talloc_free(message);
3281 return CMD_SUCCESS;
3282}
3283
3284#if 0
3285DEFUN(config_logmsg,
3286 config_logmsg_cmd,
3287 "logmsg " LOG_LEVELS " .MESSAGE",
3288 "Send a message to enabled logging destinations\n"
3289 LOG_LEVEL_DESC "The message to send\n")
3290{
3291 int level;
3292 char *message;
3293
3294 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3295 return CMD_ERR_NO_MATCH;
3296
3297 zlog(NULL, level,
3298 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3299 if (message)
3300 talloc_free(message);
3301 return CMD_SUCCESS;
3302}
3303
3304DEFUN(show_logging,
3305 show_logging_cmd,
3306 "show logging", SHOW_STR "Show current logging configuration\n")
3307{
3308 struct zlog *zl = zlog_default;
3309
3310 vty_out(vty, "Syslog logging: ");
3311 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3312 vty_out(vty, "disabled");
3313 else
3314 vty_out(vty, "level %s, facility %s, ident %s",
3315 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3316 facility_name(zl->facility), zl->ident);
3317 vty_out(vty, "%s", VTY_NEWLINE);
3318
3319 vty_out(vty, "Stdout logging: ");
3320 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3321 vty_out(vty, "disabled");
3322 else
3323 vty_out(vty, "level %s",
3324 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3325 vty_out(vty, "%s", VTY_NEWLINE);
3326
3327 vty_out(vty, "Monitor logging: ");
3328 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3329 vty_out(vty, "disabled");
3330 else
3331 vty_out(vty, "level %s",
3332 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3333 vty_out(vty, "%s", VTY_NEWLINE);
3334
3335 vty_out(vty, "File logging: ");
3336 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3337 vty_out(vty, "disabled");
3338 else
3339 vty_out(vty, "level %s, filename %s",
3340 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3341 zl->filename);
3342 vty_out(vty, "%s", VTY_NEWLINE);
3343
3344 vty_out(vty, "Protocol name: %s%s",
3345 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3346 vty_out(vty, "Record priority: %s%s",
3347 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3348
3349 return CMD_SUCCESS;
3350}
3351
3352DEFUN(config_log_stdout,
3353 config_log_stdout_cmd,
3354 "log stdout", "Logging control\n" "Set stdout logging level\n")
3355{
3356 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3357 return CMD_SUCCESS;
3358}
3359
3360DEFUN(config_log_stdout_level,
3361 config_log_stdout_level_cmd,
3362 "log stdout " LOG_LEVELS,
3363 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3364{
3365 int level;
3366
3367 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3368 return CMD_ERR_NO_MATCH;
3369 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3370 return CMD_SUCCESS;
3371}
3372
3373DEFUN(no_config_log_stdout,
3374 no_config_log_stdout_cmd,
3375 "no log stdout [LEVEL]",
3376 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3377{
3378 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3379 return CMD_SUCCESS;
3380}
3381
3382DEFUN(config_log_monitor,
3383 config_log_monitor_cmd,
3384 "log monitor",
3385 "Logging control\n" "Set terminal line (monitor) logging level\n")
3386{
3387 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3388 return CMD_SUCCESS;
3389}
3390
3391DEFUN(config_log_monitor_level,
3392 config_log_monitor_level_cmd,
3393 "log monitor " LOG_LEVELS,
3394 "Logging control\n"
3395 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3396{
3397 int level;
3398
3399 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3400 return CMD_ERR_NO_MATCH;
3401 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3402 return CMD_SUCCESS;
3403}
3404
3405DEFUN(no_config_log_monitor,
3406 no_config_log_monitor_cmd,
3407 "no log monitor [LEVEL]",
3408 NO_STR
3409 "Logging control\n"
3410 "Disable terminal line (monitor) logging\n" "Logging level\n")
3411{
3412 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3413 return CMD_SUCCESS;
3414}
3415
3416static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3417{
3418 int ret;
3419 char *p = NULL;
3420 const char *fullpath;
3421
3422 /* Path detection. */
3423 if (!IS_DIRECTORY_SEP(*fname)) {
3424 char cwd[MAXPATHLEN + 1];
3425 cwd[MAXPATHLEN] = '\0';
3426
3427 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3428 zlog_err("config_log_file: Unable to alloc mem!");
3429 return CMD_WARNING;
3430 }
3431
3432 if ((p = _talloc_zero(tall_vcmd_ctx,
3433 strlen(cwd) + strlen(fname) + 2),
3434 "set_log_file")
3435 == NULL) {
3436 zlog_err("config_log_file: Unable to alloc mem!");
3437 return CMD_WARNING;
3438 }
3439 sprintf(p, "%s/%s", cwd, fname);
3440 fullpath = p;
3441 } else
3442 fullpath = fname;
3443
3444 ret = zlog_set_file(NULL, fullpath, loglevel);
3445
3446 if (p)
3447 talloc_free(p);
3448
3449 if (!ret) {
3450 vty_out(vty, "can't open logfile %s\n", fname);
3451 return CMD_WARNING;
3452 }
3453
3454 if (host.logfile)
3455 talloc_free(host.logfile);
3456
3457 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3458
3459 return CMD_SUCCESS;
3460}
3461
3462DEFUN(config_log_file,
3463 config_log_file_cmd,
3464 "log file FILENAME",
3465 "Logging control\n" "Logging to file\n" "Logging filename\n")
3466{
3467 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3468}
3469
3470DEFUN(config_log_file_level,
3471 config_log_file_level_cmd,
3472 "log file FILENAME " LOG_LEVELS,
3473 "Logging control\n"
3474 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3475{
3476 int level;
3477
3478 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3479 return CMD_ERR_NO_MATCH;
3480 return set_log_file(vty, argv[0], level);
3481}
3482
3483DEFUN(no_config_log_file,
3484 no_config_log_file_cmd,
3485 "no log file [FILENAME]",
3486 NO_STR
3487 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3488{
3489 zlog_reset_file(NULL);
3490
3491 if (host.logfile)
3492 talloc_free(host.logfile);
3493
3494 host.logfile = NULL;
3495
3496 return CMD_SUCCESS;
3497}
3498
3499ALIAS(no_config_log_file,
3500 no_config_log_file_level_cmd,
3501 "no log file FILENAME LEVEL",
3502 NO_STR
3503 "Logging control\n"
3504 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3505
3506 DEFUN(config_log_syslog,
3507 config_log_syslog_cmd,
3508 "log syslog", "Logging control\n" "Set syslog logging level\n")
3509{
3510 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3511 return CMD_SUCCESS;
3512}
3513
3514DEFUN(config_log_syslog_level,
3515 config_log_syslog_level_cmd,
3516 "log syslog " LOG_LEVELS,
3517 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3518{
3519 int level;
3520
3521 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3522 return CMD_ERR_NO_MATCH;
3523 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3524 return CMD_SUCCESS;
3525}
3526
3527DEFUN_DEPRECATED(config_log_syslog_facility,
3528 config_log_syslog_facility_cmd,
3529 "log syslog facility " LOG_FACILITIES,
3530 "Logging control\n"
3531 "Logging goes to syslog\n"
3532 "(Deprecated) Facility parameter for syslog messages\n"
3533 LOG_FACILITY_DESC)
3534{
3535 int facility;
3536
3537 if ((facility = facility_match(argv[0])) < 0)
3538 return CMD_ERR_NO_MATCH;
3539
3540 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3541 zlog_default->facility = facility;
3542 return CMD_SUCCESS;
3543}
3544
3545DEFUN(no_config_log_syslog,
3546 no_config_log_syslog_cmd,
3547 "no log syslog [LEVEL]",
3548 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3549{
3550 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3551 return CMD_SUCCESS;
3552}
3553
3554ALIAS(no_config_log_syslog,
3555 no_config_log_syslog_facility_cmd,
3556 "no log syslog facility " LOG_FACILITIES,
3557 NO_STR
3558 "Logging control\n"
3559 "Logging goes to syslog\n"
3560 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3561
3562 DEFUN(config_log_facility,
3563 config_log_facility_cmd,
3564 "log facility " LOG_FACILITIES,
3565 "Logging control\n"
3566 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3567{
3568 int facility;
3569
3570 if ((facility = facility_match(argv[0])) < 0)
3571 return CMD_ERR_NO_MATCH;
3572 zlog_default->facility = facility;
3573 return CMD_SUCCESS;
3574}
3575
3576DEFUN(no_config_log_facility,
3577 no_config_log_facility_cmd,
3578 "no log facility [FACILITY]",
3579 NO_STR
3580 "Logging control\n"
3581 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3582{
3583 zlog_default->facility = LOG_DAEMON;
3584 return CMD_SUCCESS;
3585}
3586
3587DEFUN_DEPRECATED(config_log_trap,
3588 config_log_trap_cmd,
3589 "log trap " LOG_LEVELS,
3590 "Logging control\n"
3591 "(Deprecated) Set logging level and default for all destinations\n"
3592 LOG_LEVEL_DESC)
3593{
3594 int new_level;
3595 int i;
3596
3597 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3598 return CMD_ERR_NO_MATCH;
3599
3600 zlog_default->default_lvl = new_level;
3601 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3602 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3603 zlog_default->maxlvl[i] = new_level;
3604 return CMD_SUCCESS;
3605}
3606
3607DEFUN_DEPRECATED(no_config_log_trap,
3608 no_config_log_trap_cmd,
3609 "no log trap [LEVEL]",
3610 NO_STR
3611 "Logging control\n"
3612 "Permit all logging information\n" "Logging level\n")
3613{
3614 zlog_default->default_lvl = LOG_DEBUG;
3615 return CMD_SUCCESS;
3616}
3617
3618DEFUN(config_log_record_priority,
3619 config_log_record_priority_cmd,
3620 "log record-priority",
3621 "Logging control\n"
3622 "Log the priority of the message within the message\n")
3623{
3624 zlog_default->record_priority = 1;
3625 return CMD_SUCCESS;
3626}
3627
3628DEFUN(no_config_log_record_priority,
3629 no_config_log_record_priority_cmd,
3630 "no log record-priority",
3631 NO_STR
3632 "Logging control\n"
3633 "Do not log the priority of the message within the message\n")
3634{
3635 zlog_default->record_priority = 0;
3636 return CMD_SUCCESS;
3637}
3638#endif
3639
3640DEFUN(banner_motd_file,
3641 banner_motd_file_cmd,
3642 "banner motd file [FILE]",
3643 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3644{
3645 if (host.motdfile)
3646 talloc_free(host.motdfile);
3647 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3648
3649 return CMD_SUCCESS;
3650}
3651
3652DEFUN(banner_motd_default,
3653 banner_motd_default_cmd,
3654 "banner motd default",
3655 "Set banner string\n" "Strings for motd\n" "Default string\n")
3656{
3657 host.motd = default_motd;
3658 return CMD_SUCCESS;
3659}
3660
3661DEFUN(no_banner_motd,
3662 no_banner_motd_cmd,
3663 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3664{
3665 host.motd = NULL;
3666 if (host.motdfile)
3667 talloc_free(host.motdfile);
3668 host.motdfile = NULL;
3669 return CMD_SUCCESS;
3670}
3671
3672/* Set config filename. Called from vty.c */
3673void host_config_set(const char *filename)
3674{
3675 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3676}
3677
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003678/*! Deprecated, now happens implicitly when calling install_node().
3679 * Users of the API may still attempt to call this function, hence
3680 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003681void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003682{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003683}
3684
3685/*! Deprecated, now happens implicitly when calling install_node().
3686 * Users of the API may still attempt to call this function, hence
3687 * leave it here as a no-op. */
3688void vty_install_default(int node)
3689{
3690}
3691
3692/*! Install common commands like 'exit' and 'list'. */
3693static void install_basic_node_commands(int node)
3694{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003695 install_element(node, &config_help_cmd);
3696 install_element(node, &config_list_cmd);
3697
3698 install_element(node, &config_write_terminal_cmd);
3699 install_element(node, &config_write_file_cmd);
3700 install_element(node, &config_write_memory_cmd);
3701 install_element(node, &config_write_cmd);
3702 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003703
3704 install_element(node, &config_exit_cmd);
3705
3706 if (node >= CONFIG_NODE) {
3707 /* It's not a top node. */
3708 install_element(node, &config_end_cmd);
3709 }
3710}
3711
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003712/*! Return true if a node is installed by install_basic_node_commands(), so
3713 * that we can avoid repeating them for each and every node during 'show
3714 * running-config' */
3715static bool vty_command_is_common(struct cmd_element *cmd)
3716{
3717 if (cmd == &config_help_cmd
3718 || cmd == &config_list_cmd
3719 || cmd == &config_write_terminal_cmd
3720 || cmd == &config_write_file_cmd
3721 || cmd == &config_write_memory_cmd
3722 || cmd == &config_write_cmd
3723 || cmd == &show_running_config_cmd
3724 || cmd == &config_exit_cmd
3725 || cmd == &config_end_cmd)
3726 return true;
3727 return false;
3728}
3729
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003730/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003731 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003732 * \param[in] vty the vty of the code
3733 * \param[in] filename where to store the file
3734 * \return 0 in case of success.
3735 *
3736 * If the filename already exists create a filename.sav
3737 * version with the current code.
3738 *
3739 */
3740int osmo_vty_write_config_file(const char *filename)
3741{
3742 char *failed_file;
3743 int rc;
3744
3745 rc = write_config_file(filename, &failed_file);
3746 talloc_free(failed_file);
3747 return rc;
3748}
3749
3750/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003751 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003752 * \return 0 in case of success.
3753 *
3754 * If the filename already exists create a filename.sav
3755 * version with the current code.
3756 *
3757 */
3758int osmo_vty_save_config_file(void)
3759{
3760 char *failed_file;
3761 int rc;
3762
3763 if (host.config == NULL)
3764 return -7;
3765
3766 rc = write_config_file(host.config, &failed_file);
3767 talloc_free(failed_file);
3768 return rc;
3769}
3770
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003771/* Initialize command interface. Install basic nodes and commands. */
3772void cmd_init(int terminal)
3773{
3774 /* Allocate initial top vector of commands. */
3775 cmdvec = vector_init(VECTOR_MIN_SIZE);
3776
3777 /* Default host value settings. */
3778 host.name = NULL;
3779 host.password = NULL;
3780 host.enable = NULL;
3781 host.logfile = NULL;
3782 host.config = NULL;
3783 host.lines = -1;
3784 host.motd = default_motd;
3785 host.motdfile = NULL;
3786
3787 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003788 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003789 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003790 install_node_bare(&auth_node, NULL);
3791 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003792 install_node(&config_node, config_write_host);
3793
3794 /* Each node's basic commands. */
3795 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003796 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003797 if (terminal) {
3798 install_element(VIEW_NODE, &config_list_cmd);
3799 install_element(VIEW_NODE, &config_exit_cmd);
3800 install_element(VIEW_NODE, &config_help_cmd);
3801 install_element(VIEW_NODE, &config_enable_cmd);
3802 install_element(VIEW_NODE, &config_terminal_length_cmd);
3803 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3804 install_element(VIEW_NODE, &echo_cmd);
3805 }
3806
3807 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003808 install_element(ENABLE_NODE, &config_disable_cmd);
3809 install_element(ENABLE_NODE, &config_terminal_cmd);
3810 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3811 }
3812 install_element (ENABLE_NODE, &show_startup_config_cmd);
3813 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003814 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003815
3816 if (terminal) {
3817 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3818 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3819 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003820 }
3821
3822 install_element(CONFIG_NODE, &hostname_cmd);
3823 install_element(CONFIG_NODE, &no_hostname_cmd);
3824
3825 if (terminal) {
3826 install_element(CONFIG_NODE, &password_cmd);
3827 install_element(CONFIG_NODE, &password_text_cmd);
3828 install_element(CONFIG_NODE, &enable_password_cmd);
3829 install_element(CONFIG_NODE, &enable_password_text_cmd);
3830 install_element(CONFIG_NODE, &no_enable_password_cmd);
3831
3832#ifdef VTY_CRYPT_PW
3833 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3834 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3835#endif
3836 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3837 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3838 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3839 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3840 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3841
3842 }
3843 srand(time(NULL));
3844}
Harald Welte7acb30c2011-08-17 17:13:48 +02003845
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003846/*! @} */