blob: 52c71913e4d2fd5251cec89b44a7825568501a40 [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 Hofmeyr87e45502017-06-20 00:17:59 +0200126/*! Install top node of command vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200127void install_node(struct cmd_node *node, int (*func) (struct vty *))
128{
129 vector_set_index(cmdvec, node->node, node);
130 node->func = func;
131 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
132}
133
134/* Compare two command's string. Used in sort_node (). */
135static int cmp_node(const void *p, const void *q)
136{
137 struct cmd_element *a = *(struct cmd_element **)p;
138 struct cmd_element *b = *(struct cmd_element **)q;
139
140 return strcmp(a->string, b->string);
141}
142
143static int cmp_desc(const void *p, const void *q)
144{
145 struct desc *a = *(struct desc **)p;
146 struct desc *b = *(struct desc **)q;
147
148 return strcmp(a->cmd, b->cmd);
149}
150
Jacob Erlbeck2442e092013-09-06 16:51:58 +0200151static int is_config_child(struct vty *vty)
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800152{
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800153 if (vty->node <= CONFIG_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800154 return 0;
Holger Hans Peter Freyther8304b1e2010-09-04 11:19:39 +0800155 else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800156 return 1;
157 else if (host.app_info->is_config_node)
Holger Hans Peter Freyther8f09f012010-08-25 17:34:56 +0800158 return host.app_info->is_config_node(vty, vty->node);
Holger Hans Peter Freyther3e85e8d2010-08-26 14:37:10 +0800159 else
160 return vty->node > CONFIG_NODE;
Holger Hans Peter Freyther50cfb782010-08-25 13:23:53 +0800161}
162
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200163/*! Sort each node's command element according to command string. */
Harald Welte95b2b472011-07-16 11:58:09 +0200164void sort_node(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200165{
166 unsigned int i, j;
167 struct cmd_node *cnode;
168 vector descvec;
169 struct cmd_element *cmd_element;
170
171 for (i = 0; i < vector_active(cmdvec); i++)
172 if ((cnode = vector_slot(cmdvec, i)) != NULL) {
173 vector cmd_vector = cnode->cmd_vector;
174 qsort(cmd_vector->index, vector_active(cmd_vector),
175 sizeof(void *), cmp_node);
176
177 for (j = 0; j < vector_active(cmd_vector); j++)
178 if ((cmd_element =
179 vector_slot(cmd_vector, j)) != NULL
180 && vector_active(cmd_element->strvec)) {
181 descvec =
182 vector_slot(cmd_element->strvec,
183 vector_active
184 (cmd_element->strvec) -
185 1);
186 qsort(descvec->index,
187 vector_active(descvec),
188 sizeof(void *), cmp_desc);
189 }
190 }
191}
192
Harald Welte7acb30c2011-08-17 17:13:48 +0200193/*! Breaking up string into each command piece. I assume given
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200194 character is separated by a space character. Return value is a
195 vector which includes char ** data element. */
196vector cmd_make_strvec(const char *string)
197{
198 const char *cp, *start;
199 char *token;
200 int strlen;
201 vector strvec;
202
203 if (string == NULL)
204 return NULL;
205
206 cp = string;
207
208 /* Skip white spaces. */
209 while (isspace((int)*cp) && *cp != '\0')
210 cp++;
211
212 /* Return if there is only white spaces */
213 if (*cp == '\0')
214 return NULL;
215
216 if (*cp == '!' || *cp == '#')
217 return NULL;
218
219 /* Prepare return vector. */
220 strvec = vector_init(VECTOR_MIN_SIZE);
221
222 /* Copy each command piece and set into vector. */
223 while (1) {
224 start = cp;
225 while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
226 *cp != '\0')
227 cp++;
228 strlen = cp - start;
229 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
230 memcpy(token, start, strlen);
231 *(token + strlen) = '\0';
232 vector_set(strvec, token);
233
234 while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
235 *cp != '\0')
236 cp++;
237
238 if (*cp == '\0')
239 return strvec;
240 }
241}
242
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200243/*! Free allocated string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200244void cmd_free_strvec(vector v)
245{
246 unsigned int i;
247 char *cp;
248
249 if (!v)
250 return;
251
252 for (i = 0; i < vector_active(v); i++)
253 if ((cp = vector_slot(v, i)) != NULL)
254 talloc_free(cp);
255
256 vector_free(v);
257}
258
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200259/*! Fetch next description. Used in \ref cmd_make_descvec(). */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200260static char *cmd_desc_str(const char **string)
261{
262 const char *cp, *start;
263 char *token;
264 int strlen;
265
266 cp = *string;
267
268 if (cp == NULL)
269 return NULL;
270
271 /* Skip white spaces. */
272 while (isspace((int)*cp) && *cp != '\0')
273 cp++;
274
275 /* Return if there is only white spaces */
276 if (*cp == '\0')
277 return NULL;
278
279 start = cp;
280
281 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
282 cp++;
283
284 strlen = cp - start;
285 token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
286 memcpy(token, start, strlen);
287 *(token + strlen) = '\0';
288
289 *string = cp;
290
291 return token;
292}
293
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200294/*! New string vector. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200295static vector cmd_make_descvec(const char *string, const char *descstr)
296{
297 int multiple = 0;
298 const char *sp;
299 char *token;
300 int len;
301 const char *cp;
302 const char *dp;
303 vector allvec;
304 vector strvec = NULL;
305 struct desc *desc;
306
307 cp = string;
308 dp = descstr;
309
310 if (cp == NULL)
311 return NULL;
312
313 allvec = vector_init(VECTOR_MIN_SIZE);
314
315 while (1) {
316 while (isspace((int)*cp) && *cp != '\0')
317 cp++;
318
319 if (*cp == '(') {
320 multiple = 1;
321 cp++;
322 }
323 if (*cp == ')') {
324 multiple = 0;
325 cp++;
326 }
327 if (*cp == '|') {
Harald Weltea99d45a2015-11-12 13:48:23 +0100328 OSMO_ASSERT(multiple);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200329 cp++;
330 }
331
332 while (isspace((int)*cp) && *cp != '\0')
333 cp++;
334
335 if (*cp == '(') {
336 multiple = 1;
337 cp++;
338 }
339
340 if (*cp == '\0')
341 return allvec;
342
343 sp = cp;
344
345 while (!
346 (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
347 || *cp == ')' || *cp == '|') && *cp != '\0')
348 cp++;
349
350 len = cp - sp;
351
352 token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
353 memcpy(token, sp, len);
354 *(token + len) = '\0';
355
356 desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
357 desc->cmd = token;
358 desc->str = cmd_desc_str(&dp);
359
360 if (multiple) {
361 if (multiple == 1) {
362 strvec = vector_init(VECTOR_MIN_SIZE);
363 vector_set(allvec, strvec);
364 }
365 multiple++;
366 } else {
367 strvec = vector_init(VECTOR_MIN_SIZE);
368 vector_set(allvec, strvec);
369 }
370 vector_set(strvec, desc);
371 }
372}
373
374/* Count mandantory string vector size. This is to determine inputed
375 command has enough command length. */
376static int cmd_cmdsize(vector strvec)
377{
378 unsigned int i;
379 int size = 0;
380 vector descvec;
381 struct desc *desc;
382
383 for (i = 0; i < vector_active(strvec); i++)
384 if ((descvec = vector_slot(strvec, i)) != NULL) {
385 if ((vector_active(descvec)) == 1
386 && (desc = vector_slot(descvec, 0)) != NULL) {
387 if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
388 return size;
389 else
390 size++;
391 } else
392 size++;
393 }
394 return size;
395}
396
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200397/*! Return prompt character of specified node. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200398const char *cmd_prompt(enum node_type node)
399{
400 struct cmd_node *cnode;
401
402 cnode = vector_slot(cmdvec, node);
403 return cnode->prompt;
404}
405
Alexander Couzensad580ba2016-05-16 16:01:45 +0200406/*!
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200407 * escape all special asciidoc symbols
Alexander Couzensad580ba2016-05-16 16:01:45 +0200408 * \param unsafe string
409 * \return a new talloc char *
410 */
411char *osmo_asciidoc_escape(const char *inp)
412{
413 int _strlen;
414 char *out, *out_ptr;
415 int len = 0, i, j;
416
417 if (!inp)
418 return NULL;
419 _strlen = strlen(inp);
420
421 for (i = 0; i < _strlen; ++i) {
422 switch (inp[i]) {
423 case '|':
424 len += 2;
425 break;
426 default:
427 len += 1;
428 break;
429 }
430 }
431
432 out = talloc_size(NULL, len + 1);
433 if (!out)
434 return NULL;
435
436 out_ptr = out;
437
438#define ADD(out, str) \
439 for (j = 0; j < strlen(str); ++j) \
440 *(out++) = str[j];
441
442 for (i = 0; i < _strlen; ++i) {
443 switch (inp[i]) {
444 case '|':
445 ADD(out_ptr, "\\|");
446 break;
447 default:
448 *(out_ptr++) = inp[i];
449 break;
450 }
451 }
452
453#undef ADD
454
455 out_ptr[0] = '\0';
456 return out;
457}
458
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +0100459static char *xml_escape(const char *inp)
460{
461 int _strlen;
462 char *out, *out_ptr;
463 int len = 0, i, j;
464
465 if (!inp)
466 return NULL;
467 _strlen = strlen(inp);
468
469 for (i = 0; i < _strlen; ++i) {
470 switch (inp[i]) {
471 case '"':
472 len += 6;
473 break;
474 case '\'':
475 len += 6;
476 break;
477 case '<':
478 len += 4;
479 break;
480 case '>':
481 len += 4;
482 break;
483 case '&':
484 len += 5;
485 break;
486 default:
487 len += 1;
488 break;
489 }
490 }
491
492 out = talloc_size(NULL, len + 1);
493 if (!out)
494 return NULL;
495
496 out_ptr = out;
497
498#define ADD(out, str) \
499 for (j = 0; j < strlen(str); ++j) \
500 *(out++) = str[j];
501
502 for (i = 0; i < _strlen; ++i) {
503 switch (inp[i]) {
504 case '"':
505 ADD(out_ptr, "&quot;");
506 break;
507 case '\'':
508 ADD(out_ptr, "&apos;");
509 break;
510 case '<':
511 ADD(out_ptr, "&lt;");
512 break;
513 case '>':
514 ADD(out_ptr, "&gt;");
515 break;
516 case '&':
517 ADD(out_ptr, "&amp;");
518 break;
519 default:
520 *(out_ptr++) = inp[i];
521 break;
522 }
523 }
524
525#undef ADD
526
527 out_ptr[0] = '\0';
528 return out;
529}
530
531/*
532 * Write one cmd_element as XML to the given VTY.
533 */
534static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
535{
536 char *xml_string = xml_escape(cmd->string);
537
538 vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
539 vty_out(vty, " <params>%s", VTY_NEWLINE);
540
541 int j;
542 for (j = 0; j < vector_count(cmd->strvec); ++j) {
543 vector descvec = vector_slot(cmd->strvec, j);
544 int i;
545 for (i = 0; i < vector_active(descvec); ++i) {
546 char *xml_param, *xml_doc;
547 struct desc *desc = vector_slot(descvec, i);
548 if (desc == NULL)
549 continue;
550
551 xml_param = xml_escape(desc->cmd);
552 xml_doc = xml_escape(desc->str);
553 vty_out(vty, " <param name='%s' doc='%s' />%s",
554 xml_param, xml_doc, VTY_NEWLINE);
555 talloc_free(xml_param);
556 talloc_free(xml_doc);
557 }
558 }
559
560 vty_out(vty, " </params>%s", VTY_NEWLINE);
561 vty_out(vty, " </command>%s", VTY_NEWLINE);
562
563 talloc_free(xml_string);
564 return 0;
565}
566
567/*
568 * Dump all nodes and commands associated with a given node as XML to the VTY.
569 */
570static int vty_dump_nodes(struct vty *vty)
571{
572 int i, j;
573
574 vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
575
576 for (i = 0; i < vector_active(cmdvec); ++i) {
577 struct cmd_node *cnode;
578 cnode = vector_slot(cmdvec, i);
579 if (!cnode)
580 continue;
581
582 vty_out(vty, " <node id='%d'>%s", i, VTY_NEWLINE);
583
584 for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
585 struct cmd_element *elem;
586 elem = vector_slot(cnode->cmd_vector, j);
587 vty_dump_element(elem, vty);
588 }
589
590 vty_out(vty, " </node>%s", VTY_NEWLINE);
591 }
592
593 vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
594
595 return 0;
596}
597
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200598/* Check if a command with given string exists at given node */
Harald Welteaddeaa32017-01-07 12:52:00 +0100599static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
600{
601 int i;
602
603 for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
604 struct cmd_element *elem;
605 elem = vector_slot(cnode->cmd_vector, i);
606 if (!elem->string)
607 continue;
608 if (!strcmp(elem->string, cmdstring))
609 return 1;
610 }
611 return 0;
612}
613
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200614/*! Install a command into a node
Harald Welte7acb30c2011-08-17 17:13:48 +0200615 * \param[in] ntype Node Type
616 * \param[cmd] element to be installed
617 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +0000618void install_element(int ntype, struct cmd_element *cmd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200619{
620 struct cmd_node *cnode;
621
622 cnode = vector_slot(cmdvec, ntype);
623
Harald Weltea99d45a2015-11-12 13:48:23 +0100624 OSMO_ASSERT(cnode);
Harald Welteaddeaa32017-01-07 12:52:00 +0100625 /* ensure no _identical_ command has been registered at this
626 * node so far */
627 OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200628
629 vector_set(cnode->cmd_vector, cmd);
630
631 cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
632 cmd->cmdsize = cmd_cmdsize(cmd->strvec);
633}
634
635/* Install a command into VIEW and ENABLE node */
636void install_element_ve(struct cmd_element *cmd)
637{
638 install_element(VIEW_NODE, cmd);
639 install_element(ENABLE_NODE, cmd);
640}
641
642#ifdef VTY_CRYPT_PW
643static unsigned char itoa64[] =
644 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
645
646static void to64(char *s, long v, int n)
647{
648 while (--n >= 0) {
649 *s++ = itoa64[v & 0x3f];
650 v >>= 6;
651 }
652}
653
654static char *zencrypt(const char *passwd)
655{
656 char salt[6];
657 struct timeval tv;
658 char *crypt(const char *, const char *);
659
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200660 osmo_gettimeofday(&tv, 0);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200661
662 to64(&salt[0], random(), 3);
663 to64(&salt[3], tv.tv_usec, 3);
664 salt[5] = '\0';
665
666 return crypt(passwd, salt);
667}
668#endif
669
670/* This function write configuration of this host. */
671static int config_write_host(struct vty *vty)
672{
673 if (host.name)
674 vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
675
676 if (host.encrypt) {
677 if (host.password_encrypt)
678 vty_out(vty, "password 8 %s%s", host.password_encrypt,
679 VTY_NEWLINE);
680 if (host.enable_encrypt)
681 vty_out(vty, "enable password 8 %s%s",
682 host.enable_encrypt, VTY_NEWLINE);
683 } else {
684 if (host.password)
685 vty_out(vty, "password %s%s", host.password,
686 VTY_NEWLINE);
687 if (host.enable)
688 vty_out(vty, "enable password %s%s", host.enable,
689 VTY_NEWLINE);
690 }
691
692 if (host.advanced)
693 vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
694
695 if (host.encrypt)
696 vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
697
698 if (host.lines >= 0)
699 vty_out(vty, "service terminal-length %d%s", host.lines,
700 VTY_NEWLINE);
701
702 if (host.motdfile)
703 vty_out(vty, "banner motd file %s%s", host.motdfile,
704 VTY_NEWLINE);
705 else if (!host.motd)
706 vty_out(vty, "no banner motd%s", VTY_NEWLINE);
707
708 return 1;
709}
710
711/* Utility function for getting command vector. */
712static vector cmd_node_vector(vector v, enum node_type ntype)
713{
714 struct cmd_node *cnode = vector_slot(v, ntype);
715 return cnode->cmd_vector;
716}
717
718/* Completion match types. */
719enum match_type {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100720 no_match = 0,
721 any_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200722 extend_match,
723 ipv4_prefix_match,
724 ipv4_match,
725 ipv6_prefix_match,
726 ipv6_match,
727 range_match,
728 vararg_match,
729 partly_match,
Sylvain Munaut4d8eea42012-12-28 11:58:23 +0100730 exact_match,
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200731};
732
733static enum match_type cmd_ipv4_match(const char *str)
734{
735 const char *sp;
736 int dots = 0, nums = 0;
737 char buf[4];
738
739 if (str == NULL)
740 return partly_match;
741
742 for (;;) {
743 memset(buf, 0, sizeof(buf));
744 sp = str;
745 while (*str != '\0') {
746 if (*str == '.') {
747 if (dots >= 3)
748 return no_match;
749
750 if (*(str + 1) == '.')
751 return no_match;
752
753 if (*(str + 1) == '\0')
754 return partly_match;
755
756 dots++;
757 break;
758 }
759 if (!isdigit((int)*str))
760 return no_match;
761
762 str++;
763 }
764
765 if (str - sp > 3)
766 return no_match;
767
768 strncpy(buf, sp, str - sp);
769 if (atoi(buf) > 255)
770 return no_match;
771
772 nums++;
773
774 if (*str == '\0')
775 break;
776
777 str++;
778 }
779
780 if (nums < 4)
781 return partly_match;
782
783 return exact_match;
784}
785
786static enum match_type cmd_ipv4_prefix_match(const char *str)
787{
788 const char *sp;
789 int dots = 0;
790 char buf[4];
791
792 if (str == NULL)
793 return partly_match;
794
795 for (;;) {
796 memset(buf, 0, sizeof(buf));
797 sp = str;
798 while (*str != '\0' && *str != '/') {
799 if (*str == '.') {
800 if (dots == 3)
801 return no_match;
802
803 if (*(str + 1) == '.' || *(str + 1) == '/')
804 return no_match;
805
806 if (*(str + 1) == '\0')
807 return partly_match;
808
809 dots++;
810 break;
811 }
812
813 if (!isdigit((int)*str))
814 return no_match;
815
816 str++;
817 }
818
819 if (str - sp > 3)
820 return no_match;
821
822 strncpy(buf, sp, str - sp);
823 if (atoi(buf) > 255)
824 return no_match;
825
826 if (dots == 3) {
827 if (*str == '/') {
828 if (*(str + 1) == '\0')
829 return partly_match;
830
831 str++;
832 break;
833 } else if (*str == '\0')
834 return partly_match;
835 }
836
837 if (*str == '\0')
838 return partly_match;
839
840 str++;
841 }
842
843 sp = str;
844 while (*str != '\0') {
845 if (!isdigit((int)*str))
846 return no_match;
847
848 str++;
849 }
850
851 if (atoi(sp) > 32)
852 return no_match;
853
854 return exact_match;
855}
856
857#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
858#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
859#define STATE_START 1
860#define STATE_COLON 2
861#define STATE_DOUBLE 3
862#define STATE_ADDR 4
863#define STATE_DOT 5
864#define STATE_SLASH 6
865#define STATE_MASK 7
866
867#ifdef HAVE_IPV6
868
869static enum match_type cmd_ipv6_match(const char *str)
870{
871 int state = STATE_START;
872 int colons = 0, nums = 0, double_colon = 0;
873 const char *sp = NULL;
874 struct sockaddr_in6 sin6_dummy;
875 int ret;
876
877 if (str == NULL)
878 return partly_match;
879
880 if (strspn(str, IPV6_ADDR_STR) != strlen(str))
881 return no_match;
882
883 /* use inet_pton that has a better support,
884 * for example inet_pton can support the automatic addresses:
885 * ::1.2.3.4
886 */
887 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
888
889 if (ret == 1)
890 return exact_match;
891
892 while (*str != '\0') {
893 switch (state) {
894 case STATE_START:
895 if (*str == ':') {
896 if (*(str + 1) != ':' && *(str + 1) != '\0')
897 return no_match;
898 colons--;
899 state = STATE_COLON;
900 } else {
901 sp = str;
902 state = STATE_ADDR;
903 }
904
905 continue;
906 case STATE_COLON:
907 colons++;
908 if (*(str + 1) == ':')
909 state = STATE_DOUBLE;
910 else {
911 sp = str + 1;
912 state = STATE_ADDR;
913 }
914 break;
915 case STATE_DOUBLE:
916 if (double_colon)
917 return no_match;
918
919 if (*(str + 1) == ':')
920 return no_match;
921 else {
922 if (*(str + 1) != '\0')
923 colons++;
924 sp = str + 1;
925 state = STATE_ADDR;
926 }
927
928 double_colon++;
929 nums++;
930 break;
931 case STATE_ADDR:
932 if (*(str + 1) == ':' || *(str + 1) == '\0') {
933 if (str - sp > 3)
934 return no_match;
935
936 nums++;
937 state = STATE_COLON;
938 }
939 if (*(str + 1) == '.')
940 state = STATE_DOT;
941 break;
942 case STATE_DOT:
943 state = STATE_ADDR;
944 break;
945 default:
946 break;
947 }
948
949 if (nums > 8)
950 return no_match;
951
952 if (colons > 7)
953 return no_match;
954
955 str++;
956 }
957
958#if 0
959 if (nums < 11)
960 return partly_match;
961#endif /* 0 */
962
963 return exact_match;
964}
965
966static enum match_type cmd_ipv6_prefix_match(const char *str)
967{
968 int state = STATE_START;
969 int colons = 0, nums = 0, double_colon = 0;
970 int mask;
971 const char *sp = NULL;
972 char *endptr = NULL;
973
974 if (str == NULL)
975 return partly_match;
976
977 if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
978 return no_match;
979
980 while (*str != '\0' && state != STATE_MASK) {
981 switch (state) {
982 case STATE_START:
983 if (*str == ':') {
984 if (*(str + 1) != ':' && *(str + 1) != '\0')
985 return no_match;
986 colons--;
987 state = STATE_COLON;
988 } else {
989 sp = str;
990 state = STATE_ADDR;
991 }
992
993 continue;
994 case STATE_COLON:
995 colons++;
996 if (*(str + 1) == '/')
997 return no_match;
998 else if (*(str + 1) == ':')
999 state = STATE_DOUBLE;
1000 else {
1001 sp = str + 1;
1002 state = STATE_ADDR;
1003 }
1004 break;
1005 case STATE_DOUBLE:
1006 if (double_colon)
1007 return no_match;
1008
1009 if (*(str + 1) == ':')
1010 return no_match;
1011 else {
1012 if (*(str + 1) != '\0' && *(str + 1) != '/')
1013 colons++;
1014 sp = str + 1;
1015
1016 if (*(str + 1) == '/')
1017 state = STATE_SLASH;
1018 else
1019 state = STATE_ADDR;
1020 }
1021
1022 double_colon++;
1023 nums += 1;
1024 break;
1025 case STATE_ADDR:
1026 if (*(str + 1) == ':' || *(str + 1) == '.'
1027 || *(str + 1) == '\0' || *(str + 1) == '/') {
1028 if (str - sp > 3)
1029 return no_match;
1030
1031 for (; sp <= str; sp++)
1032 if (*sp == '/')
1033 return no_match;
1034
1035 nums++;
1036
1037 if (*(str + 1) == ':')
1038 state = STATE_COLON;
1039 else if (*(str + 1) == '.')
1040 state = STATE_DOT;
1041 else if (*(str + 1) == '/')
1042 state = STATE_SLASH;
1043 }
1044 break;
1045 case STATE_DOT:
1046 state = STATE_ADDR;
1047 break;
1048 case STATE_SLASH:
1049 if (*(str + 1) == '\0')
1050 return partly_match;
1051
1052 state = STATE_MASK;
1053 break;
1054 default:
1055 break;
1056 }
1057
1058 if (nums > 11)
1059 return no_match;
1060
1061 if (colons > 7)
1062 return no_match;
1063
1064 str++;
1065 }
1066
1067 if (state < STATE_MASK)
1068 return partly_match;
1069
1070 mask = strtol(str, &endptr, 10);
1071 if (*endptr != '\0')
1072 return no_match;
1073
1074 if (mask < 0 || mask > 128)
1075 return no_match;
1076
1077/* I don't know why mask < 13 makes command match partly.
1078 Forgive me to make this comments. I Want to set static default route
1079 because of lack of function to originate default in ospf6d; sorry
1080 yasu
1081 if (mask < 13)
1082 return partly_match;
1083*/
1084
1085 return exact_match;
1086}
1087
1088#endif /* HAVE_IPV6 */
1089
1090#define DECIMAL_STRLEN_MAX 10
1091
1092static int cmd_range_match(const char *range, const char *str)
1093{
1094 char *p;
1095 char buf[DECIMAL_STRLEN_MAX + 1];
1096 char *endptr = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001097
1098 if (str == NULL)
1099 return 1;
1100
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001101 if (range[1] == '-') {
1102 signed long min = 0, max = 0, val;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001103
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001104 val = strtol(str, &endptr, 10);
1105 if (*endptr != '\0')
1106 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001107
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001108 range += 2;
1109 p = strchr(range, '-');
1110 if (p == NULL)
1111 return 0;
1112 if (p - range > DECIMAL_STRLEN_MAX)
1113 return 0;
1114 strncpy(buf, range, p - range);
1115 buf[p - range] = '\0';
1116 min = -strtol(buf, &endptr, 10);
1117 if (*endptr != '\0')
1118 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001119
Andreas Eversberg33f0fc32010-07-13 13:50:39 +02001120 range = p + 1;
1121 p = strchr(range, '>');
1122 if (p == NULL)
1123 return 0;
1124 if (p - range > DECIMAL_STRLEN_MAX)
1125 return 0;
1126 strncpy(buf, range, p - range);
1127 buf[p - range] = '\0';
1128 max = strtol(buf, &endptr, 10);
1129 if (*endptr != '\0')
1130 return 0;
1131
1132 if (val < min || val > max)
1133 return 0;
1134 } else {
1135 unsigned long min, max, val;
1136
1137 val = strtoul(str, &endptr, 10);
1138 if (*endptr != '\0')
1139 return 0;
1140
1141 range++;
1142 p = strchr(range, '-');
1143 if (p == NULL)
1144 return 0;
1145 if (p - range > DECIMAL_STRLEN_MAX)
1146 return 0;
1147 strncpy(buf, range, p - range);
1148 buf[p - range] = '\0';
1149 min = strtoul(buf, &endptr, 10);
1150 if (*endptr != '\0')
1151 return 0;
1152
1153 range = p + 1;
1154 p = strchr(range, '>');
1155 if (p == NULL)
1156 return 0;
1157 if (p - range > DECIMAL_STRLEN_MAX)
1158 return 0;
1159 strncpy(buf, range, p - range);
1160 buf[p - range] = '\0';
1161 max = strtoul(buf, &endptr, 10);
1162 if (*endptr != '\0')
1163 return 0;
1164
1165 if (val < min || val > max)
1166 return 0;
1167 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001168
1169 return 1;
1170}
1171
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001172/* helper to retrieve the 'real' argument string from an optional argument */
1173static char *
1174cmd_deopt(const char *str)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001175{
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001176 /* we've got "[blah]". We want to strip off the []s and redo the
1177 * match check for "blah"
1178 */
1179 size_t len = strlen(str);
1180 char *tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001181
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001182 if (len < 3)
1183 return NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001184
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001185 /* tmp will hold a string of len-2 chars, so 'len' size is fine */
1186 tmp = talloc_size(NULL, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001187
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001188 memcpy(tmp, (str + 1), len - 2);
1189 tmp[len - 2] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001191 return tmp;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001192}
1193
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001194static enum match_type
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001195cmd_match(const char *str, const char *command,
1196 enum match_type min, bool recur)
1197{
1198
1199 if (recur && CMD_OPTION(str))
1200 {
1201 enum match_type ret;
1202 char *tmp = cmd_deopt(str);
1203
1204 /* this would be a bug in a command, however handle it gracefully
1205 * as it we only discover it if a user tries to run it
1206 */
1207 if (tmp == NULL)
1208 return no_match;
1209
1210 ret = cmd_match(tmp, command, min, false);
1211
1212 talloc_free(tmp);
1213
1214 return ret;
1215 }
1216 else if (CMD_VARARG(str))
1217 return vararg_match;
1218 else if (CMD_RANGE(str))
1219 {
1220 if (cmd_range_match(str, command))
1221 return range_match;
1222 }
1223#ifdef HAVE_IPV6
1224 else if (CMD_IPV6(str))
1225 {
1226 if (cmd_ipv6_match(command) >= min)
1227 return ipv6_match;
1228 }
1229 else if (CMD_IPV6_PREFIX(str))
1230 {
1231 if (cmd_ipv6_prefix_match(command) >= min)
1232 return ipv6_prefix_match;
1233 }
1234#endif /* HAVE_IPV6 */
1235 else if (CMD_IPV4(str))
1236 {
1237 if (cmd_ipv4_match(command) >= min)
1238 return ipv4_match;
1239 }
1240 else if (CMD_IPV4_PREFIX(str))
1241 {
1242 if (cmd_ipv4_prefix_match(command) >= min)
1243 return ipv4_prefix_match;
1244 }
1245 else if (CMD_VARIABLE(str))
1246 return extend_match;
1247 else if (strncmp(command, str, strlen(command)) == 0)
1248 {
1249 if (strcmp(command, str) == 0)
1250 return exact_match;
1251 else if (partly_match >= min)
1252 return partly_match;
1253 }
1254
1255 return no_match;
1256}
1257
1258/* Filter vector at the specified index and by the given command string, to
1259 * the desired matching level (thus allowing part matches), and return match
1260 * type flag.
1261 */
1262static enum match_type
1263cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001264{
1265 unsigned int i;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001266 struct cmd_element *cmd_element;
1267 enum match_type match_type;
1268 vector descvec;
1269 struct desc *desc;
1270
1271 match_type = no_match;
1272
1273 /* If command and cmd_element string does not match set NULL to vector */
1274 for (i = 0; i < vector_active(v); i++)
1275 if ((cmd_element = vector_slot(v, i)) != NULL) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001276 if (index >= vector_active(cmd_element->strvec))
1277 vector_slot(v, i) = NULL;
1278 else {
1279 unsigned int j;
1280 int matched = 0;
1281
1282 descvec =
1283 vector_slot(cmd_element->strvec, index);
1284
1285 for (j = 0; j < vector_active(descvec); j++)
1286 if ((desc = vector_slot(descvec, j))) {
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001287 enum match_type ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001288
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001289 ret = cmd_match (desc->cmd, command, level, true);
1290
1291 if (ret != no_match)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001292 matched++;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001293
1294 if (match_type < ret)
1295 match_type = ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001296 }
1297 if (!matched)
1298 vector_slot(v, i) = NULL;
1299 }
1300 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001301
1302 if (match_type == no_match)
1303 return no_match;
1304
1305 /* 2nd pass: We now know the 'strongest' match type for the index, so we
1306 * go again and filter out commands whose argument (at this index) is
1307 * 'weaker'. E.g., if we have 2 commands:
1308 *
1309 * foo bar <1-255>
1310 * foo bar BLAH
1311 *
1312 * and the command string is 'foo bar 10', then we will get here with with
1313 * 'range_match' being the strongest match. However, if 'BLAH' came
1314 * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
1315 *
1316 * If we don't do a 2nd pass and filter it out, the higher-layers will
1317 * consider this to be ambiguous.
1318 */
1319 for (i = 0; i < vector_active(v); i++)
1320 if ((cmd_element = vector_slot(v, i)) != NULL) {
1321 if (index >= vector_active(cmd_element->strvec))
1322 vector_slot(v, i) = NULL;
1323 else {
1324 unsigned int j;
1325 int matched = 0;
1326
1327 descvec =
1328 vector_slot(cmd_element->strvec, index);
1329
1330 for (j = 0; j < vector_active(descvec); j++)
1331 if ((desc = vector_slot(descvec, j))) {
1332 enum match_type ret;
1333
1334 ret = cmd_match(desc->cmd, command, any_match, true);
1335
1336 if (ret >= match_type)
1337 matched++;
1338 }
1339 if (!matched)
1340 vector_slot(v, i) = NULL;
1341 }
1342 }
1343
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001344 return match_type;
1345}
1346
1347/* Check ambiguous match */
1348static int
1349is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
1350{
1351 unsigned int i;
1352 unsigned int j;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001353 struct cmd_element *cmd_element;
1354 const char *matched = NULL;
1355 vector descvec;
1356 struct desc *desc;
1357
1358 for (i = 0; i < vector_active(v); i++)
1359 if ((cmd_element = vector_slot(v, i)) != NULL) {
1360 int match = 0;
1361
1362 descvec = vector_slot(cmd_element->strvec, index);
1363
1364 for (j = 0; j < vector_active(descvec); j++)
1365 if ((desc = vector_slot(descvec, j))) {
1366 enum match_type ret;
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001367 const char *str = desc->cmd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001368
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001369 if (CMD_OPTION(str))
1370 if ((str = cmd_deopt(str)) == NULL)
1371 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001372
1373 switch (type) {
1374 case exact_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001375 if (!(CMD_VARIABLE (str))
1376 && strcmp(command, str) == 0)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001377 match++;
1378 break;
1379 case partly_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001380 if (!(CMD_VARIABLE (str))
1381 && strncmp(command, str, strlen (command)) == 0)
1382 {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001383 if (matched
1384 && strcmp(matched,
1385 str) != 0)
1386 return 1; /* There is ambiguous match. */
1387 else
1388 matched = str;
1389 match++;
1390 }
1391 break;
1392 case range_match:
1393 if (cmd_range_match
1394 (str, command)) {
1395 if (matched
1396 && strcmp(matched,
1397 str) != 0)
1398 return 1;
1399 else
1400 matched = str;
1401 match++;
1402 }
1403 break;
1404#ifdef HAVE_IPV6
1405 case ipv6_match:
1406 if (CMD_IPV6(str))
1407 match++;
1408 break;
1409 case ipv6_prefix_match:
1410 if ((ret =
1411 cmd_ipv6_prefix_match
1412 (command)) != no_match) {
1413 if (ret == partly_match)
1414 return 2; /* There is incomplete match. */
1415
1416 match++;
1417 }
1418 break;
1419#endif /* HAVE_IPV6 */
1420 case ipv4_match:
1421 if (CMD_IPV4(str))
1422 match++;
1423 break;
1424 case ipv4_prefix_match:
1425 if ((ret =
1426 cmd_ipv4_prefix_match
1427 (command)) != no_match) {
1428 if (ret == partly_match)
1429 return 2; /* There is incomplete match. */
1430
1431 match++;
1432 }
1433 break;
1434 case extend_match:
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001435 if (CMD_VARIABLE (str))
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001436 match++;
1437 break;
1438 case no_match:
1439 default:
1440 break;
1441 }
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001442
1443 if (CMD_OPTION(desc->cmd))
1444 talloc_free((void*)str);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001445 }
1446 if (!match)
1447 vector_slot(v, i) = NULL;
1448 }
1449 return 0;
1450}
1451
1452/* If src matches dst return dst string, otherwise return NULL */
1453static const char *cmd_entry_function(const char *src, const char *dst)
1454{
1455 /* Skip variable arguments. */
1456 if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
1457 CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
1458 return NULL;
1459
1460 /* In case of 'command \t', given src is NULL string. */
1461 if (src == NULL)
1462 return dst;
1463
1464 /* Matched with input string. */
1465 if (strncmp(src, dst, strlen(src)) == 0)
1466 return dst;
1467
1468 return NULL;
1469}
1470
1471/* If src matches dst return dst string, otherwise return NULL */
1472/* This version will return the dst string always if it is
1473 CMD_VARIABLE for '?' key processing */
1474static const char *cmd_entry_function_desc(const char *src, const char *dst)
1475{
1476 if (CMD_VARARG(dst))
1477 return dst;
1478
1479 if (CMD_RANGE(dst)) {
1480 if (cmd_range_match(dst, src))
1481 return dst;
1482 else
1483 return NULL;
1484 }
1485#ifdef HAVE_IPV6
1486 if (CMD_IPV6(dst)) {
1487 if (cmd_ipv6_match(src))
1488 return dst;
1489 else
1490 return NULL;
1491 }
1492
1493 if (CMD_IPV6_PREFIX(dst)) {
1494 if (cmd_ipv6_prefix_match(src))
1495 return dst;
1496 else
1497 return NULL;
1498 }
1499#endif /* HAVE_IPV6 */
1500
1501 if (CMD_IPV4(dst)) {
1502 if (cmd_ipv4_match(src))
1503 return dst;
1504 else
1505 return NULL;
1506 }
1507
1508 if (CMD_IPV4_PREFIX(dst)) {
1509 if (cmd_ipv4_prefix_match(src))
1510 return dst;
1511 else
1512 return NULL;
1513 }
1514
1515 /* Optional or variable commands always match on '?' */
1516 if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
1517 return dst;
1518
1519 /* In case of 'command \t', given src is NULL string. */
1520 if (src == NULL)
1521 return dst;
1522
1523 if (strncmp(src, dst, strlen(src)) == 0)
1524 return dst;
1525 else
1526 return NULL;
1527}
1528
1529/* Check same string element existence. If it isn't there return
1530 1. */
1531static int cmd_unique_string(vector v, const char *str)
1532{
1533 unsigned int i;
1534 char *match;
1535
1536 for (i = 0; i < vector_active(v); i++)
1537 if ((match = vector_slot(v, i)) != NULL)
1538 if (strcmp(match, str) == 0)
1539 return 0;
1540 return 1;
1541}
1542
1543/* Compare string to description vector. If there is same string
1544 return 1 else return 0. */
1545static int desc_unique_string(vector v, const char *str)
1546{
1547 unsigned int i;
1548 struct desc *desc;
1549
1550 for (i = 0; i < vector_active(v); i++)
1551 if ((desc = vector_slot(v, i)) != NULL)
1552 if (strcmp(desc->cmd, str) == 0)
1553 return 1;
1554 return 0;
1555}
1556
1557static int cmd_try_do_shortcut(enum node_type node, char *first_word)
1558{
1559 if (first_word != NULL &&
1560 node != AUTH_NODE &&
1561 node != VIEW_NODE &&
1562 node != AUTH_ENABLE_NODE &&
1563 node != ENABLE_NODE && 0 == strcmp("do", first_word))
1564 return 1;
1565 return 0;
1566}
1567
1568/* '?' describe command support. */
1569static vector
1570cmd_describe_command_real(vector vline, struct vty *vty, int *status)
1571{
1572 unsigned int i;
1573 vector cmd_vector;
1574#define INIT_MATCHVEC_SIZE 10
1575 vector matchvec;
1576 struct cmd_element *cmd_element;
1577 unsigned int index;
1578 int ret;
1579 enum match_type match;
1580 char *command;
1581 static struct desc desc_cr = { "<cr>", "" };
1582
1583 /* Set index. */
1584 if (vector_active(vline) == 0) {
1585 *status = CMD_ERR_NO_MATCH;
1586 return NULL;
1587 } else
1588 index = vector_active(vline) - 1;
1589
1590 /* Make copy vector of current node's command vector. */
1591 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1592
1593 /* Prepare match vector */
1594 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1595
1596 /* Filter commands. */
1597 /* Only words precedes current word will be checked in this loop. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001598 for (i = 0; i < index; i++) {
1599 command = vector_slot(vline, i);
1600 if (!command)
1601 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001602
Harald Welte80d30fe2013-02-12 11:08:57 +01001603 match = cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001604
Harald Welte80d30fe2013-02-12 11:08:57 +01001605 if (match == vararg_match) {
1606 struct cmd_element *cmd_element;
1607 vector descvec;
1608 unsigned int j, k;
1609
1610 for (j = 0; j < vector_active(cmd_vector); j++)
1611 if ((cmd_element =
1612 vector_slot(cmd_vector, j)) != NULL
1613 &&
1614 (vector_active(cmd_element->strvec))) {
1615 descvec =
1616 vector_slot(cmd_element->
1617 strvec,
1618 vector_active
1619 (cmd_element->
1620 strvec) - 1);
1621 for (k = 0;
1622 k < vector_active(descvec);
1623 k++) {
1624 struct desc *desc =
1625 vector_slot(descvec,
1626 k);
1627 vector_set(matchvec,
1628 desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001629 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001630 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001631
Harald Welte80d30fe2013-02-12 11:08:57 +01001632 vector_set(matchvec, &desc_cr);
1633 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001634
Harald Welte80d30fe2013-02-12 11:08:57 +01001635 return matchvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001636 }
1637
Harald Welte80d30fe2013-02-12 11:08:57 +01001638 if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
1639 match)) == 1) {
1640 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001641 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001642 *status = CMD_ERR_AMBIGUOUS;
1643 return NULL;
1644 } else if (ret == 2) {
1645 vector_free(cmd_vector);
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001646 vector_free(matchvec);
Harald Welte80d30fe2013-02-12 11:08:57 +01001647 *status = CMD_ERR_NO_MATCH;
1648 return NULL;
1649 }
1650 }
1651
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001652 /* Prepare match vector */
1653 /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
1654
1655 /* Make sure that cmd_vector is filtered based on current word */
1656 command = vector_slot(vline, index);
1657 if (command)
Vadim Yanitskiy49a0dec2017-06-12 03:49:38 +07001658 cmd_filter(command, cmd_vector, index, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001659
1660 /* Make description vector. */
Harald Welte80d30fe2013-02-12 11:08:57 +01001661 for (i = 0; i < vector_active(cmd_vector); i++) {
1662 const char *string = NULL;
1663 vector strvec;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001664
Harald Welte80d30fe2013-02-12 11:08:57 +01001665 cmd_element = vector_slot(cmd_vector, i);
1666 if (!cmd_element)
1667 continue;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001668
Harald Welted17aa592013-02-12 11:11:34 +01001669 if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
1670 continue;
1671
Harald Welte80d30fe2013-02-12 11:08:57 +01001672 strvec = cmd_element->strvec;
1673
1674 /* if command is NULL, index may be equal to vector_active */
1675 if (command && index >= vector_active(strvec))
1676 vector_slot(cmd_vector, i) = NULL;
1677 else {
1678 /* Check if command is completed. */
1679 if (command == NULL
1680 && index == vector_active(strvec)) {
1681 string = "<cr>";
1682 if (!desc_unique_string(matchvec, string))
1683 vector_set(matchvec, &desc_cr);
1684 } else {
1685 unsigned int j;
1686 vector descvec = vector_slot(strvec, index);
1687 struct desc *desc;
1688
1689 for (j = 0; j < vector_active(descvec); j++) {
1690 desc = vector_slot(descvec, j);
1691 if (!desc)
1692 continue;
1693 string = cmd_entry_function_desc
1694 (command, desc->cmd);
1695 if (!string)
1696 continue;
1697 /* Uniqueness check */
1698 if (!desc_unique_string(matchvec, string))
1699 vector_set(matchvec, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001700 }
1701 }
1702 }
Harald Welte80d30fe2013-02-12 11:08:57 +01001703 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001704 vector_free(cmd_vector);
1705
1706 if (vector_slot(matchvec, 0) == NULL) {
1707 vector_free(matchvec);
1708 *status = CMD_ERR_NO_MATCH;
1709 } else
1710 *status = CMD_SUCCESS;
1711
1712 return matchvec;
1713}
1714
1715vector cmd_describe_command(vector vline, struct vty * vty, int *status)
1716{
1717 vector ret;
1718
1719 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1720 enum node_type onode;
1721 vector shifted_vline;
1722 unsigned int index;
1723
1724 onode = vty->node;
1725 vty->node = ENABLE_NODE;
1726 /* We can try it on enable node, cos' the vty is authenticated */
1727
1728 shifted_vline = vector_init(vector_count(vline));
1729 /* use memcpy? */
1730 for (index = 1; index < vector_active(vline); index++) {
1731 vector_set_index(shifted_vline, index - 1,
1732 vector_lookup(vline, index));
1733 }
1734
1735 ret = cmd_describe_command_real(shifted_vline, vty, status);
1736
1737 vector_free(shifted_vline);
1738 vty->node = onode;
1739 return ret;
1740 }
1741
1742 return cmd_describe_command_real(vline, vty, status);
1743}
1744
1745/* Check LCD of matched command. */
1746static int cmd_lcd(char **matched)
1747{
1748 int i;
1749 int j;
1750 int lcd = -1;
1751 char *s1, *s2;
1752 char c1, c2;
1753
1754 if (matched[0] == NULL || matched[1] == NULL)
1755 return 0;
1756
1757 for (i = 1; matched[i] != NULL; i++) {
1758 s1 = matched[i - 1];
1759 s2 = matched[i];
1760
1761 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
1762 if (c1 != c2)
1763 break;
1764
1765 if (lcd < 0)
1766 lcd = j;
1767 else {
1768 if (lcd > j)
1769 lcd = j;
1770 }
1771 }
1772 return lcd;
1773}
1774
1775/* Command line completion support. */
1776static char **cmd_complete_command_real(vector vline, struct vty *vty,
1777 int *status)
1778{
1779 unsigned int i;
1780 vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
1781#define INIT_MATCHVEC_SIZE 10
1782 vector matchvec;
1783 struct cmd_element *cmd_element;
1784 unsigned int index;
1785 char **match_str;
1786 struct desc *desc;
1787 vector descvec;
1788 char *command;
1789 int lcd;
1790
1791 if (vector_active(vline) == 0) {
1792 *status = CMD_ERR_NO_MATCH;
Holger Hans Peter Freyther047213b2013-07-03 09:32:37 +02001793 vector_free(cmd_vector);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001794 return NULL;
1795 } else
1796 index = vector_active(vline) - 1;
1797
1798 /* First, filter by preceeding command string */
1799 for (i = 0; i < index; i++)
1800 if ((command = vector_slot(vline, i))) {
1801 enum match_type match;
1802 int ret;
1803
1804 /* First try completion match, if there is exactly match return 1 */
1805 match =
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01001806 cmd_filter(command, cmd_vector, i, any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001807
1808 /* If there is exact match then filter ambiguous match else check
1809 ambiguousness. */
1810 if ((ret =
1811 is_cmd_ambiguous(command, cmd_vector, i,
1812 match)) == 1) {
1813 vector_free(cmd_vector);
1814 *status = CMD_ERR_AMBIGUOUS;
1815 return NULL;
1816 }
1817 /*
1818 else if (ret == 2)
1819 {
1820 vector_free (cmd_vector);
1821 *status = CMD_ERR_NO_MATCH;
1822 return NULL;
1823 }
1824 */
1825 }
1826
1827 /* Prepare match vector. */
1828 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1829
1830 /* Now we got into completion */
1831 for (i = 0; i < vector_active(cmd_vector); i++)
1832 if ((cmd_element = vector_slot(cmd_vector, i))) {
1833 const char *string;
1834 vector strvec = cmd_element->strvec;
1835
1836 /* Check field length */
1837 if (index >= vector_active(strvec))
1838 vector_slot(cmd_vector, i) = NULL;
1839 else {
1840 unsigned int j;
1841
1842 descvec = vector_slot(strvec, index);
1843 for (j = 0; j < vector_active(descvec); j++)
1844 if ((desc = vector_slot(descvec, j))) {
1845 if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
1846 if (cmd_unique_string (matchvec, string))
1847 vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
1848 }
1849 }
1850 }
1851
1852 /* We don't need cmd_vector any more. */
1853 vector_free(cmd_vector);
1854
1855 /* No matched command */
1856 if (vector_slot(matchvec, 0) == NULL) {
1857 vector_free(matchvec);
1858
1859 /* In case of 'command \t' pattern. Do you need '?' command at
1860 the end of the line. */
1861 if (vector_slot(vline, index) == '\0')
1862 *status = CMD_ERR_NOTHING_TODO;
1863 else
1864 *status = CMD_ERR_NO_MATCH;
1865 return NULL;
1866 }
1867
1868 /* Only one matched */
1869 if (vector_slot(matchvec, 1) == NULL) {
1870 match_str = (char **)matchvec->index;
1871 vector_only_wrapper_free(matchvec);
1872 *status = CMD_COMPLETE_FULL_MATCH;
1873 return match_str;
1874 }
1875 /* Make it sure last element is NULL. */
1876 vector_set(matchvec, NULL);
1877
1878 /* Check LCD of matched strings. */
1879 if (vector_slot(vline, index) != NULL) {
1880 lcd = cmd_lcd((char **)matchvec->index);
1881
1882 if (lcd) {
1883 int len = strlen(vector_slot(vline, index));
1884
1885 if (len < lcd) {
1886 char *lcdstr;
1887
1888 lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
1889 "complete-lcdstr");
1890 memcpy(lcdstr, matchvec->index[0], lcd);
1891 lcdstr[lcd] = '\0';
1892
1893 /* match_str = (char **) &lcdstr; */
1894
1895 /* Free matchvec. */
1896 for (i = 0; i < vector_active(matchvec); i++) {
1897 if (vector_slot(matchvec, i))
1898 talloc_free(vector_slot(matchvec, i));
1899 }
1900 vector_free(matchvec);
1901
1902 /* Make new matchvec. */
1903 matchvec = vector_init(INIT_MATCHVEC_SIZE);
1904 vector_set(matchvec, lcdstr);
1905 match_str = (char **)matchvec->index;
1906 vector_only_wrapper_free(matchvec);
1907
1908 *status = CMD_COMPLETE_MATCH;
1909 return match_str;
1910 }
1911 }
1912 }
1913
1914 match_str = (char **)matchvec->index;
1915 vector_only_wrapper_free(matchvec);
1916 *status = CMD_COMPLETE_LIST_MATCH;
1917 return match_str;
1918}
1919
1920char **cmd_complete_command(vector vline, struct vty *vty, int *status)
1921{
1922 char **ret;
1923
1924 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1925 enum node_type onode;
1926 vector shifted_vline;
1927 unsigned int index;
1928
1929 onode = vty->node;
1930 vty->node = ENABLE_NODE;
1931 /* We can try it on enable node, cos' the vty is authenticated */
1932
1933 shifted_vline = vector_init(vector_count(vline));
1934 /* use memcpy? */
1935 for (index = 1; index < vector_active(vline); index++) {
1936 vector_set_index(shifted_vline, index - 1,
1937 vector_lookup(vline, index));
1938 }
1939
1940 ret = cmd_complete_command_real(shifted_vline, vty, status);
1941
1942 vector_free(shifted_vline);
1943 vty->node = onode;
1944 return ret;
1945 }
1946
1947 return cmd_complete_command_real(vline, vty, status);
1948}
1949
1950/* return parent node */
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02001951/*
1952 * This function MUST eventually converge on a node when called repeatedly,
1953 * there must not be any cycles.
1954 * All 'config' nodes shall converge on CONFIG_NODE.
1955 * All other 'enable' nodes shall converge on ENABLE_NODE.
1956 * All 'view' only nodes shall converge on VIEW_NODE.
1957 * All other nodes shall converge on themselves or it must be ensured,
1958 * that the user's rights are not extended anyhow by calling this function.
1959 *
1960 * Note that these requirements also apply to all functions that are used
1961 * as go_parent_cb.
1962 * Note also that this function relies on the is_config_child callback to
1963 * recognize non-config nodes if go_parent_cb is not set.
1964 */
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00001965int vty_go_parent(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001966{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02001967 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02001968 case AUTH_NODE:
1969 case VIEW_NODE:
1970 case ENABLE_NODE:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02001971 case CONFIG_NODE:
1972 break;
1973
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02001974 case AUTH_ENABLE_NODE:
1975 vty->node = VIEW_NODE;
1976 break;
1977
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02001978 case CFG_LOG_NODE:
1979 case VTY_NODE:
1980 vty->node = CONFIG_NODE;
1981 break;
1982
1983 default:
1984 if (host.app_info->go_parent_cb)
1985 host.app_info->go_parent_cb(vty);
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02001986 else if (is_config_child(vty))
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02001987 vty->node = CONFIG_NODE;
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02001988 else
1989 vty->node = VIEW_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02001990 break;
1991 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001992
1993 return vty->node;
1994}
1995
1996/* Execute command by argument vline vector. */
1997static int
1998cmd_execute_command_real(vector vline, struct vty *vty,
1999 struct cmd_element **cmd)
2000{
2001 unsigned int i;
2002 unsigned int index;
2003 vector cmd_vector;
2004 struct cmd_element *cmd_element;
2005 struct cmd_element *matched_element;
2006 unsigned int matched_count, incomplete_count;
2007 int argc;
2008 const char *argv[CMD_ARGC_MAX];
2009 enum match_type match = 0;
2010 int varflag;
2011 char *command;
2012
2013 /* Make copy of command elements. */
2014 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2015
2016 for (index = 0; index < vector_active(vline); index++)
2017 if ((command = vector_slot(vline, index))) {
2018 int ret;
2019
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002020 match = cmd_filter(command, cmd_vector, index,
2021 any_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002022
2023 if (match == vararg_match)
2024 break;
2025
2026 ret =
2027 is_cmd_ambiguous(command, cmd_vector, index, match);
2028
2029 if (ret == 1) {
2030 vector_free(cmd_vector);
2031 return CMD_ERR_AMBIGUOUS;
2032 } else if (ret == 2) {
2033 vector_free(cmd_vector);
2034 return CMD_ERR_NO_MATCH;
2035 }
2036 }
2037
2038 /* Check matched count. */
2039 matched_element = NULL;
2040 matched_count = 0;
2041 incomplete_count = 0;
2042
2043 for (i = 0; i < vector_active(cmd_vector); i++)
2044 if ((cmd_element = vector_slot(cmd_vector, i))) {
2045 if (match == vararg_match
2046 || index >= cmd_element->cmdsize) {
2047 matched_element = cmd_element;
2048#if 0
2049 printf("DEBUG: %s\n", cmd_element->string);
2050#endif
2051 matched_count++;
2052 } else {
2053 incomplete_count++;
2054 }
2055 }
2056
2057 /* Finish of using cmd_vector. */
2058 vector_free(cmd_vector);
2059
2060 /* To execute command, matched_count must be 1. */
2061 if (matched_count == 0) {
2062 if (incomplete_count)
2063 return CMD_ERR_INCOMPLETE;
2064 else
2065 return CMD_ERR_NO_MATCH;
2066 }
2067
2068 if (matched_count > 1)
2069 return CMD_ERR_AMBIGUOUS;
2070
2071 /* Argument treatment */
2072 varflag = 0;
2073 argc = 0;
2074
2075 for (i = 0; i < vector_active(vline); i++) {
2076 if (varflag)
2077 argv[argc++] = vector_slot(vline, i);
2078 else {
2079 vector descvec =
2080 vector_slot(matched_element->strvec, i);
2081
2082 if (vector_active(descvec) == 1) {
2083 struct desc *desc = vector_slot(descvec, 0);
2084
2085 if (CMD_VARARG(desc->cmd))
2086 varflag = 1;
2087
2088 if (varflag || CMD_VARIABLE(desc->cmd)
2089 || CMD_OPTION(desc->cmd))
2090 argv[argc++] = vector_slot(vline, i);
2091 } else
2092 argv[argc++] = vector_slot(vline, i);
2093 }
2094
2095 if (argc >= CMD_ARGC_MAX)
2096 return CMD_ERR_EXEED_ARGC_MAX;
2097 }
2098
2099 /* For vtysh execution. */
2100 if (cmd)
2101 *cmd = matched_element;
2102
2103 if (matched_element->daemon)
2104 return CMD_SUCCESS_DAEMON;
2105
2106 /* Execute matched command. */
2107 return (*matched_element->func) (matched_element, vty, argc, argv);
2108}
2109
2110int
2111cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
2112 int vtysh)
2113{
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002114 int ret;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002115 enum node_type onode;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002116
2117 onode = vty->node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002118
2119 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
2120 vector shifted_vline;
2121 unsigned int index;
2122
2123 vty->node = ENABLE_NODE;
2124 /* We can try it on enable node, cos' the vty is authenticated */
2125
2126 shifted_vline = vector_init(vector_count(vline));
2127 /* use memcpy? */
2128 for (index = 1; index < vector_active(vline); index++) {
2129 vector_set_index(shifted_vline, index - 1,
2130 vector_lookup(vline, index));
2131 }
2132
2133 ret = cmd_execute_command_real(shifted_vline, vty, cmd);
2134
2135 vector_free(shifted_vline);
2136 vty->node = onode;
2137 return ret;
2138 }
2139
Neels Hofmeyrd64b6ae2017-09-07 04:52:05 +02002140 return cmd_execute_command_real(vline, vty, cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002141}
2142
2143/* Execute command by argument readline. */
2144int
2145cmd_execute_command_strict(vector vline, struct vty *vty,
2146 struct cmd_element **cmd)
2147{
2148 unsigned int i;
2149 unsigned int index;
2150 vector cmd_vector;
2151 struct cmd_element *cmd_element;
2152 struct cmd_element *matched_element;
2153 unsigned int matched_count, incomplete_count;
2154 int argc;
2155 const char *argv[CMD_ARGC_MAX];
2156 int varflag;
2157 enum match_type match = 0;
2158 char *command;
2159
2160 /* Make copy of command element */
2161 cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
2162
2163 for (index = 0; index < vector_active(vline); index++)
2164 if ((command = vector_slot(vline, index))) {
2165 int ret;
2166
Sylvain Munaut4d8eea42012-12-28 11:58:23 +01002167 match = cmd_filter(vector_slot(vline, index),
2168 cmd_vector, index, exact_match);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002169
2170 /* If command meets '.VARARG' then finish matching. */
2171 if (match == vararg_match)
2172 break;
2173
2174 ret =
2175 is_cmd_ambiguous(command, cmd_vector, index, match);
2176 if (ret == 1) {
2177 vector_free(cmd_vector);
2178 return CMD_ERR_AMBIGUOUS;
2179 }
2180 if (ret == 2) {
2181 vector_free(cmd_vector);
2182 return CMD_ERR_NO_MATCH;
2183 }
2184 }
2185
2186 /* Check matched count. */
2187 matched_element = NULL;
2188 matched_count = 0;
2189 incomplete_count = 0;
2190 for (i = 0; i < vector_active(cmd_vector); i++)
2191 if (vector_slot(cmd_vector, i) != NULL) {
2192 cmd_element = vector_slot(cmd_vector, i);
2193
2194 if (match == vararg_match
2195 || index >= cmd_element->cmdsize) {
2196 matched_element = cmd_element;
2197 matched_count++;
2198 } else
2199 incomplete_count++;
2200 }
2201
2202 /* Finish of using cmd_vector. */
2203 vector_free(cmd_vector);
2204
2205 /* To execute command, matched_count must be 1. */
2206 if (matched_count == 0) {
2207 if (incomplete_count)
2208 return CMD_ERR_INCOMPLETE;
2209 else
2210 return CMD_ERR_NO_MATCH;
2211 }
2212
2213 if (matched_count > 1)
2214 return CMD_ERR_AMBIGUOUS;
2215
2216 /* Argument treatment */
2217 varflag = 0;
2218 argc = 0;
2219
2220 for (i = 0; i < vector_active(vline); i++) {
2221 if (varflag)
2222 argv[argc++] = vector_slot(vline, i);
2223 else {
2224 vector descvec =
2225 vector_slot(matched_element->strvec, i);
2226
2227 if (vector_active(descvec) == 1) {
2228 struct desc *desc = vector_slot(descvec, 0);
2229
2230 if (CMD_VARARG(desc->cmd))
2231 varflag = 1;
2232
2233 if (varflag || CMD_VARIABLE(desc->cmd)
2234 || CMD_OPTION(desc->cmd))
2235 argv[argc++] = vector_slot(vline, i);
2236 } else
2237 argv[argc++] = vector_slot(vline, i);
2238 }
2239
2240 if (argc >= CMD_ARGC_MAX)
2241 return CMD_ERR_EXEED_ARGC_MAX;
2242 }
2243
2244 /* For vtysh execution. */
2245 if (cmd)
2246 *cmd = matched_element;
2247
2248 if (matched_element->daemon)
2249 return CMD_SUCCESS_DAEMON;
2250
2251 /* Now execute matched command */
2252 return (*matched_element->func) (matched_element, vty, argc, argv);
2253}
2254
2255/* Configration make from file. */
2256int config_from_file(struct vty *vty, FILE * fp)
2257{
2258 int ret;
2259 vector vline;
2260
2261 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
2262 vline = cmd_make_strvec(vty->buf);
2263
2264 /* In case of comment line */
2265 if (vline == NULL)
2266 continue;
2267 /* Execute configuration command : this is strict match */
2268 ret = cmd_execute_command_strict(vline, vty, NULL);
2269
2270 /* Try again with setting node to CONFIG_NODE */
2271 while (ret != CMD_SUCCESS && ret != CMD_WARNING
2272 && ret != CMD_ERR_NOTHING_TODO
Jacob Erlbeck2442e092013-09-06 16:51:58 +02002273 && is_config_child(vty)) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002274 vty_go_parent(vty);
2275 ret = cmd_execute_command_strict(vline, vty, NULL);
2276 }
2277
2278 cmd_free_strvec(vline);
2279
2280 if (ret != CMD_SUCCESS && ret != CMD_WARNING
2281 && ret != CMD_ERR_NOTHING_TODO)
2282 return ret;
2283 }
2284 return CMD_SUCCESS;
2285}
2286
2287/* Configration from terminal */
2288DEFUN(config_terminal,
2289 config_terminal_cmd,
2290 "configure terminal",
2291 "Configuration from vty interface\n" "Configuration terminal\n")
2292{
2293 if (vty_config_lock(vty))
2294 vty->node = CONFIG_NODE;
2295 else {
2296 vty_out(vty, "VTY configuration is locked by other VTY%s",
2297 VTY_NEWLINE);
2298 return CMD_WARNING;
2299 }
2300 return CMD_SUCCESS;
2301}
2302
2303/* Enable command */
2304DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
2305{
2306 /* If enable password is NULL, change to ENABLE_NODE */
2307 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2308 vty->type == VTY_SHELL_SERV)
2309 vty->node = ENABLE_NODE;
2310 else
2311 vty->node = AUTH_ENABLE_NODE;
2312
2313 return CMD_SUCCESS;
2314}
2315
2316/* Disable command */
2317DEFUN(disable,
2318 config_disable_cmd, "disable", "Turn off privileged mode command\n")
2319{
2320 if (vty->node == ENABLE_NODE)
2321 vty->node = VIEW_NODE;
2322 return CMD_SUCCESS;
2323}
2324
2325/* Down vty node level. */
2326gDEFUN(config_exit,
2327 config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
2328{
2329 switch (vty->node) {
Jacob Erlbeckb3657e12013-09-10 14:04:54 +02002330 case AUTH_NODE:
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002331 case VIEW_NODE:
2332 case ENABLE_NODE:
Harald Weltea99d45a2015-11-12 13:48:23 +01002333 vty->status = VTY_CLOSE;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002334 break;
2335 case CONFIG_NODE:
2336 vty->node = ENABLE_NODE;
2337 vty_config_unlock(vty);
2338 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002339 default:
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002340 if (vty->node > CONFIG_NODE)
2341 vty_go_parent (vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002342 break;
2343 }
2344 return CMD_SUCCESS;
2345}
2346
2347/* End of configuration. */
2348 gDEFUN(config_end,
2349 config_end_cmd, "end", "End current mode and change to enable mode.")
2350{
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002351 if (vty->node > ENABLE_NODE) {
Jacob Erlbeck23497212013-09-10 09:07:31 +02002352 int last_node = CONFIG_NODE;
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002353
2354 /* Repeatedly call go_parent until a top node is reached. */
2355 while (vty->node > CONFIG_NODE) {
2356 if (vty->node == last_node) {
2357 /* Ensure termination, this shouldn't happen. */
2358 break;
2359 }
2360 last_node = vty->node;
2361 vty_go_parent(vty);
2362 }
2363
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002364 vty_config_unlock(vty);
Jacob Erlbeck7eed0532013-09-06 16:51:59 +02002365 if (vty->node > ENABLE_NODE)
2366 vty->node = ENABLE_NODE;
2367 vty->index = NULL;
2368 vty->index_sub = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002369 }
2370 return CMD_SUCCESS;
2371}
2372
2373/* Show version. */
2374DEFUN(show_version,
2375 show_version_cmd, "show version", SHOW_STR "Displays program version\n")
2376{
Harald Welte237f6242010-05-25 23:00:45 +02002377 vty_out(vty, "%s %s (%s).%s", host.app_info->name,
2378 host.app_info->version,
2379 host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
2380 vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002381
2382 return CMD_SUCCESS;
2383}
2384
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01002385DEFUN(show_online_help,
2386 show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
2387{
2388 vty_dump_nodes(vty);
2389 return CMD_SUCCESS;
2390}
2391
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002392/* Help display function for all node. */
2393gDEFUN(config_help,
2394 config_help_cmd, "help", "Description of the interactive help system\n")
2395{
2396 vty_out(vty,
2397 "This VTY provides advanced help features. When you need help,%s\
2398anytime at the command line please press '?'.%s\
2399%s\
2400If nothing matches, the help list will be empty and you must backup%s\
2401 until entering a '?' shows the available options.%s\
2402Two styles of help are provided:%s\
24031. Full help is available when you are ready to enter a%s\
2404command argument (e.g. 'show ?') and describes each possible%s\
2405argument.%s\
24062. Partial help is provided when an abbreviated argument is entered%s\
2407 and you want to know what arguments match the input%s\
2408 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2409 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2410 return CMD_SUCCESS;
2411}
2412
2413/* Help display function for all node. */
2414gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
2415{
2416 unsigned int i;
2417 struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
2418 struct cmd_element *cmd;
2419
2420 for (i = 0; i < vector_active(cnode->cmd_vector); i++)
2421 if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
2422 && !(cmd->attr == CMD_ATTR_DEPRECATED
2423 || cmd->attr == CMD_ATTR_HIDDEN))
2424 vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
2425 return CMD_SUCCESS;
2426}
2427
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002428static int write_config_file(const char *config_file, char **outpath)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002429{
2430 unsigned int i;
2431 int fd;
2432 struct cmd_node *node;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002433 char *config_file_tmp = NULL;
2434 char *config_file_sav = NULL;
2435 struct vty *file_vty;
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002436 struct stat st;
2437
2438 *outpath = NULL;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002439
2440 /* Check and see if we are operating under vtysh configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002441 config_file_sav =
2442 _talloc_zero(tall_vty_cmd_ctx,
2443 strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
2444 "config_file_sav");
2445 strcpy(config_file_sav, config_file);
2446 strcat(config_file_sav, CONF_BACKUP_EXT);
2447
2448 config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
2449 "config_file_tmp");
2450 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
2451
2452 /* Open file to configuration write. */
2453 fd = mkstemp(config_file_tmp);
2454 if (fd < 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002455 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002456 talloc_free(config_file_tmp);
2457 talloc_free(config_file_sav);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002458 return -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002459 }
2460
2461 /* Make vty for configuration file. */
2462 file_vty = vty_new();
2463 file_vty->fd = fd;
2464 file_vty->type = VTY_FILE;
2465
2466 /* Config file header print. */
2467 vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
Harald Welte237f6242010-05-25 23:00:45 +02002468 host.app_info->name, host.app_info->version);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002469 //vty_time_print (file_vty, 1);
2470 vty_out(file_vty, "!\n");
2471
2472 for (i = 0; i < vector_active(cmdvec); i++)
2473 if ((node = vector_slot(cmdvec, i)) && node->func) {
2474 if ((*node->func) (file_vty))
2475 vty_out(file_vty, "!\n");
2476 }
2477 vty_close(file_vty);
2478
2479 if (unlink(config_file_sav) != 0)
2480 if (errno != ENOENT) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002481 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002482 talloc_free(config_file_sav);
2483 talloc_free(config_file_tmp);
2484 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002485 return -2;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002486 }
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002487
2488 /* Only link the .sav file if the original file exists */
2489 if (stat(config_file, &st) == 0) {
2490 if (link(config_file, config_file_sav) != 0) {
2491 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav);
2492 talloc_free(config_file_sav);
2493 talloc_free(config_file_tmp);
2494 unlink(config_file_tmp);
2495 return -3;
2496 }
2497 sync();
2498 if (unlink(config_file) != 0) {
2499 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2500 talloc_free(config_file_sav);
2501 talloc_free(config_file_tmp);
2502 unlink(config_file_tmp);
2503 return -4;
2504 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002505 }
2506 if (link(config_file_tmp, config_file) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002507 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002508 talloc_free(config_file_sav);
2509 talloc_free(config_file_tmp);
2510 unlink(config_file_tmp);
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002511 return -5;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002512 }
2513 unlink(config_file_tmp);
2514 sync();
2515
2516 talloc_free(config_file_sav);
2517 talloc_free(config_file_tmp);
2518
2519 if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002520 *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file);
2521 return -6;
2522 }
2523
2524 return 0;
2525}
2526
2527
2528/* Write current configuration into file. */
2529DEFUN(config_write_file,
2530 config_write_file_cmd,
2531 "write file",
2532 "Write running configuration to memory, network, or terminal\n"
2533 "Write to configuration file\n")
2534{
2535 char *failed_file;
2536 int rc;
2537
Holger Hans Peter Freyther9f0f9782014-11-21 10:40:07 +01002538 if (host.app_info->config_is_consistent) {
2539 rc = host.app_info->config_is_consistent(vty);
2540 if (!rc) {
2541 vty_out(vty, "Configuration is not consistent%s",
2542 VTY_NEWLINE);
2543 return CMD_WARNING;
2544 }
2545 }
2546
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002547 if (host.config == NULL) {
2548 vty_out(vty, "Can't save to configuration file, using vtysh.%s",
2549 VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002550 return CMD_WARNING;
2551 }
2552
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01002553 rc = write_config_file(host.config, &failed_file);
2554 switch (rc) {
2555 case -1:
2556 vty_out(vty, "Can't open configuration file %s.%s",
2557 failed_file, VTY_NEWLINE);
2558 rc = CMD_WARNING;
2559 break;
2560 case -2:
2561 vty_out(vty, "Can't unlink backup configuration file %s.%s",
2562 failed_file, VTY_NEWLINE);
2563 rc = CMD_WARNING;
2564 break;
2565 case -3:
2566 vty_out(vty, "Can't backup old configuration file %s.%s",
2567 failed_file, VTY_NEWLINE);
2568 rc = CMD_WARNING;
2569 break;
2570 case -4:
2571 vty_out(vty, "Can't unlink configuration file %s.%s",
2572 failed_file, VTY_NEWLINE);
2573 rc = CMD_WARNING;
2574 break;
2575 case -5:
2576 vty_out(vty, "Can't save configuration file %s.%s", failed_file,
2577 VTY_NEWLINE);
2578 rc = CMD_WARNING;
2579 break;
2580 case -6:
2581 vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
2582 failed_file, strerror(errno), errno, VTY_NEWLINE);
2583 rc = CMD_WARNING;
2584 break;
2585 default:
2586 vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE);
2587 rc = CMD_SUCCESS;
2588 break;
2589 }
2590
2591 talloc_free(failed_file);
2592 return rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02002593}
2594
2595ALIAS(config_write_file,
2596 config_write_cmd,
2597 "write", "Write running configuration to memory, network, or terminal\n")
2598
2599 ALIAS(config_write_file,
2600 config_write_memory_cmd,
2601 "write memory",
2602 "Write running configuration to memory, network, or terminal\n"
2603 "Write configuration to the file (same as write file)\n")
2604
2605 ALIAS(config_write_file,
2606 copy_runningconfig_startupconfig_cmd,
2607 "copy running-config startup-config",
2608 "Copy configuration\n"
2609 "Copy running config to... \n"
2610 "Copy running config to startup config (same as write file)\n")
2611
2612/* Write current configuration into the terminal. */
2613 DEFUN(config_write_terminal,
2614 config_write_terminal_cmd,
2615 "write terminal",
2616 "Write running configuration to memory, network, or terminal\n"
2617 "Write to terminal\n")
2618{
2619 unsigned int i;
2620 struct cmd_node *node;
2621
2622 if (vty->type == VTY_SHELL_SERV) {
2623 for (i = 0; i < vector_active(cmdvec); i++)
2624 if ((node = vector_slot(cmdvec, i)) && node->func
2625 && node->vtysh) {
2626 if ((*node->func) (vty))
2627 vty_out(vty, "!%s", VTY_NEWLINE);
2628 }
2629 } else {
2630 vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
2631 VTY_NEWLINE);
2632 vty_out(vty, "!%s", VTY_NEWLINE);
2633
2634 for (i = 0; i < vector_active(cmdvec); i++)
2635 if ((node = vector_slot(cmdvec, i)) && node->func) {
2636 if ((*node->func) (vty))
2637 vty_out(vty, "!%s", VTY_NEWLINE);
2638 }
2639 vty_out(vty, "end%s", VTY_NEWLINE);
2640 }
2641 return CMD_SUCCESS;
2642}
2643
2644/* Write current configuration into the terminal. */
2645ALIAS(config_write_terminal,
2646 show_running_config_cmd,
2647 "show running-config", SHOW_STR "running configuration\n")
2648
2649/* Write startup configuration into the terminal. */
2650 DEFUN(show_startup_config,
2651 show_startup_config_cmd,
2652 "show startup-config", SHOW_STR "Contentes of startup configuration\n")
2653{
2654 char buf[BUFSIZ];
2655 FILE *confp;
2656
2657 confp = fopen(host.config, "r");
2658 if (confp == NULL) {
2659 vty_out(vty, "Can't open configuration file [%s]%s",
2660 host.config, VTY_NEWLINE);
2661 return CMD_WARNING;
2662 }
2663
2664 while (fgets(buf, BUFSIZ, confp)) {
2665 char *cp = buf;
2666
2667 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
2668 cp++;
2669 *cp = '\0';
2670
2671 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
2672 }
2673
2674 fclose(confp);
2675
2676 return CMD_SUCCESS;
2677}
2678
2679/* Hostname configuration */
2680DEFUN(config_hostname,
2681 hostname_cmd,
2682 "hostname WORD",
2683 "Set system's network name\n" "This system's network name\n")
2684{
2685 if (!isalpha((int)*argv[0])) {
2686 vty_out(vty, "Please specify string starting with alphabet%s",
2687 VTY_NEWLINE);
2688 return CMD_WARNING;
2689 }
2690
2691 if (host.name)
2692 talloc_free(host.name);
2693
2694 host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2695 return CMD_SUCCESS;
2696}
2697
2698DEFUN(config_no_hostname,
2699 no_hostname_cmd,
2700 "no hostname [HOSTNAME]",
2701 NO_STR "Reset system's network name\n" "Host name of this router\n")
2702{
2703 if (host.name)
2704 talloc_free(host.name);
2705 host.name = NULL;
2706 return CMD_SUCCESS;
2707}
2708
2709/* VTY interface password set. */
2710DEFUN(config_password, password_cmd,
2711 "password (8|) WORD",
2712 "Assign the terminal connection password\n"
2713 "Specifies a HIDDEN password will follow\n"
2714 "dummy string \n" "The HIDDEN line password string\n")
2715{
2716 /* Argument check. */
2717 if (argc == 0) {
2718 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
2719 return CMD_WARNING;
2720 }
2721
2722 if (argc == 2) {
2723 if (*argv[0] == '8') {
2724 if (host.password)
2725 talloc_free(host.password);
2726 host.password = NULL;
2727 if (host.password_encrypt)
2728 talloc_free(host.password_encrypt);
2729 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
2730 return CMD_SUCCESS;
2731 } else {
2732 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
2733 return CMD_WARNING;
2734 }
2735 }
2736
2737 if (!isalnum((int)*argv[0])) {
2738 vty_out(vty,
2739 "Please specify string starting with alphanumeric%s",
2740 VTY_NEWLINE);
2741 return CMD_WARNING;
2742 }
2743
2744 if (host.password)
2745 talloc_free(host.password);
2746 host.password = NULL;
2747
2748#ifdef VTY_CRYPT_PW
2749 if (host.encrypt) {
2750 if (host.password_encrypt)
2751 talloc_free(host.password_encrypt);
2752 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
2753 } else
2754#endif
2755 host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2756
2757 return CMD_SUCCESS;
2758}
2759
2760ALIAS(config_password, password_text_cmd,
2761 "password LINE",
2762 "Assign the terminal connection password\n"
2763 "The UNENCRYPTED (cleartext) line password\n")
2764
2765/* VTY enable password set. */
2766 DEFUN(config_enable_password, enable_password_cmd,
2767 "enable password (8|) WORD",
2768 "Modify enable password parameters\n"
2769 "Assign the privileged level password\n"
2770 "Specifies a HIDDEN password will follow\n"
2771 "dummy string \n" "The HIDDEN 'enable' password string\n")
2772{
2773 /* Argument check. */
2774 if (argc == 0) {
2775 vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
2776 return CMD_WARNING;
2777 }
2778
2779 /* Crypt type is specified. */
2780 if (argc == 2) {
2781 if (*argv[0] == '8') {
2782 if (host.enable)
2783 talloc_free(host.enable);
2784 host.enable = NULL;
2785
2786 if (host.enable_encrypt)
2787 talloc_free(host.enable_encrypt);
2788 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
2789
2790 return CMD_SUCCESS;
2791 } else {
2792 vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
2793 return CMD_WARNING;
2794 }
2795 }
2796
2797 if (!isalnum((int)*argv[0])) {
2798 vty_out(vty,
2799 "Please specify string starting with alphanumeric%s",
2800 VTY_NEWLINE);
2801 return CMD_WARNING;
2802 }
2803
2804 if (host.enable)
2805 talloc_free(host.enable);
2806 host.enable = NULL;
2807
2808 /* Plain password input. */
2809#ifdef VTY_CRYPT_PW
2810 if (host.encrypt) {
2811 if (host.enable_encrypt)
2812 talloc_free(host.enable_encrypt);
2813 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
2814 } else
2815#endif
2816 host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
2817
2818 return CMD_SUCCESS;
2819}
2820
2821ALIAS(config_enable_password,
2822 enable_password_text_cmd,
2823 "enable password LINE",
2824 "Modify enable password parameters\n"
2825 "Assign the privileged level password\n"
2826 "The UNENCRYPTED (cleartext) 'enable' password\n")
2827
2828/* VTY enable password delete. */
2829 DEFUN(no_config_enable_password, no_enable_password_cmd,
2830 "no enable password",
2831 NO_STR
2832 "Modify enable password parameters\n"
2833 "Assign the privileged level password\n")
2834{
2835 if (host.enable)
2836 talloc_free(host.enable);
2837 host.enable = NULL;
2838
2839 if (host.enable_encrypt)
2840 talloc_free(host.enable_encrypt);
2841 host.enable_encrypt = NULL;
2842
2843 return CMD_SUCCESS;
2844}
2845
2846#ifdef VTY_CRYPT_PW
2847DEFUN(service_password_encrypt,
2848 service_password_encrypt_cmd,
2849 "service password-encryption",
2850 "Set up miscellaneous service\n" "Enable encrypted passwords\n")
2851{
2852 if (host.encrypt)
2853 return CMD_SUCCESS;
2854
2855 host.encrypt = 1;
2856
2857 if (host.password) {
2858 if (host.password_encrypt)
2859 talloc_free(host.password_encrypt);
2860 host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
2861 }
2862 if (host.enable) {
2863 if (host.enable_encrypt)
2864 talloc_free(host.enable_encrypt);
2865 host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
2866 }
2867
2868 return CMD_SUCCESS;
2869}
2870
2871DEFUN(no_service_password_encrypt,
2872 no_service_password_encrypt_cmd,
2873 "no service password-encryption",
2874 NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
2875{
2876 if (!host.encrypt)
2877 return CMD_SUCCESS;
2878
2879 host.encrypt = 0;
2880
2881 if (host.password_encrypt)
2882 talloc_free(host.password_encrypt);
2883 host.password_encrypt = NULL;
2884
2885 if (host.enable_encrypt)
2886 talloc_free(host.enable_encrypt);
2887 host.enable_encrypt = NULL;
2888
2889 return CMD_SUCCESS;
2890}
2891#endif
2892
2893DEFUN(config_terminal_length, config_terminal_length_cmd,
2894 "terminal length <0-512>",
2895 "Set terminal line parameters\n"
2896 "Set number of lines on a screen\n"
2897 "Number of lines on screen (0 for no pausing)\n")
2898{
2899 int lines;
2900 char *endptr = NULL;
2901
2902 lines = strtol(argv[0], &endptr, 10);
2903 if (lines < 0 || lines > 512 || *endptr != '\0') {
2904 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
2905 return CMD_WARNING;
2906 }
2907 vty->lines = lines;
2908
2909 return CMD_SUCCESS;
2910}
2911
2912DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
2913 "terminal no length",
2914 "Set terminal line parameters\n"
2915 NO_STR "Set number of lines on a screen\n")
2916{
2917 vty->lines = -1;
2918 return CMD_SUCCESS;
2919}
2920
2921DEFUN(service_terminal_length, service_terminal_length_cmd,
2922 "service terminal-length <0-512>",
2923 "Set up miscellaneous service\n"
2924 "System wide terminal length configuration\n"
2925 "Number of lines of VTY (0 means no line control)\n")
2926{
2927 int lines;
2928 char *endptr = NULL;
2929
2930 lines = strtol(argv[0], &endptr, 10);
2931 if (lines < 0 || lines > 512 || *endptr != '\0') {
2932 vty_out(vty, "length is malformed%s", VTY_NEWLINE);
2933 return CMD_WARNING;
2934 }
2935 host.lines = lines;
2936
2937 return CMD_SUCCESS;
2938}
2939
2940DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
2941 "no service terminal-length [<0-512>]",
2942 NO_STR
2943 "Set up miscellaneous service\n"
2944 "System wide terminal length configuration\n"
2945 "Number of lines of VTY (0 means no line control)\n")
2946{
2947 host.lines = -1;
2948 return CMD_SUCCESS;
2949}
2950
2951DEFUN_HIDDEN(do_echo,
2952 echo_cmd,
2953 "echo .MESSAGE",
2954 "Echo a message back to the vty\n" "The message to echo\n")
2955{
2956 char *message;
2957
2958 vty_out(vty, "%s%s",
2959 ((message =
2960 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
2961 if (message)
2962 talloc_free(message);
2963 return CMD_SUCCESS;
2964}
2965
2966#if 0
2967DEFUN(config_logmsg,
2968 config_logmsg_cmd,
2969 "logmsg " LOG_LEVELS " .MESSAGE",
2970 "Send a message to enabled logging destinations\n"
2971 LOG_LEVEL_DESC "The message to send\n")
2972{
2973 int level;
2974 char *message;
2975
2976 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
2977 return CMD_ERR_NO_MATCH;
2978
2979 zlog(NULL, level,
2980 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2981 if (message)
2982 talloc_free(message);
2983 return CMD_SUCCESS;
2984}
2985
2986DEFUN(show_logging,
2987 show_logging_cmd,
2988 "show logging", SHOW_STR "Show current logging configuration\n")
2989{
2990 struct zlog *zl = zlog_default;
2991
2992 vty_out(vty, "Syslog logging: ");
2993 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
2994 vty_out(vty, "disabled");
2995 else
2996 vty_out(vty, "level %s, facility %s, ident %s",
2997 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
2998 facility_name(zl->facility), zl->ident);
2999 vty_out(vty, "%s", VTY_NEWLINE);
3000
3001 vty_out(vty, "Stdout logging: ");
3002 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3003 vty_out(vty, "disabled");
3004 else
3005 vty_out(vty, "level %s",
3006 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3007 vty_out(vty, "%s", VTY_NEWLINE);
3008
3009 vty_out(vty, "Monitor logging: ");
3010 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3011 vty_out(vty, "disabled");
3012 else
3013 vty_out(vty, "level %s",
3014 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3015 vty_out(vty, "%s", VTY_NEWLINE);
3016
3017 vty_out(vty, "File logging: ");
3018 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
3019 vty_out(vty, "disabled");
3020 else
3021 vty_out(vty, "level %s, filename %s",
3022 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3023 zl->filename);
3024 vty_out(vty, "%s", VTY_NEWLINE);
3025
3026 vty_out(vty, "Protocol name: %s%s",
3027 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3028 vty_out(vty, "Record priority: %s%s",
3029 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
3030
3031 return CMD_SUCCESS;
3032}
3033
3034DEFUN(config_log_stdout,
3035 config_log_stdout_cmd,
3036 "log stdout", "Logging control\n" "Set stdout logging level\n")
3037{
3038 zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3039 return CMD_SUCCESS;
3040}
3041
3042DEFUN(config_log_stdout_level,
3043 config_log_stdout_level_cmd,
3044 "log stdout " LOG_LEVELS,
3045 "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
3046{
3047 int level;
3048
3049 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3050 return CMD_ERR_NO_MATCH;
3051 zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
3052 return CMD_SUCCESS;
3053}
3054
3055DEFUN(no_config_log_stdout,
3056 no_config_log_stdout_cmd,
3057 "no log stdout [LEVEL]",
3058 NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
3059{
3060 zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
3061 return CMD_SUCCESS;
3062}
3063
3064DEFUN(config_log_monitor,
3065 config_log_monitor_cmd,
3066 "log monitor",
3067 "Logging control\n" "Set terminal line (monitor) logging level\n")
3068{
3069 zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3070 return CMD_SUCCESS;
3071}
3072
3073DEFUN(config_log_monitor_level,
3074 config_log_monitor_level_cmd,
3075 "log monitor " LOG_LEVELS,
3076 "Logging control\n"
3077 "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
3078{
3079 int level;
3080
3081 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3082 return CMD_ERR_NO_MATCH;
3083 zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
3084 return CMD_SUCCESS;
3085}
3086
3087DEFUN(no_config_log_monitor,
3088 no_config_log_monitor_cmd,
3089 "no log monitor [LEVEL]",
3090 NO_STR
3091 "Logging control\n"
3092 "Disable terminal line (monitor) logging\n" "Logging level\n")
3093{
3094 zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3095 return CMD_SUCCESS;
3096}
3097
3098static int set_log_file(struct vty *vty, const char *fname, int loglevel)
3099{
3100 int ret;
3101 char *p = NULL;
3102 const char *fullpath;
3103
3104 /* Path detection. */
3105 if (!IS_DIRECTORY_SEP(*fname)) {
3106 char cwd[MAXPATHLEN + 1];
3107 cwd[MAXPATHLEN] = '\0';
3108
3109 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3110 zlog_err("config_log_file: Unable to alloc mem!");
3111 return CMD_WARNING;
3112 }
3113
3114 if ((p = _talloc_zero(tall_vcmd_ctx,
3115 strlen(cwd) + strlen(fname) + 2),
3116 "set_log_file")
3117 == NULL) {
3118 zlog_err("config_log_file: Unable to alloc mem!");
3119 return CMD_WARNING;
3120 }
3121 sprintf(p, "%s/%s", cwd, fname);
3122 fullpath = p;
3123 } else
3124 fullpath = fname;
3125
3126 ret = zlog_set_file(NULL, fullpath, loglevel);
3127
3128 if (p)
3129 talloc_free(p);
3130
3131 if (!ret) {
3132 vty_out(vty, "can't open logfile %s\n", fname);
3133 return CMD_WARNING;
3134 }
3135
3136 if (host.logfile)
3137 talloc_free(host.logfile);
3138
3139 host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
3140
3141 return CMD_SUCCESS;
3142}
3143
3144DEFUN(config_log_file,
3145 config_log_file_cmd,
3146 "log file FILENAME",
3147 "Logging control\n" "Logging to file\n" "Logging filename\n")
3148{
3149 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3150}
3151
3152DEFUN(config_log_file_level,
3153 config_log_file_level_cmd,
3154 "log file FILENAME " LOG_LEVELS,
3155 "Logging control\n"
3156 "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
3157{
3158 int level;
3159
3160 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3161 return CMD_ERR_NO_MATCH;
3162 return set_log_file(vty, argv[0], level);
3163}
3164
3165DEFUN(no_config_log_file,
3166 no_config_log_file_cmd,
3167 "no log file [FILENAME]",
3168 NO_STR
3169 "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
3170{
3171 zlog_reset_file(NULL);
3172
3173 if (host.logfile)
3174 talloc_free(host.logfile);
3175
3176 host.logfile = NULL;
3177
3178 return CMD_SUCCESS;
3179}
3180
3181ALIAS(no_config_log_file,
3182 no_config_log_file_level_cmd,
3183 "no log file FILENAME LEVEL",
3184 NO_STR
3185 "Logging control\n"
3186 "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
3187
3188 DEFUN(config_log_syslog,
3189 config_log_syslog_cmd,
3190 "log syslog", "Logging control\n" "Set syslog logging level\n")
3191{
3192 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3193 return CMD_SUCCESS;
3194}
3195
3196DEFUN(config_log_syslog_level,
3197 config_log_syslog_level_cmd,
3198 "log syslog " LOG_LEVELS,
3199 "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
3200{
3201 int level;
3202
3203 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3204 return CMD_ERR_NO_MATCH;
3205 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
3206 return CMD_SUCCESS;
3207}
3208
3209DEFUN_DEPRECATED(config_log_syslog_facility,
3210 config_log_syslog_facility_cmd,
3211 "log syslog facility " LOG_FACILITIES,
3212 "Logging control\n"
3213 "Logging goes to syslog\n"
3214 "(Deprecated) Facility parameter for syslog messages\n"
3215 LOG_FACILITY_DESC)
3216{
3217 int facility;
3218
3219 if ((facility = facility_match(argv[0])) < 0)
3220 return CMD_ERR_NO_MATCH;
3221
3222 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3223 zlog_default->facility = facility;
3224 return CMD_SUCCESS;
3225}
3226
3227DEFUN(no_config_log_syslog,
3228 no_config_log_syslog_cmd,
3229 "no log syslog [LEVEL]",
3230 NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
3231{
3232 zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
3233 return CMD_SUCCESS;
3234}
3235
3236ALIAS(no_config_log_syslog,
3237 no_config_log_syslog_facility_cmd,
3238 "no log syslog facility " LOG_FACILITIES,
3239 NO_STR
3240 "Logging control\n"
3241 "Logging goes to syslog\n"
3242 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3243
3244 DEFUN(config_log_facility,
3245 config_log_facility_cmd,
3246 "log facility " LOG_FACILITIES,
3247 "Logging control\n"
3248 "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
3249{
3250 int facility;
3251
3252 if ((facility = facility_match(argv[0])) < 0)
3253 return CMD_ERR_NO_MATCH;
3254 zlog_default->facility = facility;
3255 return CMD_SUCCESS;
3256}
3257
3258DEFUN(no_config_log_facility,
3259 no_config_log_facility_cmd,
3260 "no log facility [FACILITY]",
3261 NO_STR
3262 "Logging control\n"
3263 "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
3264{
3265 zlog_default->facility = LOG_DAEMON;
3266 return CMD_SUCCESS;
3267}
3268
3269DEFUN_DEPRECATED(config_log_trap,
3270 config_log_trap_cmd,
3271 "log trap " LOG_LEVELS,
3272 "Logging control\n"
3273 "(Deprecated) Set logging level and default for all destinations\n"
3274 LOG_LEVEL_DESC)
3275{
3276 int new_level;
3277 int i;
3278
3279 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3280 return CMD_ERR_NO_MATCH;
3281
3282 zlog_default->default_lvl = new_level;
3283 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3284 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3285 zlog_default->maxlvl[i] = new_level;
3286 return CMD_SUCCESS;
3287}
3288
3289DEFUN_DEPRECATED(no_config_log_trap,
3290 no_config_log_trap_cmd,
3291 "no log trap [LEVEL]",
3292 NO_STR
3293 "Logging control\n"
3294 "Permit all logging information\n" "Logging level\n")
3295{
3296 zlog_default->default_lvl = LOG_DEBUG;
3297 return CMD_SUCCESS;
3298}
3299
3300DEFUN(config_log_record_priority,
3301 config_log_record_priority_cmd,
3302 "log record-priority",
3303 "Logging control\n"
3304 "Log the priority of the message within the message\n")
3305{
3306 zlog_default->record_priority = 1;
3307 return CMD_SUCCESS;
3308}
3309
3310DEFUN(no_config_log_record_priority,
3311 no_config_log_record_priority_cmd,
3312 "no log record-priority",
3313 NO_STR
3314 "Logging control\n"
3315 "Do not log the priority of the message within the message\n")
3316{
3317 zlog_default->record_priority = 0;
3318 return CMD_SUCCESS;
3319}
3320#endif
3321
3322DEFUN(banner_motd_file,
3323 banner_motd_file_cmd,
3324 "banner motd file [FILE]",
3325 "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
3326{
3327 if (host.motdfile)
3328 talloc_free(host.motdfile);
3329 host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
3330
3331 return CMD_SUCCESS;
3332}
3333
3334DEFUN(banner_motd_default,
3335 banner_motd_default_cmd,
3336 "banner motd default",
3337 "Set banner string\n" "Strings for motd\n" "Default string\n")
3338{
3339 host.motd = default_motd;
3340 return CMD_SUCCESS;
3341}
3342
3343DEFUN(no_banner_motd,
3344 no_banner_motd_cmd,
3345 "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
3346{
3347 host.motd = NULL;
3348 if (host.motdfile)
3349 talloc_free(host.motdfile);
3350 host.motdfile = NULL;
3351 return CMD_SUCCESS;
3352}
3353
3354/* Set config filename. Called from vty.c */
3355void host_config_set(const char *filename)
3356{
3357 host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
3358}
3359
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003360void install_default(int node)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003361{
3362 install_element(node, &config_help_cmd);
3363 install_element(node, &config_list_cmd);
3364
3365 install_element(node, &config_write_terminal_cmd);
3366 install_element(node, &config_write_file_cmd);
3367 install_element(node, &config_write_memory_cmd);
3368 install_element(node, &config_write_cmd);
3369 install_element(node, &show_running_config_cmd);
3370}
3371
Holger Hans Peter Freythera9e52522015-08-02 02:14:07 +00003372void vty_install_default(int node)
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003373{
3374 install_default(node);
3375
3376 install_element(node, &config_exit_cmd);
3377
3378 if (node >= CONFIG_NODE) {
3379 /* It's not a top node. */
3380 install_element(node, &config_end_cmd);
3381 }
3382}
3383
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003384/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003385 * Write the current running config to a given file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003386 * \param[in] vty the vty of the code
3387 * \param[in] filename where to store the file
3388 * \return 0 in case of success.
3389 *
3390 * If the filename already exists create a filename.sav
3391 * version with the current code.
3392 *
3393 */
3394int osmo_vty_write_config_file(const char *filename)
3395{
3396 char *failed_file;
3397 int rc;
3398
3399 rc = write_config_file(filename, &failed_file);
3400 talloc_free(failed_file);
3401 return rc;
3402}
3403
3404/**
Neels Hofmeyr87e45502017-06-20 00:17:59 +02003405 * Save the current state to the config file
Holger Hans Peter Freyther738f1332012-03-24 18:26:24 +01003406 * \return 0 in case of success.
3407 *
3408 * If the filename already exists create a filename.sav
3409 * version with the current code.
3410 *
3411 */
3412int osmo_vty_save_config_file(void)
3413{
3414 char *failed_file;
3415 int rc;
3416
3417 if (host.config == NULL)
3418 return -7;
3419
3420 rc = write_config_file(host.config, &failed_file);
3421 talloc_free(failed_file);
3422 return rc;
3423}
3424
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003425/* Initialize command interface. Install basic nodes and commands. */
3426void cmd_init(int terminal)
3427{
3428 /* Allocate initial top vector of commands. */
3429 cmdvec = vector_init(VECTOR_MIN_SIZE);
3430
3431 /* Default host value settings. */
3432 host.name = NULL;
3433 host.password = NULL;
3434 host.enable = NULL;
3435 host.logfile = NULL;
3436 host.config = NULL;
3437 host.lines = -1;
3438 host.motd = default_motd;
3439 host.motdfile = NULL;
3440
3441 /* Install top nodes. */
3442 install_node(&view_node, NULL);
3443 install_node(&enable_node, NULL);
3444 install_node(&auth_node, NULL);
3445 install_node(&auth_enable_node, NULL);
3446 install_node(&config_node, config_write_host);
3447
3448 /* Each node's basic commands. */
3449 install_element(VIEW_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003450 install_element(VIEW_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003451 if (terminal) {
3452 install_element(VIEW_NODE, &config_list_cmd);
3453 install_element(VIEW_NODE, &config_exit_cmd);
3454 install_element(VIEW_NODE, &config_help_cmd);
3455 install_element(VIEW_NODE, &config_enable_cmd);
3456 install_element(VIEW_NODE, &config_terminal_length_cmd);
3457 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
3458 install_element(VIEW_NODE, &echo_cmd);
3459 }
3460
3461 if (terminal) {
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003462 vty_install_default(ENABLE_NODE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003463 install_element(ENABLE_NODE, &config_disable_cmd);
3464 install_element(ENABLE_NODE, &config_terminal_cmd);
3465 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
3466 }
3467 install_element (ENABLE_NODE, &show_startup_config_cmd);
3468 install_element(ENABLE_NODE, &show_version_cmd);
Holger Hans Peter Freyther8297c812011-11-18 23:14:24 +01003469 install_element(ENABLE_NODE, &show_online_help_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003470
3471 if (terminal) {
3472 install_element(ENABLE_NODE, &config_terminal_length_cmd);
3473 install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
3474 install_element(ENABLE_NODE, &echo_cmd);
3475
Jacob Erlbeck0c987bd2013-09-06 16:52:00 +02003476 vty_install_default(CONFIG_NODE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003477 }
3478
3479 install_element(CONFIG_NODE, &hostname_cmd);
3480 install_element(CONFIG_NODE, &no_hostname_cmd);
3481
3482 if (terminal) {
3483 install_element(CONFIG_NODE, &password_cmd);
3484 install_element(CONFIG_NODE, &password_text_cmd);
3485 install_element(CONFIG_NODE, &enable_password_cmd);
3486 install_element(CONFIG_NODE, &enable_password_text_cmd);
3487 install_element(CONFIG_NODE, &no_enable_password_cmd);
3488
3489#ifdef VTY_CRYPT_PW
3490 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
3491 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
3492#endif
3493 install_element(CONFIG_NODE, &banner_motd_default_cmd);
3494 install_element(CONFIG_NODE, &banner_motd_file_cmd);
3495 install_element(CONFIG_NODE, &no_banner_motd_cmd);
3496 install_element(CONFIG_NODE, &service_terminal_length_cmd);
3497 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
3498
3499 }
3500 srand(time(NULL));
3501}
Harald Welte7acb30c2011-08-17 17:13:48 +02003502
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02003503/*! @} */