blob: 8ad2e975dac8227804e456cdcaa6b78f42863e8c [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
6
7This file is part of GNU Zebra.
8
9GNU Zebra is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published
11by the Free Software Foundation; either version 2, or (at your
12option) any later version.
13
14GNU Zebra is distributed in the hope that it will be useful, but
15WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010021Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22Boston, MA 02110-1301, USA. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020023
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
Sylvain Munaut4d8eea42012-12-28 11:58:23 +010027#include <stdbool.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020028#include <syslog.h>
29#include <errno.h>
30#define _XOPEN_SOURCE
31#include <unistd.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020032#include <ctype.h>
33#include <time.h>
34#include <sys/time.h>
35#include <sys/stat.h>
36
37#include <osmocom/vty/vector.h>
38#include <osmocom/vty/vty.h>
39#include <osmocom/vty/command.h>
40
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010041#include <osmocom/core/talloc.h>
Harald Weltea99d45a2015-11-12 13:48:23 +010042#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020043
Harald Weltee881b1b2011-08-17 18:52:30 +020044/*! \addtogroup command
Harald Welte96e2a002017-06-12 21:44:18 +020045 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020046 * VTY command handling
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020047 *
48 * \file command.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020049
Harald Welte3fb0b6f2010-05-19 19:02:52 +020050#define CONFIGFILE_MASK 022
51
52void *tall_vty_cmd_ctx;
53
54/* Command vector which includes some level of command lists. Normally
55 each daemon maintains each own cmdvec. */
56vector cmdvec;
57
58/* Host information structure. */
59struct host host;
60
61/* Standard command node structures. */
62struct cmd_node auth_node = {
63 AUTH_NODE,
64 "Password: ",
65};
66
67struct cmd_node view_node = {
68 VIEW_NODE,
69 "%s> ",
70};
71
72struct cmd_node auth_enable_node = {
73 AUTH_ENABLE_NODE,
74 "Password: ",
75};
76
77struct cmd_node enable_node = {
78 ENABLE_NODE,
79 "%s# ",
80};
81
82struct cmd_node config_node = {
83 CONFIG_NODE,
84 "%s(config)# ",
85 1
86};
87
88/* Default motd string. */
89const char *default_motd = "";
90
Neels Hofmeyr87e45502017-06-20 00:17:59 +020091/*! print the version (and optionally copyright) information
Harald Welte7acb30c2011-08-17 17:13:48 +020092 *
93 * This is called from main when a daemon is invoked with -v or --version. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020094void print_version(int print_copyright)
95{
Harald Welte237f6242010-05-25 23:00:45 +020096 printf("%s version %s\n", host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +020097 if (print_copyright)
Harald Welte237f6242010-05-25 23:00:45 +020098 printf("\n%s\n", host.app_info->copyright);
Harald Welte3fb0b6f2010-05-19 19:02:52 +020099}
100
101/* Utility function to concatenate argv argument into a single string
102 with inserting ' ' character between each argument. */
103char *argv_concat(const char **argv, int argc, int shift)
104{
105 int i;
106 size_t len;
107 char *str;
108 char *p;
109
110 len = 0;
111 for (i = shift; i < argc; i++)
112 len += strlen(argv[i]) + 1;
113 if (!len)
114 return NULL;
115 p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
116 for (i = shift; i < argc; i++) {
117 size_t arglen;
118 memcpy(p, argv[i], (arglen = strlen(argv[i])));
119 p += arglen;
120 *p++ = ' ';
121 }
122 *(p - 1) = '\0';
123 return str;
124}
125
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200126/* Strip all characters from a string (prompt) except for alnum, '-' and '_'.
127 * For example used to derive a node->name from node->prompt if the user didn't provide a name;
128 * in turn, this name us used for XML IDs in 'show online-help'. */
129static const char *node_name_from_prompt(const char *prompt, char *name_buf, size_t name_buf_size)
130{
131 const char *pos;
132 int dest = 0;
133
134 if (!prompt || !*prompt)
135 return "";
136
137 for (pos = prompt; *pos && dest < (name_buf_size-1); pos++) {
138 if (pos[0] == '%' && pos[1]) {
139 /* skip "%s"; loop pos++ does the second one. */
140 pos++;
141 continue;
142 }
143 if (!(isalnum(pos[0]) || pos[0] == '-' || pos[0] == '_'))
144 continue;
145 name_buf[dest] = pos[0];
146 dest++;
147 }
148 name_buf[dest] = '\0';
149 return name_buf;
150}
151
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200152/*! Install top node of command vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200153void install_node(struct cmd_node *node, int (*func) (struct vty *))
154{
155 vector_set_index(cmdvec, node->node, node);
156 node->func = func;
157 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200158 if (!*node->name)
159 node_name_from_prompt(node->prompt, node->name, sizeof(node->name));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200160}
161
162/* Compare two command's string. Used in sort_node (). */
163static int cmp_node(const void *p, const void *q)
164{
165 struct cmd_element *a = *(struct cmd_element **)p;
166 struct cmd_element *b = *(struct cmd_element **)q;
167
168 return strcmp(a->string, b->string);
169}
170
171static int cmp_desc(const void *p, const void *q)
172{
173 struct desc *a = *(struct desc **)p;
174 struct desc *b = *(struct desc **)q;
175
176 return strcmp(a->cmd, b->cmd);
177}
178
Jacob Erlbeck2442e092013-09-06 16:51:58 +0200179static int is_config_child(struct vty *vty)
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800180{
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800181 if (vty->node <= CONFIG_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800182 return 0;
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800183 else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800184 return 1;
185 else if (host.app_info->is_config_node)
Holger Hans Peter Freyther8f09f012010-08-25 17:34:56 +0800186 return host.app_info->is_config_node(vty, vty->node);
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800187 else
188 return vty->node > CONFIG_NODE;
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800189}
190
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200191/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200192void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200193{
194 unsigned int i, j;
195 struct cmd_node *cnode;
196 vector descvec;
197 struct cmd_element *cmd_element;
198
199 for (i = 0; i < vector_active(cmdvec); i++)
200 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
201 vector cmd_vector = cnode->cmd_vector;
202 qsort(cmd_vector->index, vector_active(cmd_vector),
203 sizeof(void *), cmp_node);
204
205 for (j = 0; j < vector_active(cmd_vector); j++)
206 if ((cmd_element =
207 vector_slot(cmd_vector, j)) != NULL
208 && vector_active(cmd_element->strvec)) {
209 descvec =
210 vector_slot(cmd_element->strvec,
211 vector_active
212 (cmd_element->strvec) -
213 1);
214 qsort(descvec->index,
215 vector_active(descvec),
216 sizeof(void *), cmp_desc);
217 }
218 }
219}
220
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200221/*! Break up string in command tokens. Return leading indents.
222 * \param[in] string String to split.
223 * \param[out] indent If not NULL, return a talloc_strdup of indent characters.
224 * \param[out] strvec_p Returns vector of split tokens, must not be NULL.
225 * \returns CMD_SUCCESS or CMD_ERR_INVALID_INDENT
226 *
227 * If \a indent is passed non-NULL, only simple space ' ' indents are allowed,
228 * so that \a indent can simply return the count of leading spaces.
229 * Otherwise any isspace() characters are allowed for indenting (backwards compat).
230 */
231int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200232{
233 const char *cp, *start;
234 char *token;
235 int strlen;
236 vector strvec;
237
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200238 *strvec_p = NULL;
239 if (indent)
240 *indent = 0;
241
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200242 if (string == NULL)
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200243 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200244
245 cp = string;
246
247 /* Skip white spaces. */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200248 while (isspace((int)*cp) && *cp != '\0') {
249 /* if we're counting indents, we need to be strict about them */
250 if (indent && (*cp != ' ') && (*cp != '\t')) {
251 /* Ignore blank lines, they appear as leading whitespace with line breaks. */
252 if (*cp == '\n' || *cp == '\r') {
253 cp++;
254 string = cp;
255 continue;
256 }
257 return CMD_ERR_INVALID_INDENT;
258 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200259 cp++;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200260 }
261
262 if (indent)
263 *indent = talloc_strndup(tall_vty_cmd_ctx, string, cp - string);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200264
265 /* Return if there is only white spaces */
266 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200267 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200268
269 if (*cp == '!' || *cp == '#')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200270 return CMD_SUCCESS;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200271
272 /* Prepare return vector. */
273 strvec = vector_init(VECTOR_MIN_SIZE);
274
275 /* Copy each command piece and set into vector. */
276 while (1) {
277 start = cp;
278 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
279 *cp != '\0')
280 cp++;
281 strlen = cp - start;
282 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
283 memcpy(token, start, strlen);
284 *(token + strlen) = '\0';
285 vector_set(strvec, token);
286
287 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
288 *cp != '\0')
289 cp++;
290
291 if (*cp == '\0')
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200292 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200293 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200294
295 *strvec_p = strvec;
296 return CMD_SUCCESS;
297}
298
299/*! Breaking up string into each command piece. I assume given
300 character is separated by a space character. Return value is a
301 vector which includes char ** data element. */
302vector cmd_make_strvec(const char *string)
303{
304 vector strvec;
305 cmd_make_strvec2(string, NULL, &strvec);
306 return strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200307}
308
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200309/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200310void cmd_free_strvec(vector v)
311{
312 unsigned int i;
313 char *cp;
314
315 if (!v)
316 return;
317
318 for (i = 0; i < vector_active(v); i++)
319 if ((cp = vector_slot(v, i)) != NULL)
320 talloc_free(cp);
321
322 vector_free(v);
323}
324
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200325/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200326static char *cmd_desc_str(const char **string)
327{
328 const char *cp, *start;
329 char *token;
330 int strlen;
331
332 cp = *string;
333
334 if (cp == NULL)
335 return NULL;
336
337 /* Skip white spaces. */
338 while (isspace((int)*cp) && *cp != '\0')
339 cp++;
340
341 /* Return if there is only white spaces */
342 if (*cp == '\0')
343 return NULL;
344
345 start = cp;
346
347 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
348 cp++;
349
350 strlen = cp - start;
351 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
352 memcpy(token, start, strlen);
353 *(token + strlen) = '\0';
354
355 *string = cp;
356
357 return token;
358}
359
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200360/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200361static vector cmd_make_descvec(const char *string, const char *descstr)
362{
363 int multiple = 0;
364 const char *sp;
365 char *token;
366 int len;
367 const char *cp;
368 const char *dp;
369 vector allvec;
370 vector strvec = NULL;
371 struct desc *desc;
372
373 cp = string;
374 dp = descstr;
375
376 if (cp == NULL)
377 return NULL;
378
379 allvec = vector_init(VECTOR_MIN_SIZE);
380
381 while (1) {
382 while (isspace((int)*cp) && *cp != '\0')
383 cp++;
384
385 if (*cp == '(') {
386 multiple = 1;
387 cp++;
388 }
389 if (*cp == ')') {
390 multiple = 0;
391 cp++;
392 }
393 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100394 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200395 cp++;
396 }
397
398 while (isspace((int)*cp) && *cp != '\0')
399 cp++;
400
401 if (*cp == '(') {
402 multiple = 1;
403 cp++;
404 }
405
406 if (*cp == '\0')
407 return allvec;
408
409 sp = cp;
410
411 while (!
412 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
413 || *cp == ')' || *cp == '|') && *cp != '\0')
414 cp++;
415
416 len = cp - sp;
417
418 token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
419 memcpy(token, sp, len);
420 *(token + len) = '\0';
421
422 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
423 desc->cmd = token;
424 desc->str = cmd_desc_str(&dp);
425
426 if (multiple) {
427 if (multiple == 1) {
428 strvec = vector_init(VECTOR_MIN_SIZE);
429 vector_set(allvec, strvec);
430 }
431 multiple++;
432 } else {
433 strvec = vector_init(VECTOR_MIN_SIZE);
434 vector_set(allvec, strvec);
435 }
436 vector_set(strvec, desc);
437 }
438}
439
440/* Count mandantory string vector size. This is to determine inputed
441 command has enough command length. */
442static int cmd_cmdsize(vector strvec)
443{
444 unsigned int i;
445 int size = 0;
446 vector descvec;
447 struct desc *desc;
448
449 for (i = 0; i < vector_active(strvec); i++)
450 if ((descvec = vector_slot(strvec, i)) != NULL) {
451 if ((vector_active(descvec)) == 1
452 && (desc = vector_slot(descvec, 0)) != NULL) {
453 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
454 return size;
455 else
456 size++;
457 } else
458 size++;
459 }
460 return size;
461}
462
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200463/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200464const char *cmd_prompt(enum node_type node)
465{
466 struct cmd_node *cnode;
467
468 cnode = vector_slot(cmdvec, node);
469 return cnode->prompt;
470}
471
Alexander Couzensad580ba2016-05-16 16:01:45 +0200472/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200473 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200474 * \param unsafe string
475 * \return a new talloc char *
476 */
477char *osmo_asciidoc_escape(const char *inp)
478{
479 int _strlen;
480 char *out, *out_ptr;
481 int len = 0, i, j;
482
483 if (!inp)
484 return NULL;
485 _strlen = strlen(inp);
486
487 for (i = 0; i < _strlen; ++i) {
488 switch (inp[i]) {
489 case '|':
490 len += 2;
491 break;
492 default:
493 len += 1;
494 break;
495 }
496 }
497
498 out = talloc_size(NULL, len + 1);
499 if (!out)
500 return NULL;
501
502 out_ptr = out;
503
504#define ADD(out, str) \
505 for (j = 0; j < strlen(str); ++j) \
506 *(out++) = str[j];
507
508 for (i = 0; i < _strlen; ++i) {
509 switch (inp[i]) {
510 case '|':
511 ADD(out_ptr, "\\|");
512 break;
513 default:
514 *(out_ptr++) = inp[i];
515 break;
516 }
517 }
518
519#undef ADD
520
521 out_ptr[0] = '\0';
522 return out;
523}
524
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100525static char *xml_escape(const char *inp)
526{
527 int _strlen;
528 char *out, *out_ptr;
529 int len = 0, i, j;
530
531 if (!inp)
532 return NULL;
533 _strlen = strlen(inp);
534
535 for (i = 0; i < _strlen; ++i) {
536 switch (inp[i]) {
537 case '"':
538 len += 6;
539 break;
540 case '\'':
541 len += 6;
542 break;
543 case '<':
544 len += 4;
545 break;
546 case '>':
547 len += 4;
548 break;
549 case '&':
550 len += 5;
551 break;
552 default:
553 len += 1;
554 break;
555 }
556 }
557
558 out = talloc_size(NULL, len + 1);
559 if (!out)
560 return NULL;
561
562 out_ptr = out;
563
564#define ADD(out, str) \
565 for (j = 0; j < strlen(str); ++j) \
566 *(out++) = str[j];
567
568 for (i = 0; i < _strlen; ++i) {
569 switch (inp[i]) {
570 case '"':
571 ADD(out_ptr, "&quot;");
572 break;
573 case '\'':
574 ADD(out_ptr, "&apos;");
575 break;
576 case '<':
577 ADD(out_ptr, "&lt;");
578 break;
579 case '>':
580 ADD(out_ptr, "&gt;");
581 break;
582 case '&':
583 ADD(out_ptr, "&amp;");
584 break;
585 default:
586 *(out_ptr++) = inp[i];
587 break;
588 }
589 }
590
591#undef ADD
592
593 out_ptr[0] = '\0';
594 return out;
595}
596
597/*
598 * Write one cmd_element as XML to the given VTY.
599 */
600static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
601{
602 char *xml_string = xml_escape(cmd->string);
603
604 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
605 vty_out(vty, " <params>%s", VTY_NEWLINE);
606
607 int j;
608 for (j = 0; j < vector_count(cmd->strvec); ++j) {
609 vector descvec = vector_slot(cmd->strvec, j);
610 int i;
611 for (i = 0; i < vector_active(descvec); ++i) {
612 char *xml_param, *xml_doc;
613 struct desc *desc = vector_slot(descvec, i);
614 if (desc == NULL)
615 continue;
616
617 xml_param = xml_escape(desc->cmd);
618 xml_doc = xml_escape(desc->str);
619 vty_out(vty, " <param name='%s' doc='%s' />%s",
620 xml_param, xml_doc, VTY_NEWLINE);
621 talloc_free(xml_param);
622 talloc_free(xml_doc);
623 }
624 }
625
626 vty_out(vty, " </params>%s", VTY_NEWLINE);
627 vty_out(vty, " </command>%s", VTY_NEWLINE);
628
629 talloc_free(xml_string);
630 return 0;
631}
632
633/*
634 * Dump all nodes and commands associated with a given node as XML to the VTY.
635 */
636static int vty_dump_nodes(struct vty *vty)
637{
638 int i, j;
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200639 int same_name_count;
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100640
641 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
642
643 for (i = 0; i < vector_active(cmdvec); ++i) {
644 struct cmd_node *cnode;
645 cnode = vector_slot(cmdvec, i);
646 if (!cnode)
647 continue;
648
Neels Hofmeyr657c5b62017-09-18 16:42:06 +0200649 /* De-dup node IDs: how many times has this same name been used before? Count the first
650 * occurence as _1 and omit that first suffix, so that the first occurence is called
651 * 'name', the second becomes 'name_2', then 'name_3', ... */
652 same_name_count = 1;
653 for (j = 0; j < i; ++j) {
654 struct cmd_node *cnode2;
655 cnode2 = vector_slot(cmdvec, j);
656 if (!cnode2)
657 continue;
658 if (strcmp(cnode->name, cnode2->name) == 0)
659 same_name_count ++;
660 }
661
662 vty_out(vty, " <node id='%s", cnode->name);
663 if (same_name_count > 1 || !*cnode->name)
664 vty_out(vty, "_%d", same_name_count);
665 vty_out(vty, "'>%s", VTY_NEWLINE);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100666
667 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
668 struct cmd_element *elem;
669 elem = vector_slot(cnode->cmd_vector, j);
670 vty_dump_element(elem, vty);
671 }
672
673 vty_out(vty, " </node>%s", VTY_NEWLINE);
674 }
675
676 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
677
678 return 0;
679}
680
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200681/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100682static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
683{
684 int i;
685
686 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
687 struct cmd_element *elem;
688 elem = vector_slot(cnode->cmd_vector, i);
689 if (!elem->string)
690 continue;
691 if (!strcmp(elem->string, cmdstring))
692 return 1;
693 }
694 return 0;
695}
696
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200697/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200698 * \param[in] ntype Node Type
699 * \param[cmd] element to be installed
700 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000701void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200702{
703 struct cmd_node *cnode;
704
705 cnode = vector_slot(cmdvec, ntype);
706
Harald Weltea99d45a2015-11-12 13:48:23 +0100707 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100708 /* ensure no _identical_ command has been registered at this
709 * node so far */
710 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200711
712 vector_set(cnode->cmd_vector, cmd);
713
714 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
715 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
716}
717
718/* Install a command into VIEW and ENABLE node */
719void install_element_ve(struct cmd_element *cmd)
720{
721 install_element(VIEW_NODE, cmd);
722 install_element(ENABLE_NODE, cmd);
723}
724
725#ifdef VTY_CRYPT_PW
726static unsigned char itoa64[] =
727 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
728
729static void to64(char *s, long v, int n)
730{
731 while (--n >= 0) {
732 *s++ = itoa64[v & 0x3f];
733 v >>= 6;
734 }
735}
736
737static char *zencrypt(const char *passwd)
738{
739 char salt[6];
740 struct timeval tv;
741 char *crypt(const char *, const char *);
742
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200743 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200744
745 to64(&salt[0], random(), 3);
746 to64(&salt[3], tv.tv_usec, 3);
747 salt[5] = '\0';
748
749 return crypt(passwd, salt);
750}
751#endif
752
753/* This function write configuration of this host. */
754static int config_write_host(struct vty *vty)
755{
756 if (host.name)
757 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
758
759 if (host.encrypt) {
760 if (host.password_encrypt)
761 vty_out(vty, "password 8 %s%s", host.password_encrypt,
762 VTY_NEWLINE);
763 if (host.enable_encrypt)
764 vty_out(vty, "enable password 8 %s%s",
765 host.enable_encrypt, VTY_NEWLINE);
766 } else {
767 if (host.password)
768 vty_out(vty, "password %s%s", host.password,
769 VTY_NEWLINE);
770 if (host.enable)
771 vty_out(vty, "enable password %s%s", host.enable,
772 VTY_NEWLINE);
773 }
774
775 if (host.advanced)
776 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
777
778 if (host.encrypt)
779 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
780
781 if (host.lines >= 0)
782 vty_out(vty, "service terminal-length %d%s", host.lines,
783 VTY_NEWLINE);
784
785 if (host.motdfile)
786 vty_out(vty, "banner motd file %s%s", host.motdfile,
787 VTY_NEWLINE);
788 else if (!host.motd)
789 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
790
791 return 1;
792}
793
794/* Utility function for getting command vector. */
795static vector cmd_node_vector(vector v, enum node_type ntype)
796{
797 struct cmd_node *cnode = vector_slot(v, ntype);
798 return cnode->cmd_vector;
799}
800
801/* Completion match types. */
802enum match_type {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100803 no_match = 0,
804 any_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200805 extend_match,
806 ipv4_prefix_match,
807 ipv4_match,
808 ipv6_prefix_match,
809 ipv6_match,
810 range_match,
811 vararg_match,
812 partly_match,
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100813 exact_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200814};
815
816static enum match_type cmd_ipv4_match(const char *str)
817{
818 const char *sp;
819 int dots = 0, nums = 0;
820 char buf[4];
821
822 if (str == NULL)
823 return partly_match;
824
825 for (;;) {
826 memset(buf, 0, sizeof(buf));
827 sp = str;
828 while (*str != '\0') {
829 if (*str == '.') {
830 if (dots >= 3)
831 return no_match;
832
833 if (*(str + 1) == '.')
834 return no_match;
835
836 if (*(str + 1) == '\0')
837 return partly_match;
838
839 dots++;
840 break;
841 }
842 if (!isdigit((int)*str))
843 return no_match;
844
845 str++;
846 }
847
848 if (str - sp > 3)
849 return no_match;
850
851 strncpy(buf, sp, str - sp);
852 if (atoi(buf) > 255)
853 return no_match;
854
855 nums++;
856
857 if (*str == '\0')
858 break;
859
860 str++;
861 }
862
863 if (nums < 4)
864 return partly_match;
865
866 return exact_match;
867}
868
869static enum match_type cmd_ipv4_prefix_match(const char *str)
870{
871 const char *sp;
872 int dots = 0;
873 char buf[4];
874
875 if (str == NULL)
876 return partly_match;
877
878 for (;;) {
879 memset(buf, 0, sizeof(buf));
880 sp = str;
881 while (*str != '\0' && *str != '/') {
882 if (*str == '.') {
883 if (dots == 3)
884 return no_match;
885
886 if (*(str + 1) == '.' || *(str + 1) == '/')
887 return no_match;
888
889 if (*(str + 1) == '\0')
890 return partly_match;
891
892 dots++;
893 break;
894 }
895
896 if (!isdigit((int)*str))
897 return no_match;
898
899 str++;
900 }
901
902 if (str - sp > 3)
903 return no_match;
904
905 strncpy(buf, sp, str - sp);
906 if (atoi(buf) > 255)
907 return no_match;
908
909 if (dots == 3) {
910 if (*str == '/') {
911 if (*(str + 1) == '\0')
912 return partly_match;
913
914 str++;
915 break;
916 } else if (*str == '\0')
917 return partly_match;
918 }
919
920 if (*str == '\0')
921 return partly_match;
922
923 str++;
924 }
925
926 sp = str;
927 while (*str != '\0') {
928 if (!isdigit((int)*str))
929 return no_match;
930
931 str++;
932 }
933
934 if (atoi(sp) > 32)
935 return no_match;
936
937 return exact_match;
938}
939
940#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
941#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
942#define STATE_START 1
943#define STATE_COLON 2
944#define STATE_DOUBLE 3
945#define STATE_ADDR 4
946#define STATE_DOT 5
947#define STATE_SLASH 6
948#define STATE_MASK 7
949
950#ifdef HAVE_IPV6
951
952static enum match_type cmd_ipv6_match(const char *str)
953{
954 int state = STATE_START;
955 int colons = 0, nums = 0, double_colon = 0;
956 const char *sp = NULL;
957 struct sockaddr_in6 sin6_dummy;
958 int ret;
959
960 if (str == NULL)
961 return partly_match;
962
963 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
964 return no_match;
965
966 /* use inet_pton that has a better support,
967 * for example inet_pton can support the automatic addresses:
968 * ::1.2.3.4
969 */
970 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
971
972 if (ret == 1)
973 return exact_match;
974
975 while (*str != '\0') {
976 switch (state) {
977 case STATE_START:
978 if (*str == ':') {
979 if (*(str + 1) != ':' && *(str + 1) != '\0')
980 return no_match;
981 colons--;
982 state = STATE_COLON;
983 } else {
984 sp = str;
985 state = STATE_ADDR;
986 }
987
988 continue;
989 case STATE_COLON:
990 colons++;
991 if (*(str + 1) == ':')
992 state = STATE_DOUBLE;
993 else {
994 sp = str + 1;
995 state = STATE_ADDR;
996 }
997 break;
998 case STATE_DOUBLE:
999 if (double_colon)
1000 return no_match;
1001
1002 if (*(str + 1) == ':')
1003 return no_match;
1004 else {
1005 if (*(str + 1) != '\0')
1006 colons++;
1007 sp = str + 1;
1008 state = STATE_ADDR;
1009 }
1010
1011 double_colon++;
1012 nums++;
1013 break;
1014 case STATE_ADDR:
1015 if (*(str + 1) == ':' || *(str + 1) == '\0') {
1016 if (str - sp > 3)
1017 return no_match;
1018
1019 nums++;
1020 state = STATE_COLON;
1021 }
1022 if (*(str + 1) == '.')
1023 state = STATE_DOT;
1024 break;
1025 case STATE_DOT:
1026 state = STATE_ADDR;
1027 break;
1028 default:
1029 break;
1030 }
1031
1032 if (nums > 8)
1033 return no_match;
1034
1035 if (colons > 7)
1036 return no_match;
1037
1038 str++;
1039 }
1040
1041#if 0
1042 if (nums < 11)
1043 return partly_match;
1044#endif /* 0 */
1045
1046 return exact_match;
1047}
1048
1049static enum match_type cmd_ipv6_prefix_match(const char *str)
1050{
1051 int state = STATE_START;
1052 int colons = 0, nums = 0, double_colon = 0;
1053 int mask;
1054 const char *sp = NULL;
1055 char *endptr = NULL;
1056
1057 if (str == NULL)
1058 return partly_match;
1059
1060 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
1061 return no_match;
1062
1063 while (*str != '\0' && state != STATE_MASK) {
1064 switch (state) {
1065 case STATE_START:
1066 if (*str == ':') {
1067 if (*(str + 1) != ':' && *(str + 1) != '\0')
1068 return no_match;
1069 colons--;
1070 state = STATE_COLON;
1071 } else {
1072 sp = str;
1073 state = STATE_ADDR;
1074 }
1075
1076 continue;
1077 case STATE_COLON:
1078 colons++;
1079 if (*(str + 1) == '/')
1080 return no_match;
1081 else if (*(str + 1) == ':')
1082 state = STATE_DOUBLE;
1083 else {
1084 sp = str + 1;
1085 state = STATE_ADDR;
1086 }
1087 break;
1088 case STATE_DOUBLE:
1089 if (double_colon)
1090 return no_match;
1091
1092 if (*(str + 1) == ':')
1093 return no_match;
1094 else {
1095 if (*(str + 1) != '\0' && *(str + 1) != '/')
1096 colons++;
1097 sp = str + 1;
1098
1099 if (*(str + 1) == '/')
1100 state = STATE_SLASH;
1101 else
1102 state = STATE_ADDR;
1103 }
1104
1105 double_colon++;
1106 nums += 1;
1107 break;
1108 case STATE_ADDR:
1109 if (*(str + 1) == ':' || *(str + 1) == '.'
1110 || *(str + 1) == '\0' || *(str + 1) == '/') {
1111 if (str - sp > 3)
1112 return no_match;
1113
1114 for (; sp <= str; sp++)
1115 if (*sp == '/')
1116 return no_match;
1117
1118 nums++;
1119
1120 if (*(str + 1) == ':')
1121 state = STATE_COLON;
1122 else if (*(str + 1) == '.')
1123 state = STATE_DOT;
1124 else if (*(str + 1) == '/')
1125 state = STATE_SLASH;
1126 }
1127 break;
1128 case STATE_DOT:
1129 state = STATE_ADDR;
1130 break;
1131 case STATE_SLASH:
1132 if (*(str + 1) == '\0')
1133 return partly_match;
1134
1135 state = STATE_MASK;
1136 break;
1137 default:
1138 break;
1139 }
1140
1141 if (nums > 11)
1142 return no_match;
1143
1144 if (colons > 7)
1145 return no_match;
1146
1147 str++;
1148 }
1149
1150 if (state < STATE_MASK)
1151 return partly_match;
1152
1153 mask = strtol(str, &endptr, 10);
1154 if (*endptr != '\0')
1155 return no_match;
1156
1157 if (mask < 0 || mask > 128)
1158 return no_match;
1159
1160/* I don't know why mask < 13 makes command match partly.
1161 Forgive me to make this comments. I Want to set static default route
1162 because of lack of function to originate default in ospf6d; sorry
1163 yasu
1164 if (mask < 13)
1165 return partly_match;
1166*/
1167
1168 return exact_match;
1169}
1170
1171#endif /* HAVE_IPV6 */
1172
1173#define DECIMAL_STRLEN_MAX 10
1174
1175static int cmd_range_match(const char *range, const char *str)
1176{
1177 char *p;
1178 char buf[DECIMAL_STRLEN_MAX + 1];
1179 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001180
1181 if (str == NULL)
1182 return 1;
1183
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001184 if (range[1] == '-') {
1185 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001187 val = strtol(str, &endptr, 10);
1188 if (*endptr != '\0')
1189 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001191 range += 2;
1192 p = strchr(range, '-');
1193 if (p == NULL)
1194 return 0;
1195 if (p - range > DECIMAL_STRLEN_MAX)
1196 return 0;
1197 strncpy(buf, range, p - range);
1198 buf[p - range] = '\0';
1199 min = -strtol(buf, &endptr, 10);
1200 if (*endptr != '\0')
1201 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001202
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001203 range = p + 1;
1204 p = strchr(range, '>');
1205 if (p == NULL)
1206 return 0;
1207 if (p - range > DECIMAL_STRLEN_MAX)
1208 return 0;
1209 strncpy(buf, range, p - range);
1210 buf[p - range] = '\0';
1211 max = strtol(buf, &endptr, 10);
1212 if (*endptr != '\0')
1213 return 0;
1214
1215 if (val < min || val > max)
1216 return 0;
1217 } else {
1218 unsigned long min, max, val;
1219
1220 val = strtoul(str, &endptr, 10);
1221 if (*endptr != '\0')
1222 return 0;
1223
1224 range++;
1225 p = strchr(range, '-');
1226 if (p == NULL)
1227 return 0;
1228 if (p - range > DECIMAL_STRLEN_MAX)
1229 return 0;
1230 strncpy(buf, range, p - range);
1231 buf[p - range] = '\0';
1232 min = strtoul(buf, &endptr, 10);
1233 if (*endptr != '\0')
1234 return 0;
1235
1236 range = p + 1;
1237 p = strchr(range, '>');
1238 if (p == NULL)
1239 return 0;
1240 if (p - range > DECIMAL_STRLEN_MAX)
1241 return 0;
1242 strncpy(buf, range, p - range);
1243 buf[p - range] = '\0';
1244 max = strtoul(buf, &endptr, 10);
1245 if (*endptr != '\0')
1246 return 0;
1247
1248 if (val < min || val > max)
1249 return 0;
1250 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001251
1252 return 1;
1253}
1254
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001255/* helper to retrieve the 'real' argument string from an optional argument */
1256static char *
1257cmd_deopt(const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001258{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001259 /* we've got "[blah]". We want to strip off the []s and redo the
1260 * match check for "blah"
1261 */
1262 size_t len = strlen(str);
1263 char *tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001264
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001265 if (len < 3)
1266 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001267
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001268 /* tmp will hold a string of len-2 chars, so 'len' size is fine */
1269 tmp = talloc_size(NULL, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001270
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001271 memcpy(tmp, (str + 1), len - 2);
1272 tmp[len - 2] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001273
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001274 return tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001275}
1276
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001277static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001278cmd_match(const char *str, const char *command,
1279 enum match_type min, bool recur)
1280{
1281
1282 if (recur && CMD_OPTION(str))
1283 {
1284 enum match_type ret;
1285 char *tmp = cmd_deopt(str);
1286
1287 /* this would be a bug in a command, however handle it gracefully
1288 * as it we only discover it if a user tries to run it
1289 */
1290 if (tmp == NULL)
1291 return no_match;
1292
1293 ret = cmd_match(tmp, command, min, false);
1294
1295 talloc_free(tmp);
1296
1297 return ret;
1298 }
1299 else if (CMD_VARARG(str))
1300 return vararg_match;
1301 else if (CMD_RANGE(str))
1302 {
1303 if (cmd_range_match(str, command))
1304 return range_match;
1305 }
1306#ifdef HAVE_IPV6
1307 else if (CMD_IPV6(str))
1308 {
1309 if (cmd_ipv6_match(command) >= min)
1310 return ipv6_match;
1311 }
1312 else if (CMD_IPV6_PREFIX(str))
1313 {
1314 if (cmd_ipv6_prefix_match(command) >= min)
1315 return ipv6_prefix_match;
1316 }
1317#endif /* HAVE_IPV6 */
1318 else if (CMD_IPV4(str))
1319 {
1320 if (cmd_ipv4_match(command) >= min)
1321 return ipv4_match;
1322 }
1323 else if (CMD_IPV4_PREFIX(str))
1324 {
1325 if (cmd_ipv4_prefix_match(command) >= min)
1326 return ipv4_prefix_match;
1327 }
1328 else if (CMD_VARIABLE(str))
1329 return extend_match;
1330 else if (strncmp(command, str, strlen(command)) == 0)
1331 {
1332 if (strcmp(command, str) == 0)
1333 return exact_match;
1334 else if (partly_match >= min)
1335 return partly_match;
1336 }
1337
1338 return no_match;
1339}
1340
1341/* Filter vector at the specified index and by the given command string, to
1342 * the desired matching level (thus allowing part matches), and return match
1343 * type flag.
1344 */
1345static enum match_type
1346cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001347{
1348 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001349 struct cmd_element *cmd_element;
1350 enum match_type match_type;
1351 vector descvec;
1352 struct desc *desc;
1353
1354 match_type = no_match;
1355
1356 /* If command and cmd_element string does not match set NULL to vector */
1357 for (i = 0; i < vector_active(v); i++)
1358 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001359 if (index >= vector_active(cmd_element->strvec))
1360 vector_slot(v, i) = NULL;
1361 else {
1362 unsigned int j;
1363 int matched = 0;
1364
1365 descvec =
1366 vector_slot(cmd_element->strvec, index);
1367
1368 for (j = 0; j < vector_active(descvec); j++)
1369 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001370 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001371
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001372 ret = cmd_match (desc->cmd, command, level, true);
1373
1374 if (ret != no_match)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001375 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001376
1377 if (match_type < ret)
1378 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001379 }
1380 if (!matched)
1381 vector_slot(v, i) = NULL;
1382 }
1383 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001384
1385 if (match_type == no_match)
1386 return no_match;
1387
1388 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1389 * go again and filter out commands whose argument (at this index) is
1390 * 'weaker'. E.g., if we have 2 commands:
1391 *
1392 * foo bar <1-255>
1393 * foo bar BLAH
1394 *
1395 * and the command string is 'foo bar 10', then we will get here with with
1396 * 'range_match' being the strongest match. However, if 'BLAH' came
1397 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1398 *
1399 * If we don't do a 2nd pass and filter it out, the higher-layers will
1400 * consider this to be ambiguous.
1401 */
1402 for (i = 0; i < vector_active(v); i++)
1403 if ((cmd_element = vector_slot(v, i)) != NULL) {
1404 if (index >= vector_active(cmd_element->strvec))
1405 vector_slot(v, i) = NULL;
1406 else {
1407 unsigned int j;
1408 int matched = 0;
1409
1410 descvec =
1411 vector_slot(cmd_element->strvec, index);
1412
1413 for (j = 0; j < vector_active(descvec); j++)
1414 if ((desc = vector_slot(descvec, j))) {
1415 enum match_type ret;
1416
1417 ret = cmd_match(desc->cmd, command, any_match, true);
1418
1419 if (ret >= match_type)
1420 matched++;
1421 }
1422 if (!matched)
1423 vector_slot(v, i) = NULL;
1424 }
1425 }
1426
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001427 return match_type;
1428}
1429
1430/* Check ambiguous match */
1431static int
1432is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1433{
1434 unsigned int i;
1435 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001436 struct cmd_element *cmd_element;
1437 const char *matched = NULL;
1438 vector descvec;
1439 struct desc *desc;
1440
1441 for (i = 0; i < vector_active(v); i++)
1442 if ((cmd_element = vector_slot(v, i)) != NULL) {
1443 int match = 0;
1444
1445 descvec = vector_slot(cmd_element->strvec, index);
1446
1447 for (j = 0; j < vector_active(descvec); j++)
1448 if ((desc = vector_slot(descvec, j))) {
1449 enum match_type ret;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001450 const char *str = desc->cmd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001451
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001452 if (CMD_OPTION(str))
1453 if ((str = cmd_deopt(str)) == NULL)
1454 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001455
1456 switch (type) {
1457 case exact_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001458 if (!(CMD_VARIABLE (str))
1459 && strcmp(command, str) == 0)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001460 match++;
1461 break;
1462 case partly_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001463 if (!(CMD_VARIABLE (str))
1464 && strncmp(command, str, strlen (command)) == 0)
1465 {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001466 if (matched
1467 && strcmp(matched,
1468 str) != 0)
1469 return 1; /* There is ambiguous match. */
1470 else
1471 matched = str;
1472 match++;
1473 }
1474 break;
1475 case range_match:
1476 if (cmd_range_match
1477 (str, command)) {
1478 if (matched
1479 && strcmp(matched,
1480 str) != 0)
1481 return 1;
1482 else
1483 matched = str;
1484 match++;
1485 }
1486 break;
1487#ifdef HAVE_IPV6
1488 case ipv6_match:
1489 if (CMD_IPV6(str))
1490 match++;
1491 break;
1492 case ipv6_prefix_match:
1493 if ((ret =
1494 cmd_ipv6_prefix_match
1495 (command)) != no_match) {
1496 if (ret == partly_match)
1497 return 2; /* There is incomplete match. */
1498
1499 match++;
1500 }
1501 break;
1502#endif /* HAVE_IPV6 */
1503 case ipv4_match:
1504 if (CMD_IPV4(str))
1505 match++;
1506 break;
1507 case ipv4_prefix_match:
1508 if ((ret =
1509 cmd_ipv4_prefix_match
1510 (command)) != no_match) {
1511 if (ret == partly_match)
1512 return 2; /* There is incomplete match. */
1513
1514 match++;
1515 }
1516 break;
1517 case extend_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001518 if (CMD_VARIABLE (str))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001519 match++;
1520 break;
1521 case no_match:
1522 default:
1523 break;
1524 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001525
1526 if (CMD_OPTION(desc->cmd))
1527 talloc_free((void*)str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001528 }
1529 if (!match)
1530 vector_slot(v, i) = NULL;
1531 }
1532 return 0;
1533}
1534
1535/* If src matches dst return dst string, otherwise return NULL */
1536static const char *cmd_entry_function(const char *src, const char *dst)
1537{
1538 /* Skip variable arguments. */
1539 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1540 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1541 return NULL;
1542
1543 /* In case of 'command \t', given src is NULL string. */
1544 if (src == NULL)
1545 return dst;
1546
1547 /* Matched with input string. */
1548 if (strncmp(src, dst, strlen(src)) == 0)
1549 return dst;
1550
1551 return NULL;
1552}
1553
1554/* If src matches dst return dst string, otherwise return NULL */
1555/* This version will return the dst string always if it is
1556 CMD_VARIABLE for '?' key processing */
1557static const char *cmd_entry_function_desc(const char *src, const char *dst)
1558{
1559 if (CMD_VARARG(dst))
1560 return dst;
1561
1562 if (CMD_RANGE(dst)) {
1563 if (cmd_range_match(dst, src))
1564 return dst;
1565 else
1566 return NULL;
1567 }
1568#ifdef HAVE_IPV6
1569 if (CMD_IPV6(dst)) {
1570 if (cmd_ipv6_match(src))
1571 return dst;
1572 else
1573 return NULL;
1574 }
1575
1576 if (CMD_IPV6_PREFIX(dst)) {
1577 if (cmd_ipv6_prefix_match(src))
1578 return dst;
1579 else
1580 return NULL;
1581 }
1582#endif /* HAVE_IPV6 */
1583
1584 if (CMD_IPV4(dst)) {
1585 if (cmd_ipv4_match(src))
1586 return dst;
1587 else
1588 return NULL;
1589 }
1590
1591 if (CMD_IPV4_PREFIX(dst)) {
1592 if (cmd_ipv4_prefix_match(src))
1593 return dst;
1594 else
1595 return NULL;
1596 }
1597
1598 /* Optional or variable commands always match on '?' */
1599 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1600 return dst;
1601
1602 /* In case of 'command \t', given src is NULL string. */
1603 if (src == NULL)
1604 return dst;
1605
1606 if (strncmp(src, dst, strlen(src)) == 0)
1607 return dst;
1608 else
1609 return NULL;
1610}
1611
1612/* Check same string element existence. If it isn't there return
1613 1. */
1614static int cmd_unique_string(vector v, const char *str)
1615{
1616 unsigned int i;
1617 char *match;
1618
1619 for (i = 0; i < vector_active(v); i++)
1620 if ((match = vector_slot(v, i)) != NULL)
1621 if (strcmp(match, str) == 0)
1622 return 0;
1623 return 1;
1624}
1625
1626/* Compare string to description vector. If there is same string
1627 return 1 else return 0. */
1628static int desc_unique_string(vector v, const char *str)
1629{
1630 unsigned int i;
1631 struct desc *desc;
1632
1633 for (i = 0; i < vector_active(v); i++)
1634 if ((desc = vector_slot(v, i)) != NULL)
1635 if (strcmp(desc->cmd, str) == 0)
1636 return 1;
1637 return 0;
1638}
1639
1640static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1641{
1642 if (first_word != NULL &&
1643 node != AUTH_NODE &&
1644 node != VIEW_NODE &&
1645 node != AUTH_ENABLE_NODE &&
1646 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1647 return 1;
1648 return 0;
1649}
1650
1651/* '?' describe command support. */
1652static vector
1653cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1654{
1655 unsigned int i;
1656 vector cmd_vector;
1657#define INIT_MATCHVEC_SIZE 10
1658 vector matchvec;
1659 struct cmd_element *cmd_element;
1660 unsigned int index;
1661 int ret;
1662 enum match_type match;
1663 char *command;
1664 static struct desc desc_cr = { "<cr>", "" };
1665
1666 /* Set index. */
1667 if (vector_active(vline) == 0) {
1668 *status = CMD_ERR_NO_MATCH;
1669 return NULL;
1670 } else
1671 index = vector_active(vline) - 1;
1672
1673 /* Make copy vector of current node's command vector. */
1674 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1675
1676 /* Prepare match vector */
1677 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1678
1679 /* Filter commands. */
1680 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001681 for (i = 0; i < index; i++) {
1682 command = vector_slot(vline, i);
1683 if (!command)
1684 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001685
Harald Welte80d30fe2013-02-12 11:08:57 +01001686 match = cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001687
Harald Welte80d30fe2013-02-12 11:08:57 +01001688 if (match == vararg_match) {
1689 struct cmd_element *cmd_element;
1690 vector descvec;
1691 unsigned int j, k;
1692
1693 for (j = 0; j < vector_active(cmd_vector); j++)
1694 if ((cmd_element =
1695 vector_slot(cmd_vector, j)) != NULL
1696 &&
1697 (vector_active(cmd_element->strvec))) {
1698 descvec =
1699 vector_slot(cmd_element->
1700 strvec,
1701 vector_active
1702 (cmd_element->
1703 strvec) - 1);
1704 for (k = 0;
1705 k < vector_active(descvec);
1706 k++) {
1707 struct desc *desc =
1708 vector_slot(descvec,
1709 k);
1710 vector_set(matchvec,
1711 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001712 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001713 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001714
Harald Welte80d30fe2013-02-12 11:08:57 +01001715 vector_set(matchvec, &desc_cr);
1716 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001717
Harald Welte80d30fe2013-02-12 11:08:57 +01001718 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001719 }
1720
Harald Welte80d30fe2013-02-12 11:08:57 +01001721 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1722 match)) == 1) {
1723 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001724 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001725 *status = CMD_ERR_AMBIGUOUS;
1726 return NULL;
1727 } else if (ret == 2) {
1728 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001729 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001730 *status = CMD_ERR_NO_MATCH;
1731 return NULL;
1732 }
1733 }
1734
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001735 /* Prepare match vector */
1736 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1737
1738 /* Make sure that cmd_vector is filtered based on current word */
1739 command = vector_slot(vline, index);
1740 if (command)
Vadim Yanitskiy49a0dec2017-06-12 03:49:38 +07001741 cmd_filter(command, cmd_vector, index, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001742
1743 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001744 for (i = 0; i < vector_active(cmd_vector); i++) {
1745 const char *string = NULL;
1746 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001747
Harald Welte80d30fe2013-02-12 11:08:57 +01001748 cmd_element = vector_slot(cmd_vector, i);
1749 if (!cmd_element)
1750 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001751
Harald Welted17aa592013-02-12 11:11:34 +01001752 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1753 continue;
1754
Harald Welte80d30fe2013-02-12 11:08:57 +01001755 strvec = cmd_element->strvec;
1756
1757 /* if command is NULL, index may be equal to vector_active */
1758 if (command && index >= vector_active(strvec))
1759 vector_slot(cmd_vector, i) = NULL;
1760 else {
1761 /* Check if command is completed. */
1762 if (command == NULL
1763 && index == vector_active(strvec)) {
1764 string = "<cr>";
1765 if (!desc_unique_string(matchvec, string))
1766 vector_set(matchvec, &desc_cr);
1767 } else {
1768 unsigned int j;
1769 vector descvec = vector_slot(strvec, index);
1770 struct desc *desc;
1771
1772 for (j = 0; j < vector_active(descvec); j++) {
1773 desc = vector_slot(descvec, j);
1774 if (!desc)
1775 continue;
1776 string = cmd_entry_function_desc
1777 (command, desc->cmd);
1778 if (!string)
1779 continue;
1780 /* Uniqueness check */
1781 if (!desc_unique_string(matchvec, string))
1782 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001783 }
1784 }
1785 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001786 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001787 vector_free(cmd_vector);
1788
1789 if (vector_slot(matchvec, 0) == NULL) {
1790 vector_free(matchvec);
1791 *status = CMD_ERR_NO_MATCH;
1792 } else
1793 *status = CMD_SUCCESS;
1794
1795 return matchvec;
1796}
1797
1798vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1799{
1800 vector ret;
1801
1802 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1803 enum node_type onode;
1804 vector shifted_vline;
1805 unsigned int index;
1806
1807 onode = vty->node;
1808 vty->node = ENABLE_NODE;
1809 /* We can try it on enable node, cos' the vty is authenticated */
1810
1811 shifted_vline = vector_init(vector_count(vline));
1812 /* use memcpy? */
1813 for (index = 1; index < vector_active(vline); index++) {
1814 vector_set_index(shifted_vline, index - 1,
1815 vector_lookup(vline, index));
1816 }
1817
1818 ret = cmd_describe_command_real(shifted_vline, vty, status);
1819
1820 vector_free(shifted_vline);
1821 vty->node = onode;
1822 return ret;
1823 }
1824
1825 return cmd_describe_command_real(vline, vty, status);
1826}
1827
1828/* Check LCD of matched command. */
1829static int cmd_lcd(char **matched)
1830{
1831 int i;
1832 int j;
1833 int lcd = -1;
1834 char *s1, *s2;
1835 char c1, c2;
1836
1837 if (matched[0] == NULL || matched[1] == NULL)
1838 return 0;
1839
1840 for (i = 1; matched[i] != NULL; i++) {
1841 s1 = matched[i - 1];
1842 s2 = matched[i];
1843
1844 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1845 if (c1 != c2)
1846 break;
1847
1848 if (lcd < 0)
1849 lcd = j;
1850 else {
1851 if (lcd > j)
1852 lcd = j;
1853 }
1854 }
1855 return lcd;
1856}
1857
1858/* Command line completion support. */
1859static char **cmd_complete_command_real(vector vline, struct vty *vty,
1860 int *status)
1861{
1862 unsigned int i;
1863 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1864#define INIT_MATCHVEC_SIZE 10
1865 vector matchvec;
1866 struct cmd_element *cmd_element;
1867 unsigned int index;
1868 char **match_str;
1869 struct desc *desc;
1870 vector descvec;
1871 char *command;
1872 int lcd;
1873
1874 if (vector_active(vline) == 0) {
1875 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001876 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001877 return NULL;
1878 } else
1879 index = vector_active(vline) - 1;
1880
1881 /* First, filter by preceeding command string */
1882 for (i = 0; i < index; i++)
1883 if ((command = vector_slot(vline, i))) {
1884 enum match_type match;
1885 int ret;
1886
1887 /* First try completion match, if there is exactly match return 1 */
1888 match =
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001889 cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001890
1891 /* If there is exact match then filter ambiguous match else check
1892 ambiguousness. */
1893 if ((ret =
1894 is_cmd_ambiguous(command, cmd_vector, i,
1895 match)) == 1) {
1896 vector_free(cmd_vector);
1897 *status = CMD_ERR_AMBIGUOUS;
1898 return NULL;
1899 }
1900 /*
1901 else if (ret == 2)
1902 {
1903 vector_free (cmd_vector);
1904 *status = CMD_ERR_NO_MATCH;
1905 return NULL;
1906 }
1907 */
1908 }
1909
1910 /* Prepare match vector. */
1911 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1912
1913 /* Now we got into completion */
1914 for (i = 0; i < vector_active(cmd_vector); i++)
1915 if ((cmd_element = vector_slot(cmd_vector, i))) {
1916 const char *string;
1917 vector strvec = cmd_element->strvec;
1918
1919 /* Check field length */
1920 if (index >= vector_active(strvec))
1921 vector_slot(cmd_vector, i) = NULL;
1922 else {
1923 unsigned int j;
1924
1925 descvec = vector_slot(strvec, index);
1926 for (j = 0; j < vector_active(descvec); j++)
1927 if ((desc = vector_slot(descvec, j))) {
1928 if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
1929 if (cmd_unique_string (matchvec, string))
1930 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
1931 }
1932 }
1933 }
1934
1935 /* We don't need cmd_vector any more. */
1936 vector_free(cmd_vector);
1937
1938 /* No matched command */
1939 if (vector_slot(matchvec, 0) == NULL) {
1940 vector_free(matchvec);
1941
1942 /* In case of 'command \t' pattern. Do you need '?' command at
1943 the end of the line. */
1944 if (vector_slot(vline, index) == '\0')
1945 *status = CMD_ERR_NOTHING_TODO;
1946 else
1947 *status = CMD_ERR_NO_MATCH;
1948 return NULL;
1949 }
1950
1951 /* Only one matched */
1952 if (vector_slot(matchvec, 1) == NULL) {
1953 match_str = (char **)matchvec->index;
1954 vector_only_wrapper_free(matchvec);
1955 *status = CMD_COMPLETE_FULL_MATCH;
1956 return match_str;
1957 }
1958 /* Make it sure last element is NULL. */
1959 vector_set(matchvec, NULL);
1960
1961 /* Check LCD of matched strings. */
1962 if (vector_slot(vline, index) != NULL) {
1963 lcd = cmd_lcd((char **)matchvec->index);
1964
1965 if (lcd) {
1966 int len = strlen(vector_slot(vline, index));
1967
1968 if (len < lcd) {
1969 char *lcdstr;
1970
1971 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
1972 "complete-lcdstr");
1973 memcpy(lcdstr, matchvec->index[0], lcd);
1974 lcdstr[lcd] = '\0';
1975
1976 /* match_str = (char **) &lcdstr; */
1977
1978 /* Free matchvec. */
1979 for (i = 0; i < vector_active(matchvec); i++) {
1980 if (vector_slot(matchvec, i))
1981 talloc_free(vector_slot(matchvec, i));
1982 }
1983 vector_free(matchvec);
1984
1985 /* Make new matchvec. */
1986 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1987 vector_set(matchvec, lcdstr);
1988 match_str = (char **)matchvec->index;
1989 vector_only_wrapper_free(matchvec);
1990
1991 *status = CMD_COMPLETE_MATCH;
1992 return match_str;
1993 }
1994 }
1995 }
1996
1997 match_str = (char **)matchvec->index;
1998 vector_only_wrapper_free(matchvec);
1999 *status = CMD_COMPLETE_LIST_MATCH;
2000 return match_str;
2001}
2002
2003char **cmd_complete_command(vector vline, struct vty *vty, int *status)
2004{
2005 char **ret;
2006
2007 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2008 enum node_type onode;
2009 vector shifted_vline;
2010 unsigned int index;
2011
2012 onode = vty->node;
2013 vty->node = ENABLE_NODE;
2014 /* We can try it on enable node, cos' the vty is authenticated */
2015
2016 shifted_vline = vector_init(vector_count(vline));
2017 /* use memcpy? */
2018 for (index = 1; index < vector_active(vline); index++) {
2019 vector_set_index(shifted_vline, index - 1,
2020 vector_lookup(vline, index));
2021 }
2022
2023 ret = cmd_complete_command_real(shifted_vline, vty, status);
2024
2025 vector_free(shifted_vline);
2026 vty->node = onode;
2027 return ret;
2028 }
2029
2030 return cmd_complete_command_real(vline, vty, status);
2031}
2032
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002033static struct vty_parent_node *vty_parent(struct vty *vty)
2034{
2035 return llist_first_entry_or_null(&vty->parent_nodes,
2036 struct vty_parent_node,
2037 entry);
2038}
2039
2040static bool vty_pop_parent(struct vty *vty)
2041{
2042 struct vty_parent_node *parent = vty_parent(vty);
2043 if (!parent)
2044 return false;
2045 llist_del(&parent->entry);
2046 vty->node = parent->node;
2047 vty->priv = parent->priv;
2048 if (vty->indent)
2049 talloc_free(vty->indent);
2050 vty->indent = parent->indent;
2051 talloc_free(parent);
2052 return true;
2053}
2054
2055static void vty_clear_parents(struct vty *vty)
2056{
2057 while (vty_pop_parent(vty));
2058}
2059
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002060/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002061/*
2062 * This function MUST eventually converge on a node when called repeatedly,
2063 * there must not be any cycles.
2064 * All 'config' nodes shall converge on CONFIG_NODE.
2065 * All other 'enable' nodes shall converge on ENABLE_NODE.
2066 * All 'view' only nodes shall converge on VIEW_NODE.
2067 * All other nodes shall converge on themselves or it must be ensured,
2068 * that the user's rights are not extended anyhow by calling this function.
2069 *
2070 * Note that these requirements also apply to all functions that are used
2071 * as go_parent_cb.
2072 * Note also that this function relies on the is_config_child callback to
2073 * recognize non-config nodes if go_parent_cb is not set.
2074 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00002075int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002076{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002077 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002078 case AUTH_NODE:
2079 case VIEW_NODE:
2080 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002081 case CONFIG_NODE:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002082 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002083 break;
2084
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002085 case AUTH_ENABLE_NODE:
2086 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002087 vty_clear_parents(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002088 break;
2089
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002090 case CFG_LOG_NODE:
2091 case VTY_NODE:
2092 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002093 vty_clear_parents(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002094 break;
2095
2096 default:
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002097 if (host.app_info->go_parent_cb) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002098 host.app_info->go_parent_cb(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002099 vty_pop_parent(vty);
2100 }
2101 else if (is_config_child(vty)) {
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002102 vty->node = CONFIG_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002103 vty_clear_parents(vty);
2104 }
2105 else {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002106 vty->node = VIEW_NODE;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002107 vty_clear_parents(vty);
2108 }
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002109 break;
2110 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002111
2112 return vty->node;
2113}
2114
2115/* Execute command by argument vline vector. */
2116static int
2117cmd_execute_command_real(vector vline, struct vty *vty,
2118 struct cmd_element **cmd)
2119{
2120 unsigned int i;
2121 unsigned int index;
2122 vector cmd_vector;
2123 struct cmd_element *cmd_element;
2124 struct cmd_element *matched_element;
2125 unsigned int matched_count, incomplete_count;
2126 int argc;
2127 const char *argv[CMD_ARGC_MAX];
2128 enum match_type match = 0;
2129 int varflag;
2130 char *command;
2131
2132 /* Make copy of command elements. */
2133 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2134
2135 for (index = 0; index < vector_active(vline); index++)
2136 if ((command = vector_slot(vline, index))) {
2137 int ret;
2138
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002139 match = cmd_filter(command, cmd_vector, index,
2140 any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002141
2142 if (match == vararg_match)
2143 break;
2144
2145 ret =
2146 is_cmd_ambiguous(command, cmd_vector, index, match);
2147
2148 if (ret == 1) {
2149 vector_free(cmd_vector);
2150 return CMD_ERR_AMBIGUOUS;
2151 } else if (ret == 2) {
2152 vector_free(cmd_vector);
2153 return CMD_ERR_NO_MATCH;
2154 }
2155 }
2156
2157 /* Check matched count. */
2158 matched_element = NULL;
2159 matched_count = 0;
2160 incomplete_count = 0;
2161
2162 for (i = 0; i < vector_active(cmd_vector); i++)
2163 if ((cmd_element = vector_slot(cmd_vector, i))) {
2164 if (match == vararg_match
2165 || index >= cmd_element->cmdsize) {
2166 matched_element = cmd_element;
2167#if 0
2168 printf("DEBUG: %s\n", cmd_element->string);
2169#endif
2170 matched_count++;
2171 } else {
2172 incomplete_count++;
2173 }
2174 }
2175
2176 /* Finish of using cmd_vector. */
2177 vector_free(cmd_vector);
2178
2179 /* To execute command, matched_count must be 1. */
2180 if (matched_count == 0) {
2181 if (incomplete_count)
2182 return CMD_ERR_INCOMPLETE;
2183 else
2184 return CMD_ERR_NO_MATCH;
2185 }
2186
2187 if (matched_count > 1)
2188 return CMD_ERR_AMBIGUOUS;
2189
2190 /* Argument treatment */
2191 varflag = 0;
2192 argc = 0;
2193
2194 for (i = 0; i < vector_active(vline); i++) {
2195 if (varflag)
2196 argv[argc++] = vector_slot(vline, i);
2197 else {
2198 vector descvec =
2199 vector_slot(matched_element->strvec, i);
2200
2201 if (vector_active(descvec) == 1) {
2202 struct desc *desc = vector_slot(descvec, 0);
2203
2204 if (CMD_VARARG(desc->cmd))
2205 varflag = 1;
2206
2207 if (varflag || CMD_VARIABLE(desc->cmd)
2208 || CMD_OPTION(desc->cmd))
2209 argv[argc++] = vector_slot(vline, i);
2210 } else
2211 argv[argc++] = vector_slot(vline, i);
2212 }
2213
2214 if (argc >= CMD_ARGC_MAX)
2215 return CMD_ERR_EXEED_ARGC_MAX;
2216 }
2217
2218 /* For vtysh execution. */
2219 if (cmd)
2220 *cmd = matched_element;
2221
2222 if (matched_element->daemon)
2223 return CMD_SUCCESS_DAEMON;
2224
2225 /* Execute matched command. */
2226 return (*matched_element->func) (matched_element, vty, argc, argv);
2227}
2228
2229int
2230cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2231 int vtysh)
2232{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002233 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002234 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002235
2236 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002237
2238 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2239 vector shifted_vline;
2240 unsigned int index;
2241
2242 vty->node = ENABLE_NODE;
2243 /* We can try it on enable node, cos' the vty is authenticated */
2244
2245 shifted_vline = vector_init(vector_count(vline));
2246 /* use memcpy? */
2247 for (index = 1; index < vector_active(vline); index++) {
2248 vector_set_index(shifted_vline, index - 1,
2249 vector_lookup(vline, index));
2250 }
2251
2252 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2253
2254 vector_free(shifted_vline);
2255 vty->node = onode;
2256 return ret;
2257 }
2258
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002259 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002260}
2261
2262/* Execute command by argument readline. */
2263int
2264cmd_execute_command_strict(vector vline, struct vty *vty,
2265 struct cmd_element **cmd)
2266{
2267 unsigned int i;
2268 unsigned int index;
2269 vector cmd_vector;
2270 struct cmd_element *cmd_element;
2271 struct cmd_element *matched_element;
2272 unsigned int matched_count, incomplete_count;
2273 int argc;
2274 const char *argv[CMD_ARGC_MAX];
2275 int varflag;
2276 enum match_type match = 0;
2277 char *command;
2278
2279 /* Make copy of command element */
2280 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2281
2282 for (index = 0; index < vector_active(vline); index++)
2283 if ((command = vector_slot(vline, index))) {
2284 int ret;
2285
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002286 match = cmd_filter(vector_slot(vline, index),
2287 cmd_vector, index, exact_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002288
2289 /* If command meets '.VARARG' then finish matching. */
2290 if (match == vararg_match)
2291 break;
2292
2293 ret =
2294 is_cmd_ambiguous(command, cmd_vector, index, match);
2295 if (ret == 1) {
2296 vector_free(cmd_vector);
2297 return CMD_ERR_AMBIGUOUS;
2298 }
2299 if (ret == 2) {
2300 vector_free(cmd_vector);
2301 return CMD_ERR_NO_MATCH;
2302 }
2303 }
2304
2305 /* Check matched count. */
2306 matched_element = NULL;
2307 matched_count = 0;
2308 incomplete_count = 0;
2309 for (i = 0; i < vector_active(cmd_vector); i++)
2310 if (vector_slot(cmd_vector, i) != NULL) {
2311 cmd_element = vector_slot(cmd_vector, i);
2312
2313 if (match == vararg_match
2314 || index >= cmd_element->cmdsize) {
2315 matched_element = cmd_element;
2316 matched_count++;
2317 } else
2318 incomplete_count++;
2319 }
2320
2321 /* Finish of using cmd_vector. */
2322 vector_free(cmd_vector);
2323
2324 /* To execute command, matched_count must be 1. */
2325 if (matched_count == 0) {
2326 if (incomplete_count)
2327 return CMD_ERR_INCOMPLETE;
2328 else
2329 return CMD_ERR_NO_MATCH;
2330 }
2331
2332 if (matched_count > 1)
2333 return CMD_ERR_AMBIGUOUS;
2334
2335 /* Argument treatment */
2336 varflag = 0;
2337 argc = 0;
2338
2339 for (i = 0; i < vector_active(vline); i++) {
2340 if (varflag)
2341 argv[argc++] = vector_slot(vline, i);
2342 else {
2343 vector descvec =
2344 vector_slot(matched_element->strvec, i);
2345
2346 if (vector_active(descvec) == 1) {
2347 struct desc *desc = vector_slot(descvec, 0);
2348
2349 if (CMD_VARARG(desc->cmd))
2350 varflag = 1;
2351
2352 if (varflag || CMD_VARIABLE(desc->cmd)
2353 || CMD_OPTION(desc->cmd))
2354 argv[argc++] = vector_slot(vline, i);
2355 } else
2356 argv[argc++] = vector_slot(vline, i);
2357 }
2358
2359 if (argc >= CMD_ARGC_MAX)
2360 return CMD_ERR_EXEED_ARGC_MAX;
2361 }
2362
2363 /* For vtysh execution. */
2364 if (cmd)
2365 *cmd = matched_element;
2366
2367 if (matched_element->daemon)
2368 return CMD_SUCCESS_DAEMON;
2369
2370 /* Now execute matched command */
2371 return (*matched_element->func) (matched_element, vty, argc, argv);
2372}
2373
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002374static inline size_t len(const char *str)
2375{
2376 return str? strlen(str) : 0;
2377}
2378
Neels Hofmeyr00b5ed32017-09-20 00:46:03 +02002379/*! Make sure the common length of strings a and b is identical, then compare their lengths. I.e., if a
2380 * is longer than b, a must start with exactly b, and vice versa.
2381 * \returns EINVAL on mismatch, -1 for a < b, 0 for a == b, 1 for a > b.
2382 */
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002383static int indent_cmp(const char *a, const char *b)
2384{
2385 size_t al, bl;
2386 al = len(a);
2387 bl = len(b);
2388 if (al > bl) {
2389 if (bl && strncmp(a, b, bl) != 0)
2390 return EINVAL;
2391 return 1;
2392 }
2393 /* al <= bl */
2394 if (al && strncmp(a, b, al) != 0)
2395 return EINVAL;
2396 return (al < bl)? -1 : 0;
2397}
2398
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002399/* Configration make from file. */
2400int config_from_file(struct vty *vty, FILE * fp)
2401{
2402 int ret;
2403 vector vline;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002404 char *indent;
2405 int cmp;
2406 struct vty_parent_node this_node;
2407 struct vty_parent_node *parent;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002408
2409 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002410 indent = NULL;
2411 vline = NULL;
2412 ret = cmd_make_strvec2(vty->buf, &indent, &vline);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002413
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002414 if (ret != CMD_SUCCESS)
2415 goto return_invalid_indent;
2416
2417 /* In case of comment or empty line */
2418 if (vline == NULL) {
2419 if (indent) {
2420 talloc_free(indent);
2421 indent = NULL;
2422 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002423 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002424 }
2425
Neels Hofmeyr43063632017-09-19 23:54:01 +02002426 /* We have a nonempty line. */
2427 if (!vty->indent) {
2428 /* We have just entered a node and expecting the first child to come up; but we
2429 * may also skip right back to a parent or ancestor level. */
2430 parent = vty_parent(vty);
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002431
Neels Hofmeyr43063632017-09-19 23:54:01 +02002432 /* If there is no parent, record any indentation we encounter. */
2433 cmp = parent ? indent_cmp(indent, parent->indent) : 1;
2434
2435 if (cmp == EINVAL)
2436 goto return_invalid_indent;
2437
2438 if (cmp <= 0) {
2439 /* We have gone right back to the parent level or higher, we are skipping
2440 * this child node level entirely. Pop the parent to go back to a node
2441 * that was actually there (to reinstate vty->indent) and re-use below
2442 * go-parent while-loop to find an accurate match of indent in the node
2443 * ancestry. */
2444 vty_go_parent(vty);
2445 } else {
2446 /* The indent is deeper than the just entered parent, record the new
2447 * indentation characters. */
2448 vty->indent = talloc_strdup(vty, indent);
2449 /* This *is* the new indentation. */
2450 cmp = 0;
2451 }
2452 } else {
2453 /* There is a known indentation for this node level, validate and detect node
2454 * exits. */
2455 cmp = indent_cmp(indent, vty->indent);
2456 if (cmp == EINVAL)
2457 goto return_invalid_indent;
2458 }
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002459
2460 /* Less indent: go up the parent nodes to find matching amount of less indent. When this
2461 * loop exits, we want to have found an exact match, i.e. cmp == 0. */
2462 while (cmp < 0) {
2463 vty_go_parent(vty);
2464 cmp = indent_cmp(indent, vty->indent);
2465 if (cmp == EINVAL)
2466 goto return_invalid_indent;
2467 }
2468
2469 /* More indent without having entered a child node level? Either the parent node's indent
2470 * wasn't hit exactly (e.g. there's a space more than the parent level had further above)
2471 * or the indentation increased even though the vty command didn't enter a child. */
2472 if (cmp > 0)
2473 goto return_invalid_indent;
2474
2475 /* Remember the current node before the command possibly changes it. */
2476 this_node = (struct vty_parent_node){
2477 .node = vty->node,
2478 .priv = vty->priv,
2479 .indent = vty->indent,
2480 };
2481
2482 parent = vty_parent(vty);
2483 ret = cmd_execute_command_strict(vline, vty, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002484 cmd_free_strvec(vline);
2485
2486 if (ret != CMD_SUCCESS && ret != CMD_WARNING
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002487 && ret != CMD_ERR_NOTHING_TODO) {
2488 if (indent) {
2489 talloc_free(indent);
2490 indent = NULL;
2491 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002492 return ret;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002493 }
2494
2495 /* If we have stepped down into a child node, push a parent frame.
2496 * The causality is such: we don't expect every single node entry implementation to push
2497 * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
2498 * a parent node. Hence if the node changed without the parent node changing, we must
2499 * have stepped into a child node (and now expect a deeper indent). */
2500 if (vty->node != this_node.node && parent == vty_parent(vty)) {
2501 /* Push the parent node. */
2502 parent = talloc_zero(vty, struct vty_parent_node);
2503 *parent = this_node;
2504 llist_add(&parent->entry, &vty->parent_nodes);
2505
2506 /* The current talloc'ed vty->indent string will now be owned by this parent
2507 * struct. Indicate that we don't know what deeper indent characters the user
2508 * will choose. */
2509 vty->indent = NULL;
2510 }
2511
2512 if (indent) {
2513 talloc_free(indent);
2514 indent = NULL;
2515 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002516 }
2517 return CMD_SUCCESS;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02002518
2519return_invalid_indent:
2520 if (vline)
2521 cmd_free_strvec(vline);
2522 if (indent) {
2523 talloc_free(indent);
2524 indent = NULL;
2525 }
2526 return CMD_ERR_INVALID_INDENT;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002527}
2528
2529/* Configration from terminal */
2530DEFUN(config_terminal,
2531 config_terminal_cmd,
2532 "configure terminal",
2533 "Configuration from vty interface\n" "Configuration terminal\n")
2534{
2535 if (vty_config_lock(vty))
2536 vty->node = CONFIG_NODE;
2537 else {
2538 vty_out(vty, "VTY configuration is locked by other VTY%s",
2539 VTY_NEWLINE);
2540 return CMD_WARNING;
2541 }
2542 return CMD_SUCCESS;
2543}
2544
2545/* Enable command */
2546DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2547{
2548 /* If enable password is NULL, change to ENABLE_NODE */
2549 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2550 vty->type == VTY_SHELL_SERV)
2551 vty->node = ENABLE_NODE;
2552 else
2553 vty->node = AUTH_ENABLE_NODE;
2554
2555 return CMD_SUCCESS;
2556}
2557
2558/* Disable command */
2559DEFUN(disable,
2560 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2561{
2562 if (vty->node == ENABLE_NODE)
2563 vty->node = VIEW_NODE;
2564 return CMD_SUCCESS;
2565}
2566
2567/* Down vty node level. */
2568gDEFUN(config_exit,
2569 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2570{
2571 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002572 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002573 case VIEW_NODE:
2574 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002575 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002576 break;
2577 case CONFIG_NODE:
2578 vty->node = ENABLE_NODE;
2579 vty_config_unlock(vty);
2580 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002581 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002582 if (vty->node > CONFIG_NODE)
2583 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002584 break;
2585 }
2586 return CMD_SUCCESS;
2587}
2588
2589/* End of configuration. */
2590 gDEFUN(config_end,
2591 config_end_cmd, "end", "End current mode and change to enable mode.")
2592{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002593 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002594 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002595
2596 /* Repeatedly call go_parent until a top node is reached. */
2597 while (vty->node > CONFIG_NODE) {
2598 if (vty->node == last_node) {
2599 /* Ensure termination, this shouldn't happen. */
2600 break;
2601 }
2602 last_node = vty->node;
2603 vty_go_parent(vty);
2604 }
2605
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002606 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002607 if (vty->node > ENABLE_NODE)
2608 vty->node = ENABLE_NODE;
2609 vty->index = NULL;
2610 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002611 }
2612 return CMD_SUCCESS;
2613}
2614
2615/* Show version. */
2616DEFUN(show_version,
2617 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2618{
Harald Welte237f6242010-05-25 23:00:45 +02002619 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2620 host.app_info->version,
2621 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2622 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002623
2624 return CMD_SUCCESS;
2625}
2626
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002627DEFUN(show_online_help,
2628 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2629{
2630 vty_dump_nodes(vty);
2631 return CMD_SUCCESS;
2632}
2633
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002634/* Help display function for all node. */
2635gDEFUN(config_help,
2636 config_help_cmd, "help", "Description of the interactive help system\n")
2637{
2638 vty_out(vty,
2639 "This VTY provides advanced help features. When you need help,%s\
2640anytime at the command line please press '?'.%s\
2641%s\
2642If nothing matches, the help list will be empty and you must backup%s\
2643 until entering a '?' shows the available options.%s\
2644Two styles of help are provided:%s\
26451. Full help is available when you are ready to enter a%s\
2646command argument (e.g. 'show ?') and describes each possible%s\
2647argument.%s\
26482. Partial help is provided when an abbreviated argument is entered%s\
2649 and you want to know what arguments match the input%s\
2650 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2651 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2652 return CMD_SUCCESS;
2653}
2654
2655/* Help display function for all node. */
2656gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2657{
2658 unsigned int i;
2659 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2660 struct cmd_element *cmd;
2661
2662 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2663 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
2664 && !(cmd->attr == CMD_ATTR_DEPRECATED
2665 || cmd->attr == CMD_ATTR_HIDDEN))
2666 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2667 return CMD_SUCCESS;
2668}
2669
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002670static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002671{
2672 unsigned int i;
2673 int fd;
2674 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002675 char *config_file_tmp = NULL;
2676 char *config_file_sav = NULL;
2677 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002678 struct stat st;
2679
2680 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002681
2682 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002683 config_file_sav =
2684 _talloc_zero(tall_vty_cmd_ctx,
2685 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2686 "config_file_sav");
2687 strcpy(config_file_sav, config_file);
2688 strcat(config_file_sav, CONF_BACKUP_EXT);
2689
2690 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
2691 "config_file_tmp");
2692 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2693
2694 /* Open file to configuration write. */
2695 fd = mkstemp(config_file_tmp);
2696 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002697 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002698 talloc_free(config_file_tmp);
2699 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002700 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002701 }
2702
2703 /* Make vty for configuration file. */
2704 file_vty = vty_new();
2705 file_vty->fd = fd;
2706 file_vty->type = VTY_FILE;
2707
2708 /* Config file header print. */
2709 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002710 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002711 //vty_time_print (file_vty, 1);
2712 vty_out(file_vty, "!\n");
2713
2714 for (i = 0; i < vector_active(cmdvec); i++)
2715 if ((node = vector_slot(cmdvec, i)) && node->func) {
2716 if ((*node->func) (file_vty))
2717 vty_out(file_vty, "!\n");
2718 }
2719 vty_close(file_vty);
2720
2721 if (unlink(config_file_sav) != 0)
2722 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002723 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002724 talloc_free(config_file_sav);
2725 talloc_free(config_file_tmp);
2726 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002727 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002728 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002729
2730 /* Only link the .sav file if the original file exists */
2731 if (stat(config_file, &st) == 0) {
2732 if (link(config_file, config_file_sav) != 0) {
2733 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2734 talloc_free(config_file_sav);
2735 talloc_free(config_file_tmp);
2736 unlink(config_file_tmp);
2737 return -3;
2738 }
2739 sync();
2740 if (unlink(config_file) != 0) {
2741 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2742 talloc_free(config_file_sav);
2743 talloc_free(config_file_tmp);
2744 unlink(config_file_tmp);
2745 return -4;
2746 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002747 }
2748 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002749 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002750 talloc_free(config_file_sav);
2751 talloc_free(config_file_tmp);
2752 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002753 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002754 }
2755 unlink(config_file_tmp);
2756 sync();
2757
2758 talloc_free(config_file_sav);
2759 talloc_free(config_file_tmp);
2760
2761 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002762 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2763 return -6;
2764 }
2765
2766 return 0;
2767}
2768
2769
2770/* Write current configuration into file. */
2771DEFUN(config_write_file,
2772 config_write_file_cmd,
2773 "write file",
2774 "Write running configuration to memory, network, or terminal\n"
2775 "Write to configuration file\n")
2776{
2777 char *failed_file;
2778 int rc;
2779
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002780 if (host.app_info->config_is_consistent) {
2781 rc = host.app_info->config_is_consistent(vty);
2782 if (!rc) {
2783 vty_out(vty, "Configuration is not consistent%s",
2784 VTY_NEWLINE);
2785 return CMD_WARNING;
2786 }
2787 }
2788
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002789 if (host.config == NULL) {
2790 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2791 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002792 return CMD_WARNING;
2793 }
2794
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002795 rc = write_config_file(host.config, &failed_file);
2796 switch (rc) {
2797 case -1:
2798 vty_out(vty, "Can't open configuration file %s.%s",
2799 failed_file, VTY_NEWLINE);
2800 rc = CMD_WARNING;
2801 break;
2802 case -2:
2803 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2804 failed_file, VTY_NEWLINE);
2805 rc = CMD_WARNING;
2806 break;
2807 case -3:
2808 vty_out(vty, "Can't backup old configuration file %s.%s",
2809 failed_file, VTY_NEWLINE);
2810 rc = CMD_WARNING;
2811 break;
2812 case -4:
2813 vty_out(vty, "Can't unlink configuration file %s.%s",
2814 failed_file, VTY_NEWLINE);
2815 rc = CMD_WARNING;
2816 break;
2817 case -5:
2818 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2819 VTY_NEWLINE);
2820 rc = CMD_WARNING;
2821 break;
2822 case -6:
2823 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2824 failed_file, strerror(errno), errno, VTY_NEWLINE);
2825 rc = CMD_WARNING;
2826 break;
2827 default:
2828 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2829 rc = CMD_SUCCESS;
2830 break;
2831 }
2832
2833 talloc_free(failed_file);
2834 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002835}
2836
2837ALIAS(config_write_file,
2838 config_write_cmd,
2839 "write", "Write running configuration to memory, network, or terminal\n")
2840
2841 ALIAS(config_write_file,
2842 config_write_memory_cmd,
2843 "write memory",
2844 "Write running configuration to memory, network, or terminal\n"
2845 "Write configuration to the file (same as write file)\n")
2846
2847 ALIAS(config_write_file,
2848 copy_runningconfig_startupconfig_cmd,
2849 "copy running-config startup-config",
2850 "Copy configuration\n"
2851 "Copy running config to... \n"
2852 "Copy running config to startup config (same as write file)\n")
2853
2854/* Write current configuration into the terminal. */
2855 DEFUN(config_write_terminal,
2856 config_write_terminal_cmd,
2857 "write terminal",
2858 "Write running configuration to memory, network, or terminal\n"
2859 "Write to terminal\n")
2860{
2861 unsigned int i;
2862 struct cmd_node *node;
2863
2864 if (vty->type == VTY_SHELL_SERV) {
2865 for (i = 0; i < vector_active(cmdvec); i++)
2866 if ((node = vector_slot(cmdvec, i)) && node->func
2867 && node->vtysh) {
2868 if ((*node->func) (vty))
2869 vty_out(vty, "!%s", VTY_NEWLINE);
2870 }
2871 } else {
2872 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2873 VTY_NEWLINE);
2874 vty_out(vty, "!%s", VTY_NEWLINE);
2875
2876 for (i = 0; i < vector_active(cmdvec); i++)
2877 if ((node = vector_slot(cmdvec, i)) && node->func) {
2878 if ((*node->func) (vty))
2879 vty_out(vty, "!%s", VTY_NEWLINE);
2880 }
2881 vty_out(vty, "end%s", VTY_NEWLINE);
2882 }
2883 return CMD_SUCCESS;
2884}
2885
2886/* Write current configuration into the terminal. */
2887ALIAS(config_write_terminal,
2888 show_running_config_cmd,
2889 "show running-config", SHOW_STR "running configuration\n")
2890
2891/* Write startup configuration into the terminal. */
2892 DEFUN(show_startup_config,
2893 show_startup_config_cmd,
2894 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2895{
2896 char buf[BUFSIZ];
2897 FILE *confp;
2898
2899 confp = fopen(host.config, "r");
2900 if (confp == NULL) {
2901 vty_out(vty, "Can't open configuration file [%s]%s",
2902 host.config, VTY_NEWLINE);
2903 return CMD_WARNING;
2904 }
2905
2906 while (fgets(buf, BUFSIZ, confp)) {
2907 char *cp = buf;
2908
2909 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
2910 cp++;
2911 *cp = '\0';
2912
2913 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
2914 }
2915
2916 fclose(confp);
2917
2918 return CMD_SUCCESS;
2919}
2920
2921/* Hostname configuration */
2922DEFUN(config_hostname,
2923 hostname_cmd,
2924 "hostname WORD",
2925 "Set system's network name\n" "This system's network name\n")
2926{
2927 if (!isalpha((int)*argv[0])) {
2928 vty_out(vty, "Please specify string starting with alphabet%s",
2929 VTY_NEWLINE);
2930 return CMD_WARNING;
2931 }
2932
2933 if (host.name)
2934 talloc_free(host.name);
2935
2936 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2937 return CMD_SUCCESS;
2938}
2939
2940DEFUN(config_no_hostname,
2941 no_hostname_cmd,
2942 "no hostname [HOSTNAME]",
2943 NO_STR "Reset system's network name\n" "Host name of this router\n")
2944{
2945 if (host.name)
2946 talloc_free(host.name);
2947 host.name = NULL;
2948 return CMD_SUCCESS;
2949}
2950
2951/* VTY interface password set. */
2952DEFUN(config_password, password_cmd,
2953 "password (8|) WORD",
2954 "Assign the terminal connection password\n"
2955 "Specifies a HIDDEN password will follow\n"
2956 "dummy string \n" "The HIDDEN line password string\n")
2957{
2958 /* Argument check. */
2959 if (argc == 0) {
2960 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
2961 return CMD_WARNING;
2962 }
2963
2964 if (argc == 2) {
2965 if (*argv[0] == '8') {
2966 if (host.password)
2967 talloc_free(host.password);
2968 host.password = NULL;
2969 if (host.password_encrypt)
2970 talloc_free(host.password_encrypt);
2971 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
2972 return CMD_SUCCESS;
2973 } else {
2974 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
2975 return CMD_WARNING;
2976 }
2977 }
2978
2979 if (!isalnum((int)*argv[0])) {
2980 vty_out(vty,
2981 "Please specify string starting with alphanumeric%s",
2982 VTY_NEWLINE);
2983 return CMD_WARNING;
2984 }
2985
2986 if (host.password)
2987 talloc_free(host.password);
2988 host.password = NULL;
2989
2990#ifdef VTY_CRYPT_PW
2991 if (host.encrypt) {
2992 if (host.password_encrypt)
2993 talloc_free(host.password_encrypt);
2994 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
2995 } else
2996#endif
2997 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2998
2999 return CMD_SUCCESS;
3000}
3001
3002ALIAS(config_password, password_text_cmd,
3003 "password LINE",
3004 "Assign the terminal connection password\n"
3005 "The UNENCRYPTED (cleartext) line password\n")
3006
3007/* VTY enable password set. */
3008 DEFUN(config_enable_password, enable_password_cmd,
3009 "enable password (8|) WORD",
3010 "Modify enable password parameters\n"
3011 "Assign the privileged level password\n"
3012 "Specifies a HIDDEN password will follow\n"
3013 "dummy string \n" "The HIDDEN 'enable' password string\n")
3014{
3015 /* Argument check. */
3016 if (argc == 0) {
3017 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
3018 return CMD_WARNING;
3019 }
3020
3021 /* Crypt type is specified. */
3022 if (argc == 2) {
3023 if (*argv[0] == '8') {
3024 if (host.enable)
3025 talloc_free(host.enable);
3026 host.enable = NULL;
3027
3028 if (host.enable_encrypt)
3029 talloc_free(host.enable_encrypt);
3030 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
3031
3032 return CMD_SUCCESS;
3033 } else {
3034 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
3035 return CMD_WARNING;
3036 }
3037 }
3038
3039 if (!isalnum((int)*argv[0])) {
3040 vty_out(vty,
3041 "Please specify string starting with alphanumeric%s",
3042 VTY_NEWLINE);
3043 return CMD_WARNING;
3044 }
3045
3046 if (host.enable)
3047 talloc_free(host.enable);
3048 host.enable = NULL;
3049
3050 /* Plain password input. */
3051#ifdef VTY_CRYPT_PW
3052 if (host.encrypt) {
3053 if (host.enable_encrypt)
3054 talloc_free(host.enable_encrypt);
3055 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
3056 } else
3057#endif
3058 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3059
3060 return CMD_SUCCESS;
3061}
3062
3063ALIAS(config_enable_password,
3064 enable_password_text_cmd,
3065 "enable password LINE",
3066 "Modify enable password parameters\n"
3067 "Assign the privileged level password\n"
3068 "The UNENCRYPTED (cleartext) 'enable' password\n")
3069
3070/* VTY enable password delete. */
3071 DEFUN(no_config_enable_password, no_enable_password_cmd,
3072 "no enable password",
3073 NO_STR
3074 "Modify enable password parameters\n"
3075 "Assign the privileged level password\n")
3076{
3077 if (host.enable)
3078 talloc_free(host.enable);
3079 host.enable = NULL;
3080
3081 if (host.enable_encrypt)
3082 talloc_free(host.enable_encrypt);
3083 host.enable_encrypt = NULL;
3084
3085 return CMD_SUCCESS;
3086}
3087
3088#ifdef VTY_CRYPT_PW
3089DEFUN(service_password_encrypt,
3090 service_password_encrypt_cmd,
3091 "service password-encryption",
3092 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3093{
3094 if (host.encrypt)
3095 return CMD_SUCCESS;
3096
3097 host.encrypt = 1;
3098
3099 if (host.password) {
3100 if (host.password_encrypt)
3101 talloc_free(host.password_encrypt);
3102 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
3103 }
3104 if (host.enable) {
3105 if (host.enable_encrypt)
3106 talloc_free(host.enable_encrypt);
3107 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
3108 }
3109
3110 return CMD_SUCCESS;
3111}
3112
3113DEFUN(no_service_password_encrypt,
3114 no_service_password_encrypt_cmd,
3115 "no service password-encryption",
3116 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
3117{
3118 if (!host.encrypt)
3119 return CMD_SUCCESS;
3120
3121 host.encrypt = 0;
3122
3123 if (host.password_encrypt)
3124 talloc_free(host.password_encrypt);
3125 host.password_encrypt = NULL;
3126
3127 if (host.enable_encrypt)
3128 talloc_free(host.enable_encrypt);
3129 host.enable_encrypt = NULL;
3130
3131 return CMD_SUCCESS;
3132}
3133#endif
3134
3135DEFUN(config_terminal_length, config_terminal_length_cmd,
3136 "terminal length <0-512>",
3137 "Set terminal line parameters\n"
3138 "Set number of lines on a screen\n"
3139 "Number of lines on screen (0 for no pausing)\n")
3140{
3141 int lines;
3142 char *endptr = NULL;
3143
3144 lines = strtol(argv[0], &endptr, 10);
3145 if (lines < 0 || lines > 512 || *endptr != '\0') {
3146 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3147 return CMD_WARNING;
3148 }
3149 vty->lines = lines;
3150
3151 return CMD_SUCCESS;
3152}
3153
3154DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
3155 "terminal no length",
3156 "Set terminal line parameters\n"
3157 NO_STR "Set number of lines on a screen\n")
3158{
3159 vty->lines = -1;
3160 return CMD_SUCCESS;
3161}
3162
3163DEFUN(service_terminal_length, service_terminal_length_cmd,
3164 "service terminal-length <0-512>",
3165 "Set up miscellaneous service\n"
3166 "System wide terminal length configuration\n"
3167 "Number of lines of VTY (0 means no line control)\n")
3168{
3169 int lines;
3170 char *endptr = NULL;
3171
3172 lines = strtol(argv[0], &endptr, 10);
3173 if (lines < 0 || lines > 512 || *endptr != '\0') {
3174 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
3175 return CMD_WARNING;
3176 }
3177 host.lines = lines;
3178
3179 return CMD_SUCCESS;
3180}
3181
3182DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
3183 "no service terminal-length [<0-512>]",
3184 NO_STR
3185 "Set up miscellaneous service\n"
3186 "System wide terminal length configuration\n"
3187 "Number of lines of VTY (0 means no line control)\n")
3188{
3189 host.lines = -1;
3190 return CMD_SUCCESS;
3191}
3192
3193DEFUN_HIDDEN(do_echo,
3194 echo_cmd,
3195 "echo .MESSAGE",
3196 "Echo a message back to the vty\n" "The message to echo\n")
3197{
3198 char *message;
3199
3200 vty_out(vty, "%s%s",
3201 ((message =
3202 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
3203 if (message)
3204 talloc_free(message);
3205 return CMD_SUCCESS;
3206}
3207
3208#if 0
3209DEFUN(config_logmsg,
3210 config_logmsg_cmd,
3211 "logmsg " LOG_LEVELS " .MESSAGE",
3212 "Send a message to enabled logging destinations\n"
3213 LOG_LEVEL_DESC "The message to send\n")
3214{
3215 int level;
3216 char *message;
3217
3218 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3219 return CMD_ERR_NO_MATCH;
3220
3221 zlog(NULL, level,
3222 ((message = argv_concat(argv, argc, 1)) ? message : ""));
3223 if (message)
3224 talloc_free(message);
3225 return CMD_SUCCESS;
3226}
3227
3228DEFUN(show_logging,
3229 show_logging_cmd,
3230 "show logging", SHOW_STR "Show current logging configuration\n")
3231{
3232 struct zlog *zl = zlog_default;
3233
3234 vty_out(vty, "Syslog logging: ");
3235 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3236 vty_out(vty, "disabled");
3237 else
3238 vty_out(vty, "level %s, facility %s, ident %s",
3239 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3240 facility_name(zl->facility), zl->ident);
3241 vty_out(vty, "%s", VTY_NEWLINE);
3242
3243 vty_out(vty, "Stdout logging: ");
3244 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3245 vty_out(vty, "disabled");
3246 else
3247 vty_out(vty, "level %s",
3248 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3249 vty_out(vty, "%s", VTY_NEWLINE);
3250
3251 vty_out(vty, "Monitor logging: ");
3252 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3253 vty_out(vty, "disabled");
3254 else
3255 vty_out(vty, "level %s",
3256 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3257 vty_out(vty, "%s", VTY_NEWLINE);
3258
3259 vty_out(vty, "File logging: ");
3260 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3261 vty_out(vty, "disabled");
3262 else
3263 vty_out(vty, "level %s, filename %s",
3264 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3265 zl->filename);
3266 vty_out(vty, "%s", VTY_NEWLINE);
3267
3268 vty_out(vty, "Protocol name: %s%s",
3269 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3270 vty_out(vty, "Record priority: %s%s",
3271 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3272
3273 return CMD_SUCCESS;
3274}
3275
3276DEFUN(config_log_stdout,
3277 config_log_stdout_cmd,
3278 "log stdout", "Logging control\n" "Set stdout logging level\n")
3279{
3280 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3281 return CMD_SUCCESS;
3282}
3283
3284DEFUN(config_log_stdout_level,
3285 config_log_stdout_level_cmd,
3286 "log stdout " LOG_LEVELS,
3287 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3288{
3289 int level;
3290
3291 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3292 return CMD_ERR_NO_MATCH;
3293 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3294 return CMD_SUCCESS;
3295}
3296
3297DEFUN(no_config_log_stdout,
3298 no_config_log_stdout_cmd,
3299 "no log stdout [LEVEL]",
3300 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3301{
3302 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3303 return CMD_SUCCESS;
3304}
3305
3306DEFUN(config_log_monitor,
3307 config_log_monitor_cmd,
3308 "log monitor",
3309 "Logging control\n" "Set terminal line (monitor) logging level\n")
3310{
3311 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3312 return CMD_SUCCESS;
3313}
3314
3315DEFUN(config_log_monitor_level,
3316 config_log_monitor_level_cmd,
3317 "log monitor " LOG_LEVELS,
3318 "Logging control\n"
3319 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3320{
3321 int level;
3322
3323 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3324 return CMD_ERR_NO_MATCH;
3325 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3326 return CMD_SUCCESS;
3327}
3328
3329DEFUN(no_config_log_monitor,
3330 no_config_log_monitor_cmd,
3331 "no log monitor [LEVEL]",
3332 NO_STR
3333 "Logging control\n"
3334 "Disable terminal line (monitor) logging\n" "Logging level\n")
3335{
3336 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3337 return CMD_SUCCESS;
3338}
3339
3340static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3341{
3342 int ret;
3343 char *p = NULL;
3344 const char *fullpath;
3345
3346 /* Path detection. */
3347 if (!IS_DIRECTORY_SEP(*fname)) {
3348 char cwd[MAXPATHLEN + 1];
3349 cwd[MAXPATHLEN] = '\0';
3350
3351 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3352 zlog_err("config_log_file: Unable to alloc mem!");
3353 return CMD_WARNING;
3354 }
3355
3356 if ((p = _talloc_zero(tall_vcmd_ctx,
3357 strlen(cwd) + strlen(fname) + 2),
3358 "set_log_file")
3359 == NULL) {
3360 zlog_err("config_log_file: Unable to alloc mem!");
3361 return CMD_WARNING;
3362 }
3363 sprintf(p, "%s/%s", cwd, fname);
3364 fullpath = p;
3365 } else
3366 fullpath = fname;
3367
3368 ret = zlog_set_file(NULL, fullpath, loglevel);
3369
3370 if (p)
3371 talloc_free(p);
3372
3373 if (!ret) {
3374 vty_out(vty, "can't open logfile %s\n", fname);
3375 return CMD_WARNING;
3376 }
3377
3378 if (host.logfile)
3379 talloc_free(host.logfile);
3380
3381 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3382
3383 return CMD_SUCCESS;
3384}
3385
3386DEFUN(config_log_file,
3387 config_log_file_cmd,
3388 "log file FILENAME",
3389 "Logging control\n" "Logging to file\n" "Logging filename\n")
3390{
3391 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3392}
3393
3394DEFUN(config_log_file_level,
3395 config_log_file_level_cmd,
3396 "log file FILENAME " LOG_LEVELS,
3397 "Logging control\n"
3398 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3399{
3400 int level;
3401
3402 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3403 return CMD_ERR_NO_MATCH;
3404 return set_log_file(vty, argv[0], level);
3405}
3406
3407DEFUN(no_config_log_file,
3408 no_config_log_file_cmd,
3409 "no log file [FILENAME]",
3410 NO_STR
3411 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3412{
3413 zlog_reset_file(NULL);
3414
3415 if (host.logfile)
3416 talloc_free(host.logfile);
3417
3418 host.logfile = NULL;
3419
3420 return CMD_SUCCESS;
3421}
3422
3423ALIAS(no_config_log_file,
3424 no_config_log_file_level_cmd,
3425 "no log file FILENAME LEVEL",
3426 NO_STR
3427 "Logging control\n"
3428 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3429
3430 DEFUN(config_log_syslog,
3431 config_log_syslog_cmd,
3432 "log syslog", "Logging control\n" "Set syslog logging level\n")
3433{
3434 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3435 return CMD_SUCCESS;
3436}
3437
3438DEFUN(config_log_syslog_level,
3439 config_log_syslog_level_cmd,
3440 "log syslog " LOG_LEVELS,
3441 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3442{
3443 int level;
3444
3445 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3446 return CMD_ERR_NO_MATCH;
3447 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3448 return CMD_SUCCESS;
3449}
3450
3451DEFUN_DEPRECATED(config_log_syslog_facility,
3452 config_log_syslog_facility_cmd,
3453 "log syslog facility " LOG_FACILITIES,
3454 "Logging control\n"
3455 "Logging goes to syslog\n"
3456 "(Deprecated) Facility parameter for syslog messages\n"
3457 LOG_FACILITY_DESC)
3458{
3459 int facility;
3460
3461 if ((facility = facility_match(argv[0])) < 0)
3462 return CMD_ERR_NO_MATCH;
3463
3464 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3465 zlog_default->facility = facility;
3466 return CMD_SUCCESS;
3467}
3468
3469DEFUN(no_config_log_syslog,
3470 no_config_log_syslog_cmd,
3471 "no log syslog [LEVEL]",
3472 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3473{
3474 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3475 return CMD_SUCCESS;
3476}
3477
3478ALIAS(no_config_log_syslog,
3479 no_config_log_syslog_facility_cmd,
3480 "no log syslog facility " LOG_FACILITIES,
3481 NO_STR
3482 "Logging control\n"
3483 "Logging goes to syslog\n"
3484 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3485
3486 DEFUN(config_log_facility,
3487 config_log_facility_cmd,
3488 "log facility " LOG_FACILITIES,
3489 "Logging control\n"
3490 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3491{
3492 int facility;
3493
3494 if ((facility = facility_match(argv[0])) < 0)
3495 return CMD_ERR_NO_MATCH;
3496 zlog_default->facility = facility;
3497 return CMD_SUCCESS;
3498}
3499
3500DEFUN(no_config_log_facility,
3501 no_config_log_facility_cmd,
3502 "no log facility [FACILITY]",
3503 NO_STR
3504 "Logging control\n"
3505 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3506{
3507 zlog_default->facility = LOG_DAEMON;
3508 return CMD_SUCCESS;
3509}
3510
3511DEFUN_DEPRECATED(config_log_trap,
3512 config_log_trap_cmd,
3513 "log trap " LOG_LEVELS,
3514 "Logging control\n"
3515 "(Deprecated) Set logging level and default for all destinations\n"
3516 LOG_LEVEL_DESC)
3517{
3518 int new_level;
3519 int i;
3520
3521 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3522 return CMD_ERR_NO_MATCH;
3523
3524 zlog_default->default_lvl = new_level;
3525 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3526 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3527 zlog_default->maxlvl[i] = new_level;
3528 return CMD_SUCCESS;
3529}
3530
3531DEFUN_DEPRECATED(no_config_log_trap,
3532 no_config_log_trap_cmd,
3533 "no log trap [LEVEL]",
3534 NO_STR
3535 "Logging control\n"
3536 "Permit all logging information\n" "Logging level\n")
3537{
3538 zlog_default->default_lvl = LOG_DEBUG;
3539 return CMD_SUCCESS;
3540}
3541
3542DEFUN(config_log_record_priority,
3543 config_log_record_priority_cmd,
3544 "log record-priority",
3545 "Logging control\n"
3546 "Log the priority of the message within the message\n")
3547{
3548 zlog_default->record_priority = 1;
3549 return CMD_SUCCESS;
3550}
3551
3552DEFUN(no_config_log_record_priority,
3553 no_config_log_record_priority_cmd,
3554 "no log record-priority",
3555 NO_STR
3556 "Logging control\n"
3557 "Do not log the priority of the message within the message\n")
3558{
3559 zlog_default->record_priority = 0;
3560 return CMD_SUCCESS;
3561}
3562#endif
3563
3564DEFUN(banner_motd_file,
3565 banner_motd_file_cmd,
3566 "banner motd file [FILE]",
3567 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3568{
3569 if (host.motdfile)
3570 talloc_free(host.motdfile);
3571 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3572
3573 return CMD_SUCCESS;
3574}
3575
3576DEFUN(banner_motd_default,
3577 banner_motd_default_cmd,
3578 "banner motd default",
3579 "Set banner string\n" "Strings for motd\n" "Default string\n")
3580{
3581 host.motd = default_motd;
3582 return CMD_SUCCESS;
3583}
3584
3585DEFUN(no_banner_motd,
3586 no_banner_motd_cmd,
3587 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3588{
3589 host.motd = NULL;
3590 if (host.motdfile)
3591 talloc_free(host.motdfile);
3592 host.motdfile = NULL;
3593 return CMD_SUCCESS;
3594}
3595
3596/* Set config filename. Called from vty.c */
3597void host_config_set(const char *filename)
3598{
3599 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3600}
3601
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003602void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003603{
3604 install_element(node, &config_help_cmd);
3605 install_element(node, &config_list_cmd);
3606
3607 install_element(node, &config_write_terminal_cmd);
3608 install_element(node, &config_write_file_cmd);
3609 install_element(node, &config_write_memory_cmd);
3610 install_element(node, &config_write_cmd);
3611 install_element(node, &show_running_config_cmd);
3612}
3613
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003614void vty_install_default(int node)
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003615{
3616 install_default(node);
3617
3618 install_element(node, &config_exit_cmd);
3619
3620 if (node >= CONFIG_NODE) {
3621 /* It's not a top node. */
3622 install_element(node, &config_end_cmd);
3623 }
3624}
3625
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003626/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003627 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003628 * \param[in] vty the vty of the code
3629 * \param[in] filename where to store the file
3630 * \return 0 in case of success.
3631 *
3632 * If the filename already exists create a filename.sav
3633 * version with the current code.
3634 *
3635 */
3636int osmo_vty_write_config_file(const char *filename)
3637{
3638 char *failed_file;
3639 int rc;
3640
3641 rc = write_config_file(filename, &failed_file);
3642 talloc_free(failed_file);
3643 return rc;
3644}
3645
3646/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003647 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003648 * \return 0 in case of success.
3649 *
3650 * If the filename already exists create a filename.sav
3651 * version with the current code.
3652 *
3653 */
3654int osmo_vty_save_config_file(void)
3655{
3656 char *failed_file;
3657 int rc;
3658
3659 if (host.config == NULL)
3660 return -7;
3661
3662 rc = write_config_file(host.config, &failed_file);
3663 talloc_free(failed_file);
3664 return rc;
3665}
3666
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003667/* Initialize command interface. Install basic nodes and commands. */
3668void cmd_init(int terminal)
3669{
3670 /* Allocate initial top vector of commands. */
3671 cmdvec = vector_init(VECTOR_MIN_SIZE);
3672
3673 /* Default host value settings. */
3674 host.name = NULL;
3675 host.password = NULL;
3676 host.enable = NULL;
3677 host.logfile = NULL;
3678 host.config = NULL;
3679 host.lines = -1;
3680 host.motd = default_motd;
3681 host.motdfile = NULL;
3682
3683 /* Install top nodes. */
3684 install_node(&view_node, NULL);
3685 install_node(&enable_node, NULL);
3686 install_node(&auth_node, NULL);
3687 install_node(&auth_enable_node, NULL);
3688 install_node(&config_node, config_write_host);
3689
3690 /* Each node's basic commands. */
3691 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003692 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003693 if (terminal) {
3694 install_element(VIEW_NODE, &config_list_cmd);
3695 install_element(VIEW_NODE, &config_exit_cmd);
3696 install_element(VIEW_NODE, &config_help_cmd);
3697 install_element(VIEW_NODE, &config_enable_cmd);
3698 install_element(VIEW_NODE, &config_terminal_length_cmd);
3699 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3700 install_element(VIEW_NODE, &echo_cmd);
3701 }
3702
3703 if (terminal) {
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003704 vty_install_default(ENABLE_NODE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003705 install_element(ENABLE_NODE, &config_disable_cmd);
3706 install_element(ENABLE_NODE, &config_terminal_cmd);
3707 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3708 }
3709 install_element (ENABLE_NODE, &show_startup_config_cmd);
3710 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003711 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003712
3713 if (terminal) {
3714 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3715 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3716 install_element(ENABLE_NODE, &echo_cmd);
3717
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003718 vty_install_default(CONFIG_NODE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003719 }
3720
3721 install_element(CONFIG_NODE, &hostname_cmd);
3722 install_element(CONFIG_NODE, &no_hostname_cmd);
3723
3724 if (terminal) {
3725 install_element(CONFIG_NODE, &password_cmd);
3726 install_element(CONFIG_NODE, &password_text_cmd);
3727 install_element(CONFIG_NODE, &enable_password_cmd);
3728 install_element(CONFIG_NODE, &enable_password_text_cmd);
3729 install_element(CONFIG_NODE, &no_enable_password_cmd);
3730
3731#ifdef VTY_CRYPT_PW
3732 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3733 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3734#endif
3735 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3736 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3737 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3738 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3739 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3740
3741 }
3742 srand(time(NULL));
3743}
Harald Welte7acb30c2011-08-17 17:13:48 +02003744
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003745/*! @} */