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