blob: 43f974ced3760c1f5a064f852b96fbfe17ac6f19 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/*
2 $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
3
4 Command interpreter routine for virtual terminal [aka TeletYpe]
5 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Harald Weltee08da972017-11-13 01:00:26 +09006 Copyright (C) 2010-2011 Holger Hans Peter Freyther <zecke@selfish.org>
7 Copyright (C) 2012 Sylvain Munaut <tnt@246tNt.com>
8 Copyright (C) 2013,2015 Harald Welte <laforge@gnumonks.org>
9 Copyright (C) 2013,2017 sysmocom - s.f.m.c. GmbH
10
11 SPDX-License-Identifier: GPL-2.0+
Harald Welte3fb0b6f2010-05-19 19:02:52 +020012
13This file is part of GNU Zebra.
14
15GNU Zebra is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published
17by the Free Software Foundation; either version 2, or (at your
18option) any later version.
19
20GNU Zebra is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010027Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020029
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010033#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034#include <syslog.h>
35#include <errno.h>
36#define _XOPEN_SOURCE
37#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038#include <ctype.h>
39#include <time.h>
40#include <sys/time.h>
41#include <sys/stat.h>
42
43#include <osmocom/vty/vector.h>
44#include <osmocom/vty/vty.h>
45#include <osmocom/vty/command.h>
46
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010047#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010048#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020049
Harald Weltee881b1b2011-08-17 18:52:30 +020050/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020051 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020052 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020053 *
54 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020055
Harald Welte3fb0b6f2010-05-19 19:02:52 +020056#define CONFIGFILE_MASK 022
57
58void *tall_vty_cmd_ctx;
59
60/* Command vector which includes some level of command lists. Normally
61 each daemon maintains each own cmdvec. */
62vector cmdvec;
63
64/* Host information structure. */
65struct host host;
66
67/* Standard command node structures. */
68struct cmd_node auth_node = {
69 AUTH_NODE,
70 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010071 .name = "auth",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020072};
73
74struct cmd_node view_node = {
75 VIEW_NODE,
76 "%s> ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010077 .name = "view",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020078};
79
80struct cmd_node auth_enable_node = {
81 AUTH_ENABLE_NODE,
82 "Password: ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010083 .name = "auth-enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020084};
85
86struct cmd_node enable_node = {
87 ENABLE_NODE,
88 "%s# ",
Neels Hofmeyr3b681572017-12-09 03:54:32 +010089 .name = "enable",
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090};
91
92struct cmd_node config_node = {
93 CONFIG_NODE,
94 "%s(config)# ",
95 1
96};
97
98/* Default motd string. */
99const char *default_motd = "";
100
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200101/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +0200102 *
103 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200104void print_version(int print_copyright)
105{
Harald Welte237f6242010-05-25 23:00:45 +0200106 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200107 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +0200108 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200109}
110
111/* Utility function to concatenate argv argument into a single string
112 with inserting ' ' character between each argument. */
113char *argv_concat(const char **argv, int argc, int shift)
114{
115 int i;
116 size_t len;
117 char *str;
118 char *p;
119
120 len = 0;
121 for (i = shift; i < argc; i++)
122 len += strlen(argv[i]) + 1;
123 if (!len)
124 return NULL;
125 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
126 for (i = shift; i < argc; i++) {
127 size_t arglen;
128 memcpy(p, argv[i], (arglen = strlen(argv[i])));
129 p += arglen;
130 *p++ = ' ';
131 }
132 *(p - 1) = '\0';
133 return str;
134}
135
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200136/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
137 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
138 * in turn, this name us used for XML IDs in 'show online-help'. */
139static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
140{
141 const char *pos;
142 int dest = 0;
143
144 if (!prompt || !*prompt)
145 return "";
146
147 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
148 if (pos[0] == '%' && pos[1]) {
149 /* skip "%s"; loop pos++ does the second one. */
150 pos++;
151 continue;
152 }
153 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
154 continue;
155 name_buf[dest] = pos[0];
156 dest++;
157 }
158 name_buf[dest] = '\0';
159 return name_buf;
160}
161
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200162static void install_basic_node_commands(int node);
163
164/*! Install top node of command vector, without adding basic node commands. */
165static void install_node_bare(struct cmd_node *node, int (*func) (struct vty *))
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200166{
167 vector_set_index(cmdvec, node->node, node);
168 node->func = func;
169 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200170 if (!*node->name)
171 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200172}
173
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +0200174/*! Install top node of command vector. */
175void install_node(struct cmd_node *node, int (*func) (struct vty *))
176{
177 install_node_bare(node, func);
178 install_basic_node_commands(node->node);
179}
180
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200181/* Compare two command's string. Used in sort_node (). */
182static int cmp_node(const void *p, const void *q)
183{
184 struct cmd_element *a = *(struct cmd_element **)p;
185 struct cmd_element *b = *(struct cmd_element **)q;
186
187 return strcmp(a->string, b->string);
188}
189
190static int cmp_desc(const void *p, const void *q)
191{
192 struct desc *a = *(struct desc **)p;
193 struct desc *b = *(struct desc **)q;
194
195 return strcmp(a->cmd, b->cmd);
196}
197
Jacob Erlbeck2442e092013-09-06 16:51:58 +0200198static int is_config_child(struct vty *vty)
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800199{
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800200 if (vty->node <= CONFIG_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800201 return 0;
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800202 else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800203 return 1;
204 else if (host.app_info->is_config_node)
Holger Hans Peter Freyther8f09f012010-08-25 17:34:56 +0800205 return host.app_info->is_config_node(vty, vty->node);
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800206 else
207 return vty->node > CONFIG_NODE;
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200211void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200212{
213 unsigned int i, j;
214 struct cmd_node *cnode;
215 vector descvec;
216 struct cmd_element *cmd_element;
217
218 for (i = 0; i < vector_active(cmdvec); i++)
219 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
220 vector cmd_vector = cnode->cmd_vector;
221 qsort(cmd_vector->index, vector_active(cmd_vector),
222 sizeof(void *), cmp_node);
223
224 for (j = 0; j < vector_active(cmd_vector); j++)
225 if ((cmd_element =
226 vector_slot(cmd_vector, j)) != NULL
227 && vector_active(cmd_element->strvec)) {
228 descvec =
229 vector_slot(cmd_element->strvec,
230 vector_active
231 (cmd_element->strvec) -
232 1);
233 qsort(descvec->index,
234 vector_active(descvec),
235 sizeof(void *), cmp_desc);
236 }
237 }
238}
239
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200240/*! Break up string in command tokens. Return leading indents.
241 * \param[in] string String to split.
242 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
243 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
244 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
245 *
246 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
247 * so that \a indent can simply return the count of leading spaces.
248 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
249 */
250int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200251{
252 const char *cp, *start;
253 char *token;
254 int strlen;
255 vector strvec;
256
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200257 *strvec_p = NULL;
258 if (indent)
259 *indent = 0;
260
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200262 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200263
264 cp = string;
265
266 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200267 while (isspace((int)*cp) && *cp != '\0') {
268 /* if we're counting indents, we need to be strict about them */
269 if (indent && (*cp != ' ') && (*cp != '\t')) {
270 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
271 if (*cp == '\n' || *cp == '\r') {
272 cp++;
273 string = cp;
274 continue;
275 }
276 return CMD_ERR_INVALID_INDENT;
277 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200278 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200279 }
280
281 if (indent)
282 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200283
284 /* Return if there is only white spaces */
285 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200286 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200287
288 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200289 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200290
291 /* Prepare return vector. */
292 strvec = vector_init(VECTOR_MIN_SIZE);
293
294 /* Copy each command piece and set into vector. */
295 while (1) {
296 start = cp;
297 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
298 *cp != '\0')
299 cp++;
300 strlen = cp - start;
301 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
302 memcpy(token, start, strlen);
303 *(token + strlen) = '\0';
304 vector_set(strvec, token);
305
306 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
307 *cp != '\0')
308 cp++;
309
310 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200311 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200312 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200313
314 *strvec_p = strvec;
315 return CMD_SUCCESS;
316}
317
318/*! Breaking up string into each command piece. I assume given
319 character is separated by a space character. Return value is a
320 vector which includes char ** data element. */
321vector cmd_make_strvec(const char *string)
322{
323 vector strvec;
324 cmd_make_strvec2(string, NULL, &strvec);
325 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200326}
327
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200328/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200329void cmd_free_strvec(vector v)
330{
331 unsigned int i;
332 char *cp;
333
334 if (!v)
335 return;
336
337 for (i = 0; i < vector_active(v); i++)
338 if ((cp = vector_slot(v, i)) != NULL)
339 talloc_free(cp);
340
341 vector_free(v);
342}
343
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200344/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200345static char *cmd_desc_str(const char **string)
346{
347 const char *cp, *start;
348 char *token;
349 int strlen;
350
351 cp = *string;
352
353 if (cp == NULL)
354 return NULL;
355
356 /* Skip white spaces. */
357 while (isspace((int)*cp) && *cp != '\0')
358 cp++;
359
360 /* Return if there is only white spaces */
361 if (*cp == '\0')
362 return NULL;
363
364 start = cp;
365
366 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
367 cp++;
368
369 strlen = cp - start;
370 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
371 memcpy(token, start, strlen);
372 *(token + strlen) = '\0';
373
374 *string = cp;
375
376 return token;
377}
378
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200379/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200380static vector cmd_make_descvec(const char *string, const char *descstr)
381{
382 int multiple = 0;
383 const char *sp;
384 char *token;
385 int len;
386 const char *cp;
387 const char *dp;
388 vector allvec;
389 vector strvec = NULL;
390 struct desc *desc;
391
392 cp = string;
393 dp = descstr;
394
395 if (cp == NULL)
396 return NULL;
397
398 allvec = vector_init(VECTOR_MIN_SIZE);
399
400 while (1) {
401 while (isspace((int)*cp) && *cp != '\0')
402 cp++;
403
404 if (*cp == '(') {
405 multiple = 1;
406 cp++;
407 }
408 if (*cp == ')') {
409 multiple = 0;
410 cp++;
411 }
412 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100413 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200414 cp++;
415 }
416
417 while (isspace((int)*cp) && *cp != '\0')
418 cp++;
419
420 if (*cp == '(') {
421 multiple = 1;
422 cp++;
423 }
424
425 if (*cp == '\0')
426 return allvec;
427
428 sp = cp;
429
430 while (!
431 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
432 || *cp == ')' || *cp == '|') && *cp != '\0')
433 cp++;
434
435 len = cp - sp;
436
437 token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
438 memcpy(token, sp, len);
439 *(token + len) = '\0';
440
441 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
442 desc->cmd = token;
443 desc->str = cmd_desc_str(&dp);
444
445 if (multiple) {
446 if (multiple == 1) {
447 strvec = vector_init(VECTOR_MIN_SIZE);
448 vector_set(allvec, strvec);
449 }
450 multiple++;
451 } else {
452 strvec = vector_init(VECTOR_MIN_SIZE);
453 vector_set(allvec, strvec);
454 }
455 vector_set(strvec, desc);
456 }
457}
458
459/* Count mandantory string vector size. This is to determine inputed
460 command has enough command length. */
461static int cmd_cmdsize(vector strvec)
462{
463 unsigned int i;
464 int size = 0;
465 vector descvec;
466 struct desc *desc;
467
468 for (i = 0; i < vector_active(strvec); i++)
469 if ((descvec = vector_slot(strvec, i)) != NULL) {
470 if ((vector_active(descvec)) == 1
471 && (desc = vector_slot(descvec, 0)) != NULL) {
472 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
473 return size;
474 else
475 size++;
476 } else
477 size++;
478 }
479 return size;
480}
481
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200482/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200483const char *cmd_prompt(enum node_type node)
484{
485 struct cmd_node *cnode;
486
487 cnode = vector_slot(cmdvec, node);
488 return cnode->prompt;
489}
490
Alexander Couzensad580ba2016-05-16 16:01:45 +0200491/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200492 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200493 * \param unsafe string
494 * \return a new talloc char *
495 */
496char *osmo_asciidoc_escape(const char *inp)
497{
498 int _strlen;
499 char *out, *out_ptr;
500 int len = 0, i, j;
501
502 if (!inp)
503 return NULL;
504 _strlen = strlen(inp);
505
506 for (i = 0; i < _strlen; ++i) {
507 switch (inp[i]) {
508 case '|':
509 len += 2;
510 break;
511 default:
512 len += 1;
513 break;
514 }
515 }
516
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200517 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Alexander Couzensad580ba2016-05-16 16:01:45 +0200518 if (!out)
519 return NULL;
520
521 out_ptr = out;
522
523#define ADD(out, str) \
524 for (j = 0; j < strlen(str); ++j) \
525 *(out++) = str[j];
526
527 for (i = 0; i < _strlen; ++i) {
528 switch (inp[i]) {
529 case '|':
530 ADD(out_ptr, "\\|");
531 break;
532 default:
533 *(out_ptr++) = inp[i];
534 break;
535 }
536 }
537
538#undef ADD
539
540 out_ptr[0] = '\0';
541 return out;
542}
543
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100544static char *xml_escape(const char *inp)
545{
546 int _strlen;
547 char *out, *out_ptr;
548 int len = 0, i, j;
549
550 if (!inp)
551 return NULL;
552 _strlen = strlen(inp);
553
554 for (i = 0; i < _strlen; ++i) {
555 switch (inp[i]) {
556 case '"':
557 len += 6;
558 break;
559 case '\'':
560 len += 6;
561 break;
562 case '<':
563 len += 4;
564 break;
565 case '>':
566 len += 4;
567 break;
568 case '&':
569 len += 5;
570 break;
571 default:
572 len += 1;
573 break;
574 }
575 }
576
Neels Hofmeyrf2d323e2018-07-09 19:18:35 +0200577 out = talloc_size(tall_vty_cmd_ctx, len + 1);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100578 if (!out)
579 return NULL;
580
581 out_ptr = out;
582
583#define ADD(out, str) \
584 for (j = 0; j < strlen(str); ++j) \
585 *(out++) = str[j];
586
587 for (i = 0; i < _strlen; ++i) {
588 switch (inp[i]) {
589 case '"':
590 ADD(out_ptr, "&quot;");
591 break;
592 case '\'':
593 ADD(out_ptr, "&apos;");
594 break;
595 case '<':
596 ADD(out_ptr, "&lt;");
597 break;
598 case '>':
599 ADD(out_ptr, "&gt;");
600 break;
601 case '&':
602 ADD(out_ptr, "&amp;");
603 break;
604 default:
605 *(out_ptr++) = inp[i];
606 break;
607 }
608 }
609
610#undef ADD
611
612 out_ptr[0] = '\0';
613 return out;
614}
615
616/*
617 * Write one cmd_element as XML to the given VTY.
618 */
619static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
620{
621 char *xml_string = xml_escape(cmd->string);
622
623 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
624 vty_out(vty, " <params>%s", VTY_NEWLINE);
625
626 int j;
627 for (j = 0; j < vector_count(cmd->strvec); ++j) {
628 vector descvec = vector_slot(cmd->strvec, j);
629 int i;
630 for (i = 0; i < vector_active(descvec); ++i) {
631 char *xml_param, *xml_doc;
632 struct desc *desc = vector_slot(descvec, i);
633 if (desc == NULL)
634 continue;
635
636 xml_param = xml_escape(desc->cmd);
637 xml_doc = xml_escape(desc->str);
638 vty_out(vty, " <param name='%s' doc='%s' />%s",
639 xml_param, xml_doc, VTY_NEWLINE);
640 talloc_free(xml_param);
641 talloc_free(xml_doc);
642 }
643 }
644
645 vty_out(vty, " </params>%s", VTY_NEWLINE);
646 vty_out(vty, " </command>%s", VTY_NEWLINE);
647
648 talloc_free(xml_string);
649 return 0;
650}
651
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200652static bool vty_command_is_common(struct cmd_element *cmd);
653
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100654/*
655 * Dump all nodes and commands associated with a given node as XML to the VTY.
656 */
657static int vty_dump_nodes(struct vty *vty)
658{
659 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200660 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100661
662 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
663
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200664 /* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
665 vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
666 vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
667 vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
668 " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
669 for (i = 0; i < vector_active(cmdvec); ++i) {
670 struct cmd_node *cnode;
671 cnode = vector_slot(cmdvec, i);
672 if (!cnode)
673 continue;
674 if (cnode->node != CONFIG_NODE)
675 continue;
676
677 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
678 struct cmd_element *elem;
679 elem = vector_slot(cnode->cmd_vector, j);
680 if (!vty_command_is_common(elem))
681 continue;
Harald Welte23a299f2018-06-09 17:40:52 +0200682 if (!elem->attr & CMD_ATTR_DEPRECATED)
683 vty_dump_element(elem, vty);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200684 }
685 }
686 vty_out(vty, " </node>%s", VTY_NEWLINE);
687
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100688 for (i = 0; i < vector_active(cmdvec); ++i) {
689 struct cmd_node *cnode;
690 cnode = vector_slot(cmdvec, i);
691 if (!cnode)
692 continue;
Neels Hofmeyrf7162772017-10-22 02:31:33 +0200693 if (vector_active(cnode->cmd_vector) < 1)
694 continue;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100695
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200696 /* De-dup node IDs: how many times has this same name been used before? Count the first
697 * occurence as _1 and omit that first suffix, so that the first occurence is called
698 * 'name', the second becomes 'name_2', then 'name_3', ... */
699 same_name_count = 1;
700 for (j = 0; j < i; ++j) {
701 struct cmd_node *cnode2;
702 cnode2 = vector_slot(cmdvec, j);
703 if (!cnode2)
704 continue;
705 if (strcmp(cnode->name, cnode2->name) == 0)
706 same_name_count ++;
707 }
708
709 vty_out(vty, " <node id='%s", cnode->name);
710 if (same_name_count > 1 || !*cnode->name)
711 vty_out(vty, "_%d", same_name_count);
712 vty_out(vty, "'>%s", VTY_NEWLINE);
Neels Hofmeyr453e37e2017-10-22 02:31:33 +0200713 vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100714
715 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
716 struct cmd_element *elem;
717 elem = vector_slot(cnode->cmd_vector, j);
Neels Hofmeyr69054e22017-10-19 02:44:57 +0200718 if (vty_command_is_common(elem))
719 continue;
Harald Welte23a299f2018-06-09 17:40:52 +0200720 if (!elem->attr & CMD_ATTR_DEPRECATED)
721 vty_dump_element(elem, vty);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100722 }
723
724 vty_out(vty, " </node>%s", VTY_NEWLINE);
725 }
726
727 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
728
729 return 0;
730}
731
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200732/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100733static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
734{
735 int i;
736
737 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
738 struct cmd_element *elem;
739 elem = vector_slot(cnode->cmd_vector, i);
740 if (!elem->string)
741 continue;
742 if (!strcmp(elem->string, cmdstring))
743 return 1;
744 }
745 return 0;
746}
747
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200748/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200749 * \param[in] ntype Node Type
750 * \param[cmd] element to be installed
751 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000752void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200753{
754 struct cmd_node *cnode;
755
756 cnode = vector_slot(cmdvec, ntype);
757
Harald Weltea99d45a2015-11-12 13:48:23 +0100758 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100759 /* ensure no _identical_ command has been registered at this
760 * node so far */
761 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200762
763 vector_set(cnode->cmd_vector, cmd);
764
765 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
766 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
767}
768
769/* Install a command into VIEW and ENABLE node */
770void install_element_ve(struct cmd_element *cmd)
771{
772 install_element(VIEW_NODE, cmd);
773 install_element(ENABLE_NODE, cmd);
774}
775
776#ifdef VTY_CRYPT_PW
777static unsigned char itoa64[] =
778 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
779
780static void to64(char *s, long v, int n)
781{
782 while (--n >= 0) {
783 *s++ = itoa64[v & 0x3f];
784 v >>= 6;
785 }
786}
787
788static char *zencrypt(const char *passwd)
789{
790 char salt[6];
791 struct timeval tv;
792 char *crypt(const char *, const char *);
793
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200794 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200795
796 to64(&salt[0], random(), 3);
797 to64(&salt[3], tv.tv_usec, 3);
798 salt[5] = '\0';
799
800 return crypt(passwd, salt);
801}
802#endif
803
804/* This function write configuration of this host. */
805static int config_write_host(struct vty *vty)
806{
807 if (host.name)
808 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
809
810 if (host.encrypt) {
811 if (host.password_encrypt)
812 vty_out(vty, "password 8 %s%s", host.password_encrypt,
813 VTY_NEWLINE);
814 if (host.enable_encrypt)
815 vty_out(vty, "enable password 8 %s%s",
816 host.enable_encrypt, VTY_NEWLINE);
817 } else {
818 if (host.password)
819 vty_out(vty, "password %s%s", host.password,
820 VTY_NEWLINE);
821 if (host.enable)
822 vty_out(vty, "enable password %s%s", host.enable,
823 VTY_NEWLINE);
824 }
825
826 if (host.advanced)
827 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
828
829 if (host.encrypt)
830 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
831
832 if (host.lines >= 0)
833 vty_out(vty, "service terminal-length %d%s", host.lines,
834 VTY_NEWLINE);
835
836 if (host.motdfile)
837 vty_out(vty, "banner motd file %s%s", host.motdfile,
838 VTY_NEWLINE);
839 else if (!host.motd)
840 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
841
842 return 1;
843}
844
845/* Utility function for getting command vector. */
846static vector cmd_node_vector(vector v, enum node_type ntype)
847{
848 struct cmd_node *cnode = vector_slot(v, ntype);
849 return cnode->cmd_vector;
850}
851
852/* Completion match types. */
853enum match_type {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100854 no_match = 0,
855 any_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200856 extend_match,
857 ipv4_prefix_match,
858 ipv4_match,
859 ipv6_prefix_match,
860 ipv6_match,
861 range_match,
862 vararg_match,
863 partly_match,
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100864 exact_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200865};
866
867static enum match_type cmd_ipv4_match(const char *str)
868{
869 const char *sp;
870 int dots = 0, nums = 0;
871 char buf[4];
872
873 if (str == NULL)
874 return partly_match;
875
876 for (;;) {
877 memset(buf, 0, sizeof(buf));
878 sp = str;
879 while (*str != '\0') {
880 if (*str == '.') {
881 if (dots >= 3)
882 return no_match;
883
884 if (*(str + 1) == '.')
885 return no_match;
886
887 if (*(str + 1) == '\0')
888 return partly_match;
889
890 dots++;
891 break;
892 }
893 if (!isdigit((int)*str))
894 return no_match;
895
896 str++;
897 }
898
899 if (str - sp > 3)
900 return no_match;
901
902 strncpy(buf, sp, str - sp);
903 if (atoi(buf) > 255)
904 return no_match;
905
906 nums++;
907
908 if (*str == '\0')
909 break;
910
911 str++;
912 }
913
914 if (nums < 4)
915 return partly_match;
916
917 return exact_match;
918}
919
920static enum match_type cmd_ipv4_prefix_match(const char *str)
921{
922 const char *sp;
923 int dots = 0;
924 char buf[4];
925
926 if (str == NULL)
927 return partly_match;
928
929 for (;;) {
930 memset(buf, 0, sizeof(buf));
931 sp = str;
932 while (*str != '\0' && *str != '/') {
933 if (*str == '.') {
934 if (dots == 3)
935 return no_match;
936
937 if (*(str + 1) == '.' || *(str + 1) == '/')
938 return no_match;
939
940 if (*(str + 1) == '\0')
941 return partly_match;
942
943 dots++;
944 break;
945 }
946
947 if (!isdigit((int)*str))
948 return no_match;
949
950 str++;
951 }
952
953 if (str - sp > 3)
954 return no_match;
955
956 strncpy(buf, sp, str - sp);
957 if (atoi(buf) > 255)
958 return no_match;
959
960 if (dots == 3) {
961 if (*str == '/') {
962 if (*(str + 1) == '\0')
963 return partly_match;
964
965 str++;
966 break;
967 } else if (*str == '\0')
968 return partly_match;
969 }
970
971 if (*str == '\0')
972 return partly_match;
973
974 str++;
975 }
976
977 sp = str;
978 while (*str != '\0') {
979 if (!isdigit((int)*str))
980 return no_match;
981
982 str++;
983 }
984
985 if (atoi(sp) > 32)
986 return no_match;
987
988 return exact_match;
989}
990
991#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
992#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
993#define STATE_START 1
994#define STATE_COLON 2
995#define STATE_DOUBLE 3
996#define STATE_ADDR 4
997#define STATE_DOT 5
998#define STATE_SLASH 6
999#define STATE_MASK 7
1000
1001#ifdef HAVE_IPV6
1002
1003static enum match_type cmd_ipv6_match(const char *str)
1004{
1005 int state = STATE_START;
1006 int colons = 0, nums = 0, double_colon = 0;
1007 const char *sp = NULL;
1008 struct sockaddr_in6 sin6_dummy;
1009 int ret;
1010
1011 if (str == NULL)
1012 return partly_match;
1013
1014 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
1015 return no_match;
1016
1017 /* use inet_pton that has a better support,
1018 * for example inet_pton can support the automatic addresses:
1019 * ::1.2.3.4
1020 */
1021 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1022
1023 if (ret == 1)
1024 return exact_match;
1025
1026 while (*str != '\0') {
1027 switch (state) {
1028 case STATE_START:
1029 if (*str == ':') {
1030 if (*(str + 1) != ':' && *(str + 1) != '\0')
1031 return no_match;
1032 colons--;
1033 state = STATE_COLON;
1034 } else {
1035 sp = str;
1036 state = STATE_ADDR;
1037 }
1038
1039 continue;
1040 case STATE_COLON:
1041 colons++;
1042 if (*(str + 1) == ':')
1043 state = STATE_DOUBLE;
1044 else {
1045 sp = str + 1;
1046 state = STATE_ADDR;
1047 }
1048 break;
1049 case STATE_DOUBLE:
1050 if (double_colon)
1051 return no_match;
1052
1053 if (*(str + 1) == ':')
1054 return no_match;
1055 else {
1056 if (*(str + 1) != '\0')
1057 colons++;
1058 sp = str + 1;
1059 state = STATE_ADDR;
1060 }
1061
1062 double_colon++;
1063 nums++;
1064 break;
1065 case STATE_ADDR:
1066 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1067 if (str - sp > 3)
1068 return no_match;
1069
1070 nums++;
1071 state = STATE_COLON;
1072 }
1073 if (*(str + 1) == '.')
1074 state = STATE_DOT;
1075 break;
1076 case STATE_DOT:
1077 state = STATE_ADDR;
1078 break;
1079 default:
1080 break;
1081 }
1082
1083 if (nums > 8)
1084 return no_match;
1085
1086 if (colons > 7)
1087 return no_match;
1088
1089 str++;
1090 }
1091
1092#if 0
1093 if (nums < 11)
1094 return partly_match;
1095#endif /* 0 */
1096
1097 return exact_match;
1098}
1099
1100static enum match_type cmd_ipv6_prefix_match(const char *str)
1101{
1102 int state = STATE_START;
1103 int colons = 0, nums = 0, double_colon = 0;
1104 int mask;
1105 const char *sp = NULL;
1106 char *endptr = NULL;
1107
1108 if (str == NULL)
1109 return partly_match;
1110
1111 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
1112 return no_match;
1113
1114 while (*str != '\0' && state != STATE_MASK) {
1115 switch (state) {
1116 case STATE_START:
1117 if (*str == ':') {
1118 if (*(str + 1) != ':' && *(str + 1) != '\0')
1119 return no_match;
1120 colons--;
1121 state = STATE_COLON;
1122 } else {
1123 sp = str;
1124 state = STATE_ADDR;
1125 }
1126
1127 continue;
1128 case STATE_COLON:
1129 colons++;
1130 if (*(str + 1) == '/')
1131 return no_match;
1132 else if (*(str + 1) == ':')
1133 state = STATE_DOUBLE;
1134 else {
1135 sp = str + 1;
1136 state = STATE_ADDR;
1137 }
1138 break;
1139 case STATE_DOUBLE:
1140 if (double_colon)
1141 return no_match;
1142
1143 if (*(str + 1) == ':')
1144 return no_match;
1145 else {
1146 if (*(str + 1) != '\0' && *(str + 1) != '/')
1147 colons++;
1148 sp = str + 1;
1149
1150 if (*(str + 1) == '/')
1151 state = STATE_SLASH;
1152 else
1153 state = STATE_ADDR;
1154 }
1155
1156 double_colon++;
1157 nums += 1;
1158 break;
1159 case STATE_ADDR:
1160 if (*(str + 1) == ':' || *(str + 1) == '.'
1161 || *(str + 1) == '\0' || *(str + 1) == '/') {
1162 if (str - sp > 3)
1163 return no_match;
1164
1165 for (; sp <= str; sp++)
1166 if (*sp == '/')
1167 return no_match;
1168
1169 nums++;
1170
1171 if (*(str + 1) == ':')
1172 state = STATE_COLON;
1173 else if (*(str + 1) == '.')
1174 state = STATE_DOT;
1175 else if (*(str + 1) == '/')
1176 state = STATE_SLASH;
1177 }
1178 break;
1179 case STATE_DOT:
1180 state = STATE_ADDR;
1181 break;
1182 case STATE_SLASH:
1183 if (*(str + 1) == '\0')
1184 return partly_match;
1185
1186 state = STATE_MASK;
1187 break;
1188 default:
1189 break;
1190 }
1191
1192 if (nums > 11)
1193 return no_match;
1194
1195 if (colons > 7)
1196 return no_match;
1197
1198 str++;
1199 }
1200
1201 if (state < STATE_MASK)
1202 return partly_match;
1203
1204 mask = strtol(str, &endptr, 10);
1205 if (*endptr != '\0')
1206 return no_match;
1207
1208 if (mask < 0 || mask > 128)
1209 return no_match;
1210
1211/* I don't know why mask < 13 makes command match partly.
1212 Forgive me to make this comments. I Want to set static default route
1213 because of lack of function to originate default in ospf6d; sorry
1214 yasu
1215 if (mask < 13)
1216 return partly_match;
1217*/
1218
1219 return exact_match;
1220}
1221
1222#endif /* HAVE_IPV6 */
1223
1224#define DECIMAL_STRLEN_MAX 10
1225
1226static int cmd_range_match(const char *range, const char *str)
1227{
1228 char *p;
1229 char buf[DECIMAL_STRLEN_MAX + 1];
1230 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001231
1232 if (str == NULL)
1233 return 1;
1234
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001235 if (range[1] == '-') {
1236 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001237
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001238 val = strtol(str, &endptr, 10);
1239 if (*endptr != '\0')
1240 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001241
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001242 range += 2;
1243 p = strchr(range, '-');
1244 if (p == NULL)
1245 return 0;
1246 if (p - range > DECIMAL_STRLEN_MAX)
1247 return 0;
1248 strncpy(buf, range, p - range);
1249 buf[p - range] = '\0';
1250 min = -strtol(buf, &endptr, 10);
1251 if (*endptr != '\0')
1252 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001253
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001254 range = p + 1;
1255 p = strchr(range, '>');
1256 if (p == NULL)
1257 return 0;
1258 if (p - range > DECIMAL_STRLEN_MAX)
1259 return 0;
1260 strncpy(buf, range, p - range);
1261 buf[p - range] = '\0';
1262 max = strtol(buf, &endptr, 10);
1263 if (*endptr != '\0')
1264 return 0;
1265
1266 if (val < min || val > max)
1267 return 0;
1268 } else {
1269 unsigned long min, max, val;
1270
1271 val = strtoul(str, &endptr, 10);
1272 if (*endptr != '\0')
1273 return 0;
1274
1275 range++;
1276 p = strchr(range, '-');
1277 if (p == NULL)
1278 return 0;
1279 if (p - range > DECIMAL_STRLEN_MAX)
1280 return 0;
1281 strncpy(buf, range, p - range);
1282 buf[p - range] = '\0';
1283 min = strtoul(buf, &endptr, 10);
1284 if (*endptr != '\0')
1285 return 0;
1286
1287 range = p + 1;
1288 p = strchr(range, '>');
1289 if (p == NULL)
1290 return 0;
1291 if (p - range > DECIMAL_STRLEN_MAX)
1292 return 0;
1293 strncpy(buf, range, p - range);
1294 buf[p - range] = '\0';
1295 max = strtoul(buf, &endptr, 10);
1296 if (*endptr != '\0')
1297 return 0;
1298
1299 if (val < min || val > max)
1300 return 0;
1301 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001302
1303 return 1;
1304}
1305
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001306/* helper to retrieve the 'real' argument string from an optional argument */
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
2731 && !(cmd->attr == CMD_ATTR_DEPRECATED
2732 || cmd->attr == CMD_ATTR_HIDDEN))
2733 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2734 return CMD_SUCCESS;
2735}
2736
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002737static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002738{
2739 unsigned int i;
2740 int fd;
2741 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002742 char *config_file_tmp = NULL;
2743 char *config_file_sav = NULL;
2744 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002745 struct stat st;
2746
2747 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002748
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002749 /* The string composition code here would be a case for talloc_asprintf(), but the pseudotalloc.c
2750 * talloc_asprintf() implementation would truncate a too-long path with "[...]", so doing it
2751 * manually instead. */
2752
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002753 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002754 config_file_sav =
2755 _talloc_zero(tall_vty_cmd_ctx,
2756 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2757 "config_file_sav");
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002758 if (!config_file_sav)
2759 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002760 strcpy(config_file_sav, config_file);
2761 strcat(config_file_sav, CONF_BACKUP_EXT);
2762
2763 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
Neels Hofmeyrd65f3ea2018-03-28 16:12:45 +02002764 "config_file_tmp");
2765 if (!config_file_tmp) {
2766 talloc_free(config_file_sav);
2767 return -1;
2768 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002769 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2770
2771 /* Open file to configuration write. */
2772 fd = mkstemp(config_file_tmp);
2773 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002774 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002775 talloc_free(config_file_tmp);
2776 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002777 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002778 }
2779
2780 /* Make vty for configuration file. */
2781 file_vty = vty_new();
2782 file_vty->fd = fd;
2783 file_vty->type = VTY_FILE;
2784
2785 /* Config file header print. */
2786 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002787 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002788 //vty_time_print (file_vty, 1);
2789 vty_out(file_vty, "!\n");
2790
2791 for (i = 0; i < vector_active(cmdvec); i++)
2792 if ((node = vector_slot(cmdvec, i)) && node->func) {
2793 if ((*node->func) (file_vty))
2794 vty_out(file_vty, "!\n");
2795 }
2796 vty_close(file_vty);
2797
2798 if (unlink(config_file_sav) != 0)
2799 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002800 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002801 talloc_free(config_file_sav);
2802 talloc_free(config_file_tmp);
2803 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002804 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002805 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002806
2807 /* Only link the .sav file if the original file exists */
2808 if (stat(config_file, &st) == 0) {
2809 if (link(config_file, config_file_sav) != 0) {
2810 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2811 talloc_free(config_file_sav);
2812 talloc_free(config_file_tmp);
2813 unlink(config_file_tmp);
2814 return -3;
2815 }
2816 sync();
2817 if (unlink(config_file) != 0) {
2818 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2819 talloc_free(config_file_sav);
2820 talloc_free(config_file_tmp);
2821 unlink(config_file_tmp);
2822 return -4;
2823 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002824 }
2825 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002826 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002827 talloc_free(config_file_sav);
2828 talloc_free(config_file_tmp);
2829 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002830 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002831 }
2832 unlink(config_file_tmp);
2833 sync();
2834
2835 talloc_free(config_file_sav);
2836 talloc_free(config_file_tmp);
2837
2838 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002839 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2840 return -6;
2841 }
2842
2843 return 0;
2844}
2845
2846
2847/* Write current configuration into file. */
2848DEFUN(config_write_file,
2849 config_write_file_cmd,
2850 "write file",
2851 "Write running configuration to memory, network, or terminal\n"
2852 "Write to configuration file\n")
2853{
2854 char *failed_file;
2855 int rc;
2856
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002857 if (host.app_info->config_is_consistent) {
2858 rc = host.app_info->config_is_consistent(vty);
2859 if (!rc) {
2860 vty_out(vty, "Configuration is not consistent%s",
2861 VTY_NEWLINE);
2862 return CMD_WARNING;
2863 }
2864 }
2865
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002866 if (host.config == NULL) {
2867 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2868 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002869 return CMD_WARNING;
2870 }
2871
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002872 rc = write_config_file(host.config, &failed_file);
2873 switch (rc) {
2874 case -1:
2875 vty_out(vty, "Can't open configuration file %s.%s",
2876 failed_file, VTY_NEWLINE);
2877 rc = CMD_WARNING;
2878 break;
2879 case -2:
2880 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2881 failed_file, VTY_NEWLINE);
2882 rc = CMD_WARNING;
2883 break;
2884 case -3:
2885 vty_out(vty, "Can't backup old configuration file %s.%s",
2886 failed_file, VTY_NEWLINE);
2887 rc = CMD_WARNING;
2888 break;
2889 case -4:
2890 vty_out(vty, "Can't unlink configuration file %s.%s",
2891 failed_file, VTY_NEWLINE);
2892 rc = CMD_WARNING;
2893 break;
2894 case -5:
2895 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2896 VTY_NEWLINE);
2897 rc = CMD_WARNING;
2898 break;
2899 case -6:
2900 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2901 failed_file, strerror(errno), errno, VTY_NEWLINE);
2902 rc = CMD_WARNING;
2903 break;
2904 default:
2905 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2906 rc = CMD_SUCCESS;
2907 break;
2908 }
2909
2910 talloc_free(failed_file);
2911 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002912}
2913
2914ALIAS(config_write_file,
2915 config_write_cmd,
2916 "write", "Write running configuration to memory, network, or terminal\n")
2917
2918 ALIAS(config_write_file,
2919 config_write_memory_cmd,
2920 "write memory",
2921 "Write running configuration to memory, network, or terminal\n"
2922 "Write configuration to the file (same as write file)\n")
2923
2924 ALIAS(config_write_file,
2925 copy_runningconfig_startupconfig_cmd,
2926 "copy running-config startup-config",
2927 "Copy configuration\n"
2928 "Copy running config to... \n"
2929 "Copy running config to startup config (same as write file)\n")
2930
2931/* Write current configuration into the terminal. */
2932 DEFUN(config_write_terminal,
2933 config_write_terminal_cmd,
2934 "write terminal",
2935 "Write running configuration to memory, network, or terminal\n"
2936 "Write to terminal\n")
2937{
2938 unsigned int i;
2939 struct cmd_node *node;
2940
2941 if (vty->type == VTY_SHELL_SERV) {
2942 for (i = 0; i < vector_active(cmdvec); i++)
2943 if ((node = vector_slot(cmdvec, i)) && node->func
2944 && node->vtysh) {
2945 if ((*node->func) (vty))
2946 vty_out(vty, "!%s", VTY_NEWLINE);
2947 }
2948 } else {
2949 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2950 VTY_NEWLINE);
2951 vty_out(vty, "!%s", VTY_NEWLINE);
2952
2953 for (i = 0; i < vector_active(cmdvec); i++)
2954 if ((node = vector_slot(cmdvec, i)) && node->func) {
2955 if ((*node->func) (vty))
2956 vty_out(vty, "!%s", VTY_NEWLINE);
2957 }
2958 vty_out(vty, "end%s", VTY_NEWLINE);
2959 }
2960 return CMD_SUCCESS;
2961}
2962
2963/* Write current configuration into the terminal. */
2964ALIAS(config_write_terminal,
2965 show_running_config_cmd,
2966 "show running-config", SHOW_STR "running configuration\n")
2967
2968/* Write startup configuration into the terminal. */
2969 DEFUN(show_startup_config,
2970 show_startup_config_cmd,
2971 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2972{
2973 char buf[BUFSIZ];
2974 FILE *confp;
2975
2976 confp = fopen(host.config, "r");
2977 if (confp == NULL) {
2978 vty_out(vty, "Can't open configuration file [%s]%s",
2979 host.config, VTY_NEWLINE);
2980 return CMD_WARNING;
2981 }
2982
2983 while (fgets(buf, BUFSIZ, confp)) {
2984 char *cp = buf;
2985
2986 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
2987 cp++;
2988 *cp = '\0';
2989
2990 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
2991 }
2992
2993 fclose(confp);
2994
2995 return CMD_SUCCESS;
2996}
2997
2998/* Hostname configuration */
2999DEFUN(config_hostname,
3000 hostname_cmd,
3001 "hostname WORD",
3002 "Set system's network name\n" "This system's network name\n")
3003{
3004 if (!isalpha((int)*argv[0])) {
3005 vty_out(vty, "Please specify string starting with alphabet%s",
3006 VTY_NEWLINE);
3007 return CMD_WARNING;
3008 }
3009
3010 if (host.name)
3011 talloc_free(host.name);
3012
3013 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3014 return CMD_SUCCESS;
3015}
3016
3017DEFUN(config_no_hostname,
3018 no_hostname_cmd,
3019 "no hostname [HOSTNAME]",
3020 NO_STR "Reset system's network name\n" "Host name of this router\n")
3021{
3022 if (host.name)
3023 talloc_free(host.name);
3024 host.name = NULL;
3025 return CMD_SUCCESS;
3026}
3027
3028/* VTY interface password set. */
3029DEFUN(config_password, password_cmd,
3030 "password (8|) WORD",
3031 "Assign the terminal connection password\n"
3032 "Specifies a HIDDEN password will follow\n"
3033 "dummy string \n" "The HIDDEN line password string\n")
3034{
3035 /* Argument check. */
3036 if (argc == 0) {
3037 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3038 return CMD_WARNING;
3039 }
3040
3041 if (argc == 2) {
3042 if (*argv[0] == '8') {
3043 if (host.password)
3044 talloc_free(host.password);
3045 host.password = NULL;
3046 if (host.password_encrypt)
3047 talloc_free(host.password_encrypt);
3048 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3049 return CMD_SUCCESS;
3050 } else {
3051 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3052 return CMD_WARNING;
3053 }
3054 }
3055
3056 if (!isalnum((int)*argv[0])) {
3057 vty_out(vty,
3058 "Please specify string starting with alphanumeric%s",
3059 VTY_NEWLINE);
3060 return CMD_WARNING;
3061 }
3062
3063 if (host.password)
3064 talloc_free(host.password);
3065 host.password = NULL;
3066
3067#ifdef VTY_CRYPT_PW
3068 if (host.encrypt) {
3069 if (host.password_encrypt)
3070 talloc_free(host.password_encrypt);
3071 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3072 } else
3073#endif
3074 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3075
3076 return CMD_SUCCESS;
3077}
3078
3079ALIAS(config_password, password_text_cmd,
3080 "password LINE",
3081 "Assign the terminal connection password\n"
3082 "The UNENCRYPTED (cleartext) line password\n")
3083
3084/* VTY enable password set. */
3085 DEFUN(config_enable_password, enable_password_cmd,
3086 "enable password (8|) WORD",
3087 "Modify enable password parameters\n"
3088 "Assign the privileged level password\n"
3089 "Specifies a HIDDEN password will follow\n"
3090 "dummy string \n" "The HIDDEN 'enable' password string\n")
3091{
3092 /* Argument check. */
3093 if (argc == 0) {
3094 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3095 return CMD_WARNING;
3096 }
3097
3098 /* Crypt type is specified. */
3099 if (argc == 2) {
3100 if (*argv[0] == '8') {
3101 if (host.enable)
3102 talloc_free(host.enable);
3103 host.enable = NULL;
3104
3105 if (host.enable_encrypt)
3106 talloc_free(host.enable_encrypt);
3107 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3108
3109 return CMD_SUCCESS;
3110 } else {
3111 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3112 return CMD_WARNING;
3113 }
3114 }
3115
3116 if (!isalnum((int)*argv[0])) {
3117 vty_out(vty,
3118 "Please specify string starting with alphanumeric%s",
3119 VTY_NEWLINE);
3120 return CMD_WARNING;
3121 }
3122
3123 if (host.enable)
3124 talloc_free(host.enable);
3125 host.enable = NULL;
3126
3127 /* Plain password input. */
3128#ifdef VTY_CRYPT_PW
3129 if (host.encrypt) {
3130 if (host.enable_encrypt)
3131 talloc_free(host.enable_encrypt);
3132 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3133 } else
3134#endif
3135 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3136
3137 return CMD_SUCCESS;
3138}
3139
3140ALIAS(config_enable_password,
3141 enable_password_text_cmd,
3142 "enable password LINE",
3143 "Modify enable password parameters\n"
3144 "Assign the privileged level password\n"
3145 "The UNENCRYPTED (cleartext) 'enable' password\n")
3146
3147/* VTY enable password delete. */
3148 DEFUN(no_config_enable_password, no_enable_password_cmd,
3149 "no enable password",
3150 NO_STR
3151 "Modify enable password parameters\n"
3152 "Assign the privileged level password\n")
3153{
3154 if (host.enable)
3155 talloc_free(host.enable);
3156 host.enable = NULL;
3157
3158 if (host.enable_encrypt)
3159 talloc_free(host.enable_encrypt);
3160 host.enable_encrypt = NULL;
3161
3162 return CMD_SUCCESS;
3163}
3164
3165#ifdef VTY_CRYPT_PW
3166DEFUN(service_password_encrypt,
3167 service_password_encrypt_cmd,
3168 "service password-encryption",
3169 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3170{
3171 if (host.encrypt)
3172 return CMD_SUCCESS;
3173
3174 host.encrypt = 1;
3175
3176 if (host.password) {
3177 if (host.password_encrypt)
3178 talloc_free(host.password_encrypt);
3179 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3180 }
3181 if (host.enable) {
3182 if (host.enable_encrypt)
3183 talloc_free(host.enable_encrypt);
3184 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3185 }
3186
3187 return CMD_SUCCESS;
3188}
3189
3190DEFUN(no_service_password_encrypt,
3191 no_service_password_encrypt_cmd,
3192 "no service password-encryption",
3193 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3194{
3195 if (!host.encrypt)
3196 return CMD_SUCCESS;
3197
3198 host.encrypt = 0;
3199
3200 if (host.password_encrypt)
3201 talloc_free(host.password_encrypt);
3202 host.password_encrypt = NULL;
3203
3204 if (host.enable_encrypt)
3205 talloc_free(host.enable_encrypt);
3206 host.enable_encrypt = NULL;
3207
3208 return CMD_SUCCESS;
3209}
3210#endif
3211
3212DEFUN(config_terminal_length, config_terminal_length_cmd,
3213 "terminal length <0-512>",
3214 "Set terminal line parameters\n"
3215 "Set number of lines on a screen\n"
3216 "Number of lines on screen (0 for no pausing)\n")
3217{
3218 int lines;
3219 char *endptr = NULL;
3220
3221 lines = strtol(argv[0], &endptr, 10);
3222 if (lines < 0 || lines > 512 || *endptr != '\0') {
3223 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3224 return CMD_WARNING;
3225 }
3226 vty->lines = lines;
3227
3228 return CMD_SUCCESS;
3229}
3230
3231DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3232 "terminal no length",
3233 "Set terminal line parameters\n"
3234 NO_STR "Set number of lines on a screen\n")
3235{
3236 vty->lines = -1;
3237 return CMD_SUCCESS;
3238}
3239
3240DEFUN(service_terminal_length, service_terminal_length_cmd,
3241 "service terminal-length <0-512>",
3242 "Set up miscellaneous service\n"
3243 "System wide terminal length configuration\n"
3244 "Number of lines of VTY (0 means no line control)\n")
3245{
3246 int lines;
3247 char *endptr = NULL;
3248
3249 lines = strtol(argv[0], &endptr, 10);
3250 if (lines < 0 || lines > 512 || *endptr != '\0') {
3251 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3252 return CMD_WARNING;
3253 }
3254 host.lines = lines;
3255
3256 return CMD_SUCCESS;
3257}
3258
3259DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3260 "no service terminal-length [<0-512>]",
3261 NO_STR
3262 "Set up miscellaneous service\n"
3263 "System wide terminal length configuration\n"
3264 "Number of lines of VTY (0 means no line control)\n")
3265{
3266 host.lines = -1;
3267 return CMD_SUCCESS;
3268}
3269
3270DEFUN_HIDDEN(do_echo,
3271 echo_cmd,
3272 "echo .MESSAGE",
3273 "Echo a message back to the vty\n" "The message to echo\n")
3274{
3275 char *message;
3276
3277 vty_out(vty, "%s%s",
3278 ((message =
3279 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3280 if (message)
3281 talloc_free(message);
3282 return CMD_SUCCESS;
3283}
3284
3285#if 0
3286DEFUN(config_logmsg,
3287 config_logmsg_cmd,
3288 "logmsg " LOG_LEVELS " .MESSAGE",
3289 "Send a message to enabled logging destinations\n"
3290 LOG_LEVEL_DESC "The message to send\n")
3291{
3292 int level;
3293 char *message;
3294
3295 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3296 return CMD_ERR_NO_MATCH;
3297
3298 zlog(NULL, level,
3299 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3300 if (message)
3301 talloc_free(message);
3302 return CMD_SUCCESS;
3303}
3304
3305DEFUN(show_logging,
3306 show_logging_cmd,
3307 "show logging", SHOW_STR "Show current logging configuration\n")
3308{
3309 struct zlog *zl = zlog_default;
3310
3311 vty_out(vty, "Syslog logging: ");
3312 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3313 vty_out(vty, "disabled");
3314 else
3315 vty_out(vty, "level %s, facility %s, ident %s",
3316 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3317 facility_name(zl->facility), zl->ident);
3318 vty_out(vty, "%s", VTY_NEWLINE);
3319
3320 vty_out(vty, "Stdout logging: ");
3321 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3322 vty_out(vty, "disabled");
3323 else
3324 vty_out(vty, "level %s",
3325 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3326 vty_out(vty, "%s", VTY_NEWLINE);
3327
3328 vty_out(vty, "Monitor logging: ");
3329 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3330 vty_out(vty, "disabled");
3331 else
3332 vty_out(vty, "level %s",
3333 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3334 vty_out(vty, "%s", VTY_NEWLINE);
3335
3336 vty_out(vty, "File logging: ");
3337 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3338 vty_out(vty, "disabled");
3339 else
3340 vty_out(vty, "level %s, filename %s",
3341 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3342 zl->filename);
3343 vty_out(vty, "%s", VTY_NEWLINE);
3344
3345 vty_out(vty, "Protocol name: %s%s",
3346 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3347 vty_out(vty, "Record priority: %s%s",
3348 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3349
3350 return CMD_SUCCESS;
3351}
3352
3353DEFUN(config_log_stdout,
3354 config_log_stdout_cmd,
3355 "log stdout", "Logging control\n" "Set stdout logging level\n")
3356{
3357 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3358 return CMD_SUCCESS;
3359}
3360
3361DEFUN(config_log_stdout_level,
3362 config_log_stdout_level_cmd,
3363 "log stdout " LOG_LEVELS,
3364 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3365{
3366 int level;
3367
3368 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3369 return CMD_ERR_NO_MATCH;
3370 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3371 return CMD_SUCCESS;
3372}
3373
3374DEFUN(no_config_log_stdout,
3375 no_config_log_stdout_cmd,
3376 "no log stdout [LEVEL]",
3377 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3378{
3379 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3380 return CMD_SUCCESS;
3381}
3382
3383DEFUN(config_log_monitor,
3384 config_log_monitor_cmd,
3385 "log monitor",
3386 "Logging control\n" "Set terminal line (monitor) logging level\n")
3387{
3388 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3389 return CMD_SUCCESS;
3390}
3391
3392DEFUN(config_log_monitor_level,
3393 config_log_monitor_level_cmd,
3394 "log monitor " LOG_LEVELS,
3395 "Logging control\n"
3396 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3397{
3398 int level;
3399
3400 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3401 return CMD_ERR_NO_MATCH;
3402 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3403 return CMD_SUCCESS;
3404}
3405
3406DEFUN(no_config_log_monitor,
3407 no_config_log_monitor_cmd,
3408 "no log monitor [LEVEL]",
3409 NO_STR
3410 "Logging control\n"
3411 "Disable terminal line (monitor) logging\n" "Logging level\n")
3412{
3413 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3414 return CMD_SUCCESS;
3415}
3416
3417static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3418{
3419 int ret;
3420 char *p = NULL;
3421 const char *fullpath;
3422
3423 /* Path detection. */
3424 if (!IS_DIRECTORY_SEP(*fname)) {
3425 char cwd[MAXPATHLEN + 1];
3426 cwd[MAXPATHLEN] = '\0';
3427
3428 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3429 zlog_err("config_log_file: Unable to alloc mem!");
3430 return CMD_WARNING;
3431 }
3432
3433 if ((p = _talloc_zero(tall_vcmd_ctx,
3434 strlen(cwd) + strlen(fname) + 2),
3435 "set_log_file")
3436 == NULL) {
3437 zlog_err("config_log_file: Unable to alloc mem!");
3438 return CMD_WARNING;
3439 }
3440 sprintf(p, "%s/%s", cwd, fname);
3441 fullpath = p;
3442 } else
3443 fullpath = fname;
3444
3445 ret = zlog_set_file(NULL, fullpath, loglevel);
3446
3447 if (p)
3448 talloc_free(p);
3449
3450 if (!ret) {
3451 vty_out(vty, "can't open logfile %s\n", fname);
3452 return CMD_WARNING;
3453 }
3454
3455 if (host.logfile)
3456 talloc_free(host.logfile);
3457
3458 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3459
3460 return CMD_SUCCESS;
3461}
3462
3463DEFUN(config_log_file,
3464 config_log_file_cmd,
3465 "log file FILENAME",
3466 "Logging control\n" "Logging to file\n" "Logging filename\n")
3467{
3468 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3469}
3470
3471DEFUN(config_log_file_level,
3472 config_log_file_level_cmd,
3473 "log file FILENAME " LOG_LEVELS,
3474 "Logging control\n"
3475 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3476{
3477 int level;
3478
3479 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3480 return CMD_ERR_NO_MATCH;
3481 return set_log_file(vty, argv[0], level);
3482}
3483
3484DEFUN(no_config_log_file,
3485 no_config_log_file_cmd,
3486 "no log file [FILENAME]",
3487 NO_STR
3488 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3489{
3490 zlog_reset_file(NULL);
3491
3492 if (host.logfile)
3493 talloc_free(host.logfile);
3494
3495 host.logfile = NULL;
3496
3497 return CMD_SUCCESS;
3498}
3499
3500ALIAS(no_config_log_file,
3501 no_config_log_file_level_cmd,
3502 "no log file FILENAME LEVEL",
3503 NO_STR
3504 "Logging control\n"
3505 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3506
3507 DEFUN(config_log_syslog,
3508 config_log_syslog_cmd,
3509 "log syslog", "Logging control\n" "Set syslog logging level\n")
3510{
3511 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3512 return CMD_SUCCESS;
3513}
3514
3515DEFUN(config_log_syslog_level,
3516 config_log_syslog_level_cmd,
3517 "log syslog " LOG_LEVELS,
3518 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3519{
3520 int level;
3521
3522 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3523 return CMD_ERR_NO_MATCH;
3524 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3525 return CMD_SUCCESS;
3526}
3527
3528DEFUN_DEPRECATED(config_log_syslog_facility,
3529 config_log_syslog_facility_cmd,
3530 "log syslog facility " LOG_FACILITIES,
3531 "Logging control\n"
3532 "Logging goes to syslog\n"
3533 "(Deprecated) Facility parameter for syslog messages\n"
3534 LOG_FACILITY_DESC)
3535{
3536 int facility;
3537
3538 if ((facility = facility_match(argv[0])) < 0)
3539 return CMD_ERR_NO_MATCH;
3540
3541 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3542 zlog_default->facility = facility;
3543 return CMD_SUCCESS;
3544}
3545
3546DEFUN(no_config_log_syslog,
3547 no_config_log_syslog_cmd,
3548 "no log syslog [LEVEL]",
3549 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3550{
3551 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3552 return CMD_SUCCESS;
3553}
3554
3555ALIAS(no_config_log_syslog,
3556 no_config_log_syslog_facility_cmd,
3557 "no log syslog facility " LOG_FACILITIES,
3558 NO_STR
3559 "Logging control\n"
3560 "Logging goes to syslog\n"
3561 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3562
3563 DEFUN(config_log_facility,
3564 config_log_facility_cmd,
3565 "log facility " LOG_FACILITIES,
3566 "Logging control\n"
3567 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3568{
3569 int facility;
3570
3571 if ((facility = facility_match(argv[0])) < 0)
3572 return CMD_ERR_NO_MATCH;
3573 zlog_default->facility = facility;
3574 return CMD_SUCCESS;
3575}
3576
3577DEFUN(no_config_log_facility,
3578 no_config_log_facility_cmd,
3579 "no log facility [FACILITY]",
3580 NO_STR
3581 "Logging control\n"
3582 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3583{
3584 zlog_default->facility = LOG_DAEMON;
3585 return CMD_SUCCESS;
3586}
3587
3588DEFUN_DEPRECATED(config_log_trap,
3589 config_log_trap_cmd,
3590 "log trap " LOG_LEVELS,
3591 "Logging control\n"
3592 "(Deprecated) Set logging level and default for all destinations\n"
3593 LOG_LEVEL_DESC)
3594{
3595 int new_level;
3596 int i;
3597
3598 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3599 return CMD_ERR_NO_MATCH;
3600
3601 zlog_default->default_lvl = new_level;
3602 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3603 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3604 zlog_default->maxlvl[i] = new_level;
3605 return CMD_SUCCESS;
3606}
3607
3608DEFUN_DEPRECATED(no_config_log_trap,
3609 no_config_log_trap_cmd,
3610 "no log trap [LEVEL]",
3611 NO_STR
3612 "Logging control\n"
3613 "Permit all logging information\n" "Logging level\n")
3614{
3615 zlog_default->default_lvl = LOG_DEBUG;
3616 return CMD_SUCCESS;
3617}
3618
3619DEFUN(config_log_record_priority,
3620 config_log_record_priority_cmd,
3621 "log record-priority",
3622 "Logging control\n"
3623 "Log the priority of the message within the message\n")
3624{
3625 zlog_default->record_priority = 1;
3626 return CMD_SUCCESS;
3627}
3628
3629DEFUN(no_config_log_record_priority,
3630 no_config_log_record_priority_cmd,
3631 "no log record-priority",
3632 NO_STR
3633 "Logging control\n"
3634 "Do not log the priority of the message within the message\n")
3635{
3636 zlog_default->record_priority = 0;
3637 return CMD_SUCCESS;
3638}
3639#endif
3640
3641DEFUN(banner_motd_file,
3642 banner_motd_file_cmd,
3643 "banner motd file [FILE]",
3644 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3645{
3646 if (host.motdfile)
3647 talloc_free(host.motdfile);
3648 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3649
3650 return CMD_SUCCESS;
3651}
3652
3653DEFUN(banner_motd_default,
3654 banner_motd_default_cmd,
3655 "banner motd default",
3656 "Set banner string\n" "Strings for motd\n" "Default string\n")
3657{
3658 host.motd = default_motd;
3659 return CMD_SUCCESS;
3660}
3661
3662DEFUN(no_banner_motd,
3663 no_banner_motd_cmd,
3664 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3665{
3666 host.motd = NULL;
3667 if (host.motdfile)
3668 talloc_free(host.motdfile);
3669 host.motdfile = NULL;
3670 return CMD_SUCCESS;
3671}
3672
3673/* Set config filename. Called from vty.c */
3674void host_config_set(const char *filename)
3675{
3676 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3677}
3678
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003679/*! Deprecated, now happens implicitly when calling install_node().
3680 * Users of the API may still attempt to call this function, hence
3681 * leave it here as a no-op. */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003682void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003683{
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003684}
3685
3686/*! Deprecated, now happens implicitly when calling install_node().
3687 * Users of the API may still attempt to call this function, hence
3688 * leave it here as a no-op. */
3689void vty_install_default(int node)
3690{
3691}
3692
3693/*! Install common commands like 'exit' and 'list'. */
3694static void install_basic_node_commands(int node)
3695{
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003696 install_element(node, &config_help_cmd);
3697 install_element(node, &config_list_cmd);
3698
3699 install_element(node, &config_write_terminal_cmd);
3700 install_element(node, &config_write_file_cmd);
3701 install_element(node, &config_write_memory_cmd);
3702 install_element(node, &config_write_cmd);
3703 install_element(node, &show_running_config_cmd);
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003704
3705 install_element(node, &config_exit_cmd);
3706
3707 if (node >= CONFIG_NODE) {
3708 /* It's not a top node. */
3709 install_element(node, &config_end_cmd);
3710 }
3711}
3712
Neels Hofmeyr69054e22017-10-19 02:44:57 +02003713/*! Return true if a node is installed by install_basic_node_commands(), so
3714 * that we can avoid repeating them for each and every node during 'show
3715 * running-config' */
3716static bool vty_command_is_common(struct cmd_element *cmd)
3717{
3718 if (cmd == &config_help_cmd
3719 || cmd == &config_list_cmd
3720 || cmd == &config_write_terminal_cmd
3721 || cmd == &config_write_file_cmd
3722 || cmd == &config_write_memory_cmd
3723 || cmd == &config_write_cmd
3724 || cmd == &show_running_config_cmd
3725 || cmd == &config_exit_cmd
3726 || cmd == &config_end_cmd)
3727 return true;
3728 return false;
3729}
3730
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003731/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003732 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003733 * \param[in] vty the vty of the code
3734 * \param[in] filename where to store the file
3735 * \return 0 in case of success.
3736 *
3737 * If the filename already exists create a filename.sav
3738 * version with the current code.
3739 *
3740 */
3741int osmo_vty_write_config_file(const char *filename)
3742{
3743 char *failed_file;
3744 int rc;
3745
3746 rc = write_config_file(filename, &failed_file);
3747 talloc_free(failed_file);
3748 return rc;
3749}
3750
3751/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003752 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003753 * \return 0 in case of success.
3754 *
3755 * If the filename already exists create a filename.sav
3756 * version with the current code.
3757 *
3758 */
3759int osmo_vty_save_config_file(void)
3760{
3761 char *failed_file;
3762 int rc;
3763
3764 if (host.config == NULL)
3765 return -7;
3766
3767 rc = write_config_file(host.config, &failed_file);
3768 talloc_free(failed_file);
3769 return rc;
3770}
3771
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003772/* Initialize command interface. Install basic nodes and commands. */
3773void cmd_init(int terminal)
3774{
3775 /* Allocate initial top vector of commands. */
3776 cmdvec = vector_init(VECTOR_MIN_SIZE);
3777
3778 /* Default host value settings. */
3779 host.name = NULL;
3780 host.password = NULL;
3781 host.enable = NULL;
3782 host.logfile = NULL;
3783 host.config = NULL;
3784 host.lines = -1;
3785 host.motd = default_motd;
3786 host.motdfile = NULL;
3787
3788 /* Install top nodes. */
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003789 install_node_bare(&view_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003790 install_node(&enable_node, NULL);
Neels Hofmeyrf4f23bd2017-09-20 15:39:37 +02003791 install_node_bare(&auth_node, NULL);
3792 install_node_bare(&auth_enable_node, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003793 install_node(&config_node, config_write_host);
3794
3795 /* Each node's basic commands. */
3796 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003797 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003798 if (terminal) {
3799 install_element(VIEW_NODE, &config_list_cmd);
3800 install_element(VIEW_NODE, &config_exit_cmd);
3801 install_element(VIEW_NODE, &config_help_cmd);
3802 install_element(VIEW_NODE, &config_enable_cmd);
3803 install_element(VIEW_NODE, &config_terminal_length_cmd);
3804 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3805 install_element(VIEW_NODE, &echo_cmd);
3806 }
3807
3808 if (terminal) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003809 install_element(ENABLE_NODE, &config_disable_cmd);
3810 install_element(ENABLE_NODE, &config_terminal_cmd);
3811 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3812 }
3813 install_element (ENABLE_NODE, &show_startup_config_cmd);
3814 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003815 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003816
3817 if (terminal) {
3818 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3819 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3820 install_element(ENABLE_NODE, &echo_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003821 }
3822
3823 install_element(CONFIG_NODE, &hostname_cmd);
3824 install_element(CONFIG_NODE, &no_hostname_cmd);
3825
3826 if (terminal) {
3827 install_element(CONFIG_NODE, &password_cmd);
3828 install_element(CONFIG_NODE, &password_text_cmd);
3829 install_element(CONFIG_NODE, &enable_password_cmd);
3830 install_element(CONFIG_NODE, &enable_password_text_cmd);
3831 install_element(CONFIG_NODE, &no_enable_password_cmd);
3832
3833#ifdef VTY_CRYPT_PW
3834 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3835 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3836#endif
3837 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3838 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3839 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3840 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3841 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3842
3843 }
3844 srand(time(NULL));
3845}
Harald Welte7acb30c2011-08-17 17:13:48 +02003846
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003847/*! @} */