]> git.proxmox.com Git - mirror_frr.git/blame - lib/command.c
bgpd: speed up "no-hit" withdraws for routeservers
[mirror_frr.git] / lib / command.c
CommitLineData
274a4a44 1/*
274a4a44 2 Command interpreter routine for virtual terminal [aka TeletYpe]
718e3744 3 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
cd40b329
CF
4 Copyright (C) 2013 by Open Source Routing.
5 Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
718e3744 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
21Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22Boston, MA 02111-1307, USA. */
23
24#include <zebra.h>
25
b21b19c5 26
718e3744 27#include "memory.h"
28#include "log.h"
5e4fa164 29#include <lib/version.h>
9ab6812d 30#include "thread.h"
b21b19c5 31#include "vector.h"
32#include "vty.h"
33#include "command.h"
354d119a 34#include "workqueue.h"
19dc275e 35#include "vrf.h"
718e3744 36
37/* Command vector which includes some level of command lists. Normally
38 each daemon maintains each own cmdvec. */
eb820afe 39vector cmdvec = NULL;
718e3744 40
cd40b329 41struct cmd_token token_cr;
228da428
CC
42char *command_cr = NULL;
43
cd40b329
CF
44enum filter_type
45{
46 FILTER_RELAXED,
47 FILTER_STRICT
48};
49
50enum matcher_rv
51{
52 MATCHER_OK,
53 MATCHER_COMPLETE,
54 MATCHER_INCOMPLETE,
55 MATCHER_NO_MATCH,
56 MATCHER_AMBIGUOUS,
57 MATCHER_EXCEED_ARGC_MAX
58};
59
60#define MATCHER_ERROR(matcher_rv) \
61 ( (matcher_rv) == MATCHER_INCOMPLETE \
62 || (matcher_rv) == MATCHER_NO_MATCH \
63 || (matcher_rv) == MATCHER_AMBIGUOUS \
64 || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
65 )
66
718e3744 67/* Host information structure. */
68struct host host;
69
718e3744 70/* Standard command node structures. */
7fc626de 71static struct cmd_node auth_node =
718e3744 72{
73 AUTH_NODE,
74 "Password: ",
75};
76
7fc626de 77static struct cmd_node view_node =
718e3744 78{
79 VIEW_NODE,
80 "%s> ",
81};
82
7fc626de 83static struct cmd_node restricted_node =
62687ff1
PJ
84{
85 RESTRICTED_NODE,
86 "%s$ ",
87};
88
7fc626de 89static struct cmd_node auth_enable_node =
718e3744 90{
91 AUTH_ENABLE_NODE,
92 "Password: ",
93};
94
7fc626de 95static struct cmd_node enable_node =
718e3744 96{
97 ENABLE_NODE,
98 "%s# ",
99};
100
7fc626de 101static struct cmd_node config_node =
718e3744 102{
103 CONFIG_NODE,
104 "%s(config)# ",
105 1
106};
6590f2c3 107
108/* Default motd string. */
2d362d10 109static const char *default_motd =
6590f2c3 110"\r\n\
111Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
112" QUAGGA_COPYRIGHT "\r\n\
0be793e6 113" GIT_INFO "\r\n";
6590f2c3 114
274a4a44 115
2d362d10 116static const struct facility_map {
274a4a44 117 int facility;
118 const char *name;
119 size_t match;
120} syslog_facilities[] =
121 {
122 { LOG_KERN, "kern", 1 },
123 { LOG_USER, "user", 2 },
124 { LOG_MAIL, "mail", 1 },
125 { LOG_DAEMON, "daemon", 1 },
126 { LOG_AUTH, "auth", 1 },
127 { LOG_SYSLOG, "syslog", 1 },
128 { LOG_LPR, "lpr", 2 },
129 { LOG_NEWS, "news", 1 },
130 { LOG_UUCP, "uucp", 2 },
131 { LOG_CRON, "cron", 1 },
132#ifdef LOG_FTP
133 { LOG_FTP, "ftp", 1 },
134#endif
135 { LOG_LOCAL0, "local0", 6 },
136 { LOG_LOCAL1, "local1", 6 },
137 { LOG_LOCAL2, "local2", 6 },
138 { LOG_LOCAL3, "local3", 6 },
139 { LOG_LOCAL4, "local4", 6 },
140 { LOG_LOCAL5, "local5", 6 },
141 { LOG_LOCAL6, "local6", 6 },
142 { LOG_LOCAL7, "local7", 6 },
143 { 0, NULL, 0 },
144 };
145
146static const char *
147facility_name(int facility)
148{
2d362d10 149 const struct facility_map *fm;
274a4a44 150
151 for (fm = syslog_facilities; fm->name; fm++)
152 if (fm->facility == facility)
153 return fm->name;
154 return "";
155}
156
157static int
158facility_match(const char *str)
159{
2d362d10 160 const struct facility_map *fm;
274a4a44 161
162 for (fm = syslog_facilities; fm->name; fm++)
163 if (!strncmp(str,fm->name,fm->match))
164 return fm->facility;
165 return -1;
166}
167
168static int
169level_match(const char *s)
170{
171 int level ;
172
173 for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )
174 if (!strncmp (s, zlog_priority[level], 2))
175 return level;
176 return ZLOG_DISABLED;
177}
178
cb585b65 179/* This is called from main when a daemon is invoked with -v or --version. */
6590f2c3 180void
181print_version (const char *progname)
182{
cb585b65 183 printf ("%s version %s\n", progname, QUAGGA_VERSION);
184 printf ("%s\n", QUAGGA_COPYRIGHT);
80db5ac1 185 printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS);
6590f2c3 186}
187
6b0655a2 188
718e3744 189/* Utility function to concatenate argv argument into a single string
190 with inserting ' ' character between each argument. */
191char *
42d49865 192argv_concat (const char **argv, int argc, int shift)
718e3744 193{
194 int i;
f6834d4c 195 size_t len;
718e3744 196 char *str;
f6834d4c 197 char *p;
718e3744 198
f6834d4c 199 len = 0;
200 for (i = shift; i < argc; i++)
201 len += strlen(argv[i])+1;
202 if (!len)
203 return NULL;
204 p = str = XMALLOC(MTYPE_TMP, len);
718e3744 205 for (i = shift; i < argc; i++)
206 {
f6834d4c 207 size_t arglen;
208 memcpy(p, argv[i], (arglen = strlen(argv[i])));
209 p += arglen;
210 *p++ = ' ';
718e3744 211 }
f6834d4c 212 *(p-1) = '\0';
718e3744 213 return str;
214}
215
216/* Install top node of command vector. */
217void
218install_node (struct cmd_node *node,
219 int (*func) (struct vty *))
220{
221 vector_set_index (cmdvec, node->node, node);
222 node->func = func;
223 node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
224}
225
718e3744 226/* Breaking up string into each command piece. I assume given
227 character is separated by a space character. Return value is a
228 vector which includes char ** data element. */
229vector
ea8e9d97 230cmd_make_strvec (const char *string)
718e3744 231{
ea8e9d97 232 const char *cp, *start;
233 char *token;
718e3744 234 int strlen;
235 vector strvec;
236
237 if (string == NULL)
238 return NULL;
239
240 cp = string;
241
242 /* Skip white spaces. */
243 while (isspace ((int) *cp) && *cp != '\0')
244 cp++;
245
246 /* Return if there is only white spaces */
247 if (*cp == '\0')
248 return NULL;
249
250 if (*cp == '!' || *cp == '#')
251 return NULL;
252
253 /* Prepare return vector. */
254 strvec = vector_init (VECTOR_MIN_SIZE);
255
256 /* Copy each command piece and set into vector. */
257 while (1)
258 {
259 start = cp;
260 while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
261 *cp != '\0')
262 cp++;
263 strlen = cp - start;
264 token = XMALLOC (MTYPE_STRVEC, strlen + 1);
265 memcpy (token, start, strlen);
266 *(token + strlen) = '\0';
267 vector_set (strvec, token);
268
269 while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
270 *cp != '\0')
271 cp++;
272
273 if (*cp == '\0')
274 return strvec;
275 }
276}
277
278/* Free allocated string vector. */
279void
280cmd_free_strvec (vector v)
281{
8c328f11 282 unsigned int i;
718e3744 283 char *cp;
284
285 if (!v)
286 return;
287
55468c86 288 for (i = 0; i < vector_active (v); i++)
718e3744 289 if ((cp = vector_slot (v, i)) != NULL)
290 XFREE (MTYPE_STRVEC, cp);
291
292 vector_free (v);
293}
294
cd40b329
CF
295struct format_parser_state
296{
297 vector topvect; /* Top level vector */
298 vector intvect; /* Intermediate level vector, used when there's
299 * a multiple in a keyword. */
300 vector curvect; /* current vector where read tokens should be
301 appended. */
302
303 const char *string; /* pointer to command string, not modified */
304 const char *cp; /* pointer in command string, moved along while
305 parsing */
306 const char *dp; /* pointer in description string, moved along while
307 parsing */
308
309 int in_keyword; /* flag to remember if we are in a keyword group */
310 int in_multiple; /* flag to remember if we are in a multiple group */
311 int just_read_word; /* flag to remember if the last thing we red was a
312 * real word and not some abstract token */
313};
314
315static void
316format_parser_error(struct format_parser_state *state, const char *message)
317{
318 int offset = state->cp - state->string + 1;
319
320 fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string);
321 fprintf(stderr, " %*c\n", offset, '^');
322 fprintf(stderr, "%s at offset %d.\n", message, offset);
323 fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n");
324 exit(1);
325}
326
274a4a44 327static char *
cd40b329 328format_parser_desc_str(struct format_parser_state *state)
718e3744 329{
6ad96ea1 330 const char *cp, *start;
331 char *token;
718e3744 332 int strlen;
cd40b329
CF
333
334 cp = state->dp;
718e3744 335
336 if (cp == NULL)
337 return NULL;
338
339 /* Skip white spaces. */
340 while (isspace ((int) *cp) && *cp != '\0')
341 cp++;
342
343 /* Return if there is only white spaces */
344 if (*cp == '\0')
345 return NULL;
346
347 start = cp;
348
349 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
350 cp++;
351
352 strlen = cp - start;
cd40b329 353 token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1);
718e3744 354 memcpy (token, start, strlen);
355 *(token + strlen) = '\0';
356
cd40b329 357 state->dp = cp;
718e3744 358
359 return token;
360}
361
cd40b329
CF
362static void
363format_parser_begin_keyword(struct format_parser_state *state)
718e3744 364{
cd40b329
CF
365 struct cmd_token *token;
366 vector keyword_vect;
718e3744 367
cd40b329
CF
368 if (state->in_keyword
369 || state->in_multiple)
370 format_parser_error(state, "Unexpected '{'");
718e3744 371
cd40b329
CF
372 state->cp++;
373 state->in_keyword = 1;
718e3744 374
cd40b329
CF
375 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
376 token->type = TOKEN_KEYWORD;
377 token->keyword = vector_init(VECTOR_MIN_SIZE);
718e3744 378
cd40b329
CF
379 keyword_vect = vector_init(VECTOR_MIN_SIZE);
380 vector_set(token->keyword, keyword_vect);
718e3744 381
cd40b329
CF
382 vector_set(state->curvect, token);
383 state->curvect = keyword_vect;
384}
718e3744 385
cd40b329
CF
386static void
387format_parser_begin_multiple(struct format_parser_state *state)
388{
389 struct cmd_token *token;
718e3744 390
cd40b329
CF
391 if (state->in_keyword == 1)
392 format_parser_error(state, "Keyword starting with '('");
718e3744 393
cd40b329
CF
394 if (state->in_multiple)
395 format_parser_error(state, "Nested group");
718e3744 396
cd40b329
CF
397 state->cp++;
398 state->in_multiple = 1;
399 state->just_read_word = 0;
400
401 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
402 token->type = TOKEN_MULTIPLE;
403 token->multiple = vector_init(VECTOR_MIN_SIZE);
718e3744 404
cd40b329
CF
405 vector_set(state->curvect, token);
406 if (state->curvect != state->topvect)
407 state->intvect = state->curvect;
408 state->curvect = token->multiple;
409}
718e3744 410
cd40b329
CF
411static void
412format_parser_end_keyword(struct format_parser_state *state)
413{
414 if (state->in_multiple
415 || !state->in_keyword)
416 format_parser_error(state, "Unexpected '}'");
718e3744 417
cd40b329
CF
418 if (state->in_keyword == 1)
419 format_parser_error(state, "Empty keyword group");
718e3744 420
cd40b329
CF
421 state->cp++;
422 state->in_keyword = 0;
423 state->curvect = state->topvect;
424}
425
426static void
427format_parser_end_multiple(struct format_parser_state *state)
428{
429 char *dummy;
430
431 if (!state->in_multiple)
432 format_parser_error(state, "Unepexted ')'");
433
434 if (vector_active(state->curvect) == 0)
435 format_parser_error(state, "Empty multiple section");
436
437 if (!state->just_read_word)
438 {
439 /* There are constructions like
440 * 'show ip ospf database ... (self-originate|)'
441 * in use.
442 * The old parser reads a description string for the
443 * word '' between |) which will never match.
444 * Simulate this behvaior by dropping the next desc
445 * string in such a case. */
446
447 dummy = format_parser_desc_str(state);
448 XFREE(MTYPE_CMD_TOKENS, dummy);
718e3744 449 }
cd40b329
CF
450
451 state->cp++;
452 state->in_multiple = 0;
453
454 if (state->intvect)
455 state->curvect = state->intvect;
456 else
457 state->curvect = state->topvect;
718e3744 458}
459
cd40b329
CF
460static void
461format_parser_handle_pipe(struct format_parser_state *state)
718e3744 462{
cd40b329
CF
463 struct cmd_token *keyword_token;
464 vector keyword_vect;
718e3744 465
cd40b329 466 if (state->in_multiple)
718e3744 467 {
cd40b329
CF
468 state->just_read_word = 0;
469 state->cp++;
470 }
471 else if (state->in_keyword)
472 {
473 state->in_keyword = 1;
474 state->cp++;
475
476 keyword_token = vector_slot(state->topvect,
477 vector_active(state->topvect) - 1);
478 keyword_vect = vector_init(VECTOR_MIN_SIZE);
479 vector_set(keyword_token->keyword, keyword_vect);
480 state->curvect = keyword_vect;
481 }
482 else
483 {
484 format_parser_error(state, "Unexpected '|'");
485 }
486}
487
488static void
489format_parser_read_word(struct format_parser_state *state)
490{
491 const char *start;
492 int len;
493 char *cmd;
494 struct cmd_token *token;
495
496 start = state->cp;
497
498 while (state->cp[0] != '\0'
499 && !strchr("\r\n(){}|", state->cp[0])
500 && !isspace((int)state->cp[0]))
501 state->cp++;
502
503 len = state->cp - start;
504 cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1);
505 memcpy(cmd, start, len);
506 cmd[len] = '\0';
507
508 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
509 token->type = TOKEN_TERMINAL;
c117e027
DL
510 if (cmd[0] == '[')
511 token->terminal = TERMINAL_OPTION;
512 else if ((cmd[0] >= 'A' && cmd[0] <= 'Z') || (cmd[0] == '<'))
513 token->terminal = TERMINAL_VARIABLE;
514 else if (cmd[0] == '.')
515 token->terminal = TERMINAL_VARARG;
516 else if (cmd[0] == '<')
517 token->terminal = TERMINAL_RANGE;
518 else if (strcmp (cmd, "A.B.C.D") == 0)
519 token->terminal = TERMINAL_IPV4;
520 else if (strcmp (cmd, "A.B.C.D/M") == 0)
521 token->terminal = TERMINAL_IPV4_PREFIX;
522 else if (strcmp (cmd, "X:X::X:X") == 0)
523 token->terminal = TERMINAL_IPV6;
524 else if (strcmp (cmd, "X:X::X:X/M") == 0)
525 token->terminal = TERMINAL_IPV6_PREFIX;
526 else
527 token->terminal = TERMINAL_LITERAL;
528
cd40b329
CF
529 token->cmd = cmd;
530 token->desc = format_parser_desc_str(state);
531 vector_set(state->curvect, token);
532
533 if (state->in_keyword == 1)
534 state->in_keyword = 2;
535
536 state->just_read_word = 1;
537}
538
539/**
540 * Parse a given command format string and build a tree of tokens from
541 * it that is suitable to be used by the command subsystem.
542 *
543 * @param string Command format string.
544 * @param descstr Description string.
545 * @return A vector of struct cmd_token representing the given command,
546 * or NULL on error.
547 */
548static vector
549cmd_parse_format(const char *string, const char *descstr)
550{
551 struct format_parser_state state;
552
553 if (string == NULL)
554 return NULL;
555
556 memset(&state, 0, sizeof(state));
557 state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE);
558 state.cp = state.string = string;
559 state.dp = descstr;
560
561 while (1)
562 {
563 while (isspace((int)state.cp[0]) && state.cp[0] != '\0')
564 state.cp++;
565
566 switch (state.cp[0])
567 {
568 case '\0':
569 if (state.in_keyword
570 || state.in_multiple)
571 format_parser_error(&state, "Unclosed group/keyword");
572 return state.topvect;
573 case '{':
574 format_parser_begin_keyword(&state);
575 break;
576 case '(':
577 format_parser_begin_multiple(&state);
578 break;
579 case '}':
580 format_parser_end_keyword(&state);
581 break;
582 case ')':
583 format_parser_end_multiple(&state);
584 break;
585 case '|':
586 format_parser_handle_pipe(&state);
587 break;
588 default:
589 format_parser_read_word(&state);
590 }
718e3744 591 }
718e3744 592}
593
594/* Return prompt character of specified node. */
8c328f11 595const char *
718e3744 596cmd_prompt (enum node_type node)
597{
598 struct cmd_node *cnode;
599
600 cnode = vector_slot (cmdvec, node);
601 return cnode->prompt;
602}
603
604/* Install a command into a node. */
605void
606install_element (enum node_type ntype, struct cmd_element *cmd)
607{
608 struct cmd_node *cnode;
eb820afe 609
610 /* cmd_init hasn't been called */
611 if (!cmdvec)
612 return;
613
718e3744 614 cnode = vector_slot (cmdvec, ntype);
615
616 if (cnode == NULL)
617 {
618 fprintf (stderr, "Command node %d doesn't exist, please check it\n",
619 ntype);
620 exit (1);
621 }
622
623 vector_set (cnode->cmd_vector, cmd);
cd40b329
CF
624 if (cmd->tokens == NULL)
625 cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
718e3744 626}
627
2d362d10 628static const unsigned char itoa64[] =
718e3744 629"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
630
274a4a44 631static void
718e3744 632to64(char *s, long v, int n)
633{
634 while (--n >= 0)
635 {
636 *s++ = itoa64[v&0x3f];
637 v >>= 6;
638 }
639}
640
274a4a44 641static char *
642zencrypt (const char *passwd)
718e3744 643{
644 char salt[6];
645 struct timeval tv;
646 char *crypt (const char *, const char *);
647
648 gettimeofday(&tv,0);
649
650 to64(&salt[0], random(), 3);
651 to64(&salt[3], tv.tv_usec, 3);
652 salt[5] = '\0';
653
654 return crypt (passwd, salt);
655}
656
657/* This function write configuration of this host. */
274a4a44 658static int
718e3744 659config_write_host (struct vty *vty)
660{
661 if (host.name)
662 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
663
664 if (host.encrypt)
665 {
666 if (host.password_encrypt)
667 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
668 if (host.enable_encrypt)
669 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
670 }
671 else
672 {
673 if (host.password)
674 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
675 if (host.enable)
676 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
677 }
678
274a4a44 679 if (zlog_default->default_lvl != LOG_DEBUG)
82146b88 680 {
681 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
682 VTY_NEWLINE);
683 vty_out (vty, "log trap %s%s",
684 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
685 }
274a4a44 686
687 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
688 {
689 vty_out (vty, "log file %s", host.logfile);
690 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
691 vty_out (vty, " %s",
692 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
693 vty_out (vty, "%s", VTY_NEWLINE);
694 }
695
696 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
697 {
698 vty_out (vty, "log stdout");
699 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
700 vty_out (vty, " %s",
701 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
702 vty_out (vty, "%s", VTY_NEWLINE);
703 }
718e3744 704
274a4a44 705 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
706 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
707 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
708 vty_out(vty,"log monitor %s%s",
709 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
718e3744 710
274a4a44 711 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
12ab19f1 712 {
713 vty_out (vty, "log syslog");
274a4a44 714 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
715 vty_out (vty, " %s",
716 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
12ab19f1 717 vty_out (vty, "%s", VTY_NEWLINE);
718 }
274a4a44 719
720 if (zlog_default->facility != LOG_DAEMON)
721 vty_out (vty, "log facility %s%s",
722 facility_name(zlog_default->facility), VTY_NEWLINE);
718e3744 723
724 if (zlog_default->record_priority == 1)
725 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
726
1ed72e0b
AS
727 if (zlog_default->timestamp_precision > 0)
728 vty_out (vty, "log timestamp precision %d%s",
729 zlog_default->timestamp_precision, VTY_NEWLINE);
730
718e3744 731 if (host.advanced)
732 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
733
734 if (host.encrypt)
735 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
736
737 if (host.lines >= 0)
738 vty_out (vty, "service terminal-length %d%s", host.lines,
739 VTY_NEWLINE);
740
3b0c5d9a 741 if (host.motdfile)
742 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
743 else if (! host.motd)
718e3744 744 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
745
746 return 1;
747}
748
749/* Utility function for getting command vector. */
274a4a44 750static vector
718e3744 751cmd_node_vector (vector v, enum node_type ntype)
752{
753 struct cmd_node *cnode = vector_slot (v, ntype);
754 return cnode->cmd_vector;
755}
756
274a4a44 757#if 0
758/* Filter command vector by symbol. This function is not actually used;
759 * should it be deleted? */
760static int
718e3744 761cmd_filter_by_symbol (char *command, char *symbol)
762{
763 int i, lim;
764
765 if (strcmp (symbol, "IPV4_ADDRESS") == 0)
766 {
767 i = 0;
768 lim = strlen (command);
769 while (i < lim)
770 {
771 if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
772 return 1;
773 i++;
774 }
775 return 0;
776 }
777 if (strcmp (symbol, "STRING") == 0)
778 {
779 i = 0;
780 lim = strlen (command);
781 while (i < lim)
782 {
783 if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
784 return 1;
785 i++;
786 }
787 return 0;
788 }
789 if (strcmp (symbol, "IFNAME") == 0)
790 {
791 i = 0;
792 lim = strlen (command);
793 while (i < lim)
794 {
795 if (! isalnum ((int) command[i]))
796 return 1;
797 i++;
798 }
799 return 0;
800 }
801 return 0;
802}
274a4a44 803#endif
718e3744 804
805/* Completion match types. */
806enum match_type
807{
808 no_match,
809 extend_match,
810 ipv4_prefix_match,
811 ipv4_match,
812 ipv6_prefix_match,
813 ipv6_match,
814 range_match,
815 vararg_match,
816 partly_match,
817 exact_match
818};
819
274a4a44 820static enum match_type
8c328f11 821cmd_ipv4_match (const char *str)
718e3744 822{
8c328f11 823 const char *sp;
718e3744 824 int dots = 0, nums = 0;
825 char buf[4];
826
827 if (str == NULL)
828 return partly_match;
829
830 for (;;)
831 {
832 memset (buf, 0, sizeof (buf));
833 sp = str;
834 while (*str != '\0')
835 {
836 if (*str == '.')
837 {
838 if (dots >= 3)
839 return no_match;
840
841 if (*(str + 1) == '.')
842 return no_match;
843
844 if (*(str + 1) == '\0')
845 return partly_match;
846
847 dots++;
848 break;
849 }
850 if (!isdigit ((int) *str))
851 return no_match;
852
853 str++;
854 }
855
856 if (str - sp > 3)
857 return no_match;
858
859 strncpy (buf, sp, str - sp);
860 if (atoi (buf) > 255)
861 return no_match;
862
863 nums++;
864
865 if (*str == '\0')
866 break;
867
868 str++;
869 }
870
871 if (nums < 4)
872 return partly_match;
873
874 return exact_match;
875}
876
274a4a44 877static enum match_type
8c328f11 878cmd_ipv4_prefix_match (const char *str)
718e3744 879{
8c328f11 880 const char *sp;
718e3744 881 int dots = 0;
882 char buf[4];
883
884 if (str == NULL)
885 return partly_match;
886
887 for (;;)
888 {
889 memset (buf, 0, sizeof (buf));
890 sp = str;
891 while (*str != '\0' && *str != '/')
892 {
893 if (*str == '.')
894 {
895 if (dots == 3)
896 return no_match;
897
898 if (*(str + 1) == '.' || *(str + 1) == '/')
899 return no_match;
900
901 if (*(str + 1) == '\0')
902 return partly_match;
903
904 dots++;
905 break;
906 }
907
908 if (!isdigit ((int) *str))
909 return no_match;
910
911 str++;
912 }
913
914 if (str - sp > 3)
915 return no_match;
916
917 strncpy (buf, sp, str - sp);
918 if (atoi (buf) > 255)
919 return no_match;
920
921 if (dots == 3)
922 {
923 if (*str == '/')
924 {
925 if (*(str + 1) == '\0')
926 return partly_match;
927
928 str++;
929 break;
930 }
931 else if (*str == '\0')
932 return partly_match;
933 }
934
935 if (*str == '\0')
936 return partly_match;
937
938 str++;
939 }
940
941 sp = str;
942 while (*str != '\0')
943 {
944 if (!isdigit ((int) *str))
945 return no_match;
946
947 str++;
948 }
949
950 if (atoi (sp) > 32)
951 return no_match;
952
953 return exact_match;
954}
955
956#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
957#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
958#define STATE_START 1
959#define STATE_COLON 2
960#define STATE_DOUBLE 3
961#define STATE_ADDR 4
962#define STATE_DOT 5
963#define STATE_SLASH 6
964#define STATE_MASK 7
965
22e0a9e6 966#ifdef HAVE_IPV6
967
274a4a44 968static enum match_type
8c328f11 969cmd_ipv6_match (const char *str)
718e3744 970{
726f9b2b 971 struct sockaddr_in6 sin6_dummy;
972 int ret;
718e3744 973
974 if (str == NULL)
975 return partly_match;
976
977 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
978 return no_match;
979
726f9b2b 980 /* use inet_pton that has a better support,
981 * for example inet_pton can support the automatic addresses:
982 * ::1.2.3.4
983 */
984 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
985
986 if (ret == 1)
987 return exact_match;
988
7c9c6aeb 989 return no_match;
718e3744 990}
991
274a4a44 992static enum match_type
8c328f11 993cmd_ipv6_prefix_match (const char *str)
718e3744 994{
995 int state = STATE_START;
996 int colons = 0, nums = 0, double_colon = 0;
997 int mask;
8c328f11 998 const char *sp = NULL;
718e3744 999 char *endptr = NULL;
1000
1001 if (str == NULL)
1002 return partly_match;
1003
1004 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
1005 return no_match;
1006
1007 while (*str != '\0' && state != STATE_MASK)
1008 {
1009 switch (state)
1010 {
1011 case STATE_START:
1012 if (*str == ':')
1013 {
1014 if (*(str + 1) != ':' && *(str + 1) != '\0')
1015 return no_match;
1016 colons--;
1017 state = STATE_COLON;
1018 }
1019 else
1020 {
1021 sp = str;
1022 state = STATE_ADDR;
1023 }
1024
1025 continue;
1026 case STATE_COLON:
1027 colons++;
1028 if (*(str + 1) == '/')
1029 return no_match;
1030 else if (*(str + 1) == ':')
1031 state = STATE_DOUBLE;
1032 else
1033 {
1034 sp = str + 1;
1035 state = STATE_ADDR;
1036 }
1037 break;
1038 case STATE_DOUBLE:
1039 if (double_colon)
1040 return no_match;
1041
1042 if (*(str + 1) == ':')
1043 return no_match;
1044 else
1045 {
1046 if (*(str + 1) != '\0' && *(str + 1) != '/')
1047 colons++;
1048 sp = str + 1;
1049
1050 if (*(str + 1) == '/')
1051 state = STATE_SLASH;
1052 else
1053 state = STATE_ADDR;
1054 }
1055
1056 double_colon++;
1057 nums += 1;
1058 break;
1059 case STATE_ADDR:
1060 if (*(str + 1) == ':' || *(str + 1) == '.'
1061 || *(str + 1) == '\0' || *(str + 1) == '/')
1062 {
1063 if (str - sp > 3)
1064 return no_match;
1065
1066 for (; sp <= str; sp++)
1067 if (*sp == '/')
1068 return no_match;
1069
1070 nums++;
1071
1072 if (*(str + 1) == ':')
1073 state = STATE_COLON;
1074 else if (*(str + 1) == '.')
aa5cf24b
DL
1075 {
1076 if (colons || double_colon)
1077 state = STATE_DOT;
1078 else
1079 return no_match;
1080 }
718e3744 1081 else if (*(str + 1) == '/')
1082 state = STATE_SLASH;
1083 }
1084 break;
1085 case STATE_DOT:
1086 state = STATE_ADDR;
1087 break;
1088 case STATE_SLASH:
1089 if (*(str + 1) == '\0')
1090 return partly_match;
1091
1092 state = STATE_MASK;
1093 break;
1094 default:
1095 break;
1096 }
1097
1098 if (nums > 11)
1099 return no_match;
1100
1101 if (colons > 7)
1102 return no_match;
1103
1104 str++;
1105 }
1106
1107 if (state < STATE_MASK)
1108 return partly_match;
1109
1110 mask = strtol (str, &endptr, 10);
1111 if (*endptr != '\0')
1112 return no_match;
1113
1114 if (mask < 0 || mask > 128)
1115 return no_match;
1116
1117/* I don't know why mask < 13 makes command match partly.
1118 Forgive me to make this comments. I Want to set static default route
1119 because of lack of function to originate default in ospf6d; sorry
1120 yasu
1121 if (mask < 13)
1122 return partly_match;
1123*/
1124
1125 return exact_match;
1126}
1127
22e0a9e6 1128#endif /* HAVE_IPV6 */
1129
f702c39f 1130#define DECIMAL_STRLEN_MAX 20
718e3744 1131
274a4a44 1132static int
8c328f11 1133cmd_range_match (const char *range, const char *str)
718e3744 1134{
1135 char *p;
1136 char buf[DECIMAL_STRLEN_MAX + 1];
1137 char *endptr = NULL;
f702c39f 1138 signed long long min, max, val;
718e3744 1139
1140 if (str == NULL)
1141 return 1;
1142
f702c39f 1143 val = strtoll (str, &endptr, 10);
718e3744 1144 if (*endptr != '\0')
1145 return 0;
f702c39f 1146 val = llabs(val);
718e3744 1147
1148 range++;
1149 p = strchr (range, '-');
1150 if (p == NULL)
1151 return 0;
1152 if (p - range > DECIMAL_STRLEN_MAX)
1153 return 0;
1154 strncpy (buf, range, p - range);
1155 buf[p - range] = '\0';
f702c39f 1156 min = strtoll (buf, &endptr, 10);
718e3744 1157 if (*endptr != '\0')
1158 return 0;
1159
1160 range = p + 1;
1161 p = strchr (range, '>');
1162 if (p == NULL)
1163 return 0;
1164 if (p - range > DECIMAL_STRLEN_MAX)
1165 return 0;
1166 strncpy (buf, range, p - range);
1167 buf[p - range] = '\0';
f702c39f 1168 max = strtoll (buf, &endptr, 10);
718e3744 1169 if (*endptr != '\0')
1170 return 0;
1171
1172 if (val < min || val > max)
1173 return 0;
1174
1175 return 1;
1176}
1177
274a4a44 1178static enum match_type
cd40b329
CF
1179cmd_word_match(struct cmd_token *token,
1180 enum filter_type filter,
1181 const char *word)
718e3744 1182{
8c328f11 1183 const char *str;
718e3744 1184 enum match_type match_type;
909a2155 1185
cd40b329 1186 str = token->cmd;
718e3744 1187
cd40b329
CF
1188 if (filter == FILTER_RELAXED)
1189 if (!word || !strlen(word))
1190 return partly_match;
718e3744 1191
cd40b329
CF
1192 if (!word)
1193 return no_match;
909a2155 1194
c117e027 1195 switch (token->terminal)
cd40b329 1196 {
c117e027
DL
1197 case TERMINAL_VARARG:
1198 return vararg_match;
1199
1200 case TERMINAL_RANGE:
1201 if (cmd_range_match(str, word))
1202 return range_match;
1203 break;
1204
1205 case TERMINAL_IPV6:
1206 match_type = cmd_ipv6_match(word);
1207 if ((filter == FILTER_RELAXED && match_type != no_match)
cd40b329 1208 || (filter == FILTER_STRICT && match_type == exact_match))
c117e027
DL
1209 return ipv6_match;
1210 break;
1211
1212 case TERMINAL_IPV6_PREFIX:
1213 match_type = cmd_ipv6_prefix_match(word);
1214 if ((filter == FILTER_RELAXED && match_type != no_match)
1215 || (filter == FILTER_STRICT && match_type == exact_match))
1216 return ipv6_prefix_match;
1217 break;
1218
1219 case TERMINAL_IPV4:
1220 match_type = cmd_ipv4_match(word);
1221 if ((filter == FILTER_RELAXED && match_type != no_match)
1222 || (filter == FILTER_STRICT && match_type == exact_match))
1223 return ipv4_match;
1224 break;
1225
1226 case TERMINAL_IPV4_PREFIX:
1227 match_type = cmd_ipv4_prefix_match(word);
1228 if ((filter == FILTER_RELAXED && match_type != no_match)
1229 || (filter == FILTER_STRICT && match_type == exact_match))
1230 return ipv4_prefix_match;
1231 break;
1232
1233 case TERMINAL_OPTION:
1234 case TERMINAL_VARIABLE:
1235 return extend_match;
1236
1237 case TERMINAL_LITERAL:
1238 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1239 {
1240 if (!strcmp(str, word))
1241 return exact_match;
1242 return partly_match;
1243 }
1244 if (filter == FILTER_STRICT && !strcmp(str, word))
1245 return exact_match;
1246 break;
1247
1248 default:
1249 assert (0);
cd40b329 1250 }
909a2155 1251
cd40b329
CF
1252 return no_match;
1253}
909a2155 1254
cd40b329
CF
1255struct cmd_matcher
1256{
1257 struct cmd_element *cmd; /* The command element the matcher is using */
1258 enum filter_type filter; /* Whether to use strict or relaxed matching */
1259 vector vline; /* The tokenized commandline which is to be matched */
1260 unsigned int index; /* The index up to which matching should be done */
909a2155 1261
cd40b329
CF
1262 /* If set, construct a list of matches at the position given by index */
1263 enum match_type *match_type;
1264 vector *match;
1265
1266 unsigned int word_index; /* iterating over vline */
1267};
1268
1269static int
1270push_argument(int *argc, const char **argv, const char *arg)
1271{
1272 if (!arg || !strlen(arg))
1273 arg = NULL;
1274
1275 if (!argc || !argv)
1276 return 0;
1277
1278 if (*argc >= CMD_ARGC_MAX)
1279 return -1;
1280
1281 argv[(*argc)++] = arg;
1282 return 0;
1283}
1284
1285static void
1286cmd_matcher_record_match(struct cmd_matcher *matcher,
1287 enum match_type match_type,
1288 struct cmd_token *token)
1289{
1290 if (matcher->word_index != matcher->index)
1291 return;
1292
1293 if (matcher->match)
1294 {
1295 if (!*matcher->match)
1296 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1297 vector_set(*matcher->match, token);
1298 }
1299
1300 if (matcher->match_type)
1301 {
1302 if (match_type > *matcher->match_type)
1303 *matcher->match_type = match_type;
1304 }
1305}
1306
1307static int
1308cmd_matcher_words_left(struct cmd_matcher *matcher)
1309{
1310 return matcher->word_index < vector_active(matcher->vline);
1311}
1312
1313static const char*
1314cmd_matcher_get_word(struct cmd_matcher *matcher)
1315{
1316 assert(cmd_matcher_words_left(matcher));
1317
1318 return vector_slot(matcher->vline, matcher->word_index);
1319}
1320
1321static enum matcher_rv
1322cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1323 struct cmd_token *token,
1324 int *argc, const char **argv)
1325{
1326 const char *word;
1327 enum match_type word_match;
1328
1329 assert(token->type == TOKEN_TERMINAL);
1330
1331 if (!cmd_matcher_words_left(matcher))
1332 {
c117e027 1333 if (token->terminal == TERMINAL_OPTION)
cd40b329
CF
1334 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1335 else
1336 return MATCHER_INCOMPLETE;
1337 }
1338
1339 word = cmd_matcher_get_word(matcher);
1340 word_match = cmd_word_match(token, matcher->filter, word);
1341 if (word_match == no_match)
1342 return MATCHER_NO_MATCH;
1343
1344 /* We have to record the input word as argument if it matched
1345 * against a variable. */
c117e027
DL
1346 if (token->terminal == TERMINAL_VARARG
1347 || token->terminal == TERMINAL_VARIABLE
1348 || token->terminal == TERMINAL_OPTION)
cd40b329
CF
1349 {
1350 if (push_argument(argc, argv, word))
1351 return MATCHER_EXCEED_ARGC_MAX;
1352 }
1353
1354 cmd_matcher_record_match(matcher, word_match, token);
1355
1356 matcher->word_index++;
1357
1358 /* A vararg token should consume all left over words as arguments */
c117e027 1359 if (token->terminal == TERMINAL_VARARG)
cd40b329
CF
1360 while (cmd_matcher_words_left(matcher))
1361 {
1362 word = cmd_matcher_get_word(matcher);
1363 if (word && strlen(word))
1364 push_argument(argc, argv, word);
1365 matcher->word_index++;
718e3744 1366 }
cd40b329
CF
1367
1368 return MATCHER_OK;
718e3744 1369}
1370
cd40b329
CF
1371static enum matcher_rv
1372cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1373 struct cmd_token *token,
1374 int *argc, const char **argv)
1375{
1376 enum match_type multiple_match;
1377 unsigned int multiple_index;
1378 const char *word;
c5e0075f 1379 const char *arg = NULL;
cd40b329
CF
1380 struct cmd_token *word_token;
1381 enum match_type word_match;
1382
1383 assert(token->type == TOKEN_MULTIPLE);
1384
1385 multiple_match = no_match;
1386
1387 if (!cmd_matcher_words_left(matcher))
1388 return MATCHER_INCOMPLETE;
1389
1390 word = cmd_matcher_get_word(matcher);
1391 for (multiple_index = 0;
1392 multiple_index < vector_active(token->multiple);
1393 multiple_index++)
1394 {
1395 word_token = vector_slot(token->multiple, multiple_index);
1396
1397 word_match = cmd_word_match(word_token, matcher->filter, word);
1398 if (word_match == no_match)
1399 continue;
1400
1401 cmd_matcher_record_match(matcher, word_match, word_token);
1402
1403 if (word_match > multiple_match)
1404 {
1405 multiple_match = word_match;
1406 arg = word;
1407 }
1408 /* To mimic the behavior of the old command implementation, we
1409 * tolerate any ambiguities here :/ */
1410 }
1411
1412 matcher->word_index++;
1413
1414 if (multiple_match == no_match)
1415 return MATCHER_NO_MATCH;
1416
1417 if (push_argument(argc, argv, arg))
1418 return MATCHER_EXCEED_ARGC_MAX;
1419
1420 return MATCHER_OK;
1421}
1422
1423static enum matcher_rv
1424cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1425 struct cmd_token *token,
1426 vector args_vector)
718e3744 1427{
8c328f11 1428 unsigned int i;
cd40b329
CF
1429 unsigned long keyword_mask;
1430 unsigned int keyword_found;
1431 enum match_type keyword_match;
1432 enum match_type word_match;
1433 vector keyword_vector;
1434 struct cmd_token *word_token;
1435 const char *word;
1436 int keyword_argc;
1437 const char **keyword_argv;
24873f0c 1438 enum matcher_rv rv = MATCHER_OK;
cd40b329
CF
1439
1440 keyword_mask = 0;
1441 while (1)
1442 {
1443 if (!cmd_matcher_words_left(matcher))
1444 return MATCHER_OK;
909a2155 1445
cd40b329 1446 word = cmd_matcher_get_word(matcher);
718e3744 1447
cd40b329
CF
1448 keyword_found = -1;
1449 keyword_match = no_match;
1450 for (i = 0; i < vector_active(token->keyword); i++)
1451 {
1452 if (keyword_mask & (1 << i))
1453 continue;
1454
1455 keyword_vector = vector_slot(token->keyword, i);
1456 word_token = vector_slot(keyword_vector, 0);
1457
1458 word_match = cmd_word_match(word_token, matcher->filter, word);
1459 if (word_match == no_match)
1460 continue;
1461
1462 cmd_matcher_record_match(matcher, word_match, word_token);
1463
1464 if (word_match > keyword_match)
1465 {
1466 keyword_match = word_match;
1467 keyword_found = i;
1468 }
1469 else if (word_match == keyword_match)
1470 {
1471 if (matcher->word_index != matcher->index || args_vector)
1472 return MATCHER_AMBIGUOUS;
1473 }
1474 }
718e3744 1475
cd40b329
CF
1476 if (keyword_found == (unsigned int)-1)
1477 return MATCHER_NO_MATCH;
718e3744 1478
cd40b329 1479 matcher->word_index++;
909a2155 1480
cd40b329
CF
1481 if (matcher->word_index > matcher->index)
1482 return MATCHER_OK;
1483
1484 keyword_mask |= (1 << keyword_found);
1485
1486 if (args_vector)
1487 {
1488 keyword_argc = 0;
1489 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1490 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1491 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1492 keyword_argv[i] = (void*)-1;
1493 vector_set_index(args_vector, keyword_found, keyword_argv);
1494 }
1495 else
1496 {
1497 keyword_argv = NULL;
1498 }
1499
1500 keyword_vector = vector_slot(token->keyword, keyword_found);
1501 /* the keyword itself is at 0. We are only interested in the arguments,
1502 * so start counting at 1. */
1503 for (i = 1; i < vector_active(keyword_vector); i++)
1504 {
1505 word_token = vector_slot(keyword_vector, i);
1506
1507 switch (word_token->type)
1508 {
1509 case TOKEN_TERMINAL:
1510 rv = cmd_matcher_match_terminal(matcher, word_token,
1511 &keyword_argc, keyword_argv);
1512 break;
1513 case TOKEN_MULTIPLE:
1514 rv = cmd_matcher_match_multiple(matcher, word_token,
1515 &keyword_argc, keyword_argv);
1516 break;
1517 case TOKEN_KEYWORD:
1518 assert(!"Keywords should never be nested.");
1519 break;
1520 }
1521
1522 if (MATCHER_ERROR(rv))
1523 return rv;
1524
1525 if (matcher->word_index > matcher->index)
1526 return MATCHER_OK;
1527 }
1528 }
1529 /* not reached */
1530}
1531
1532static enum matcher_rv
1533cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1534 struct cmd_token *token,
1535 int *argc, const char **argv,
1536 vector keyword_args_vector)
1537{
1538 unsigned int i, j;
1539 const char **keyword_args;
1540 vector keyword_vector;
1541 struct cmd_token *word_token;
1542 const char *arg;
1543 enum matcher_rv rv;
1544
1545 rv = MATCHER_OK;
1546
1547 if (keyword_args_vector == NULL)
1548 return rv;
1549
1550 for (i = 0; i < vector_active(token->keyword); i++)
1551 {
1552 keyword_vector = vector_slot(token->keyword, i);
1553 keyword_args = vector_lookup(keyword_args_vector, i);
1554
1555 if (vector_active(keyword_vector) == 1)
1556 {
1557 /* this is a keyword without arguments */
1558 if (keyword_args)
1559 {
1560 word_token = vector_slot(keyword_vector, 0);
1561 arg = word_token->cmd;
1562 }
1563 else
1564 {
1565 arg = NULL;
1566 }
1567
1568 if (push_argument(argc, argv, arg))
1569 rv = MATCHER_EXCEED_ARGC_MAX;
1570 }
1571 else
1572 {
1573 /* this is a keyword with arguments */
1574 if (keyword_args)
1575 {
1576 /* the keyword was present, so just fill in the arguments */
1577 for (j = 0; keyword_args[j] != (void*)-1; j++)
1578 if (push_argument(argc, argv, keyword_args[j]))
1579 rv = MATCHER_EXCEED_ARGC_MAX;
1580 XFREE(MTYPE_TMP, keyword_args);
1581 }
1582 else
1583 {
1584 /* the keyword was not present, insert NULL for the arguments
1585 * the keyword would have taken. */
1586 for (j = 1; j < vector_active(keyword_vector); j++)
1587 {
1588 word_token = vector_slot(keyword_vector, j);
1589 if ((word_token->type == TOKEN_TERMINAL
c117e027
DL
1590 && (word_token->terminal == TERMINAL_VARARG
1591 || word_token->terminal == TERMINAL_VARIABLE
1592 || word_token->terminal == TERMINAL_OPTION))
cd40b329
CF
1593 || word_token->type == TOKEN_MULTIPLE)
1594 {
1595 if (push_argument(argc, argv, NULL))
1596 rv = MATCHER_EXCEED_ARGC_MAX;
1597 }
1598 }
1599 }
1600 }
1601 }
1602 vector_free(keyword_args_vector);
1603 return rv;
1604}
1605
1606static enum matcher_rv
1607cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1608 struct cmd_token *token,
1609 int *argc, const char **argv)
1610{
1611 vector keyword_args_vector;
1612 enum matcher_rv reader_rv;
1613 enum matcher_rv builder_rv;
1614
1615 assert(token->type == TOKEN_KEYWORD);
1616
1617 if (argc && argv)
1618 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1619 else
1620 keyword_args_vector = NULL;
1621
1622 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1623 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1624 argv, keyword_args_vector);
1625 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1626
1627 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1628 return builder_rv;
1629
1630 return reader_rv;
1631}
1632
1633static void
1634cmd_matcher_init(struct cmd_matcher *matcher,
1635 struct cmd_element *cmd,
1636 enum filter_type filter,
1637 vector vline,
1638 unsigned int index,
1639 enum match_type *match_type,
1640 vector *match)
1641{
1642 memset(matcher, 0, sizeof(*matcher));
1643
1644 matcher->cmd = cmd;
1645 matcher->filter = filter;
1646 matcher->vline = vline;
1647 matcher->index = index;
1648
1649 matcher->match_type = match_type;
1650 if (matcher->match_type)
1651 *matcher->match_type = no_match;
1652 matcher->match = match;
1653
1654 matcher->word_index = 0;
1655}
1656
1657static enum matcher_rv
1658cmd_element_match(struct cmd_element *cmd_element,
1659 enum filter_type filter,
1660 vector vline,
1661 unsigned int index,
1662 enum match_type *match_type,
1663 vector *match,
1664 int *argc,
1665 const char **argv)
1666{
1667 struct cmd_matcher matcher;
1668 unsigned int token_index;
24873f0c 1669 enum matcher_rv rv = MATCHER_OK;
cd40b329
CF
1670
1671 cmd_matcher_init(&matcher, cmd_element, filter,
1672 vline, index, match_type, match);
1673
1674 if (argc != NULL)
1675 *argc = 0;
1676
1677 for (token_index = 0;
1678 token_index < vector_active(cmd_element->tokens);
1679 token_index++)
1680 {
1681 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1682
1683 switch (token->type)
1684 {
1685 case TOKEN_TERMINAL:
1686 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1687 break;
1688 case TOKEN_MULTIPLE:
1689 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1690 break;
1691 case TOKEN_KEYWORD:
1692 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1693 }
1694
1695 if (MATCHER_ERROR(rv))
1696 return rv;
1697
1698 if (matcher.word_index > index)
1699 return MATCHER_OK;
1700 }
1701
1702 /* return MATCHER_COMPLETE if all words were consumed */
1703 if (matcher.word_index >= vector_active(vline))
1704 return MATCHER_COMPLETE;
1705
1706 /* return MATCHER_COMPLETE also if only an empty word is left. */
1707 if (matcher.word_index == vector_active(vline) - 1
1708 && (!vector_slot(vline, matcher.word_index)
1709 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1710 return MATCHER_COMPLETE;
1711
1712 return MATCHER_NO_MATCH; /* command is too long to match */
1713}
1714
1715/**
1716 * Filter a given vector of commands against a given commandline and
1717 * calculate possible completions.
1718 *
1719 * @param commands A vector of struct cmd_element*. Commands that don't
1720 * match against the given command line will be overwritten
1721 * with NULL in that vector.
1722 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1723 * determines how incomplete commands are handled, compare with
1724 * cmd_word_match for details.
1725 * @param vline A vector of char* containing the tokenized commandline.
1726 * @param index Only match up to the given token of the commandline.
1727 * @param match_type Record the type of the best match here.
1728 * @param matches Record the matches here. For each cmd_element in the commands
1729 * vector, a match vector will be created in the matches vector.
1730 * That vector will contain all struct command_token* of the
1731 * cmd_element which matched against the given vline at the given
1732 * index.
1733 * @return A code specifying if an error occured. If all went right, it's
1734 * CMD_SUCCESS.
1735 */
1736static int
1737cmd_vector_filter(vector commands,
1738 enum filter_type filter,
1739 vector vline,
1740 unsigned int index,
1741 enum match_type *match_type,
1742 vector *matches)
1743{
1744 unsigned int i;
1745 struct cmd_element *cmd_element;
1746 enum match_type best_match;
1747 enum match_type element_match;
1748 enum matcher_rv matcher_rv;
1749
1750 best_match = no_match;
1751 *matches = vector_init(VECTOR_MIN_SIZE);
1752
1753 for (i = 0; i < vector_active (commands); i++)
1754 if ((cmd_element = vector_slot (commands, i)) != NULL)
1755 {
1756 vector_set_index(*matches, i, NULL);
1757 matcher_rv = cmd_element_match(cmd_element, filter,
1758 vline, index,
1759 &element_match,
1760 (vector*)&vector_slot(*matches, i),
1761 NULL, NULL);
1762 if (MATCHER_ERROR(matcher_rv))
1763 {
1764 vector_slot(commands, i) = NULL;
1765 if (matcher_rv == MATCHER_AMBIGUOUS)
1766 return CMD_ERR_AMBIGUOUS;
1767 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1768 return CMD_ERR_EXEED_ARGC_MAX;
1769 }
1770 else if (element_match > best_match)
1771 {
1772 best_match = element_match;
1773 }
718e3744 1774 }
cd40b329
CF
1775 *match_type = best_match;
1776 return CMD_SUCCESS;
1777}
1778
1779/**
1780 * Check whether a given commandline is complete if used for a specific
1781 * cmd_element.
1782 *
1783 * @param cmd_element A cmd_element against which the commandline should be
1784 * checked.
1785 * @param vline The tokenized commandline.
1786 * @return 1 if the given commandline is complete, 0 otherwise.
1787 */
1788static int
1789cmd_is_complete(struct cmd_element *cmd_element,
1790 vector vline)
1791{
1792 enum matcher_rv rv;
1793
1794 rv = cmd_element_match(cmd_element,
1795 FILTER_RELAXED,
1796 vline, -1,
1797 NULL, NULL,
1798 NULL, NULL);
1799 return (rv == MATCHER_COMPLETE);
1800}
1801
1802/**
1803 * Parse a given commandline and construct a list of arguments for the
1804 * given command_element.
1805 *
1806 * @param cmd_element The cmd_element for which we want to construct arguments.
1807 * @param vline The tokenized commandline.
1808 * @param argc Where to store the argument count.
1809 * @param argv Where to store the argument list. Should be at least
1810 * CMD_ARGC_MAX elements long.
1811 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1812 */
1813static int
1814cmd_parse(struct cmd_element *cmd_element,
1815 vector vline,
1816 int *argc, const char **argv)
1817{
1818 enum matcher_rv rv = cmd_element_match(cmd_element,
1819 FILTER_RELAXED,
1820 vline, -1,
1821 NULL, NULL,
1822 argc, argv);
1823 switch (rv)
1824 {
1825 case MATCHER_COMPLETE:
1826 return CMD_SUCCESS;
1827
1828 case MATCHER_NO_MATCH:
1829 return CMD_ERR_NO_MATCH;
1830
1831 case MATCHER_AMBIGUOUS:
1832 return CMD_ERR_AMBIGUOUS;
1833
1834 case MATCHER_EXCEED_ARGC_MAX:
1835 return CMD_ERR_EXEED_ARGC_MAX;
1836
1837 default:
1838 return CMD_ERR_INCOMPLETE;
1839 }
718e3744 1840}
1841
1842/* Check ambiguous match */
274a4a44 1843static int
cd40b329
CF
1844is_cmd_ambiguous (vector cmd_vector,
1845 const char *command,
1846 vector matches,
1847 enum match_type type)
718e3744 1848{
8c328f11 1849 unsigned int i;
1850 unsigned int j;
1851 const char *str = NULL;
8c328f11 1852 const char *matched = NULL;
cd40b329
CF
1853 vector match_vector;
1854 struct cmd_token *cmd_token;
909a2155 1855
cd40b329
CF
1856 if (command == NULL)
1857 command = "";
1858
1859 for (i = 0; i < vector_active (matches); i++)
1860 if ((match_vector = vector_slot (matches, i)) != NULL)
718e3744 1861 {
1862 int match = 0;
1863
cd40b329
CF
1864 for (j = 0; j < vector_active (match_vector); j++)
1865 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
909a2155 1866 {
1867 enum match_type ret;
cd40b329
CF
1868
1869 assert(cmd_token->type == TOKEN_TERMINAL);
1870 if (cmd_token->type != TOKEN_TERMINAL)
1871 continue;
1872
1873 str = cmd_token->cmd;
718e3744 1874
909a2155 1875 switch (type)
1876 {
1877 case exact_match:
c117e027
DL
1878 if (!(cmd_token->terminal == TERMINAL_OPTION
1879 || cmd_token->terminal == TERMINAL_VARIABLE)
909a2155 1880 && strcmp (command, str) == 0)
718e3744 1881 match++;
909a2155 1882 break;
1883 case partly_match:
c117e027
DL
1884 if (!(cmd_token->terminal == TERMINAL_OPTION
1885 || cmd_token->terminal == TERMINAL_VARIABLE)
909a2155 1886 && strncmp (command, str, strlen (command)) == 0)
1887 {
1888 if (matched && strcmp (matched, str) != 0)
1889 return 1; /* There is ambiguous match. */
1890 else
1891 matched = str;
1892 match++;
1893 }
1894 break;
1895 case range_match:
1896 if (cmd_range_match (str, command))
1897 {
1898 if (matched && strcmp (matched, str) != 0)
1899 return 1;
1900 else
1901 matched = str;
1902 match++;
1903 }
1904 break;
1905#ifdef HAVE_IPV6
1906 case ipv6_match:
c117e027 1907 if (cmd_token->terminal == TERMINAL_IPV6)
718e3744 1908 match++;
909a2155 1909 break;
1910 case ipv6_prefix_match:
1911 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1912 {
1913 if (ret == partly_match)
1914 return 2; /* There is incomplete match. */
1915
1916 match++;
1917 }
1918 break;
1919#endif /* HAVE_IPV6 */
1920 case ipv4_match:
c117e027 1921 if (cmd_token->terminal == TERMINAL_IPV4)
718e3744 1922 match++;
909a2155 1923 break;
1924 case ipv4_prefix_match:
1925 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1926 {
1927 if (ret == partly_match)
1928 return 2; /* There is incomplete match. */
1929
1930 match++;
1931 }
1932 break;
1933 case extend_match:
c117e027
DL
1934 if (cmd_token->terminal == TERMINAL_OPTION
1935 || cmd_token->terminal == TERMINAL_VARIABLE)
718e3744 1936 match++;
909a2155 1937 break;
1938 case no_match:
1939 default:
1940 break;
1941 }
1942 }
1943 if (!match)
cd40b329 1944 vector_slot (cmd_vector, i) = NULL;
718e3744 1945 }
1946 return 0;
1947}
1948
1949/* If src matches dst return dst string, otherwise return NULL */
274a4a44 1950static const char *
c117e027 1951cmd_entry_function (const char *src, struct cmd_token *token)
718e3744 1952{
c117e027
DL
1953 const char *dst = token->cmd;
1954
718e3744 1955 /* Skip variable arguments. */
c117e027
DL
1956 switch (token->terminal)
1957 {
1958 case TERMINAL_OPTION:
1959 case TERMINAL_VARIABLE:
1960 case TERMINAL_VARARG:
1961 case TERMINAL_IPV4:
1962 case TERMINAL_IPV4_PREFIX:
1963 case TERMINAL_RANGE:
1964 return NULL;
1965 default:
1966 break;
1967 }
718e3744 1968
1969 /* In case of 'command \t', given src is NULL string. */
1970 if (src == NULL)
1971 return dst;
1972
1973 /* Matched with input string. */
1974 if (strncmp (src, dst, strlen (src)) == 0)
1975 return dst;
1976
1977 return NULL;
1978}
1979
1980/* If src matches dst return dst string, otherwise return NULL */
1981/* This version will return the dst string always if it is
1982 CMD_VARIABLE for '?' key processing */
274a4a44 1983static const char *
c117e027 1984cmd_entry_function_desc (const char *src, struct cmd_token *token)
718e3744 1985{
c117e027 1986 const char *dst = token->cmd;
718e3744 1987
c117e027 1988 switch (token->terminal)
718e3744 1989 {
c117e027
DL
1990 case TERMINAL_VARARG:
1991 return dst;
1992
1993 case TERMINAL_RANGE:
1994 if (cmd_range_match (dst, src))
1995 return dst;
1996 else
1997 return NULL;
1998
1999 case TERMINAL_IPV6:
2000 if (cmd_ipv6_match (src))
2001 return dst;
2002 else
2003 return NULL;
2004
2005 case TERMINAL_IPV6_PREFIX:
2006 if (cmd_ipv6_prefix_match (src))
2007 return dst;
2008 else
2009 return NULL;
2010
2011 case TERMINAL_IPV4:
2012 if (cmd_ipv4_match (src))
2013 return dst;
2014 else
2015 return NULL;
2016
2017 case TERMINAL_IPV4_PREFIX:
2018 if (cmd_ipv4_prefix_match (src))
2019 return dst;
2020 else
2021 return NULL;
2022
2023 /* Optional or variable commands always match on '?' */
2024 case TERMINAL_OPTION:
2025 case TERMINAL_VARIABLE:
2026 return dst;
2027
2028 case TERMINAL_LITERAL:
2029 /* In case of 'command \t', given src is NULL string. */
2030 if (src == NULL)
2031 return dst;
2032
2033 if (strncmp (src, dst, strlen (src)) == 0)
2034 return dst;
2035 else
2036 return NULL;
2037
2038 default:
2039 assert(0);
718e3744 2040 }
718e3744 2041}
2042
cd40b329
CF
2043/**
2044 * Check whether a string is already present in a vector of strings.
2045 * @param v A vector of char*.
2046 * @param str A char*.
2047 * @return 0 if str is already present in the vector, 1 otherwise.
2048 */
274a4a44 2049static int
8c328f11 2050cmd_unique_string (vector v, const char *str)
718e3744 2051{
8c328f11 2052 unsigned int i;
718e3744 2053 char *match;
2054
55468c86 2055 for (i = 0; i < vector_active (v); i++)
718e3744 2056 if ((match = vector_slot (v, i)) != NULL)
2057 if (strcmp (match, str) == 0)
2058 return 0;
2059 return 1;
2060}
2061
cd40b329
CF
2062/**
2063 * Check whether a struct cmd_token matching a given string is already
2064 * present in a vector of struct cmd_token.
2065 * @param v A vector of struct cmd_token*.
2066 * @param str A char* which should be searched for.
2067 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2068 * 1 otherwise.
2069 */
274a4a44 2070static int
8c328f11 2071desc_unique_string (vector v, const char *str)
718e3744 2072{
8c328f11 2073 unsigned int i;
cd40b329 2074 struct cmd_token *token;
718e3744 2075
55468c86 2076 for (i = 0; i < vector_active (v); i++)
cd40b329
CF
2077 if ((token = vector_slot (v, i)) != NULL)
2078 if (strcmp (token->cmd, str) == 0)
2079 return 0;
2080 return 1;
718e3744 2081}
2082
274a4a44 2083static int
b92938a7 2084cmd_try_do_shortcut (enum node_type node, char* first_word) {
2085 if ( first_word != NULL &&
2086 node != AUTH_NODE &&
2087 node != VIEW_NODE &&
2088 node != AUTH_ENABLE_NODE &&
2089 node != ENABLE_NODE &&
62687ff1 2090 node != RESTRICTED_NODE &&
b92938a7 2091 0 == strcmp( "do", first_word ) )
2092 return 1;
2093 return 0;
2094}
2095
cd40b329
CF
2096static void
2097cmd_matches_free(vector *matches)
2098{
2099 unsigned int i;
2100 vector cmd_matches;
2101
2102 for (i = 0; i < vector_active(*matches); i++)
2103 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2104 vector_free(cmd_matches);
2105 vector_free(*matches);
2106 *matches = NULL;
2107}
2108
2109static int
2110cmd_describe_cmp(const void *a, const void *b)
2111{
2112 const struct cmd_token *first = *(struct cmd_token * const *)a;
2113 const struct cmd_token *second = *(struct cmd_token * const *)b;
2114
2115 return strcmp(first->cmd, second->cmd);
2116}
2117
2118static void
2119cmd_describe_sort(vector matchvec)
2120{
2121 qsort(matchvec->index, vector_active(matchvec),
2122 sizeof(void*), cmd_describe_cmp);
2123}
2124
718e3744 2125/* '?' describe command support. */
274a4a44 2126static vector
b92938a7 2127cmd_describe_command_real (vector vline, struct vty *vty, int *status)
718e3744 2128{
b8961476 2129 unsigned int i;
718e3744 2130 vector cmd_vector;
2131#define INIT_MATCHVEC_SIZE 10
2132 vector matchvec;
2133 struct cmd_element *cmd_element;
b8961476 2134 unsigned int index;
54aba54c 2135 int ret;
2136 enum match_type match;
2137 char *command;
cd40b329
CF
2138 vector matches = NULL;
2139 vector match_vector;
16cf945a
DS
2140 uint32_t command_found = 0;
2141 const char *last_word;
718e3744 2142
2143 /* Set index. */
55468c86 2144 if (vector_active (vline) == 0)
b8961476 2145 {
2146 *status = CMD_ERR_NO_MATCH;
2147 return NULL;
2148 }
cd40b329
CF
2149
2150 index = vector_active (vline) - 1;
2151
718e3744 2152 /* Make copy vector of current node's command vector. */
2153 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2154
2155 /* Prepare match vector */
2156 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2157
cd40b329
CF
2158 /* Filter commands and build a list how they could possibly continue. */
2159 for (i = 0; i <= index; i++)
2160 {
2161 command = vector_slot (vline, i);
718e3744 2162
cd40b329
CF
2163 if (matches)
2164 cmd_matches_free(&matches);
718e3744 2165
cd40b329
CF
2166 ret = cmd_vector_filter(cmd_vector,
2167 FILTER_RELAXED,
2168 vline, i,
2169 &match,
2170 &matches);
718e3744 2171
cd40b329
CF
2172 if (ret != CMD_SUCCESS)
2173 {
2174 vector_free (cmd_vector);
2175 vector_free (matchvec);
2176 cmd_matches_free(&matches);
2177 *status = ret;
2178 return NULL;
2179 }
718e3744 2180
cd40b329
CF
2181 /* The last match may well be ambigious, so break here */
2182 if (i == index)
2183 break;
2184
2185 if (match == vararg_match)
2186 {
2187 /* We found a vararg match - so we can throw out the current matches here
2188 * and don't need to continue checking the command input */
2189 unsigned int j, k;
2190
2191 for (j = 0; j < vector_active (matches); j++)
2192 if ((match_vector = vector_slot (matches, j)) != NULL)
2193 for (k = 0; k < vector_active (match_vector); k++)
2194 {
2195 struct cmd_token *token = vector_slot (match_vector, k);
2196 vector_set (matchvec, token);
2197 }
2198
2199 *status = CMD_SUCCESS;
2200 vector_set(matchvec, &token_cr);
2201 vector_free (cmd_vector);
2202 cmd_matches_free(&matches);
2203 cmd_describe_sort(matchvec);
2204 return matchvec;
2205 }
718e3744 2206
cd40b329
CF
2207 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2208 if (ret == 1)
2209 {
2210 vector_free (cmd_vector);
2211 vector_free (matchvec);
2212 cmd_matches_free(&matches);
2213 *status = CMD_ERR_AMBIGUOUS;
2214 return NULL;
2215 }
2216 else if (ret == 2)
2217 {
2218 vector_free (cmd_vector);
2219 vector_free (matchvec);
2220 cmd_matches_free(&matches);
2221 *status = CMD_ERR_NO_MATCH;
2222 return NULL;
2223 }
2224 }
54aba54c 2225
718e3744 2226 /* Make description vector. */
16cf945a 2227 for (i = 0; i < vector_active (matches); i++) {
abaaab4e
DW
2228 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL &&
2229 !(cmd_element->attr == CMD_ATTR_DEPRECATED ||
2230 cmd_element->attr == CMD_ATTR_HIDDEN))
718e3744 2231 {
cd40b329 2232 unsigned int j;
cd40b329 2233 vector vline_trimmed;
718e3744 2234
16cf945a 2235 command_found++;
cd40b329
CF
2236 last_word = vector_slot(vline, vector_active(vline) - 1);
2237 if (last_word == NULL || !strlen(last_word))
2238 {
2239 vline_trimmed = vector_copy(vline);
2240 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
2241
2242 if (cmd_is_complete(cmd_element, vline_trimmed)
2243 && desc_unique_string(matchvec, command_cr))
2244 {
2245 if (match != vararg_match)
2246 vector_set(matchvec, &token_cr);
2247 }
2248
2249 vector_free(vline_trimmed);
2250 }
2251
2252 match_vector = vector_slot (matches, i);
2253 if (match_vector)
2254 for (j = 0; j < vector_active(match_vector); j++)
2255 {
2256 struct cmd_token *token = vector_slot(match_vector, j);
2257 const char *string;
2258
c117e027 2259 string = cmd_entry_function_desc(command, token);
cd40b329
CF
2260 if (string && desc_unique_string(matchvec, string))
2261 vector_set(matchvec, token);
2262 }
718e3744 2263 }
16cf945a
DS
2264 }
2265
2266 /*
2267 * We can get into this situation when the command is complete
2268 * but the last part of the command is an optional piece of
2269 * cli.
2270 */
2271 last_word = vector_slot(vline, vector_active(vline) - 1);
2272 if (command_found == 0 && (last_word == NULL || !strlen(last_word))) {
2273 vector_set(matchvec, &token_cr);
2274 }
2275
718e3744 2276 vector_free (cmd_vector);
cd40b329 2277 cmd_matches_free(&matches);
718e3744 2278
2279 if (vector_slot (matchvec, 0) == NULL)
2280 {
2281 vector_free (matchvec);
909a2155 2282 *status = CMD_ERR_NO_MATCH;
5fc60519 2283 return NULL;
718e3744 2284 }
718e3744 2285
5fc60519 2286 *status = CMD_SUCCESS;
cd40b329 2287 cmd_describe_sort(matchvec);
718e3744 2288 return matchvec;
2289}
2290
b92938a7 2291vector
2292cmd_describe_command (vector vline, struct vty *vty, int *status)
2293{
2294 vector ret;
2295
2296 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2297 {
2298 enum node_type onode;
2299 vector shifted_vline;
8c328f11 2300 unsigned int index;
b92938a7 2301
2302 onode = vty->node;
2303 vty->node = ENABLE_NODE;
2304 /* We can try it on enable node, cos' the vty is authenticated */
2305
2306 shifted_vline = vector_init (vector_count(vline));
2307 /* use memcpy? */
55468c86 2308 for (index = 1; index < vector_active (vline); index++)
b92938a7 2309 {
2310 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2311 }
2312
2313 ret = cmd_describe_command_real (shifted_vline, vty, status);
2314
2315 vector_free(shifted_vline);
2316 vty->node = onode;
2317 return ret;
2318 }
2319
2320
2321 return cmd_describe_command_real (vline, vty, status);
2322}
2323
2324
718e3744 2325/* Check LCD of matched command. */
274a4a44 2326static int
718e3744 2327cmd_lcd (char **matched)
2328{
2329 int i;
2330 int j;
2331 int lcd = -1;
2332 char *s1, *s2;
2333 char c1, c2;
2334
2335 if (matched[0] == NULL || matched[1] == NULL)
2336 return 0;
2337
2338 for (i = 1; matched[i] != NULL; i++)
2339 {
2340 s1 = matched[i - 1];
2341 s2 = matched[i];
2342
2343 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2344 if (c1 != c2)
2345 break;
2346
2347 if (lcd < 0)
2348 lcd = j;
2349 else
2350 {
2351 if (lcd > j)
2352 lcd = j;
2353 }
2354 }
2355 return lcd;
2356}
2357
cd40b329
CF
2358static int
2359cmd_complete_cmp(const void *a, const void *b)
2360{
2361 const char *first = *(char * const *)a;
2362 const char *second = *(char * const *)b;
2363
2364 if (!first)
2365 {
2366 if (!second)
2367 return 0;
2368 return 1;
2369 }
2370 if (!second)
2371 return -1;
2372
2373 return strcmp(first, second);
2374}
2375
2376static void
2377cmd_complete_sort(vector matchvec)
2378{
2379 qsort(matchvec->index, vector_active(matchvec),
2380 sizeof(void*), cmd_complete_cmp);
2381}
2382
718e3744 2383/* Command line completion support. */
274a4a44 2384static char **
cde9f101 2385cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib)
718e3744 2386{
b8961476 2387 unsigned int i;
718e3744 2388 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2389#define INIT_MATCHVEC_SIZE 10
2390 vector matchvec;
b8961476 2391 unsigned int index;
718e3744 2392 char **match_str;
cd40b329 2393 struct cmd_token *token;
718e3744 2394 char *command;
2395 int lcd;
cd40b329
CF
2396 vector matches = NULL;
2397 vector match_vector;
718e3744 2398
55468c86 2399 if (vector_active (vline) == 0)
b8961476 2400 {
d2519962 2401 vector_free (cmd_vector);
b8961476 2402 *status = CMD_ERR_NO_MATCH;
2403 return NULL;
2404 }
2405 else
55468c86 2406 index = vector_active (vline) - 1;
b8961476 2407
cd40b329
CF
2408 /* First, filter by command string */
2409 for (i = 0; i <= index; i++)
2410 {
2411 command = vector_slot (vline, i);
2412 enum match_type match;
2413 int ret;
718e3744 2414
cd40b329
CF
2415 if (matches)
2416 cmd_matches_free(&matches);
718e3744 2417
cd40b329
CF
2418 /* First try completion match, if there is exactly match return 1 */
2419 ret = cmd_vector_filter(cmd_vector,
2420 FILTER_RELAXED,
2421 vline, i,
2422 &match,
2423 &matches);
2424
2425 if (ret != CMD_SUCCESS)
2426 {
2427 vector_free(cmd_vector);
2428 cmd_matches_free(&matches);
2429 *status = ret;
2430 return NULL;
2431 }
2432
2433 /* Break here - the completion mustn't be checked to be non-ambiguous */
2434 if (i == index)
2435 break;
2436
2437 /* If there is exact match then filter ambiguous match else check
2438 ambiguousness. */
2439 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2440 if (ret == 1)
2441 {
2442 vector_free (cmd_vector);
2443 cmd_matches_free(&matches);
2444 *status = CMD_ERR_AMBIGUOUS;
2445 return NULL;
2446 }
2447 /*
909a2155 2448 else if (ret == 2)
2449 {
2450 vector_free (cmd_vector);
cd40b329 2451 cmd_matches_free(&matches);
909a2155 2452 *status = CMD_ERR_NO_MATCH;
2453 return NULL;
2454 }
2455 */
cd40b329 2456 }
909a2155 2457
718e3744 2458 /* Prepare match vector. */
2459 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2460
cd40b329
CF
2461 /* Build the possible list of continuations into a list of completions */
2462 for (i = 0; i < vector_active (matches); i++)
2463 if ((match_vector = vector_slot (matches, i)))
718e3744 2464 {
8c328f11 2465 const char *string;
cd40b329 2466 unsigned int j;
718e3744 2467
cd40b329
CF
2468 for (j = 0; j < vector_active (match_vector); j++)
2469 if ((token = vector_slot (match_vector, j)))
cde9f101
LB
2470 {
2471 string = cmd_entry_function (vector_slot (vline, index),
c117e027 2472 token);
cde9f101
LB
2473 if (string && cmd_unique_string (matchvec, string))
2474 vector_set (matchvec, (islib != 0 ?
2475 XSTRDUP (MTYPE_TMP, string) :
2476 strdup (string) /* rl freed */));
2477 }
718e3744 2478 }
2479
2480 /* We don't need cmd_vector any more. */
2481 vector_free (cmd_vector);
cd40b329 2482 cmd_matches_free(&matches);
718e3744 2483
2484 /* No matched command */
2485 if (vector_slot (matchvec, 0) == NULL)
2486 {
2487 vector_free (matchvec);
2488
2489 /* In case of 'command \t' pattern. Do you need '?' command at
2490 the end of the line. */
2491 if (vector_slot (vline, index) == '\0')
2492 *status = CMD_ERR_NOTHING_TODO;
2493 else
2494 *status = CMD_ERR_NO_MATCH;
2495 return NULL;
2496 }
2497
2498 /* Only one matched */
2499 if (vector_slot (matchvec, 1) == NULL)
2500 {
2501 match_str = (char **) matchvec->index;
2502 vector_only_wrapper_free (matchvec);
2503 *status = CMD_COMPLETE_FULL_MATCH;
2504 return match_str;
2505 }
2506 /* Make it sure last element is NULL. */
2507 vector_set (matchvec, NULL);
2508
2509 /* Check LCD of matched strings. */
2510 if (vector_slot (vline, index) != NULL)
2511 {
2512 lcd = cmd_lcd ((char **) matchvec->index);
2513
2514 if (lcd)
2515 {
2516 int len = strlen (vector_slot (vline, index));
909a2155 2517
718e3744 2518 if (len < lcd)
2519 {
2520 char *lcdstr;
909a2155 2521
cde9f101
LB
2522 lcdstr = (islib != 0 ?
2523 XMALLOC (MTYPE_TMP, lcd + 1) :
2524 malloc(lcd + 1));
718e3744 2525 memcpy (lcdstr, matchvec->index[0], lcd);
2526 lcdstr[lcd] = '\0';
2527
2528 /* match_str = (char **) &lcdstr; */
2529
2530 /* Free matchvec. */
55468c86 2531 for (i = 0; i < vector_active (matchvec); i++)
cde9f101
LB
2532 {
2533 if (vector_slot (matchvec, i))
2534 {
2535 if (islib != 0)
2536 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
2537 else
2538 free (vector_slot (matchvec, i));
2539 }
2540 }
718e3744 2541 vector_free (matchvec);
2542
909a2155 2543 /* Make new matchvec. */
718e3744 2544 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2545 vector_set (matchvec, lcdstr);
2546 match_str = (char **) matchvec->index;
2547 vector_only_wrapper_free (matchvec);
2548
2549 *status = CMD_COMPLETE_MATCH;
2550 return match_str;
2551 }
2552 }
2553 }
2554
2555 match_str = (char **) matchvec->index;
cd40b329 2556 cmd_complete_sort(matchvec);
718e3744 2557 vector_only_wrapper_free (matchvec);
2558 *status = CMD_COMPLETE_LIST_MATCH;
2559 return match_str;
2560}
2561
b92938a7 2562char **
cde9f101 2563cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)
b92938a7 2564{
2565 char **ret;
2566
2567 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2568 {
2569 enum node_type onode;
2570 vector shifted_vline;
8c328f11 2571 unsigned int index;
b92938a7 2572
2573 onode = vty->node;
2574 vty->node = ENABLE_NODE;
2575 /* We can try it on enable node, cos' the vty is authenticated */
2576
2577 shifted_vline = vector_init (vector_count(vline));
2578 /* use memcpy? */
55468c86 2579 for (index = 1; index < vector_active (vline); index++)
b92938a7 2580 {
2581 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2582 }
2583
cde9f101 2584 ret = cmd_complete_command_real (shifted_vline, vty, status, islib);
b92938a7 2585
2586 vector_free(shifted_vline);
2587 vty->node = onode;
2588 return ret;
2589 }
2590
cde9f101
LB
2591 return cmd_complete_command_real (vline, vty, status, islib);
2592}
b92938a7 2593
cde9f101
LB
2594char **
2595cmd_complete_command (vector vline, struct vty *vty, int *status)
2596{
2597 return cmd_complete_command_lib (vline, vty, status, 0);
b92938a7 2598}
2599
2600/* return parent node */
2601/* MUST eventually converge on CONFIG_NODE */
13bfca7a 2602enum node_type
274a4a44 2603node_parent ( enum node_type node )
b92938a7 2604{
2605 enum node_type ret;
2606
9ab6812d 2607 assert (node > CONFIG_NODE);
2608
2609 switch (node)
2610 {
2611 case BGP_VPNV4_NODE:
8ecd3266 2612 case BGP_VPNV6_NODE:
9ab6812d 2613 case BGP_IPV4_NODE:
2614 case BGP_IPV4M_NODE:
2615 case BGP_IPV6_NODE:
1e836590 2616 case BGP_IPV6M_NODE:
9ab6812d 2617 ret = BGP_NODE;
2618 break;
2619 case KEYCHAIN_KEY_NODE:
2620 ret = KEYCHAIN_NODE;
2621 break;
2622 default:
2623 ret = CONFIG_NODE;
b92938a7 2624 }
2625
2626 return ret;
2627}
2628
718e3744 2629/* Execute command by argument vline vector. */
274a4a44 2630static int
cd40b329
CF
2631cmd_execute_command_real (vector vline,
2632 enum filter_type filter,
2633 struct vty *vty,
b8961476 2634 struct cmd_element **cmd)
718e3744 2635{
8c328f11 2636 unsigned int i;
2637 unsigned int index;
718e3744 2638 vector cmd_vector;
2639 struct cmd_element *cmd_element;
2640 struct cmd_element *matched_element;
2641 unsigned int matched_count, incomplete_count;
2642 int argc;
9035efaa 2643 const char *argv[CMD_ARGC_MAX];
718e3744 2644 enum match_type match = 0;
718e3744 2645 char *command;
cd40b329
CF
2646 int ret;
2647 vector matches;
718e3744 2648
2649 /* Make copy of command elements. */
2650 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2651
55468c86 2652 for (index = 0; index < vector_active (vline); index++)
cd40b329
CF
2653 {
2654 command = vector_slot (vline, index);
2655 ret = cmd_vector_filter(cmd_vector,
2656 filter,
2657 vline, index,
2658 &match,
2659 &matches);
2660
2661 if (ret != CMD_SUCCESS)
2662 {
2663 cmd_matches_free(&matches);
2664 return ret;
2665 }
718e3744 2666
cd40b329
CF
2667 if (match == vararg_match)
2668 {
2669 cmd_matches_free(&matches);
909a2155 2670 break;
cd40b329 2671 }
718e3744 2672
cd40b329
CF
2673 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2674 cmd_matches_free(&matches);
2675
2676 if (ret == 1)
2677 {
2678 vector_free(cmd_vector);
2679 return CMD_ERR_AMBIGUOUS;
2680 }
2681 else if (ret == 2)
2682 {
2683 vector_free(cmd_vector);
2684 return CMD_ERR_NO_MATCH;
2685 }
2686 }
718e3744 2687
2688 /* Check matched count. */
2689 matched_element = NULL;
2690 matched_count = 0;
2691 incomplete_count = 0;
2692
55468c86 2693 for (i = 0; i < vector_active (cmd_vector); i++)
b8961476 2694 if ((cmd_element = vector_slot (cmd_vector, i)))
718e3744 2695 {
cd40b329 2696 if (cmd_is_complete(cmd_element, vline))
718e3744 2697 {
2698 matched_element = cmd_element;
718e3744 2699 matched_count++;
2700 }
2701 else
2702 {
2703 incomplete_count++;
2704 }
2705 }
909a2155 2706
718e3744 2707 /* Finish of using cmd_vector. */
2708 vector_free (cmd_vector);
2709
909a2155 2710 /* To execute command, matched_count must be 1. */
2711 if (matched_count == 0)
718e3744 2712 {
2713 if (incomplete_count)
2714 return CMD_ERR_INCOMPLETE;
2715 else
2716 return CMD_ERR_NO_MATCH;
2717 }
2718
909a2155 2719 if (matched_count > 1)
718e3744 2720 return CMD_ERR_AMBIGUOUS;
2721
cd40b329
CF
2722 ret = cmd_parse(matched_element, vline, &argc, argv);
2723 if (ret != CMD_SUCCESS)
2724 return ret;
718e3744 2725
2726 /* For vtysh execution. */
2727 if (cmd)
2728 *cmd = matched_element;
2729
2730 if (matched_element->daemon)
2731 return CMD_SUCCESS_DAEMON;
2732
2733 /* Execute matched command. */
2734 return (*matched_element->func) (matched_element, vty, argc, argv);
2735}
2736
cd40b329
CF
2737/**
2738 * Execute a given command, handling things like "do ..." and checking
2739 * whether the given command might apply at a parent node if doesn't
2740 * apply for the current node.
2741 *
2742 * @param vline Command line input, vector of char* where each element is
2743 * one input token.
2744 * @param vty The vty context in which the command should be executed.
2745 * @param cmd Pointer where the struct cmd_element of the matched command
2746 * will be stored, if any. May be set to NULL if this info is
2747 * not needed.
2748 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2749 * @return The status of the command that has been executed or an error code
2750 * as to why no command could be executed.
2751 */
eda031f6 2752int
87d683b0 2753cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2754 int vtysh) {
9ab6812d 2755 int ret, saved_ret, tried = 0;
2756 enum node_type onode, try_node;
eda031f6 2757
9ab6812d 2758 onode = try_node = vty->node;
b92938a7 2759
2760 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2761 {
2762 vector shifted_vline;
8c328f11 2763 unsigned int index;
b92938a7 2764
2765 vty->node = ENABLE_NODE;
2766 /* We can try it on enable node, cos' the vty is authenticated */
2767
2768 shifted_vline = vector_init (vector_count(vline));
2769 /* use memcpy? */
55468c86 2770 for (index = 1; index < vector_active (vline); index++)
b92938a7 2771 {
2772 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2773 }
2774
cd40b329 2775 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
b92938a7 2776
2777 vector_free(shifted_vline);
2778 vty->node = onode;
2779 return ret;
2780 }
2781
2782
cd40b329 2783 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
b92938a7 2784
87d683b0 2785 if (vtysh)
2786 return saved_ret;
2787
b92938a7 2788 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
9ab6812d 2789 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
b92938a7 2790 && vty->node > CONFIG_NODE )
2791 {
9ab6812d 2792 try_node = node_parent(try_node);
2793 vty->node = try_node;
cd40b329 2794 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
9ab6812d 2795 tried = 1;
2796 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
b92938a7 2797 {
9ab6812d 2798 /* succesfull command, leave the node as is */
b92938a7 2799 return ret;
2800 }
b92938a7 2801 }
9ab6812d 2802 /* no command succeeded, reset the vty to the original node and
2803 return the error for this node */
2804 if ( tried )
2805 vty->node = onode;
2806 return saved_ret;
b92938a7 2807}
2808
cd40b329
CF
2809/**
2810 * Execute a given command, matching it strictly against the current node.
2811 * This mode is used when reading config files.
2812 *
2813 * @param vline Command line input, vector of char* where each element is
2814 * one input token.
2815 * @param vty The vty context in which the command should be executed.
2816 * @param cmd Pointer where the struct cmd_element* of the matched command
2817 * will be stored, if any. May be set to NULL if this info is
2818 * not needed.
2819 * @return The status of the command that has been executed or an error code
2820 * as to why no command could be executed.
2821 */
718e3744 2822int
909a2155 2823cmd_execute_command_strict (vector vline, struct vty *vty,
718e3744 2824 struct cmd_element **cmd)
2825{
cd40b329 2826 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
718e3744 2827}
2828
bed578b8
DS
2829/**
2830 * Parse one line of config, walking up the parse tree attempting to find a match
2831 *
2832 * @param vty The vty context in which the command should be executed.
2833 * @param cmd Pointer where the struct cmd_element* of the match command
2834 * will be stored, if any. May be set to NULL if this info is
2835 * not needed.
2836 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
2837 * or not.
2838 * @return The status of the command that has been executed or an error code
2839 * as to why no command could be executed.
2840 */
2841int
2842command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
2843{
2844 vector vline;
2845 int saved_node;
2846 int ret;
2847
2848 vline = cmd_make_strvec (vty->buf);
2849
2850 /* In case of comment line */
2851 if (vline == NULL)
2852 return CMD_SUCCESS;
2853
2854 /* Execute configuration command : this is strict match */
2855 ret = cmd_execute_command_strict (vline, vty, cmd);
2856
2857 // Climb the tree and try the command again at each node
2858 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
fd715b78
DW
2859 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
2860 ret != CMD_SUCCESS &&
2861 ret != CMD_WARNING &&
2862 vty->node != CONFIG_NODE) {
bed578b8
DS
2863
2864 saved_node = vty->node;
2865
2866 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
fd715b78
DW
2867 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
2868 ret != CMD_SUCCESS &&
2869 ret != CMD_WARNING &&
73d2dad0 2870 vty->node > CONFIG_NODE) {
bed578b8 2871 vty->node = node_parent(vty->node);
fd715b78 2872 ret = cmd_execute_command_strict (vline, vty, cmd);
bed578b8
DS
2873 }
2874
2875 // If climbing the tree did not work then ignore the command and
2876 // stay at the same node
2877 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
fd715b78
DW
2878 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
2879 ret != CMD_SUCCESS &&
2880 ret != CMD_WARNING)
bed578b8
DS
2881 {
2882 vty->node = saved_node;
bed578b8
DS
2883 memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ);
2884 }
2885 }
2886
2887 cmd_free_strvec (vline);
2888
2889 return ret;
2890}
2891
5689fe5f 2892/* Configuration make from file. */
718e3744 2893int
13fbc82d 2894config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
718e3744 2895{
5689fe5f 2896 int ret, error_ret=0;
13fbc82d 2897 *line_num = 0;
718e3744 2898
2899 while (fgets (vty->buf, VTY_BUFSIZ, fp))
2900 {
13fbc82d
SH
2901 if (!error_ret)
2902 ++(*line_num);
2903
bed578b8 2904 ret = command_config_read_one_line (vty, NULL, 0);
718e3744 2905
5689fe5f 2906 if (ret != CMD_SUCCESS && ret != CMD_WARNING &&
bed578b8
DS
2907 ret != CMD_ERR_NOTHING_TODO)
2908 error_ret = ret;
718e3744 2909 }
5689fe5f
DW
2910
2911 if (error_ret) {
2912 return error_ret;
2913 }
2914
718e3744 2915 return CMD_SUCCESS;
2916}
2917
5689fe5f 2918/* Configuration from terminal */
718e3744 2919DEFUN (config_terminal,
2920 config_terminal_cmd,
2921 "configure terminal",
2922 "Configuration from vty interface\n"
2923 "Configuration terminal\n")
2924{
2925 if (vty_config_lock (vty))
2926 vty->node = CONFIG_NODE;
2927 else
2928 {
2929 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2930 return CMD_WARNING;
2931 }
2932 return CMD_SUCCESS;
2933}
2934
2935/* Enable command */
2936DEFUN (enable,
2937 config_enable_cmd,
2938 "enable",
2939 "Turn on privileged mode command\n")
2940{
2941 /* If enable password is NULL, change to ENABLE_NODE */
2942 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2943 vty->type == VTY_SHELL_SERV)
2944 vty->node = ENABLE_NODE;
2945 else
2946 vty->node = AUTH_ENABLE_NODE;
2947
2948 return CMD_SUCCESS;
2949}
2950
2951/* Disable command */
2952DEFUN (disable,
2953 config_disable_cmd,
2954 "disable",
2955 "Turn off privileged mode command\n")
2956{
2957 if (vty->node == ENABLE_NODE)
2958 vty->node = VIEW_NODE;
2959 return CMD_SUCCESS;
2960}
2961
2962/* Down vty node level. */
2963DEFUN (config_exit,
2964 config_exit_cmd,
2965 "exit",
2966 "Exit current mode and down to previous mode\n")
2967{
2968 switch (vty->node)
2969 {
2970 case VIEW_NODE:
2971 case ENABLE_NODE:
62687ff1 2972 case RESTRICTED_NODE:
718e3744 2973 if (vty_shell (vty))
2974 exit (0);
2975 else
2976 vty->status = VTY_CLOSE;
2977 break;
2978 case CONFIG_NODE:
2979 vty->node = ENABLE_NODE;
2980 vty_config_unlock (vty);
2981 break;
2982 case INTERFACE_NODE:
f93e3f69 2983 case VRF_NODE:
718e3744 2984 case ZEBRA_NODE:
2985 case BGP_NODE:
2986 case RIP_NODE:
2987 case RIPNG_NODE:
2988 case OSPF_NODE:
2989 case OSPF6_NODE:
9e867fe6 2990 case ISIS_NODE:
718e3744 2991 case KEYCHAIN_NODE:
2992 case MASC_NODE:
2993 case RMAP_NODE:
12e41d03 2994 case PIM_NODE:
718e3744 2995 case VTY_NODE:
2996 vty->node = CONFIG_NODE;
2997 break;
718e3744 2998 case BGP_IPV4_NODE:
2999 case BGP_IPV4M_NODE:
8ecd3266 3000 case BGP_VPNV4_NODE:
3001 case BGP_VPNV6_NODE:
718e3744 3002 case BGP_IPV6_NODE:
1e836590 3003 case BGP_IPV6M_NODE:
718e3744 3004 vty->node = BGP_NODE;
3005 break;
3006 case KEYCHAIN_KEY_NODE:
3007 vty->node = KEYCHAIN_NODE;
3008 break;
3009 default:
3010 break;
3011 }
3012 return CMD_SUCCESS;
3013}
3014
3015/* quit is alias of exit. */
3016ALIAS (config_exit,
3017 config_quit_cmd,
3018 "quit",
3019 "Exit current mode and down to previous mode\n")
3020
3021/* End of configuration. */
3022DEFUN (config_end,
3023 config_end_cmd,
3024 "end",
3025 "End current mode and change to enable mode.")
3026{
3027 switch (vty->node)
3028 {
3029 case VIEW_NODE:
3030 case ENABLE_NODE:
62687ff1 3031 case RESTRICTED_NODE:
718e3744 3032 /* Nothing to do. */
3033 break;
3034 case CONFIG_NODE:
3035 case INTERFACE_NODE:
f93e3f69 3036 case VRF_NODE:
718e3744 3037 case ZEBRA_NODE:
3038 case RIP_NODE:
3039 case RIPNG_NODE:
3040 case BGP_NODE:
3041 case BGP_VPNV4_NODE:
8ecd3266 3042 case BGP_VPNV6_NODE:
718e3744 3043 case BGP_IPV4_NODE:
3044 case BGP_IPV4M_NODE:
3045 case BGP_IPV6_NODE:
1e836590 3046 case BGP_IPV6M_NODE:
718e3744 3047 case RMAP_NODE:
3048 case OSPF_NODE:
3049 case OSPF6_NODE:
9e867fe6 3050 case ISIS_NODE:
718e3744 3051 case KEYCHAIN_NODE:
3052 case KEYCHAIN_KEY_NODE:
3053 case MASC_NODE:
12e41d03 3054 case PIM_NODE:
718e3744 3055 case VTY_NODE:
3056 vty_config_unlock (vty);
3057 vty->node = ENABLE_NODE;
3058 break;
3059 default:
3060 break;
3061 }
3062 return CMD_SUCCESS;
3063}
3064
3065/* Show version. */
3066DEFUN (show_version,
3067 show_version_cmd,
3068 "show version",
3069 SHOW_STR
3070 "Displays zebra version\n")
3071{
12f6ea23 3072 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
3073 VTY_NEWLINE);
0be793e6 3074 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
80db5ac1
DL
3075 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
3076 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
718e3744 3077
3078 return CMD_SUCCESS;
3079}
3080
3081/* Help display function for all node. */
3082DEFUN (config_help,
3083 config_help_cmd,
3084 "help",
3085 "Description of the interactive help system\n")
3086{
3087 vty_out (vty,
6590f2c3 3088 "Quagga VTY provides advanced help feature. When you need help,%s\
718e3744 3089anytime at the command line please press '?'.%s\
3090%s\
3091If nothing matches, the help list will be empty and you must backup%s\
3092 until entering a '?' shows the available options.%s\
3093Two styles of help are provided:%s\
30941. Full help is available when you are ready to enter a%s\
3095command argument (e.g. 'show ?') and describes each possible%s\
3096argument.%s\
30972. Partial help is provided when an abbreviated argument is entered%s\
3098 and you want to know what arguments match the input%s\
3099 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3100 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3101 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
3102 return CMD_SUCCESS;
3103}
3104
3105/* Help display function for all node. */
3106DEFUN (config_list,
3107 config_list_cmd,
3108 "list",
3109 "Print command list\n")
3110{
8c328f11 3111 unsigned int i;
718e3744 3112 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
3113 struct cmd_element *cmd;
3114
55468c86 3115 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
4275b1de 3116 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
3117 && !(cmd->attr == CMD_ATTR_DEPRECATED
3118 || cmd->attr == CMD_ATTR_HIDDEN))
718e3744 3119 vty_out (vty, " %s%s", cmd->string,
3120 VTY_NEWLINE);
3121 return CMD_SUCCESS;
3122}
3123
3124/* Write current configuration into file. */
3125DEFUN (config_write_file,
3126 config_write_file_cmd,
3127 "write file",
3128 "Write running configuration to memory, network, or terminal\n"
3129 "Write to configuration file\n")
3130{
8c328f11 3131 unsigned int i;
718e3744 3132 int fd;
3133 struct cmd_node *node;
3134 char *config_file;
3135 char *config_file_tmp = NULL;
3136 char *config_file_sav = NULL;
05865c90 3137 int ret = CMD_WARNING;
718e3744 3138 struct vty *file_vty;
e4421165 3139 struct stat conf_stat;
718e3744 3140
3141 /* Check and see if we are operating under vtysh configuration */
3142 if (host.config == NULL)
3143 {
3144 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3145 VTY_NEWLINE);
3146 return CMD_WARNING;
3147 }
3148
3149 /* Get filename. */
3150 config_file = host.config;
3151
05865c90 3152 config_file_sav =
3153 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
718e3744 3154 strcpy (config_file_sav, config_file);
3155 strcat (config_file_sav, CONF_BACKUP_EXT);
3156
3157
05865c90 3158 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
718e3744 3159 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3160
3161 /* Open file to configuration write. */
3162 fd = mkstemp (config_file_tmp);
3163 if (fd < 0)
3164 {
3165 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3166 VTY_NEWLINE);
05865c90 3167 goto finished;
718e3744 3168 }
3169
3170 /* Make vty for configuration file. */
3171 file_vty = vty_new ();
3172 file_vty->fd = fd;
3173 file_vty->type = VTY_FILE;
3174
3175 /* Config file header print. */
3176 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3177 vty_time_print (file_vty, 1);
3178 vty_out (file_vty, "!\n");
3179
55468c86 3180 for (i = 0; i < vector_active (cmdvec); i++)
718e3744 3181 if ((node = vector_slot (cmdvec, i)) && node->func)
3182 {
3183 if ((*node->func) (file_vty))
3184 vty_out (file_vty, "!\n");
3185 }
3186 vty_close (file_vty);
3187
e4421165 3188 if (stat(config_file, &conf_stat) >= 0)
718e3744 3189 {
e4421165
DS
3190 if (unlink (config_file_sav) != 0)
3191 if (errno != ENOENT)
3192 {
3193 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3194 VTY_NEWLINE);
3195 goto finished;
3196 }
3197 if (link (config_file, config_file_sav) != 0)
3198 {
3199 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3200 VTY_NEWLINE);
3201 goto finished;
3202 }
3203 sync ();
3204 if (unlink (config_file) != 0)
3205 {
3206 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3207 VTY_NEWLINE);
3208 goto finished;
3209 }
718e3744 3210 }
3211 if (link (config_file_tmp, config_file) != 0)
3212 {
3213 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3214 VTY_NEWLINE);
05865c90 3215 goto finished;
718e3744 3216 }
718e3744 3217 sync ();
3218
aa593d5e 3219 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3220 {
3221 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
6099b3b5 3222 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
05865c90 3223 goto finished;
aa593d5e 3224 }
3225
718e3744 3226 vty_out (vty, "Configuration saved to %s%s", config_file,
3227 VTY_NEWLINE);
05865c90 3228 ret = CMD_SUCCESS;
3229
3230finished:
3231 unlink (config_file_tmp);
3232 XFREE (MTYPE_TMP, config_file_tmp);
3233 XFREE (MTYPE_TMP, config_file_sav);
3234 return ret;
718e3744 3235}
3236
3237ALIAS (config_write_file,
3238 config_write_cmd,
3239 "write",
3240 "Write running configuration to memory, network, or terminal\n")
3241
3242ALIAS (config_write_file,
3243 config_write_memory_cmd,
3244 "write memory",
3245 "Write running configuration to memory, network, or terminal\n"
3246 "Write configuration to the file (same as write file)\n")
3247
3248ALIAS (config_write_file,
3249 copy_runningconfig_startupconfig_cmd,
3250 "copy running-config startup-config",
3251 "Copy configuration\n"
3252 "Copy running config to... \n"
3253 "Copy running config to startup config (same as write file)\n")
3254
3255/* Write current configuration into the terminal. */
3256DEFUN (config_write_terminal,
3257 config_write_terminal_cmd,
3258 "write terminal",
3259 "Write running configuration to memory, network, or terminal\n"
3260 "Write to terminal\n")
3261{
8c328f11 3262 unsigned int i;
718e3744 3263 struct cmd_node *node;
3264
3265 if (vty->type == VTY_SHELL_SERV)
3266 {
55468c86 3267 for (i = 0; i < vector_active (cmdvec); i++)
718e3744 3268 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3269 {
3270 if ((*node->func) (vty))
3271 vty_out (vty, "!%s", VTY_NEWLINE);
3272 }
3273 }
3274 else
3275 {
3276 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3277 VTY_NEWLINE);
3278 vty_out (vty, "!%s", VTY_NEWLINE);
3279
55468c86 3280 for (i = 0; i < vector_active (cmdvec); i++)
718e3744 3281 if ((node = vector_slot (cmdvec, i)) && node->func)
3282 {
3283 if ((*node->func) (vty))
3284 vty_out (vty, "!%s", VTY_NEWLINE);
3285 }
3286 vty_out (vty, "end%s",VTY_NEWLINE);
3287 }
3288 return CMD_SUCCESS;
3289}
3290
3291/* Write current configuration into the terminal. */
3292ALIAS (config_write_terminal,
3293 show_running_config_cmd,
3294 "show running-config",
3295 SHOW_STR
3296 "running configuration\n")
3297
3298/* Write startup configuration into the terminal. */
3299DEFUN (show_startup_config,
3300 show_startup_config_cmd,
3301 "show startup-config",
3302 SHOW_STR
3303 "Contentes of startup configuration\n")
3304{
3305 char buf[BUFSIZ];
3306 FILE *confp;
3307
3308 confp = fopen (host.config, "r");
3309 if (confp == NULL)
3310 {
1db63918
DS
3311 vty_out (vty, "Can't open configuration file [%s] due to '%s'%s",
3312 host.config, safe_strerror(errno), VTY_NEWLINE);
718e3744 3313 return CMD_WARNING;
3314 }
3315
3316 while (fgets (buf, BUFSIZ, confp))
3317 {
3318 char *cp = buf;
3319
3320 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3321 cp++;
3322 *cp = '\0';
3323
3324 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3325 }
3326
3327 fclose (confp);
3328
3329 return CMD_SUCCESS;
3330}
3331
3332/* Hostname configuration */
3333DEFUN (config_hostname,
3334 hostname_cmd,
3335 "hostname WORD",
3336 "Set system's network name\n"
3337 "This system's network name\n")
3338{
3339 if (!isalpha((int) *argv[0]))
3340 {
3341 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3342 return CMD_WARNING;
3343 }
3344
3345 if (host.name)
05865c90 3346 XFREE (MTYPE_HOST, host.name);
718e3744 3347
05865c90 3348 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
718e3744 3349 return CMD_SUCCESS;
3350}
3351
3352DEFUN (config_no_hostname,
3353 no_hostname_cmd,
3354 "no hostname [HOSTNAME]",
3355 NO_STR
3356 "Reset system's network name\n"
3357 "Host name of this router\n")
3358{
3359 if (host.name)
05865c90 3360 XFREE (MTYPE_HOST, host.name);
718e3744 3361 host.name = NULL;
3362 return CMD_SUCCESS;
3363}
3364
3365/* VTY interface password set. */
3366DEFUN (config_password, password_cmd,
3367 "password (8|) WORD",
3368 "Assign the terminal connection password\n"
3369 "Specifies a HIDDEN password will follow\n"
3370 "dummy string \n"
3371 "The HIDDEN line password string\n")
3372{
3373 /* Argument check. */
3374 if (argc == 0)
3375 {
3376 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3377 return CMD_WARNING;
3378 }
3379
3380 if (argc == 2)
3381 {
3382 if (*argv[0] == '8')
3383 {
3384 if (host.password)
05865c90 3385 XFREE (MTYPE_HOST, host.password);
718e3744 3386 host.password = NULL;
3387 if (host.password_encrypt)
05865c90 3388 XFREE (MTYPE_HOST, host.password_encrypt);
3389 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
718e3744 3390 return CMD_SUCCESS;
3391 }
3392 else
3393 {
3394 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3395 return CMD_WARNING;
3396 }
3397 }
3398
3399 if (!isalnum ((int) *argv[0]))
3400 {
3401 vty_out (vty,
3402 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3403 return CMD_WARNING;
3404 }
3405
3406 if (host.password)
05865c90 3407 XFREE (MTYPE_HOST, host.password);
718e3744 3408 host.password = NULL;
3409
3410 if (host.encrypt)
3411 {
3412 if (host.password_encrypt)
05865c90 3413 XFREE (MTYPE_HOST, host.password_encrypt);
3414 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
718e3744 3415 }
3416 else
05865c90 3417 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
718e3744 3418
3419 return CMD_SUCCESS;
3420}
3421
3422ALIAS (config_password, password_text_cmd,
3423 "password LINE",
3424 "Assign the terminal connection password\n"
3425 "The UNENCRYPTED (cleartext) line password\n")
3426
3427/* VTY enable password set. */
3428DEFUN (config_enable_password, enable_password_cmd,
3429 "enable password (8|) WORD",
3430 "Modify enable password parameters\n"
3431 "Assign the privileged level password\n"
3432 "Specifies a HIDDEN password will follow\n"
3433 "dummy string \n"
3434 "The HIDDEN 'enable' password string\n")
3435{
3436 /* Argument check. */
3437 if (argc == 0)
3438 {
3439 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3440 return CMD_WARNING;
3441 }
3442
3443 /* Crypt type is specified. */
3444 if (argc == 2)
3445 {
3446 if (*argv[0] == '8')
3447 {
3448 if (host.enable)
05865c90 3449 XFREE (MTYPE_HOST, host.enable);
718e3744 3450 host.enable = NULL;
3451
3452 if (host.enable_encrypt)
05865c90 3453 XFREE (MTYPE_HOST, host.enable_encrypt);
3454 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
718e3744 3455
3456 return CMD_SUCCESS;
3457 }
3458 else
3459 {
3460 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3461 return CMD_WARNING;
3462 }
3463 }
3464
3465 if (!isalnum ((int) *argv[0]))
3466 {
3467 vty_out (vty,
3468 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3469 return CMD_WARNING;
3470 }
3471
3472 if (host.enable)
05865c90 3473 XFREE (MTYPE_HOST, host.enable);
718e3744 3474 host.enable = NULL;
3475
3476 /* Plain password input. */
3477 if (host.encrypt)
3478 {
3479 if (host.enable_encrypt)
05865c90 3480 XFREE (MTYPE_HOST, host.enable_encrypt);
3481 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
718e3744 3482 }
3483 else
05865c90 3484 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
718e3744 3485
3486 return CMD_SUCCESS;
3487}
3488
3489ALIAS (config_enable_password,
3490 enable_password_text_cmd,
3491 "enable password LINE",
3492 "Modify enable password parameters\n"
3493 "Assign the privileged level password\n"
3494 "The UNENCRYPTED (cleartext) 'enable' password\n")
3495
3496/* VTY enable password delete. */
3497DEFUN (no_config_enable_password, no_enable_password_cmd,
3498 "no enable password",
3499 NO_STR
3500 "Modify enable password parameters\n"
3501 "Assign the privileged level password\n")
3502{
3503 if (host.enable)
05865c90 3504 XFREE (MTYPE_HOST, host.enable);
718e3744 3505 host.enable = NULL;
3506
3507 if (host.enable_encrypt)
05865c90 3508 XFREE (MTYPE_HOST, host.enable_encrypt);
718e3744 3509 host.enable_encrypt = NULL;
3510
3511 return CMD_SUCCESS;
3512}
3513
3514DEFUN (service_password_encrypt,
3515 service_password_encrypt_cmd,
3516 "service password-encryption",
3517 "Set up miscellaneous service\n"
3518 "Enable encrypted passwords\n")
3519{
3520 if (host.encrypt)
3521 return CMD_SUCCESS;
3522
3523 host.encrypt = 1;
3524
3525 if (host.password)
3526 {
3527 if (host.password_encrypt)
05865c90 3528 XFREE (MTYPE_HOST, host.password_encrypt);
3529 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
718e3744 3530 }
3531 if (host.enable)
3532 {
3533 if (host.enable_encrypt)
05865c90 3534 XFREE (MTYPE_HOST, host.enable_encrypt);
3535 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
718e3744 3536 }
3537
3538 return CMD_SUCCESS;
3539}
3540
3541DEFUN (no_service_password_encrypt,
3542 no_service_password_encrypt_cmd,
3543 "no service password-encryption",
3544 NO_STR
3545 "Set up miscellaneous service\n"
3546 "Enable encrypted passwords\n")
3547{
3548 if (! host.encrypt)
3549 return CMD_SUCCESS;
3550
3551 host.encrypt = 0;
3552
3553 if (host.password_encrypt)
05865c90 3554 XFREE (MTYPE_HOST, host.password_encrypt);
718e3744 3555 host.password_encrypt = NULL;
3556
3557 if (host.enable_encrypt)
05865c90 3558 XFREE (MTYPE_HOST, host.enable_encrypt);
718e3744 3559 host.enable_encrypt = NULL;
3560
3561 return CMD_SUCCESS;
3562}
3563
3564DEFUN (config_terminal_length, config_terminal_length_cmd,
3565 "terminal length <0-512>",
3566 "Set terminal line parameters\n"
3567 "Set number of lines on a screen\n"
3568 "Number of lines on screen (0 for no pausing)\n")
3569{
3570 int lines;
3571 char *endptr = NULL;
3572
3573 lines = strtol (argv[0], &endptr, 10);
3574 if (lines < 0 || lines > 512 || *endptr != '\0')
3575 {
3576 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3577 return CMD_WARNING;
3578 }
3579 vty->lines = lines;
3580
3581 return CMD_SUCCESS;
3582}
3583
3584DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3585 "terminal no length",
3586 "Set terminal line parameters\n"
3587 NO_STR
3588 "Set number of lines on a screen\n")
3589{
3590 vty->lines = -1;
3591 return CMD_SUCCESS;
3592}
3593
3594DEFUN (service_terminal_length, service_terminal_length_cmd,
3595 "service terminal-length <0-512>",
3596 "Set up miscellaneous service\n"
3597 "System wide terminal length configuration\n"
3598 "Number of lines of VTY (0 means no line control)\n")
3599{
3600 int lines;
3601 char *endptr = NULL;
3602
3603 lines = strtol (argv[0], &endptr, 10);
3604 if (lines < 0 || lines > 512 || *endptr != '\0')
3605 {
3606 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3607 return CMD_WARNING;
3608 }
3609 host.lines = lines;
3610
3611 return CMD_SUCCESS;
3612}
3613
3614DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3615 "no service terminal-length [<0-512>]",
3616 NO_STR
3617 "Set up miscellaneous service\n"
3618 "System wide terminal length configuration\n"
3619 "Number of lines of VTY (0 means no line control)\n")
3620{
3621 host.lines = -1;
3622 return CMD_SUCCESS;
3623}
3624
2885f72d 3625DEFUN_HIDDEN (do_echo,
3626 echo_cmd,
3627 "echo .MESSAGE",
3628 "Echo a message back to the vty\n"
3629 "The message to echo\n")
3630{
3631 char *message;
3632
f6834d4c 3633 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3634 VTY_NEWLINE);
3635 if (message)
3636 XFREE(MTYPE_TMP, message);
2885f72d 3637 return CMD_SUCCESS;
3638}
3639
274a4a44 3640DEFUN (config_logmsg,
3641 config_logmsg_cmd,
3642 "logmsg "LOG_LEVELS" .MESSAGE",
3643 "Send a message to enabled logging destinations\n"
3644 LOG_LEVEL_DESC
3645 "The message to send\n")
3646{
3647 int level;
3648 char *message;
3649
3650 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3651 return CMD_ERR_NO_MATCH;
3652
fc95186c 3653 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
f6834d4c 3654 if (message)
3655 XFREE(MTYPE_TMP, message);
274a4a44 3656 return CMD_SUCCESS;
3657}
3658
3659DEFUN (show_logging,
3660 show_logging_cmd,
3661 "show logging",
3662 SHOW_STR
3663 "Show current logging configuration\n")
3664{
3665 struct zlog *zl = zlog_default;
3666
3667 vty_out (vty, "Syslog logging: ");
3668 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3669 vty_out (vty, "disabled");
3670 else
3671 vty_out (vty, "level %s, facility %s, ident %s",
3672 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3673 facility_name(zl->facility), zl->ident);
3674 vty_out (vty, "%s", VTY_NEWLINE);
3675
3676 vty_out (vty, "Stdout logging: ");
3677 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3678 vty_out (vty, "disabled");
3679 else
3680 vty_out (vty, "level %s",
3681 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3682 vty_out (vty, "%s", VTY_NEWLINE);
3683
3684 vty_out (vty, "Monitor logging: ");
3685 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3686 vty_out (vty, "disabled");
3687 else
3688 vty_out (vty, "level %s",
3689 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3690 vty_out (vty, "%s", VTY_NEWLINE);
3691
3692 vty_out (vty, "File logging: ");
3693 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3694 !zl->fp)
3695 vty_out (vty, "disabled");
3696 else
3697 vty_out (vty, "level %s, filename %s",
3698 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3699 zl->filename);
3700 vty_out (vty, "%s", VTY_NEWLINE);
3701
3702 vty_out (vty, "Protocol name: %s%s",
3703 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3704 vty_out (vty, "Record priority: %s%s",
3705 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
1ed72e0b
AS
3706 vty_out (vty, "Timestamp precision: %d%s",
3707 zl->timestamp_precision, VTY_NEWLINE);
274a4a44 3708
3709 return CMD_SUCCESS;
3710}
3711
718e3744 3712DEFUN (config_log_stdout,
3713 config_log_stdout_cmd,
3714 "log stdout",
3715 "Logging control\n"
274a4a44 3716 "Set stdout logging level\n")
718e3744 3717{
274a4a44 3718 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3719 return CMD_SUCCESS;
3720}
3721
3722DEFUN (config_log_stdout_level,
3723 config_log_stdout_level_cmd,
3724 "log stdout "LOG_LEVELS,
3725 "Logging control\n"
3726 "Set stdout logging level\n"
3727 LOG_LEVEL_DESC)
3728{
3729 int level;
3730
3731 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3732 return CMD_ERR_NO_MATCH;
3733 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
718e3744 3734 return CMD_SUCCESS;
3735}
3736
3737DEFUN (no_config_log_stdout,
3738 no_config_log_stdout_cmd,
274a4a44 3739 "no log stdout [LEVEL]",
718e3744 3740 NO_STR
3741 "Logging control\n"
274a4a44 3742 "Cancel logging to stdout\n"
3743 "Logging level\n")
718e3744 3744{
274a4a44 3745 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
718e3744 3746 return CMD_SUCCESS;
3747}
3748
274a4a44 3749DEFUN (config_log_monitor,
3750 config_log_monitor_cmd,
3751 "log monitor",
718e3744 3752 "Logging control\n"
274a4a44 3753 "Set terminal line (monitor) logging level\n")
3754{
3755 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3756 return CMD_SUCCESS;
3757}
3758
3759DEFUN (config_log_monitor_level,
3760 config_log_monitor_level_cmd,
3761 "log monitor "LOG_LEVELS,
3762 "Logging control\n"
3763 "Set terminal line (monitor) logging level\n"
3764 LOG_LEVEL_DESC)
3765{
3766 int level;
3767
3768 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3769 return CMD_ERR_NO_MATCH;
3770 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3771 return CMD_SUCCESS;
3772}
3773
3774DEFUN (no_config_log_monitor,
3775 no_config_log_monitor_cmd,
3776 "no log monitor [LEVEL]",
3777 NO_STR
3778 "Logging control\n"
3779 "Disable terminal line (monitor) logging\n"
3780 "Logging level\n")
3781{
3782 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3783 return CMD_SUCCESS;
3784}
3785
3786static int
3787set_log_file(struct vty *vty, const char *fname, int loglevel)
718e3744 3788{
3789 int ret;
9035efaa 3790 char *p = NULL;
3791 const char *fullpath;
3792
718e3744 3793 /* Path detection. */
274a4a44 3794 if (! IS_DIRECTORY_SEP (*fname))
718e3744 3795 {
9035efaa 3796 char cwd[MAXPATHLEN+1];
3797 cwd[MAXPATHLEN] = '\0';
3798
3799 if (getcwd (cwd, MAXPATHLEN) == NULL)
3800 {
3801 zlog_err ("config_log_file: Unable to alloc mem!");
3802 return CMD_WARNING;
3803 }
3804
274a4a44 3805 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
9035efaa 3806 == NULL)
3807 {
3808 zlog_err ("config_log_file: Unable to alloc mem!");
3809 return CMD_WARNING;
3810 }
274a4a44 3811 sprintf (p, "%s/%s", cwd, fname);
9035efaa 3812 fullpath = p;
718e3744 3813 }
3814 else
274a4a44 3815 fullpath = fname;
718e3744 3816
274a4a44 3817 ret = zlog_set_file (NULL, fullpath, loglevel);
718e3744 3818
9035efaa 3819 if (p)
3820 XFREE (MTYPE_TMP, p);
3821
718e3744 3822 if (!ret)
3823 {
274a4a44 3824 vty_out (vty, "can't open logfile %s\n", fname);
718e3744 3825 return CMD_WARNING;
3826 }
3827
3828 if (host.logfile)
05865c90 3829 XFREE (MTYPE_HOST, host.logfile);
718e3744 3830
05865c90 3831 host.logfile = XSTRDUP (MTYPE_HOST, fname);
718e3744 3832
3833 return CMD_SUCCESS;
3834}
3835
274a4a44 3836DEFUN (config_log_file,
3837 config_log_file_cmd,
3838 "log file FILENAME",
3839 "Logging control\n"
3840 "Logging to file\n"
3841 "Logging filename\n")
3842{
3843 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3844}
3845
3846DEFUN (config_log_file_level,
3847 config_log_file_level_cmd,
3848 "log file FILENAME "LOG_LEVELS,
3849 "Logging control\n"
3850 "Logging to file\n"
3851 "Logging filename\n"
3852 LOG_LEVEL_DESC)
3853{
3854 int level;
3855
3856 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3857 return CMD_ERR_NO_MATCH;
3858 return set_log_file(vty, argv[0], level);
3859}
3860
718e3744 3861DEFUN (no_config_log_file,
3862 no_config_log_file_cmd,
3863 "no log file [FILENAME]",
3864 NO_STR
3865 "Logging control\n"
3866 "Cancel logging to file\n"
3867 "Logging file name\n")
3868{
3869 zlog_reset_file (NULL);
3870
3871 if (host.logfile)
05865c90 3872 XFREE (MTYPE_HOST, host.logfile);
718e3744 3873
3874 host.logfile = NULL;
3875
3876 return CMD_SUCCESS;
3877}
3878
274a4a44 3879ALIAS (no_config_log_file,
3880 no_config_log_file_level_cmd,
3881 "no log file FILENAME LEVEL",
3882 NO_STR
3883 "Logging control\n"
3884 "Cancel logging to file\n"
3885 "Logging file name\n"
3886 "Logging level\n")
3887
718e3744 3888DEFUN (config_log_syslog,
3889 config_log_syslog_cmd,
3890 "log syslog",
3891 "Logging control\n"
274a4a44 3892 "Set syslog logging level\n")
718e3744 3893{
274a4a44 3894 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
12ab19f1 3895 return CMD_SUCCESS;
3896}
3897
274a4a44 3898DEFUN (config_log_syslog_level,
3899 config_log_syslog_level_cmd,
3900 "log syslog "LOG_LEVELS,
12ab19f1 3901 "Logging control\n"
274a4a44 3902 "Set syslog logging level\n"
3903 LOG_LEVEL_DESC)
3904{
3905 int level;
12ab19f1 3906
274a4a44 3907 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3908 return CMD_ERR_NO_MATCH;
3909 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3910 return CMD_SUCCESS;
3911}
3912
3913DEFUN_DEPRECATED (config_log_syslog_facility,
3914 config_log_syslog_facility_cmd,
3915 "log syslog facility "LOG_FACILITIES,
3916 "Logging control\n"
3917 "Logging goes to syslog\n"
3918 "(Deprecated) Facility parameter for syslog messages\n"
3919 LOG_FACILITY_DESC)
3920{
3921 int facility;
3922
3923 if ((facility = facility_match(argv[0])) < 0)
3924 return CMD_ERR_NO_MATCH;
12ab19f1 3925
274a4a44 3926 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3927 zlog_default->facility = facility;
718e3744 3928 return CMD_SUCCESS;
3929}
3930
3931DEFUN (no_config_log_syslog,
3932 no_config_log_syslog_cmd,
274a4a44 3933 "no log syslog [LEVEL]",
718e3744 3934 NO_STR
3935 "Logging control\n"
274a4a44 3936 "Cancel logging to syslog\n"
3937 "Logging level\n")
718e3744 3938{
274a4a44 3939 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
718e3744 3940 return CMD_SUCCESS;
3941}
3942
12ab19f1 3943ALIAS (no_config_log_syslog,
3944 no_config_log_syslog_facility_cmd,
274a4a44 3945 "no log syslog facility "LOG_FACILITIES,
12ab19f1 3946 NO_STR
3947 "Logging control\n"
3948 "Logging goes to syslog\n"
3949 "Facility parameter for syslog messages\n"
274a4a44 3950 LOG_FACILITY_DESC)
3951
3952DEFUN (config_log_facility,
3953 config_log_facility_cmd,
3954 "log facility "LOG_FACILITIES,
718e3744 3955 "Logging control\n"
274a4a44 3956 "Facility parameter for syslog messages\n"
3957 LOG_FACILITY_DESC)
718e3744 3958{
274a4a44 3959 int facility;
3960
3961 if ((facility = facility_match(argv[0])) < 0)
3962 return CMD_ERR_NO_MATCH;
3963 zlog_default->facility = facility;
3964 return CMD_SUCCESS;
718e3744 3965}
3966
274a4a44 3967DEFUN (no_config_log_facility,
3968 no_config_log_facility_cmd,
3969 "no log facility [FACILITY]",
718e3744 3970 NO_STR
3971 "Logging control\n"
274a4a44 3972 "Reset syslog facility to default (daemon)\n"
3973 "Syslog facility\n")
3974{
3975 zlog_default->facility = LOG_DAEMON;
3976 return CMD_SUCCESS;
3977}
3978
3979DEFUN_DEPRECATED (config_log_trap,
3980 config_log_trap_cmd,
3981 "log trap "LOG_LEVELS,
3982 "Logging control\n"
3983 "(Deprecated) Set logging level and default for all destinations\n"
3984 LOG_LEVEL_DESC)
3985{
3986 int new_level ;
3987 int i;
3988
3989 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3990 return CMD_ERR_NO_MATCH;
3991
3992 zlog_default->default_lvl = new_level;
3993 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3994 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3995 zlog_default->maxlvl[i] = new_level;
3996 return CMD_SUCCESS;
3997}
3998
3999DEFUN_DEPRECATED (no_config_log_trap,
4000 no_config_log_trap_cmd,
4001 "no log trap [LEVEL]",
4002 NO_STR
4003 "Logging control\n"
4004 "Permit all logging information\n"
4005 "Logging level\n")
718e3744 4006{
274a4a44 4007 zlog_default->default_lvl = LOG_DEBUG;
718e3744 4008 return CMD_SUCCESS;
4009}
4010
4011DEFUN (config_log_record_priority,
4012 config_log_record_priority_cmd,
4013 "log record-priority",
4014 "Logging control\n"
4015 "Log the priority of the message within the message\n")
4016{
4017 zlog_default->record_priority = 1 ;
4018 return CMD_SUCCESS;
4019}
4020
4021DEFUN (no_config_log_record_priority,
4022 no_config_log_record_priority_cmd,
4023 "no log record-priority",
4024 NO_STR
4025 "Logging control\n"
4026 "Do not log the priority of the message within the message\n")
4027{
4028 zlog_default->record_priority = 0 ;
4029 return CMD_SUCCESS;
4030}
4031
1ed72e0b
AS
4032DEFUN (config_log_timestamp_precision,
4033 config_log_timestamp_precision_cmd,
4034 "log timestamp precision <0-6>",
4035 "Logging control\n"
4036 "Timestamp configuration\n"
4037 "Set the timestamp precision\n"
4038 "Number of subsecond digits\n")
4039{
4040 if (argc != 1)
4041 {
4042 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
4043 return CMD_WARNING;
4044 }
4045
4046 VTY_GET_INTEGER_RANGE("Timestamp Precision",
4047 zlog_default->timestamp_precision, argv[0], 0, 6);
4048 return CMD_SUCCESS;
4049}
4050
4051DEFUN (no_config_log_timestamp_precision,
4052 no_config_log_timestamp_precision_cmd,
4053 "no log timestamp precision",
4054 NO_STR
4055 "Logging control\n"
4056 "Timestamp configuration\n"
4057 "Reset the timestamp precision to the default value of 0\n")
4058{
4059 zlog_default->timestamp_precision = 0 ;
4060 return CMD_SUCCESS;
4061}
4062
7cfc61d3
DS
4063int
4064cmd_banner_motd_file (const char *file)
4065{
4066 if (host.motdfile)
4067 XFREE (MTYPE_HOST, host.motdfile);
4068 host.motdfile = XSTRDUP (MTYPE_HOST, file);
4069
4070 return CMD_SUCCESS;
4071}
4072
3b0c5d9a 4073DEFUN (banner_motd_file,
4074 banner_motd_file_cmd,
4d833e55 4075 "banner motd file FILE",
3b0c5d9a 4076 "Set banner\n"
4077 "Banner for motd\n"
4078 "Banner from a file\n"
4079 "Filename\n")
4080{
7cfc61d3 4081 return cmd_banner_motd_file (argv[0]);
3b0c5d9a 4082}
718e3744 4083
4084DEFUN (banner_motd_default,
4085 banner_motd_default_cmd,
4086 "banner motd default",
4087 "Set banner string\n"
4088 "Strings for motd\n"
4089 "Default string\n")
4090{
4091 host.motd = default_motd;
4092 return CMD_SUCCESS;
4093}
4094
4095DEFUN (no_banner_motd,
4096 no_banner_motd_cmd,
4097 "no banner motd",
4098 NO_STR
4099 "Set banner string\n"
4100 "Strings for motd\n")
4101{
4102 host.motd = NULL;
22085181 4103 if (host.motdfile)
05865c90 4104 XFREE (MTYPE_HOST, host.motdfile);
3b0c5d9a 4105 host.motdfile = NULL;
718e3744 4106 return CMD_SUCCESS;
4107}
4108
bcd9fa7f
LB
4109DEFUN (show_commandtree,
4110 show_commandtree_cmd,
4111 "show commandtree",
4112 NO_STR
4113 "Show command tree\n")
4114{
4115 /* TBD */
4116 vector cmd_vector;
4117 unsigned int i;
4118
4119 vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE);
4120
4121 /* vector of all commands installed at this node */
4122 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
4123
4124 /* loop over all commands at this node */
4125 for (i = 0; i < vector_active(cmd_vector); ++i)
4126 {
4127 struct cmd_element *cmd_element;
4128
4129 /* A cmd_element (seems to be) is an individual command */
4130 if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
4131 continue;
4132
4133 vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE);
4134 }
4135
4136 vector_free (cmd_vector);
4137 return CMD_SUCCESS;
4138}
4139
718e3744 4140/* Set config filename. Called from vty.c */
4141void
c0e8c16f 4142host_config_set (const char *filename)
718e3744 4143{
228da428
CC
4144 if (host.config)
4145 XFREE (MTYPE_HOST, host.config);
05865c90 4146 host.config = XSTRDUP (MTYPE_HOST, filename);
718e3744 4147}
4148
4149void
4150install_default (enum node_type node)
4151{
4152 install_element (node, &config_exit_cmd);
4153 install_element (node, &config_quit_cmd);
4154 install_element (node, &config_end_cmd);
4155 install_element (node, &config_help_cmd);
4156 install_element (node, &config_list_cmd);
4157
4158 install_element (node, &config_write_terminal_cmd);
4159 install_element (node, &config_write_file_cmd);
4160 install_element (node, &config_write_memory_cmd);
4161 install_element (node, &config_write_cmd);
4162 install_element (node, &show_running_config_cmd);
4163}
4164
4165/* Initialize command interface. Install basic nodes and commands. */
4166void
4167cmd_init (int terminal)
4168{
cd40b329
CF
4169 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4170 token_cr.type = TOKEN_TERMINAL;
c117e027 4171 token_cr.terminal = TERMINAL_LITERAL;
cd40b329
CF
4172 token_cr.cmd = command_cr;
4173 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
228da428 4174
718e3744 4175 /* Allocate initial top vector of commands. */
4176 cmdvec = vector_init (VECTOR_MIN_SIZE);
4177
4178 /* Default host value settings. */
4179 host.name = NULL;
4180 host.password = NULL;
4181 host.enable = NULL;
4182 host.logfile = NULL;
4183 host.config = NULL;
4184 host.lines = -1;
4185 host.motd = default_motd;
3b0c5d9a 4186 host.motdfile = NULL;
718e3744 4187
4188 /* Install top nodes. */
4189 install_node (&view_node, NULL);
4190 install_node (&enable_node, NULL);
4191 install_node (&auth_node, NULL);
4192 install_node (&auth_enable_node, NULL);
62687ff1 4193 install_node (&restricted_node, NULL);
718e3744 4194 install_node (&config_node, config_write_host);
4195
4196 /* Each node's basic commands. */
4197 install_element (VIEW_NODE, &show_version_cmd);
4198 if (terminal)
4199 {
4200 install_element (VIEW_NODE, &config_list_cmd);
4201 install_element (VIEW_NODE, &config_exit_cmd);
4202 install_element (VIEW_NODE, &config_quit_cmd);
4203 install_element (VIEW_NODE, &config_help_cmd);
4204 install_element (VIEW_NODE, &config_enable_cmd);
4205 install_element (VIEW_NODE, &config_terminal_length_cmd);
4206 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
274a4a44 4207 install_element (VIEW_NODE, &show_logging_cmd);
bcd9fa7f 4208 install_element (VIEW_NODE, &show_commandtree_cmd);
2885f72d 4209 install_element (VIEW_NODE, &echo_cmd);
62687ff1
PJ
4210
4211 install_element (RESTRICTED_NODE, &config_list_cmd);
4212 install_element (RESTRICTED_NODE, &config_exit_cmd);
4213 install_element (RESTRICTED_NODE, &config_quit_cmd);
4214 install_element (RESTRICTED_NODE, &config_help_cmd);
4215 install_element (RESTRICTED_NODE, &config_enable_cmd);
4216 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4217 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
bcd9fa7f 4218 install_element (RESTRICTED_NODE, &show_commandtree_cmd);
62687ff1 4219 install_element (RESTRICTED_NODE, &echo_cmd);
718e3744 4220 }
4221
4222 if (terminal)
4223 {
4224 install_default (ENABLE_NODE);
4225 install_element (ENABLE_NODE, &config_disable_cmd);
4226 install_element (ENABLE_NODE, &config_terminal_cmd);
4227 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4228 }
4229 install_element (ENABLE_NODE, &show_startup_config_cmd);
4230 install_element (ENABLE_NODE, &show_version_cmd);
bcd9fa7f 4231 install_element (ENABLE_NODE, &show_commandtree_cmd);
718e3744 4232
718e3744 4233 if (terminal)
4234 {
e7168df4 4235 install_element (ENABLE_NODE, &config_terminal_length_cmd);
4236 install_element (ENABLE_NODE, &config_terminal_no_length_cmd);
274a4a44 4237 install_element (ENABLE_NODE, &show_logging_cmd);
2885f72d 4238 install_element (ENABLE_NODE, &echo_cmd);
274a4a44 4239 install_element (ENABLE_NODE, &config_logmsg_cmd);
e7168df4 4240
4241 install_default (CONFIG_NODE);
ea8e9d97 4242 }
4243
4244 install_element (CONFIG_NODE, &hostname_cmd);
4245 install_element (CONFIG_NODE, &no_hostname_cmd);
e7168df4 4246
ea8e9d97 4247 if (terminal)
4248 {
e7168df4 4249 install_element (CONFIG_NODE, &password_cmd);
4250 install_element (CONFIG_NODE, &password_text_cmd);
4251 install_element (CONFIG_NODE, &enable_password_cmd);
4252 install_element (CONFIG_NODE, &enable_password_text_cmd);
4253 install_element (CONFIG_NODE, &no_enable_password_cmd);
4254
718e3744 4255 install_element (CONFIG_NODE, &config_log_stdout_cmd);
274a4a44 4256 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
718e3744 4257 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
274a4a44 4258 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4259 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4260 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
718e3744 4261 install_element (CONFIG_NODE, &config_log_file_cmd);
274a4a44 4262 install_element (CONFIG_NODE, &config_log_file_level_cmd);
718e3744 4263 install_element (CONFIG_NODE, &no_config_log_file_cmd);
274a4a44 4264 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
718e3744 4265 install_element (CONFIG_NODE, &config_log_syslog_cmd);
274a4a44 4266 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
12ab19f1 4267 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
718e3744 4268 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
12ab19f1 4269 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
274a4a44 4270 install_element (CONFIG_NODE, &config_log_facility_cmd);
4271 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
718e3744 4272 install_element (CONFIG_NODE, &config_log_trap_cmd);
4273 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4274 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4275 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
1ed72e0b
AS
4276 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4277 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
718e3744 4278 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4279 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4280 install_element (CONFIG_NODE, &banner_motd_default_cmd);
3b0c5d9a 4281 install_element (CONFIG_NODE, &banner_motd_file_cmd);
718e3744 4282 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4283 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4284 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
718e3744 4285
354d119a 4286 install_element (VIEW_NODE, &show_thread_cpu_cmd);
4287 install_element (ENABLE_NODE, &show_thread_cpu_cmd);
62687ff1 4288 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
e276eb82
PJ
4289
4290 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
354d119a 4291 install_element (VIEW_NODE, &show_work_queues_cmd);
4292 install_element (ENABLE_NODE, &show_work_queues_cmd);
19dc275e
DS
4293
4294 vrf_install_commands ();
9ab6812d 4295 }
bcd9fa7f 4296 install_element (CONFIG_NODE, &show_commandtree_cmd);
b06fd125 4297 srandom(time(NULL));
718e3744 4298}
228da428 4299
cd40b329
CF
4300static void
4301cmd_terminate_token(struct cmd_token *token)
4302{
4303 unsigned int i, j;
4304 vector keyword_vect;
4305
4306 if (token->multiple)
4307 {
4308 for (i = 0; i < vector_active(token->multiple); i++)
4309 cmd_terminate_token(vector_slot(token->multiple, i));
4310 vector_free(token->multiple);
4311 token->multiple = NULL;
4312 }
4313
4314 if (token->keyword)
4315 {
4316 for (i = 0; i < vector_active(token->keyword); i++)
4317 {
4318 keyword_vect = vector_slot(token->keyword, i);
4319 for (j = 0; j < vector_active(keyword_vect); j++)
4320 cmd_terminate_token(vector_slot(keyword_vect, j));
4321 vector_free(keyword_vect);
4322 }
4323 vector_free(token->keyword);
4324 token->keyword = NULL;
4325 }
4326
4327 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4328 XFREE(MTYPE_CMD_TOKENS, token->desc);
4329
4330 XFREE(MTYPE_CMD_TOKENS, token);
4331}
4332
4333static void
4334cmd_terminate_element(struct cmd_element *cmd)
4335{
4336 unsigned int i;
4337
4338 if (cmd->tokens == NULL)
4339 return;
4340
4341 for (i = 0; i < vector_active(cmd->tokens); i++)
4342 cmd_terminate_token(vector_slot(cmd->tokens, i));
4343
4344 vector_free(cmd->tokens);
4345 cmd->tokens = NULL;
4346}
4347
228da428
CC
4348void
4349cmd_terminate ()
4350{
cd40b329 4351 unsigned int i, j;
228da428
CC
4352 struct cmd_node *cmd_node;
4353 struct cmd_element *cmd_element;
cd40b329 4354 vector cmd_node_v;
228da428
CC
4355
4356 if (cmdvec)
4357 {
4358 for (i = 0; i < vector_active (cmdvec); i++)
4359 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4360 {
4361 cmd_node_v = cmd_node->cmd_vector;
4362
4363 for (j = 0; j < vector_active (cmd_node_v); j++)
cd40b329
CF
4364 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4365 cmd_terminate_element(cmd_element);
228da428
CC
4366
4367 vector_free (cmd_node_v);
4368 }
4369
4370 vector_free (cmdvec);
4371 cmdvec = NULL;
4372 }
4373
4374 if (command_cr)
cd40b329
CF
4375 XFREE(MTYPE_CMD_TOKENS, command_cr);
4376 if (token_cr.desc)
4377 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
228da428
CC
4378 if (host.name)
4379 XFREE (MTYPE_HOST, host.name);
4380 if (host.password)
4381 XFREE (MTYPE_HOST, host.password);
4382 if (host.password_encrypt)
4383 XFREE (MTYPE_HOST, host.password_encrypt);
4384 if (host.enable)
4385 XFREE (MTYPE_HOST, host.enable);
4386 if (host.enable_encrypt)
4387 XFREE (MTYPE_HOST, host.enable_encrypt);
4388 if (host.logfile)
4389 XFREE (MTYPE_HOST, host.logfile);
4390 if (host.motdfile)
4391 XFREE (MTYPE_HOST, host.motdfile);
4392 if (host.config)
4393 XFREE (MTYPE_HOST, host.config);
4394}