blob: 80893602da178c69cb76012cf937019128490147 [file] [log] [blame]
ajs274a4a42004-12-07 15:39:31 +00001/*
ajs274a4a42004-12-07 15:39:31 +00002 Command interpreter routine for virtual terminal [aka TeletYpe]
paul718e3742002-12-13 20:15:29 +00003 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Christian Frankecd40b322013-09-30 12:27:51 +00004 Copyright (C) 2013 by Open Source Routing.
5 Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
paul718e3742002-12-13 20:15:29 +00006
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
paulb21b19c2003-06-15 01:28:29 +000026
paul718e3742002-12-13 20:15:29 +000027#include "memory.h"
28#include "log.h"
gdt5e4fa162004-03-16 14:38:36 +000029#include <lib/version.h>
paul9ab68122003-01-18 01:16:20 +000030#include "thread.h"
paulb21b19c2003-06-15 01:28:29 +000031#include "vector.h"
32#include "vty.h"
33#include "command.h"
paul354d1192005-04-25 16:26:42 +000034#include "workqueue.h"
paul718e3742002-12-13 20:15:29 +000035
36/* Command vector which includes some level of command lists. Normally
37 each daemon maintains each own cmdvec. */
pauleb820af2005-09-05 11:54:13 +000038vector cmdvec = NULL;
paul718e3742002-12-13 20:15:29 +000039
Christian Frankecd40b322013-09-30 12:27:51 +000040struct cmd_token token_cr;
Chris Caputo228da422009-07-18 05:44:03 +000041char *command_cr = NULL;
42
Christian Frankecd40b322013-09-30 12:27:51 +000043enum filter_type
44{
45 FILTER_RELAXED,
46 FILTER_STRICT
47};
48
49enum matcher_rv
50{
51 MATCHER_OK,
52 MATCHER_COMPLETE,
53 MATCHER_INCOMPLETE,
54 MATCHER_NO_MATCH,
55 MATCHER_AMBIGUOUS,
56 MATCHER_EXCEED_ARGC_MAX
57};
58
59#define MATCHER_ERROR(matcher_rv) \
60 ( (matcher_rv) == MATCHER_INCOMPLETE \
61 || (matcher_rv) == MATCHER_NO_MATCH \
62 || (matcher_rv) == MATCHER_AMBIGUOUS \
63 || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
64 )
65
paul718e3742002-12-13 20:15:29 +000066/* Host information structure. */
67struct host host;
68
paul718e3742002-12-13 20:15:29 +000069/* Standard command node structures. */
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080070static struct cmd_node auth_node =
paul718e3742002-12-13 20:15:29 +000071{
72 AUTH_NODE,
73 "Password: ",
74};
75
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080076static struct cmd_node view_node =
paul718e3742002-12-13 20:15:29 +000077{
78 VIEW_NODE,
79 "%s> ",
80};
81
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080082static struct cmd_node restricted_node =
Paul Jakma62687ff2008-08-23 14:27:06 +010083{
84 RESTRICTED_NODE,
85 "%s$ ",
86};
87
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080088static struct cmd_node auth_enable_node =
paul718e3742002-12-13 20:15:29 +000089{
90 AUTH_ENABLE_NODE,
91 "Password: ",
92};
93
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080094static struct cmd_node enable_node =
paul718e3742002-12-13 20:15:29 +000095{
96 ENABLE_NODE,
97 "%s# ",
98};
99
Stephen Hemminger7fc626d2008-12-01 11:10:34 -0800100static struct cmd_node config_node =
paul718e3742002-12-13 20:15:29 +0000101{
102 CONFIG_NODE,
103 "%s(config)# ",
104 1
105};
hasso6590f2c2004-10-19 20:40:08 +0000106
107/* Default motd string. */
Everton Marques74b4fad2014-09-18 12:06:53 -0300108static const char *default_motd =
hasso6590f2c2004-10-19 20:40:08 +0000109"\r\n\
110Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
111" QUAGGA_COPYRIGHT "\r\n\
David Lamparter0be793e2012-11-27 01:34:56 +0000112" GIT_INFO "\r\n";
hasso6590f2c2004-10-19 20:40:08 +0000113
ajs274a4a42004-12-07 15:39:31 +0000114
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300115static const struct facility_map {
ajs274a4a42004-12-07 15:39:31 +0000116 int facility;
117 const char *name;
118 size_t match;
119} syslog_facilities[] =
120 {
121 { LOG_KERN, "kern", 1 },
122 { LOG_USER, "user", 2 },
123 { LOG_MAIL, "mail", 1 },
124 { LOG_DAEMON, "daemon", 1 },
125 { LOG_AUTH, "auth", 1 },
126 { LOG_SYSLOG, "syslog", 1 },
127 { LOG_LPR, "lpr", 2 },
128 { LOG_NEWS, "news", 1 },
129 { LOG_UUCP, "uucp", 2 },
130 { LOG_CRON, "cron", 1 },
131#ifdef LOG_FTP
132 { LOG_FTP, "ftp", 1 },
133#endif
134 { LOG_LOCAL0, "local0", 6 },
135 { LOG_LOCAL1, "local1", 6 },
136 { LOG_LOCAL2, "local2", 6 },
137 { LOG_LOCAL3, "local3", 6 },
138 { LOG_LOCAL4, "local4", 6 },
139 { LOG_LOCAL5, "local5", 6 },
140 { LOG_LOCAL6, "local6", 6 },
141 { LOG_LOCAL7, "local7", 6 },
142 { 0, NULL, 0 },
143 };
144
145static const char *
146facility_name(int facility)
147{
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300148 const struct facility_map *fm;
ajs274a4a42004-12-07 15:39:31 +0000149
150 for (fm = syslog_facilities; fm->name; fm++)
151 if (fm->facility == facility)
152 return fm->name;
153 return "";
154}
155
156static int
157facility_match(const char *str)
158{
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300159 const struct facility_map *fm;
ajs274a4a42004-12-07 15:39:31 +0000160
161 for (fm = syslog_facilities; fm->name; fm++)
162 if (!strncmp(str,fm->name,fm->match))
163 return fm->facility;
164 return -1;
165}
166
167static int
168level_match(const char *s)
169{
170 int level ;
171
172 for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )
173 if (!strncmp (s, zlog_priority[level], 2))
174 return level;
175 return ZLOG_DISABLED;
176}
177
ajscb585b62005-01-14 17:09:38 +0000178/* This is called from main when a daemon is invoked with -v or --version. */
hasso6590f2c2004-10-19 20:40:08 +0000179void
180print_version (const char *progname)
181{
ajscb585b62005-01-14 17:09:38 +0000182 printf ("%s version %s\n", progname, QUAGGA_VERSION);
183 printf ("%s\n", QUAGGA_COPYRIGHT);
David Lamparter7abd8752014-11-22 10:43:29 -0800184 printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS);
hasso6590f2c2004-10-19 20:40:08 +0000185}
186
David Lamparter6b0655a2014-06-04 06:53:35 +0200187
paul718e3742002-12-13 20:15:29 +0000188/* Utility function to concatenate argv argument into a single string
189 with inserting ' ' character between each argument. */
190char *
paul42d49862004-10-13 05:22:18 +0000191argv_concat (const char **argv, int argc, int shift)
paul718e3742002-12-13 20:15:29 +0000192{
193 int i;
ajsf6834d42005-01-28 20:28:35 +0000194 size_t len;
paul718e3742002-12-13 20:15:29 +0000195 char *str;
ajsf6834d42005-01-28 20:28:35 +0000196 char *p;
paul718e3742002-12-13 20:15:29 +0000197
ajsf6834d42005-01-28 20:28:35 +0000198 len = 0;
199 for (i = shift; i < argc; i++)
200 len += strlen(argv[i])+1;
201 if (!len)
202 return NULL;
203 p = str = XMALLOC(MTYPE_TMP, len);
paul718e3742002-12-13 20:15:29 +0000204 for (i = shift; i < argc; i++)
205 {
ajsf6834d42005-01-28 20:28:35 +0000206 size_t arglen;
207 memcpy(p, argv[i], (arglen = strlen(argv[i])));
208 p += arglen;
209 *p++ = ' ';
paul718e3742002-12-13 20:15:29 +0000210 }
ajsf6834d42005-01-28 20:28:35 +0000211 *(p-1) = '\0';
paul718e3742002-12-13 20:15:29 +0000212 return str;
213}
214
215/* Install top node of command vector. */
216void
217install_node (struct cmd_node *node,
218 int (*func) (struct vty *))
219{
220 vector_set_index (cmdvec, node->node, node);
221 node->func = func;
222 node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
223}
224
paul718e3742002-12-13 20:15:29 +0000225/* Breaking up string into each command piece. I assume given
226 character is separated by a space character. Return value is a
227 vector which includes char ** data element. */
228vector
hassoea8e9d92004-10-07 21:32:14 +0000229cmd_make_strvec (const char *string)
paul718e3742002-12-13 20:15:29 +0000230{
hassoea8e9d92004-10-07 21:32:14 +0000231 const char *cp, *start;
232 char *token;
paul718e3742002-12-13 20:15:29 +0000233 int strlen;
234 vector strvec;
235
236 if (string == NULL)
237 return NULL;
238
239 cp = string;
240
241 /* Skip white spaces. */
242 while (isspace ((int) *cp) && *cp != '\0')
243 cp++;
244
245 /* Return if there is only white spaces */
246 if (*cp == '\0')
247 return NULL;
248
249 if (*cp == '!' || *cp == '#')
250 return NULL;
251
252 /* Prepare return vector. */
253 strvec = vector_init (VECTOR_MIN_SIZE);
254
255 /* Copy each command piece and set into vector. */
256 while (1)
257 {
258 start = cp;
259 while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
260 *cp != '\0')
261 cp++;
262 strlen = cp - start;
263 token = XMALLOC (MTYPE_STRVEC, strlen + 1);
264 memcpy (token, start, strlen);
265 *(token + strlen) = '\0';
266 vector_set (strvec, token);
267
268 while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
269 *cp != '\0')
270 cp++;
271
272 if (*cp == '\0')
273 return strvec;
274 }
275}
276
277/* Free allocated string vector. */
278void
279cmd_free_strvec (vector v)
280{
hasso8c328f12004-10-05 21:01:23 +0000281 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000282 char *cp;
283
284 if (!v)
285 return;
286
paul55468c82005-03-14 20:19:01 +0000287 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +0000288 if ((cp = vector_slot (v, i)) != NULL)
289 XFREE (MTYPE_STRVEC, cp);
290
291 vector_free (v);
292}
293
Christian Frankecd40b322013-09-30 12:27:51 +0000294struct format_parser_state
295{
296 vector topvect; /* Top level vector */
297 vector intvect; /* Intermediate level vector, used when there's
298 * a multiple in a keyword. */
299 vector curvect; /* current vector where read tokens should be
300 appended. */
301
302 const char *string; /* pointer to command string, not modified */
303 const char *cp; /* pointer in command string, moved along while
304 parsing */
305 const char *dp; /* pointer in description string, moved along while
306 parsing */
307
308 int in_keyword; /* flag to remember if we are in a keyword group */
309 int in_multiple; /* flag to remember if we are in a multiple group */
310 int just_read_word; /* flag to remember if the last thing we red was a
311 * real word and not some abstract token */
312};
313
314static void
315format_parser_error(struct format_parser_state *state, const char *message)
316{
317 int offset = state->cp - state->string + 1;
318
319 fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string);
320 fprintf(stderr, " %*c\n", offset, '^');
321 fprintf(stderr, "%s at offset %d.\n", message, offset);
322 fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n");
323 exit(1);
324}
325
ajs274a4a42004-12-07 15:39:31 +0000326static char *
Christian Frankecd40b322013-09-30 12:27:51 +0000327format_parser_desc_str(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000328{
hasso6ad96ea2004-10-07 19:33:46 +0000329 const char *cp, *start;
330 char *token;
paul718e3742002-12-13 20:15:29 +0000331 int strlen;
Christian Frankecd40b322013-09-30 12:27:51 +0000332
333 cp = state->dp;
paul718e3742002-12-13 20:15:29 +0000334
335 if (cp == NULL)
336 return NULL;
337
338 /* Skip white spaces. */
339 while (isspace ((int) *cp) && *cp != '\0')
340 cp++;
341
342 /* Return if there is only white spaces */
343 if (*cp == '\0')
344 return NULL;
345
346 start = cp;
347
348 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
349 cp++;
350
351 strlen = cp - start;
Christian Frankecd40b322013-09-30 12:27:51 +0000352 token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1);
paul718e3742002-12-13 20:15:29 +0000353 memcpy (token, start, strlen);
354 *(token + strlen) = '\0';
355
Christian Frankecd40b322013-09-30 12:27:51 +0000356 state->dp = cp;
paul718e3742002-12-13 20:15:29 +0000357
358 return token;
359}
360
Christian Frankecd40b322013-09-30 12:27:51 +0000361static void
362format_parser_begin_keyword(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000363{
Christian Frankecd40b322013-09-30 12:27:51 +0000364 struct cmd_token *token;
365 vector keyword_vect;
paul718e3742002-12-13 20:15:29 +0000366
Christian Frankecd40b322013-09-30 12:27:51 +0000367 if (state->in_keyword
368 || state->in_multiple)
369 format_parser_error(state, "Unexpected '{'");
paul718e3742002-12-13 20:15:29 +0000370
Christian Frankecd40b322013-09-30 12:27:51 +0000371 state->cp++;
372 state->in_keyword = 1;
paul718e3742002-12-13 20:15:29 +0000373
Christian Frankecd40b322013-09-30 12:27:51 +0000374 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
375 token->type = TOKEN_KEYWORD;
376 token->keyword = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +0000377
Christian Frankecd40b322013-09-30 12:27:51 +0000378 keyword_vect = vector_init(VECTOR_MIN_SIZE);
379 vector_set(token->keyword, keyword_vect);
380
381 vector_set(state->curvect, token);
382 state->curvect = keyword_vect;
383}
384
385static void
386format_parser_begin_multiple(struct format_parser_state *state)
387{
388 struct cmd_token *token;
389
390 if (state->in_keyword == 1)
391 format_parser_error(state, "Keyword starting with '('");
392
393 if (state->in_multiple)
394 format_parser_error(state, "Nested group");
395
396 state->cp++;
397 state->in_multiple = 1;
398 state->just_read_word = 0;
399
400 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
401 token->type = TOKEN_MULTIPLE;
402 token->multiple = vector_init(VECTOR_MIN_SIZE);
403
404 vector_set(state->curvect, token);
405 if (state->curvect != state->topvect)
406 state->intvect = state->curvect;
407 state->curvect = token->multiple;
408}
409
410static void
411format_parser_end_keyword(struct format_parser_state *state)
412{
413 if (state->in_multiple
414 || !state->in_keyword)
415 format_parser_error(state, "Unexpected '}'");
416
417 if (state->in_keyword == 1)
418 format_parser_error(state, "Empty keyword group");
419
420 state->cp++;
421 state->in_keyword = 0;
422 state->curvect = state->topvect;
423}
424
425static void
426format_parser_end_multiple(struct format_parser_state *state)
427{
428 char *dummy;
429
430 if (!state->in_multiple)
431 format_parser_error(state, "Unepexted ')'");
432
433 if (vector_active(state->curvect) == 0)
434 format_parser_error(state, "Empty multiple section");
435
436 if (!state->just_read_word)
paul718e3742002-12-13 20:15:29 +0000437 {
Christian Frankecd40b322013-09-30 12:27:51 +0000438 /* There are constructions like
439 * 'show ip ospf database ... (self-originate|)'
440 * in use.
441 * The old parser reads a description string for the
442 * word '' between |) which will never match.
443 * Simulate this behvaior by dropping the next desc
444 * string in such a case. */
paul718e3742002-12-13 20:15:29 +0000445
Christian Frankecd40b322013-09-30 12:27:51 +0000446 dummy = format_parser_desc_str(state);
447 XFREE(MTYPE_CMD_TOKENS, dummy);
448 }
paul718e3742002-12-13 20:15:29 +0000449
Christian Frankecd40b322013-09-30 12:27:51 +0000450 state->cp++;
451 state->in_multiple = 0;
paul718e3742002-12-13 20:15:29 +0000452
Christian Frankecd40b322013-09-30 12:27:51 +0000453 if (state->intvect)
454 state->curvect = state->intvect;
455 else
456 state->curvect = state->topvect;
457}
paul718e3742002-12-13 20:15:29 +0000458
Christian Frankecd40b322013-09-30 12:27:51 +0000459static void
460format_parser_handle_pipe(struct format_parser_state *state)
461{
462 struct cmd_token *keyword_token;
463 vector keyword_vect;
paul718e3742002-12-13 20:15:29 +0000464
Christian Frankecd40b322013-09-30 12:27:51 +0000465 if (state->in_multiple)
466 {
467 state->just_read_word = 0;
468 state->cp++;
469 }
470 else if (state->in_keyword)
471 {
472 state->in_keyword = 1;
473 state->cp++;
paul718e3742002-12-13 20:15:29 +0000474
Christian Frankecd40b322013-09-30 12:27:51 +0000475 keyword_token = vector_slot(state->topvect,
476 vector_active(state->topvect) - 1);
477 keyword_vect = vector_init(VECTOR_MIN_SIZE);
478 vector_set(keyword_token->keyword, keyword_vect);
479 state->curvect = keyword_vect;
480 }
481 else
482 {
483 format_parser_error(state, "Unexpected '|'");
paul718e3742002-12-13 20:15:29 +0000484 }
485}
486
Christian Frankecd40b322013-09-30 12:27:51 +0000487static void
488format_parser_read_word(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000489{
Christian Frankecd40b322013-09-30 12:27:51 +0000490 const char *start;
491 int len;
492 char *cmd;
493 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +0000494
Christian Frankecd40b322013-09-30 12:27:51 +0000495 start = state->cp;
496
497 while (state->cp[0] != '\0'
498 && !strchr("\r\n(){}|", state->cp[0])
499 && !isspace((int)state->cp[0]))
500 state->cp++;
501
502 len = state->cp - start;
503 cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1);
504 memcpy(cmd, start, len);
505 cmd[len] = '\0';
506
507 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
508 token->type = TOKEN_TERMINAL;
David Lamparter14162932015-05-12 17:18:04 +0200509 if (strcmp (cmd, "A.B.C.D") == 0)
David Lamparter10bac802015-05-05 11:10:20 +0200510 token->terminal = TERMINAL_IPV4;
511 else if (strcmp (cmd, "A.B.C.D/M") == 0)
512 token->terminal = TERMINAL_IPV4_PREFIX;
513 else if (strcmp (cmd, "X:X::X:X") == 0)
514 token->terminal = TERMINAL_IPV6;
515 else if (strcmp (cmd, "X:X::X:X/M") == 0)
516 token->terminal = TERMINAL_IPV6_PREFIX;
David Lamparter14162932015-05-12 17:18:04 +0200517 else if (cmd[0] == '[')
518 token->terminal = TERMINAL_OPTION;
519 else if (cmd[0] == '.')
520 token->terminal = TERMINAL_VARARG;
521 else if (cmd[0] == '<')
522 token->terminal = TERMINAL_RANGE;
523 else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
524 token->terminal = TERMINAL_VARIABLE;
David Lamparter10bac802015-05-05 11:10:20 +0200525 else
526 token->terminal = TERMINAL_LITERAL;
527
Christian Frankecd40b322013-09-30 12:27:51 +0000528 token->cmd = cmd;
529 token->desc = format_parser_desc_str(state);
530 vector_set(state->curvect, token);
531
532 if (state->in_keyword == 1)
533 state->in_keyword = 2;
534
535 state->just_read_word = 1;
536}
537
538/**
539 * Parse a given command format string and build a tree of tokens from
540 * it that is suitable to be used by the command subsystem.
541 *
542 * @param string Command format string.
543 * @param descstr Description string.
544 * @return A vector of struct cmd_token representing the given command,
545 * or NULL on error.
546 */
547static vector
548cmd_parse_format(const char *string, const char *descstr)
549{
550 struct format_parser_state state;
551
552 if (string == NULL)
553 return NULL;
554
555 memset(&state, 0, sizeof(state));
556 state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE);
557 state.cp = state.string = string;
558 state.dp = descstr;
559
560 while (1)
paul718e3742002-12-13 20:15:29 +0000561 {
Christian Frankecd40b322013-09-30 12:27:51 +0000562 while (isspace((int)state.cp[0]) && state.cp[0] != '\0')
563 state.cp++;
564
565 switch (state.cp[0])
566 {
567 case '\0':
568 if (state.in_keyword
569 || state.in_multiple)
570 format_parser_error(&state, "Unclosed group/keyword");
571 return state.topvect;
572 case '{':
573 format_parser_begin_keyword(&state);
574 break;
575 case '(':
576 format_parser_begin_multiple(&state);
577 break;
578 case '}':
579 format_parser_end_keyword(&state);
580 break;
581 case ')':
582 format_parser_end_multiple(&state);
583 break;
584 case '|':
585 format_parser_handle_pipe(&state);
586 break;
587 default:
588 format_parser_read_word(&state);
589 }
paul718e3742002-12-13 20:15:29 +0000590 }
paul718e3742002-12-13 20:15:29 +0000591}
592
593/* Return prompt character of specified node. */
hasso8c328f12004-10-05 21:01:23 +0000594const char *
paul718e3742002-12-13 20:15:29 +0000595cmd_prompt (enum node_type node)
596{
597 struct cmd_node *cnode;
598
599 cnode = vector_slot (cmdvec, node);
600 return cnode->prompt;
601}
602
603/* Install a command into a node. */
604void
605install_element (enum node_type ntype, struct cmd_element *cmd)
606{
607 struct cmd_node *cnode;
pauleb820af2005-09-05 11:54:13 +0000608
609 /* cmd_init hasn't been called */
610 if (!cmdvec)
611 return;
612
paul718e3742002-12-13 20:15:29 +0000613 cnode = vector_slot (cmdvec, ntype);
614
615 if (cnode == NULL)
616 {
617 fprintf (stderr, "Command node %d doesn't exist, please check it\n",
618 ntype);
619 exit (1);
620 }
621
622 vector_set (cnode->cmd_vector, cmd);
Christian Frankecd40b322013-09-30 12:27:51 +0000623 if (cmd->tokens == NULL)
624 cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
paul718e3742002-12-13 20:15:29 +0000625}
626
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300627static const unsigned char itoa64[] =
paul718e3742002-12-13 20:15:29 +0000628"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
629
ajs274a4a42004-12-07 15:39:31 +0000630static void
paul718e3742002-12-13 20:15:29 +0000631to64(char *s, long v, int n)
632{
633 while (--n >= 0)
634 {
635 *s++ = itoa64[v&0x3f];
636 v >>= 6;
637 }
638}
639
ajs274a4a42004-12-07 15:39:31 +0000640static char *
641zencrypt (const char *passwd)
paul718e3742002-12-13 20:15:29 +0000642{
643 char salt[6];
644 struct timeval tv;
645 char *crypt (const char *, const char *);
646
647 gettimeofday(&tv,0);
648
649 to64(&salt[0], random(), 3);
650 to64(&salt[3], tv.tv_usec, 3);
651 salt[5] = '\0';
652
653 return crypt (passwd, salt);
654}
655
656/* This function write configuration of this host. */
ajs274a4a42004-12-07 15:39:31 +0000657static int
paul718e3742002-12-13 20:15:29 +0000658config_write_host (struct vty *vty)
659{
660 if (host.name)
661 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
662
663 if (host.encrypt)
664 {
665 if (host.password_encrypt)
666 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
667 if (host.enable_encrypt)
668 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
669 }
670 else
671 {
672 if (host.password)
673 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
674 if (host.enable)
675 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
676 }
677
ajs274a4a42004-12-07 15:39:31 +0000678 if (zlog_default->default_lvl != LOG_DEBUG)
ajs82146b82004-12-07 17:15:55 +0000679 {
680 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
681 VTY_NEWLINE);
682 vty_out (vty, "log trap %s%s",
683 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
684 }
paul718e3742002-12-13 20:15:29 +0000685
ajs274a4a42004-12-07 15:39:31 +0000686 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
paul12ab19f2003-07-26 06:14:55 +0000687 {
ajs274a4a42004-12-07 15:39:31 +0000688 vty_out (vty, "log file %s", host.logfile);
689 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
690 vty_out (vty, " %s",
691 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
paul12ab19f2003-07-26 06:14:55 +0000692 vty_out (vty, "%s", VTY_NEWLINE);
693 }
ajs274a4a42004-12-07 15:39:31 +0000694
695 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
696 {
697 vty_out (vty, "log stdout");
698 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
699 vty_out (vty, " %s",
700 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
701 vty_out (vty, "%s", VTY_NEWLINE);
702 }
703
704 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
705 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
706 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
707 vty_out(vty,"log monitor %s%s",
708 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
709
710 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
711 {
712 vty_out (vty, "log syslog");
713 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
714 vty_out (vty, " %s",
715 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
716 vty_out (vty, "%s", VTY_NEWLINE);
717 }
718
719 if (zlog_default->facility != LOG_DAEMON)
720 vty_out (vty, "log facility %s%s",
721 facility_name(zlog_default->facility), VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000722
723 if (zlog_default->record_priority == 1)
724 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
725
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000726 if (zlog_default->timestamp_precision > 0)
727 vty_out (vty, "log timestamp precision %d%s",
728 zlog_default->timestamp_precision, VTY_NEWLINE);
729
paul718e3742002-12-13 20:15:29 +0000730 if (host.advanced)
731 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
732
733 if (host.encrypt)
734 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
735
736 if (host.lines >= 0)
737 vty_out (vty, "service terminal-length %d%s", host.lines,
738 VTY_NEWLINE);
739
paul3b0c5d92005-03-08 10:43:43 +0000740 if (host.motdfile)
741 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
742 else if (! host.motd)
paul718e3742002-12-13 20:15:29 +0000743 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
744
745 return 1;
746}
747
748/* Utility function for getting command vector. */
ajs274a4a42004-12-07 15:39:31 +0000749static vector
paul718e3742002-12-13 20:15:29 +0000750cmd_node_vector (vector v, enum node_type ntype)
751{
752 struct cmd_node *cnode = vector_slot (v, ntype);
753 return cnode->cmd_vector;
754}
755
ajs274a4a42004-12-07 15:39:31 +0000756#if 0
757/* Filter command vector by symbol. This function is not actually used;
758 * should it be deleted? */
759static int
paul718e3742002-12-13 20:15:29 +0000760cmd_filter_by_symbol (char *command, char *symbol)
761{
762 int i, lim;
763
764 if (strcmp (symbol, "IPV4_ADDRESS") == 0)
765 {
766 i = 0;
767 lim = strlen (command);
768 while (i < lim)
769 {
770 if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
771 return 1;
772 i++;
773 }
774 return 0;
775 }
776 if (strcmp (symbol, "STRING") == 0)
777 {
778 i = 0;
779 lim = strlen (command);
780 while (i < lim)
781 {
782 if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
783 return 1;
784 i++;
785 }
786 return 0;
787 }
788 if (strcmp (symbol, "IFNAME") == 0)
789 {
790 i = 0;
791 lim = strlen (command);
792 while (i < lim)
793 {
794 if (! isalnum ((int) command[i]))
795 return 1;
796 i++;
797 }
798 return 0;
799 }
800 return 0;
801}
ajs274a4a42004-12-07 15:39:31 +0000802#endif
paul718e3742002-12-13 20:15:29 +0000803
804/* Completion match types. */
805enum match_type
806{
807 no_match,
808 extend_match,
809 ipv4_prefix_match,
810 ipv4_match,
811 ipv6_prefix_match,
812 ipv6_match,
813 range_match,
814 vararg_match,
815 partly_match,
816 exact_match
817};
818
ajs274a4a42004-12-07 15:39:31 +0000819static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000820cmd_ipv4_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000821{
hasso8c328f12004-10-05 21:01:23 +0000822 const char *sp;
paul718e3742002-12-13 20:15:29 +0000823 int dots = 0, nums = 0;
824 char buf[4];
825
826 if (str == NULL)
827 return partly_match;
828
829 for (;;)
830 {
831 memset (buf, 0, sizeof (buf));
832 sp = str;
833 while (*str != '\0')
834 {
835 if (*str == '.')
836 {
837 if (dots >= 3)
838 return no_match;
839
840 if (*(str + 1) == '.')
841 return no_match;
842
843 if (*(str + 1) == '\0')
844 return partly_match;
845
846 dots++;
847 break;
848 }
849 if (!isdigit ((int) *str))
850 return no_match;
851
852 str++;
853 }
854
855 if (str - sp > 3)
856 return no_match;
857
858 strncpy (buf, sp, str - sp);
859 if (atoi (buf) > 255)
860 return no_match;
861
862 nums++;
863
864 if (*str == '\0')
865 break;
866
867 str++;
868 }
869
870 if (nums < 4)
871 return partly_match;
872
873 return exact_match;
874}
875
ajs274a4a42004-12-07 15:39:31 +0000876static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000877cmd_ipv4_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000878{
hasso8c328f12004-10-05 21:01:23 +0000879 const char *sp;
paul718e3742002-12-13 20:15:29 +0000880 int dots = 0;
881 char buf[4];
882
883 if (str == NULL)
884 return partly_match;
885
886 for (;;)
887 {
888 memset (buf, 0, sizeof (buf));
889 sp = str;
890 while (*str != '\0' && *str != '/')
891 {
892 if (*str == '.')
893 {
894 if (dots == 3)
895 return no_match;
896
897 if (*(str + 1) == '.' || *(str + 1) == '/')
898 return no_match;
899
900 if (*(str + 1) == '\0')
901 return partly_match;
902
903 dots++;
904 break;
905 }
906
907 if (!isdigit ((int) *str))
908 return no_match;
909
910 str++;
911 }
912
913 if (str - sp > 3)
914 return no_match;
915
916 strncpy (buf, sp, str - sp);
917 if (atoi (buf) > 255)
918 return no_match;
919
920 if (dots == 3)
921 {
922 if (*str == '/')
923 {
924 if (*(str + 1) == '\0')
925 return partly_match;
926
927 str++;
928 break;
929 }
930 else if (*str == '\0')
931 return partly_match;
932 }
933
934 if (*str == '\0')
935 return partly_match;
936
937 str++;
938 }
939
940 sp = str;
941 while (*str != '\0')
942 {
943 if (!isdigit ((int) *str))
944 return no_match;
945
946 str++;
947 }
948
949 if (atoi (sp) > 32)
950 return no_match;
951
952 return exact_match;
953}
954
955#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
956#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
957#define STATE_START 1
958#define STATE_COLON 2
959#define STATE_DOUBLE 3
960#define STATE_ADDR 4
961#define STATE_DOT 5
962#define STATE_SLASH 6
963#define STATE_MASK 7
964
paul22e0a9e2003-07-11 17:55:46 +0000965#ifdef HAVE_IPV6
966
ajs274a4a42004-12-07 15:39:31 +0000967static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000968cmd_ipv6_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000969{
hasso726f9b22003-05-25 21:04:54 +0000970 struct sockaddr_in6 sin6_dummy;
971 int ret;
paul718e3742002-12-13 20:15:29 +0000972
973 if (str == NULL)
974 return partly_match;
975
976 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
977 return no_match;
978
hasso726f9b22003-05-25 21:04:54 +0000979 /* use inet_pton that has a better support,
980 * for example inet_pton can support the automatic addresses:
981 * ::1.2.3.4
982 */
983 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
984
985 if (ret == 1)
986 return exact_match;
987
Roman Hoog Antink7c9c6ae2012-05-09 06:35:34 +0000988 return no_match;
paul718e3742002-12-13 20:15:29 +0000989}
990
ajs274a4a42004-12-07 15:39:31 +0000991static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000992cmd_ipv6_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000993{
994 int state = STATE_START;
995 int colons = 0, nums = 0, double_colon = 0;
996 int mask;
hasso8c328f12004-10-05 21:01:23 +0000997 const char *sp = NULL;
paul718e3742002-12-13 20:15:29 +0000998 char *endptr = NULL;
999
1000 if (str == NULL)
1001 return partly_match;
1002
1003 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
1004 return no_match;
1005
1006 while (*str != '\0' && state != STATE_MASK)
1007 {
1008 switch (state)
1009 {
1010 case STATE_START:
1011 if (*str == ':')
1012 {
1013 if (*(str + 1) != ':' && *(str + 1) != '\0')
1014 return no_match;
1015 colons--;
1016 state = STATE_COLON;
1017 }
1018 else
1019 {
1020 sp = str;
1021 state = STATE_ADDR;
1022 }
1023
1024 continue;
1025 case STATE_COLON:
1026 colons++;
1027 if (*(str + 1) == '/')
1028 return no_match;
1029 else if (*(str + 1) == ':')
1030 state = STATE_DOUBLE;
1031 else
1032 {
1033 sp = str + 1;
1034 state = STATE_ADDR;
1035 }
1036 break;
1037 case STATE_DOUBLE:
1038 if (double_colon)
1039 return no_match;
1040
1041 if (*(str + 1) == ':')
1042 return no_match;
1043 else
1044 {
1045 if (*(str + 1) != '\0' && *(str + 1) != '/')
1046 colons++;
1047 sp = str + 1;
1048
1049 if (*(str + 1) == '/')
1050 state = STATE_SLASH;
1051 else
1052 state = STATE_ADDR;
1053 }
1054
1055 double_colon++;
1056 nums += 1;
1057 break;
1058 case STATE_ADDR:
1059 if (*(str + 1) == ':' || *(str + 1) == '.'
1060 || *(str + 1) == '\0' || *(str + 1) == '/')
1061 {
1062 if (str - sp > 3)
1063 return no_match;
1064
1065 for (; sp <= str; sp++)
1066 if (*sp == '/')
1067 return no_match;
1068
1069 nums++;
1070
1071 if (*(str + 1) == ':')
1072 state = STATE_COLON;
1073 else if (*(str + 1) == '.')
David Lamparteraa5cf242012-07-19 16:11:50 +02001074 {
1075 if (colons || double_colon)
1076 state = STATE_DOT;
1077 else
1078 return no_match;
1079 }
paul718e3742002-12-13 20:15:29 +00001080 else if (*(str + 1) == '/')
1081 state = STATE_SLASH;
1082 }
1083 break;
1084 case STATE_DOT:
1085 state = STATE_ADDR;
1086 break;
1087 case STATE_SLASH:
1088 if (*(str + 1) == '\0')
1089 return partly_match;
1090
1091 state = STATE_MASK;
1092 break;
1093 default:
1094 break;
1095 }
1096
1097 if (nums > 11)
1098 return no_match;
1099
1100 if (colons > 7)
1101 return no_match;
1102
1103 str++;
1104 }
1105
1106 if (state < STATE_MASK)
1107 return partly_match;
1108
1109 mask = strtol (str, &endptr, 10);
1110 if (*endptr != '\0')
1111 return no_match;
1112
1113 if (mask < 0 || mask > 128)
1114 return no_match;
1115
1116/* I don't know why mask < 13 makes command match partly.
1117 Forgive me to make this comments. I Want to set static default route
1118 because of lack of function to originate default in ospf6d; sorry
1119 yasu
1120 if (mask < 13)
1121 return partly_match;
1122*/
1123
1124 return exact_match;
1125}
1126
paul22e0a9e2003-07-11 17:55:46 +00001127#endif /* HAVE_IPV6 */
1128
paul718e3742002-12-13 20:15:29 +00001129#define DECIMAL_STRLEN_MAX 10
1130
ajs274a4a42004-12-07 15:39:31 +00001131static int
hasso8c328f12004-10-05 21:01:23 +00001132cmd_range_match (const char *range, const char *str)
paul718e3742002-12-13 20:15:29 +00001133{
1134 char *p;
1135 char buf[DECIMAL_STRLEN_MAX + 1];
1136 char *endptr = NULL;
1137 unsigned long min, max, val;
1138
1139 if (str == NULL)
1140 return 1;
1141
1142 val = strtoul (str, &endptr, 10);
1143 if (*endptr != '\0')
1144 return 0;
1145
1146 range++;
1147 p = strchr (range, '-');
1148 if (p == NULL)
1149 return 0;
1150 if (p - range > DECIMAL_STRLEN_MAX)
1151 return 0;
1152 strncpy (buf, range, p - range);
1153 buf[p - range] = '\0';
1154 min = strtoul (buf, &endptr, 10);
1155 if (*endptr != '\0')
1156 return 0;
1157
1158 range = p + 1;
1159 p = strchr (range, '>');
1160 if (p == NULL)
1161 return 0;
1162 if (p - range > DECIMAL_STRLEN_MAX)
1163 return 0;
1164 strncpy (buf, range, p - range);
1165 buf[p - range] = '\0';
1166 max = strtoul (buf, &endptr, 10);
1167 if (*endptr != '\0')
1168 return 0;
1169
1170 if (val < min || val > max)
1171 return 0;
1172
1173 return 1;
1174}
1175
ajs274a4a42004-12-07 15:39:31 +00001176static enum match_type
Christian Frankecd40b322013-09-30 12:27:51 +00001177cmd_word_match(struct cmd_token *token,
1178 enum filter_type filter,
1179 const char *word)
paul718e3742002-12-13 20:15:29 +00001180{
hasso8c328f12004-10-05 21:01:23 +00001181 const char *str;
paul718e3742002-12-13 20:15:29 +00001182 enum match_type match_type;
paul909a2152005-03-14 17:41:45 +00001183
Christian Frankecd40b322013-09-30 12:27:51 +00001184 str = token->cmd;
paul718e3742002-12-13 20:15:29 +00001185
Christian Frankecd40b322013-09-30 12:27:51 +00001186 if (filter == FILTER_RELAXED)
1187 if (!word || !strlen(word))
1188 return partly_match;
paul718e3742002-12-13 20:15:29 +00001189
Christian Frankecd40b322013-09-30 12:27:51 +00001190 if (!word)
1191 return no_match;
paul909a2152005-03-14 17:41:45 +00001192
David Lamparter10bac802015-05-05 11:10:20 +02001193 switch (token->terminal)
Christian Frankecd40b322013-09-30 12:27:51 +00001194 {
David Lamparter10bac802015-05-05 11:10:20 +02001195 case TERMINAL_VARARG:
1196 return vararg_match;
1197
1198 case TERMINAL_RANGE:
1199 if (cmd_range_match(str, word))
1200 return range_match;
1201 break;
1202
1203 case TERMINAL_IPV6:
1204 match_type = cmd_ipv6_match(word);
1205 if ((filter == FILTER_RELAXED && match_type != no_match)
Christian Frankecd40b322013-09-30 12:27:51 +00001206 || (filter == FILTER_STRICT && match_type == exact_match))
David Lamparter10bac802015-05-05 11:10:20 +02001207 return ipv6_match;
1208 break;
1209
1210 case TERMINAL_IPV6_PREFIX:
1211 match_type = cmd_ipv6_prefix_match(word);
1212 if ((filter == FILTER_RELAXED && match_type != no_match)
1213 || (filter == FILTER_STRICT && match_type == exact_match))
1214 return ipv6_prefix_match;
1215 break;
1216
1217 case TERMINAL_IPV4:
1218 match_type = cmd_ipv4_match(word);
1219 if ((filter == FILTER_RELAXED && match_type != no_match)
1220 || (filter == FILTER_STRICT && match_type == exact_match))
1221 return ipv4_match;
1222 break;
1223
1224 case TERMINAL_IPV4_PREFIX:
1225 match_type = cmd_ipv4_prefix_match(word);
1226 if ((filter == FILTER_RELAXED && match_type != no_match)
1227 || (filter == FILTER_STRICT && match_type == exact_match))
1228 return ipv4_prefix_match;
1229 break;
1230
1231 case TERMINAL_OPTION:
1232 case TERMINAL_VARIABLE:
1233 return extend_match;
1234
1235 case TERMINAL_LITERAL:
1236 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1237 {
1238 if (!strcmp(str, word))
1239 return exact_match;
1240 return partly_match;
1241 }
1242 if (filter == FILTER_STRICT && !strcmp(str, word))
1243 return exact_match;
1244 break;
1245
1246 default:
1247 assert (0);
Christian Frankecd40b322013-09-30 12:27:51 +00001248 }
paul718e3742002-12-13 20:15:29 +00001249
Christian Frankecd40b322013-09-30 12:27:51 +00001250 return no_match;
paul718e3742002-12-13 20:15:29 +00001251}
1252
Christian Frankecd40b322013-09-30 12:27:51 +00001253struct cmd_matcher
1254{
1255 struct cmd_element *cmd; /* The command element the matcher is using */
1256 enum filter_type filter; /* Whether to use strict or relaxed matching */
1257 vector vline; /* The tokenized commandline which is to be matched */
1258 unsigned int index; /* The index up to which matching should be done */
1259
1260 /* If set, construct a list of matches at the position given by index */
1261 enum match_type *match_type;
1262 vector *match;
1263
1264 unsigned int word_index; /* iterating over vline */
1265};
1266
1267static int
1268push_argument(int *argc, const char **argv, const char *arg)
1269{
1270 if (!arg || !strlen(arg))
1271 arg = NULL;
1272
1273 if (!argc || !argv)
1274 return 0;
1275
1276 if (*argc >= CMD_ARGC_MAX)
1277 return -1;
1278
1279 argv[(*argc)++] = arg;
1280 return 0;
1281}
1282
1283static void
1284cmd_matcher_record_match(struct cmd_matcher *matcher,
1285 enum match_type match_type,
1286 struct cmd_token *token)
1287{
1288 if (matcher->word_index != matcher->index)
1289 return;
1290
1291 if (matcher->match)
1292 {
1293 if (!*matcher->match)
1294 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1295 vector_set(*matcher->match, token);
1296 }
1297
1298 if (matcher->match_type)
1299 {
1300 if (match_type > *matcher->match_type)
1301 *matcher->match_type = match_type;
1302 }
1303}
1304
1305static int
1306cmd_matcher_words_left(struct cmd_matcher *matcher)
1307{
1308 return matcher->word_index < vector_active(matcher->vline);
1309}
1310
1311static const char*
1312cmd_matcher_get_word(struct cmd_matcher *matcher)
1313{
1314 assert(cmd_matcher_words_left(matcher));
1315
1316 return vector_slot(matcher->vline, matcher->word_index);
1317}
1318
1319static enum matcher_rv
1320cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1321 struct cmd_token *token,
1322 int *argc, const char **argv)
1323{
1324 const char *word;
1325 enum match_type word_match;
1326
1327 assert(token->type == TOKEN_TERMINAL);
1328
1329 if (!cmd_matcher_words_left(matcher))
1330 {
David Lamparter10bac802015-05-05 11:10:20 +02001331 if (token->terminal == TERMINAL_OPTION)
Christian Frankecd40b322013-09-30 12:27:51 +00001332 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1333 else
1334 return MATCHER_INCOMPLETE;
1335 }
1336
1337 word = cmd_matcher_get_word(matcher);
1338 word_match = cmd_word_match(token, matcher->filter, word);
1339 if (word_match == no_match)
1340 return MATCHER_NO_MATCH;
1341
1342 /* We have to record the input word as argument if it matched
1343 * against a variable. */
David Lamparter14162932015-05-12 17:18:04 +02001344 if (TERMINAL_RECORD (token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001345 {
1346 if (push_argument(argc, argv, word))
1347 return MATCHER_EXCEED_ARGC_MAX;
1348 }
1349
1350 cmd_matcher_record_match(matcher, word_match, token);
1351
1352 matcher->word_index++;
1353
1354 /* A vararg token should consume all left over words as arguments */
David Lamparter10bac802015-05-05 11:10:20 +02001355 if (token->terminal == TERMINAL_VARARG)
Christian Frankecd40b322013-09-30 12:27:51 +00001356 while (cmd_matcher_words_left(matcher))
1357 {
1358 word = cmd_matcher_get_word(matcher);
1359 if (word && strlen(word))
1360 push_argument(argc, argv, word);
1361 matcher->word_index++;
1362 }
1363
1364 return MATCHER_OK;
1365}
1366
1367static enum matcher_rv
1368cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1369 struct cmd_token *token,
1370 int *argc, const char **argv)
1371{
1372 enum match_type multiple_match;
1373 unsigned int multiple_index;
1374 const char *word;
David Lamparterab90fc02015-03-03 09:07:25 +01001375 const char *arg = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001376 struct cmd_token *word_token;
1377 enum match_type word_match;
1378
1379 assert(token->type == TOKEN_MULTIPLE);
1380
1381 multiple_match = no_match;
1382
1383 if (!cmd_matcher_words_left(matcher))
1384 return MATCHER_INCOMPLETE;
1385
1386 word = cmd_matcher_get_word(matcher);
1387 for (multiple_index = 0;
1388 multiple_index < vector_active(token->multiple);
1389 multiple_index++)
1390 {
1391 word_token = vector_slot(token->multiple, multiple_index);
1392
1393 word_match = cmd_word_match(word_token, matcher->filter, word);
1394 if (word_match == no_match)
1395 continue;
1396
1397 cmd_matcher_record_match(matcher, word_match, word_token);
1398
1399 if (word_match > multiple_match)
1400 {
1401 multiple_match = word_match;
1402 arg = word;
1403 }
1404 /* To mimic the behavior of the old command implementation, we
1405 * tolerate any ambiguities here :/ */
1406 }
1407
1408 matcher->word_index++;
1409
1410 if (multiple_match == no_match)
1411 return MATCHER_NO_MATCH;
1412
1413 if (push_argument(argc, argv, arg))
1414 return MATCHER_EXCEED_ARGC_MAX;
1415
1416 return MATCHER_OK;
1417}
1418
1419static enum matcher_rv
1420cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1421 struct cmd_token *token,
1422 vector args_vector)
paul718e3742002-12-13 20:15:29 +00001423{
hasso8c328f12004-10-05 21:01:23 +00001424 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00001425 unsigned long keyword_mask;
1426 unsigned int keyword_found;
1427 enum match_type keyword_match;
1428 enum match_type word_match;
1429 vector keyword_vector;
1430 struct cmd_token *word_token;
1431 const char *word;
1432 int keyword_argc;
1433 const char **keyword_argv;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001434 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001435
1436 keyword_mask = 0;
1437 while (1)
1438 {
1439 if (!cmd_matcher_words_left(matcher))
1440 return MATCHER_OK;
1441
1442 word = cmd_matcher_get_word(matcher);
1443
1444 keyword_found = -1;
1445 keyword_match = no_match;
1446 for (i = 0; i < vector_active(token->keyword); i++)
1447 {
1448 if (keyword_mask & (1 << i))
1449 continue;
1450
1451 keyword_vector = vector_slot(token->keyword, i);
1452 word_token = vector_slot(keyword_vector, 0);
1453
1454 word_match = cmd_word_match(word_token, matcher->filter, word);
1455 if (word_match == no_match)
1456 continue;
1457
1458 cmd_matcher_record_match(matcher, word_match, word_token);
1459
1460 if (word_match > keyword_match)
1461 {
1462 keyword_match = word_match;
1463 keyword_found = i;
1464 }
1465 else if (word_match == keyword_match)
1466 {
1467 if (matcher->word_index != matcher->index || args_vector)
1468 return MATCHER_AMBIGUOUS;
1469 }
1470 }
1471
1472 if (keyword_found == (unsigned int)-1)
1473 return MATCHER_NO_MATCH;
1474
1475 matcher->word_index++;
1476
1477 if (matcher->word_index > matcher->index)
1478 return MATCHER_OK;
1479
1480 keyword_mask |= (1 << keyword_found);
1481
1482 if (args_vector)
1483 {
1484 keyword_argc = 0;
1485 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1486 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1487 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1488 keyword_argv[i] = (void*)-1;
1489 vector_set_index(args_vector, keyword_found, keyword_argv);
1490 }
1491 else
1492 {
1493 keyword_argv = NULL;
1494 }
1495
1496 keyword_vector = vector_slot(token->keyword, keyword_found);
1497 /* the keyword itself is at 0. We are only interested in the arguments,
1498 * so start counting at 1. */
1499 for (i = 1; i < vector_active(keyword_vector); i++)
1500 {
1501 word_token = vector_slot(keyword_vector, i);
1502
1503 switch (word_token->type)
1504 {
1505 case TOKEN_TERMINAL:
1506 rv = cmd_matcher_match_terminal(matcher, word_token,
1507 &keyword_argc, keyword_argv);
1508 break;
1509 case TOKEN_MULTIPLE:
1510 rv = cmd_matcher_match_multiple(matcher, word_token,
1511 &keyword_argc, keyword_argv);
1512 break;
1513 case TOKEN_KEYWORD:
1514 assert(!"Keywords should never be nested.");
1515 break;
1516 }
1517
1518 if (MATCHER_ERROR(rv))
1519 return rv;
1520
1521 if (matcher->word_index > matcher->index)
1522 return MATCHER_OK;
1523 }
1524 }
1525 /* not reached */
1526}
1527
1528static enum matcher_rv
1529cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1530 struct cmd_token *token,
1531 int *argc, const char **argv,
1532 vector keyword_args_vector)
1533{
1534 unsigned int i, j;
1535 const char **keyword_args;
1536 vector keyword_vector;
1537 struct cmd_token *word_token;
1538 const char *arg;
1539 enum matcher_rv rv;
1540
1541 rv = MATCHER_OK;
1542
1543 if (keyword_args_vector == NULL)
1544 return rv;
1545
1546 for (i = 0; i < vector_active(token->keyword); i++)
1547 {
1548 keyword_vector = vector_slot(token->keyword, i);
1549 keyword_args = vector_lookup(keyword_args_vector, i);
1550
1551 if (vector_active(keyword_vector) == 1)
1552 {
1553 /* this is a keyword without arguments */
1554 if (keyword_args)
1555 {
1556 word_token = vector_slot(keyword_vector, 0);
1557 arg = word_token->cmd;
1558 }
1559 else
1560 {
1561 arg = NULL;
1562 }
1563
1564 if (push_argument(argc, argv, arg))
1565 rv = MATCHER_EXCEED_ARGC_MAX;
1566 }
1567 else
1568 {
1569 /* this is a keyword with arguments */
1570 if (keyword_args)
1571 {
1572 /* the keyword was present, so just fill in the arguments */
1573 for (j = 0; keyword_args[j] != (void*)-1; j++)
1574 if (push_argument(argc, argv, keyword_args[j]))
1575 rv = MATCHER_EXCEED_ARGC_MAX;
1576 XFREE(MTYPE_TMP, keyword_args);
1577 }
1578 else
1579 {
1580 /* the keyword was not present, insert NULL for the arguments
1581 * the keyword would have taken. */
1582 for (j = 1; j < vector_active(keyword_vector); j++)
1583 {
1584 word_token = vector_slot(keyword_vector, j);
1585 if ((word_token->type == TOKEN_TERMINAL
David Lamparter14162932015-05-12 17:18:04 +02001586 && TERMINAL_RECORD (word_token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001587 || word_token->type == TOKEN_MULTIPLE)
1588 {
1589 if (push_argument(argc, argv, NULL))
1590 rv = MATCHER_EXCEED_ARGC_MAX;
1591 }
1592 }
1593 }
1594 }
1595 }
1596 vector_free(keyword_args_vector);
1597 return rv;
1598}
1599
1600static enum matcher_rv
1601cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1602 struct cmd_token *token,
1603 int *argc, const char **argv)
1604{
1605 vector keyword_args_vector;
1606 enum matcher_rv reader_rv;
1607 enum matcher_rv builder_rv;
1608
1609 assert(token->type == TOKEN_KEYWORD);
1610
1611 if (argc && argv)
1612 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1613 else
1614 keyword_args_vector = NULL;
1615
1616 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1617 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1618 argv, keyword_args_vector);
1619 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1620
1621 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1622 return builder_rv;
1623
1624 return reader_rv;
1625}
1626
1627static void
1628cmd_matcher_init(struct cmd_matcher *matcher,
1629 struct cmd_element *cmd,
1630 enum filter_type filter,
1631 vector vline,
1632 unsigned int index,
1633 enum match_type *match_type,
1634 vector *match)
1635{
1636 memset(matcher, 0, sizeof(*matcher));
1637
1638 matcher->cmd = cmd;
1639 matcher->filter = filter;
1640 matcher->vline = vline;
1641 matcher->index = index;
1642
1643 matcher->match_type = match_type;
1644 if (matcher->match_type)
1645 *matcher->match_type = no_match;
1646 matcher->match = match;
1647
1648 matcher->word_index = 0;
1649}
1650
1651static enum matcher_rv
1652cmd_element_match(struct cmd_element *cmd_element,
1653 enum filter_type filter,
1654 vector vline,
1655 unsigned int index,
1656 enum match_type *match_type,
1657 vector *match,
1658 int *argc,
1659 const char **argv)
1660{
1661 struct cmd_matcher matcher;
1662 unsigned int token_index;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001663 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001664
1665 cmd_matcher_init(&matcher, cmd_element, filter,
1666 vline, index, match_type, match);
1667
1668 if (argc != NULL)
1669 *argc = 0;
1670
1671 for (token_index = 0;
1672 token_index < vector_active(cmd_element->tokens);
1673 token_index++)
1674 {
1675 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1676
1677 switch (token->type)
1678 {
1679 case TOKEN_TERMINAL:
1680 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1681 break;
1682 case TOKEN_MULTIPLE:
1683 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1684 break;
1685 case TOKEN_KEYWORD:
1686 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1687 }
1688
1689 if (MATCHER_ERROR(rv))
1690 return rv;
1691
1692 if (matcher.word_index > index)
1693 return MATCHER_OK;
1694 }
1695
1696 /* return MATCHER_COMPLETE if all words were consumed */
1697 if (matcher.word_index >= vector_active(vline))
1698 return MATCHER_COMPLETE;
1699
1700 /* return MATCHER_COMPLETE also if only an empty word is left. */
1701 if (matcher.word_index == vector_active(vline) - 1
1702 && (!vector_slot(vline, matcher.word_index)
1703 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1704 return MATCHER_COMPLETE;
1705
1706 return MATCHER_NO_MATCH; /* command is too long to match */
1707}
1708
1709/**
1710 * Filter a given vector of commands against a given commandline and
1711 * calculate possible completions.
1712 *
1713 * @param commands A vector of struct cmd_element*. Commands that don't
1714 * match against the given command line will be overwritten
1715 * with NULL in that vector.
1716 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1717 * determines how incomplete commands are handled, compare with
1718 * cmd_word_match for details.
1719 * @param vline A vector of char* containing the tokenized commandline.
1720 * @param index Only match up to the given token of the commandline.
1721 * @param match_type Record the type of the best match here.
1722 * @param matches Record the matches here. For each cmd_element in the commands
1723 * vector, a match vector will be created in the matches vector.
1724 * That vector will contain all struct command_token* of the
1725 * cmd_element which matched against the given vline at the given
1726 * index.
1727 * @return A code specifying if an error occured. If all went right, it's
1728 * CMD_SUCCESS.
1729 */
1730static int
1731cmd_vector_filter(vector commands,
1732 enum filter_type filter,
1733 vector vline,
1734 unsigned int index,
1735 enum match_type *match_type,
1736 vector *matches)
1737{
1738 unsigned int i;
paul718e3742002-12-13 20:15:29 +00001739 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00001740 enum match_type best_match;
1741 enum match_type element_match;
1742 enum matcher_rv matcher_rv;
paul909a2152005-03-14 17:41:45 +00001743
Christian Frankecd40b322013-09-30 12:27:51 +00001744 best_match = no_match;
1745 *matches = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +00001746
Christian Frankecd40b322013-09-30 12:27:51 +00001747 for (i = 0; i < vector_active (commands); i++)
1748 if ((cmd_element = vector_slot (commands, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001749 {
Christian Frankecd40b322013-09-30 12:27:51 +00001750 vector_set_index(*matches, i, NULL);
1751 matcher_rv = cmd_element_match(cmd_element, filter,
1752 vline, index,
1753 &element_match,
1754 (vector*)&vector_slot(*matches, i),
1755 NULL, NULL);
1756 if (MATCHER_ERROR(matcher_rv))
1757 {
1758 vector_slot(commands, i) = NULL;
1759 if (matcher_rv == MATCHER_AMBIGUOUS)
1760 return CMD_ERR_AMBIGUOUS;
1761 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1762 return CMD_ERR_EXEED_ARGC_MAX;
1763 }
1764 else if (element_match > best_match)
1765 {
1766 best_match = element_match;
1767 }
paul718e3742002-12-13 20:15:29 +00001768 }
Christian Frankecd40b322013-09-30 12:27:51 +00001769 *match_type = best_match;
1770 return CMD_SUCCESS;
1771}
1772
1773/**
1774 * Check whether a given commandline is complete if used for a specific
1775 * cmd_element.
1776 *
1777 * @param cmd_element A cmd_element against which the commandline should be
1778 * checked.
1779 * @param vline The tokenized commandline.
1780 * @return 1 if the given commandline is complete, 0 otherwise.
1781 */
1782static int
1783cmd_is_complete(struct cmd_element *cmd_element,
1784 vector vline)
1785{
1786 enum matcher_rv rv;
1787
1788 rv = cmd_element_match(cmd_element,
1789 FILTER_RELAXED,
1790 vline, -1,
1791 NULL, NULL,
1792 NULL, NULL);
1793 return (rv == MATCHER_COMPLETE);
1794}
1795
1796/**
1797 * Parse a given commandline and construct a list of arguments for the
1798 * given command_element.
1799 *
1800 * @param cmd_element The cmd_element for which we want to construct arguments.
1801 * @param vline The tokenized commandline.
1802 * @param argc Where to store the argument count.
1803 * @param argv Where to store the argument list. Should be at least
1804 * CMD_ARGC_MAX elements long.
1805 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1806 */
1807static int
1808cmd_parse(struct cmd_element *cmd_element,
1809 vector vline,
1810 int *argc, const char **argv)
1811{
1812 enum matcher_rv rv = cmd_element_match(cmd_element,
1813 FILTER_RELAXED,
1814 vline, -1,
1815 NULL, NULL,
1816 argc, argv);
1817 switch (rv)
1818 {
1819 case MATCHER_COMPLETE:
1820 return CMD_SUCCESS;
1821
1822 case MATCHER_NO_MATCH:
1823 return CMD_ERR_NO_MATCH;
1824
1825 case MATCHER_AMBIGUOUS:
1826 return CMD_ERR_AMBIGUOUS;
1827
1828 case MATCHER_EXCEED_ARGC_MAX:
1829 return CMD_ERR_EXEED_ARGC_MAX;
1830
1831 default:
1832 return CMD_ERR_INCOMPLETE;
1833 }
paul718e3742002-12-13 20:15:29 +00001834}
1835
1836/* Check ambiguous match */
ajs274a4a42004-12-07 15:39:31 +00001837static int
Christian Frankecd40b322013-09-30 12:27:51 +00001838is_cmd_ambiguous (vector cmd_vector,
1839 const char *command,
1840 vector matches,
1841 enum match_type type)
paul718e3742002-12-13 20:15:29 +00001842{
hasso8c328f12004-10-05 21:01:23 +00001843 unsigned int i;
1844 unsigned int j;
1845 const char *str = NULL;
hasso8c328f12004-10-05 21:01:23 +00001846 const char *matched = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001847 vector match_vector;
1848 struct cmd_token *cmd_token;
paul909a2152005-03-14 17:41:45 +00001849
Christian Frankecd40b322013-09-30 12:27:51 +00001850 if (command == NULL)
1851 command = "";
1852
1853 for (i = 0; i < vector_active (matches); i++)
1854 if ((match_vector = vector_slot (matches, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001855 {
1856 int match = 0;
1857
Christian Frankecd40b322013-09-30 12:27:51 +00001858 for (j = 0; j < vector_active (match_vector); j++)
1859 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
paul909a2152005-03-14 17:41:45 +00001860 {
1861 enum match_type ret;
Christian Frankecd40b322013-09-30 12:27:51 +00001862
1863 assert(cmd_token->type == TOKEN_TERMINAL);
1864 if (cmd_token->type != TOKEN_TERMINAL)
1865 continue;
1866
1867 str = cmd_token->cmd;
paul718e3742002-12-13 20:15:29 +00001868
paul909a2152005-03-14 17:41:45 +00001869 switch (type)
1870 {
1871 case exact_match:
David Lamparter14162932015-05-12 17:18:04 +02001872 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001873 && strcmp (command, str) == 0)
1874 match++;
1875 break;
1876 case partly_match:
David Lamparter14162932015-05-12 17:18:04 +02001877 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001878 && strncmp (command, str, strlen (command)) == 0)
1879 {
1880 if (matched && strcmp (matched, str) != 0)
1881 return 1; /* There is ambiguous match. */
1882 else
1883 matched = str;
1884 match++;
1885 }
1886 break;
1887 case range_match:
1888 if (cmd_range_match (str, command))
1889 {
1890 if (matched && strcmp (matched, str) != 0)
1891 return 1;
1892 else
1893 matched = str;
1894 match++;
1895 }
1896 break;
1897#ifdef HAVE_IPV6
1898 case ipv6_match:
David Lamparter10bac802015-05-05 11:10:20 +02001899 if (cmd_token->terminal == TERMINAL_IPV6)
paul909a2152005-03-14 17:41:45 +00001900 match++;
1901 break;
1902 case ipv6_prefix_match:
1903 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1904 {
1905 if (ret == partly_match)
1906 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001907
paul909a2152005-03-14 17:41:45 +00001908 match++;
1909 }
1910 break;
1911#endif /* HAVE_IPV6 */
1912 case ipv4_match:
David Lamparter10bac802015-05-05 11:10:20 +02001913 if (cmd_token->terminal == TERMINAL_IPV4)
paul718e3742002-12-13 20:15:29 +00001914 match++;
paul909a2152005-03-14 17:41:45 +00001915 break;
1916 case ipv4_prefix_match:
1917 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1918 {
1919 if (ret == partly_match)
1920 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001921
paul909a2152005-03-14 17:41:45 +00001922 match++;
1923 }
1924 break;
1925 case extend_match:
David Lamparter14162932015-05-12 17:18:04 +02001926 if (TERMINAL_RECORD (cmd_token->terminal))
paul718e3742002-12-13 20:15:29 +00001927 match++;
paul909a2152005-03-14 17:41:45 +00001928 break;
1929 case no_match:
1930 default:
1931 break;
1932 }
1933 }
1934 if (!match)
Christian Frankecd40b322013-09-30 12:27:51 +00001935 vector_slot (cmd_vector, i) = NULL;
paul718e3742002-12-13 20:15:29 +00001936 }
1937 return 0;
1938}
1939
1940/* If src matches dst return dst string, otherwise return NULL */
ajs274a4a42004-12-07 15:39:31 +00001941static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001942cmd_entry_function (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001943{
David Lamparter10bac802015-05-05 11:10:20 +02001944 const char *dst = token->cmd;
1945
paul718e3742002-12-13 20:15:29 +00001946 /* Skip variable arguments. */
David Lamparter14162932015-05-12 17:18:04 +02001947 if (TERMINAL_RECORD (token->terminal))
1948 return NULL;
paul718e3742002-12-13 20:15:29 +00001949
1950 /* In case of 'command \t', given src is NULL string. */
1951 if (src == NULL)
1952 return dst;
1953
1954 /* Matched with input string. */
1955 if (strncmp (src, dst, strlen (src)) == 0)
1956 return dst;
1957
1958 return NULL;
1959}
1960
1961/* If src matches dst return dst string, otherwise return NULL */
1962/* This version will return the dst string always if it is
1963 CMD_VARIABLE for '?' key processing */
ajs274a4a42004-12-07 15:39:31 +00001964static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001965cmd_entry_function_desc (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001966{
David Lamparter10bac802015-05-05 11:10:20 +02001967 const char *dst = token->cmd;
paul718e3742002-12-13 20:15:29 +00001968
David Lamparter10bac802015-05-05 11:10:20 +02001969 switch (token->terminal)
paul718e3742002-12-13 20:15:29 +00001970 {
David Lamparter10bac802015-05-05 11:10:20 +02001971 case TERMINAL_VARARG:
1972 return dst;
1973
1974 case TERMINAL_RANGE:
1975 if (cmd_range_match (dst, src))
1976 return dst;
1977 else
1978 return NULL;
1979
1980 case TERMINAL_IPV6:
1981 if (cmd_ipv6_match (src))
1982 return dst;
1983 else
1984 return NULL;
1985
1986 case TERMINAL_IPV6_PREFIX:
1987 if (cmd_ipv6_prefix_match (src))
1988 return dst;
1989 else
1990 return NULL;
1991
1992 case TERMINAL_IPV4:
1993 if (cmd_ipv4_match (src))
1994 return dst;
1995 else
1996 return NULL;
1997
1998 case TERMINAL_IPV4_PREFIX:
1999 if (cmd_ipv4_prefix_match (src))
2000 return dst;
2001 else
2002 return NULL;
2003
2004 /* Optional or variable commands always match on '?' */
2005 case TERMINAL_OPTION:
2006 case TERMINAL_VARIABLE:
2007 return dst;
2008
2009 case TERMINAL_LITERAL:
2010 /* In case of 'command \t', given src is NULL string. */
2011 if (src == NULL)
2012 return dst;
2013
2014 if (strncmp (src, dst, strlen (src)) == 0)
2015 return dst;
2016 else
2017 return NULL;
2018
2019 default:
2020 assert(0);
David Lamparterf1fc3272015-05-13 12:44:50 +02002021 return NULL;
paul718e3742002-12-13 20:15:29 +00002022 }
paul718e3742002-12-13 20:15:29 +00002023}
2024
Christian Frankecd40b322013-09-30 12:27:51 +00002025/**
2026 * Check whether a string is already present in a vector of strings.
2027 * @param v A vector of char*.
2028 * @param str A char*.
2029 * @return 0 if str is already present in the vector, 1 otherwise.
2030 */
ajs274a4a42004-12-07 15:39:31 +00002031static int
hasso8c328f12004-10-05 21:01:23 +00002032cmd_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002033{
hasso8c328f12004-10-05 21:01:23 +00002034 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002035 char *match;
2036
paul55468c82005-03-14 20:19:01 +00002037 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +00002038 if ((match = vector_slot (v, i)) != NULL)
2039 if (strcmp (match, str) == 0)
2040 return 0;
2041 return 1;
2042}
2043
Christian Frankecd40b322013-09-30 12:27:51 +00002044/**
2045 * Check whether a struct cmd_token matching a given string is already
2046 * present in a vector of struct cmd_token.
2047 * @param v A vector of struct cmd_token*.
2048 * @param str A char* which should be searched for.
2049 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2050 * 1 otherwise.
2051 */
ajs274a4a42004-12-07 15:39:31 +00002052static int
hasso8c328f12004-10-05 21:01:23 +00002053desc_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002054{
hasso8c328f12004-10-05 21:01:23 +00002055 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00002056 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002057
paul55468c82005-03-14 20:19:01 +00002058 for (i = 0; i < vector_active (v); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00002059 if ((token = vector_slot (v, i)) != NULL)
2060 if (strcmp (token->cmd, str) == 0)
2061 return 0;
2062 return 1;
paul718e3742002-12-13 20:15:29 +00002063}
2064
ajs274a4a42004-12-07 15:39:31 +00002065static int
paulb92938a2002-12-13 21:20:42 +00002066cmd_try_do_shortcut (enum node_type node, char* first_word) {
2067 if ( first_word != NULL &&
2068 node != AUTH_NODE &&
2069 node != VIEW_NODE &&
2070 node != AUTH_ENABLE_NODE &&
2071 node != ENABLE_NODE &&
Paul Jakma62687ff2008-08-23 14:27:06 +01002072 node != RESTRICTED_NODE &&
paulb92938a2002-12-13 21:20:42 +00002073 0 == strcmp( "do", first_word ) )
2074 return 1;
2075 return 0;
2076}
2077
Christian Frankecd40b322013-09-30 12:27:51 +00002078static void
2079cmd_matches_free(vector *matches)
2080{
2081 unsigned int i;
2082 vector cmd_matches;
2083
2084 for (i = 0; i < vector_active(*matches); i++)
2085 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2086 vector_free(cmd_matches);
2087 vector_free(*matches);
2088 *matches = NULL;
2089}
2090
2091static int
2092cmd_describe_cmp(const void *a, const void *b)
2093{
2094 const struct cmd_token *first = *(struct cmd_token * const *)a;
2095 const struct cmd_token *second = *(struct cmd_token * const *)b;
2096
2097 return strcmp(first->cmd, second->cmd);
2098}
2099
2100static void
2101cmd_describe_sort(vector matchvec)
2102{
2103 qsort(matchvec->index, vector_active(matchvec),
2104 sizeof(void*), cmd_describe_cmp);
2105}
2106
paul718e3742002-12-13 20:15:29 +00002107/* '?' describe command support. */
ajs274a4a42004-12-07 15:39:31 +00002108static vector
paulb92938a2002-12-13 21:20:42 +00002109cmd_describe_command_real (vector vline, struct vty *vty, int *status)
paul718e3742002-12-13 20:15:29 +00002110{
paulb8961472005-03-14 17:35:52 +00002111 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002112 vector cmd_vector;
2113#define INIT_MATCHVEC_SIZE 10
2114 vector matchvec;
2115 struct cmd_element *cmd_element;
paulb8961472005-03-14 17:35:52 +00002116 unsigned int index;
paul54aba542003-08-21 20:28:24 +00002117 int ret;
2118 enum match_type match;
2119 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002120 vector matches = NULL;
2121 vector match_vector;
Donald Sharpf7332802015-07-17 22:36:57 -04002122 uint32_t command_found = 0;
2123 const char *last_word;
paul718e3742002-12-13 20:15:29 +00002124
2125 /* Set index. */
paul55468c82005-03-14 20:19:01 +00002126 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002127 {
2128 *status = CMD_ERR_NO_MATCH;
2129 return NULL;
2130 }
Christian Frankecd40b322013-09-30 12:27:51 +00002131
2132 index = vector_active (vline) - 1;
2133
paul718e3742002-12-13 20:15:29 +00002134 /* Make copy vector of current node's command vector. */
2135 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2136
2137 /* Prepare match vector */
2138 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2139
Christian Frankecd40b322013-09-30 12:27:51 +00002140 /* Filter commands and build a list how they could possibly continue. */
2141 for (i = 0; i <= index; i++)
2142 {
2143 command = vector_slot (vline, i);
paul718e3742002-12-13 20:15:29 +00002144
Christian Frankecd40b322013-09-30 12:27:51 +00002145 if (matches)
2146 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002147
Christian Frankecd40b322013-09-30 12:27:51 +00002148 ret = cmd_vector_filter(cmd_vector,
2149 FILTER_RELAXED,
2150 vline, i,
2151 &match,
2152 &matches);
paul718e3742002-12-13 20:15:29 +00002153
Christian Frankecd40b322013-09-30 12:27:51 +00002154 if (ret != CMD_SUCCESS)
2155 {
2156 vector_free (cmd_vector);
2157 vector_free (matchvec);
2158 cmd_matches_free(&matches);
2159 *status = ret;
2160 return NULL;
2161 }
paul718e3742002-12-13 20:15:29 +00002162
Christian Frankecd40b322013-09-30 12:27:51 +00002163 /* The last match may well be ambigious, so break here */
2164 if (i == index)
2165 break;
paul718e3742002-12-13 20:15:29 +00002166
Christian Frankecd40b322013-09-30 12:27:51 +00002167 if (match == vararg_match)
2168 {
2169 /* We found a vararg match - so we can throw out the current matches here
2170 * and don't need to continue checking the command input */
2171 unsigned int j, k;
2172
2173 for (j = 0; j < vector_active (matches); j++)
2174 if ((match_vector = vector_slot (matches, j)) != NULL)
2175 for (k = 0; k < vector_active (match_vector); k++)
2176 {
2177 struct cmd_token *token = vector_slot (match_vector, k);
2178 vector_set (matchvec, token);
2179 }
2180
2181 *status = CMD_SUCCESS;
2182 vector_set(matchvec, &token_cr);
2183 vector_free (cmd_vector);
2184 cmd_matches_free(&matches);
2185 cmd_describe_sort(matchvec);
2186 return matchvec;
2187 }
2188
2189 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2190 if (ret == 1)
2191 {
2192 vector_free (cmd_vector);
2193 vector_free (matchvec);
2194 cmd_matches_free(&matches);
2195 *status = CMD_ERR_AMBIGUOUS;
2196 return NULL;
2197 }
2198 else if (ret == 2)
2199 {
2200 vector_free (cmd_vector);
2201 vector_free (matchvec);
2202 cmd_matches_free(&matches);
2203 *status = CMD_ERR_NO_MATCH;
2204 return NULL;
2205 }
2206 }
paul54aba542003-08-21 20:28:24 +00002207
paul718e3742002-12-13 20:15:29 +00002208 /* Make description vector. */
Christian Frankecd40b322013-09-30 12:27:51 +00002209 for (i = 0; i < vector_active (matches); i++)
Donald Sharpf7332802015-07-17 22:36:57 -04002210 {
2211 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
2212 {
2213 unsigned int j;
2214 vector vline_trimmed;
paul718e3742002-12-13 20:15:29 +00002215
Donald Sharpf7332802015-07-17 22:36:57 -04002216 command_found++;
2217 last_word = vector_slot(vline, vector_active(vline) - 1);
2218 if (last_word == NULL || !strlen(last_word))
2219 {
2220 vline_trimmed = vector_copy(vline);
2221 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
paul718e3742002-12-13 20:15:29 +00002222
Donald Sharpf7332802015-07-17 22:36:57 -04002223 if (cmd_is_complete(cmd_element, vline_trimmed)
2224 && desc_unique_string(matchvec, command_cr))
2225 {
2226 if (match != vararg_match)
2227 vector_set(matchvec, &token_cr);
2228 }
Chris Caputo228da422009-07-18 05:44:03 +00002229
Donald Sharpf7332802015-07-17 22:36:57 -04002230 vector_free(vline_trimmed);
2231 }
Christian Frankecd40b322013-09-30 12:27:51 +00002232
Donald Sharpf7332802015-07-17 22:36:57 -04002233 match_vector = vector_slot (matches, i);
2234 if (match_vector)
2235 {
2236 for (j = 0; j < vector_active(match_vector); j++)
2237 {
2238 struct cmd_token *token = vector_slot(match_vector, j);
2239 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002240
Donald Sharpf7332802015-07-17 22:36:57 -04002241 string = cmd_entry_function_desc(command, token);
2242 if (string && desc_unique_string(matchvec, string))
2243 vector_set(matchvec, token);
2244 }
2245 }
2246 }
2247 }
2248
2249 /*
2250 * We can get into this situation when the command is complete
2251 * but the last part of the command is an optional piece of
2252 * the cli.
2253 */
2254 last_word = vector_slot(vline, vector_active(vline) - 1);
2255 if (command_found == 0 && (last_word == NULL || !strlen(last_word)))
2256 vector_set(matchvec, &token_cr);
2257
paul718e3742002-12-13 20:15:29 +00002258 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002259 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002260
2261 if (vector_slot (matchvec, 0) == NULL)
2262 {
2263 vector_free (matchvec);
paul909a2152005-03-14 17:41:45 +00002264 *status = CMD_ERR_NO_MATCH;
Paul Jakma5fc60512006-05-12 23:24:09 +00002265 return NULL;
paul718e3742002-12-13 20:15:29 +00002266 }
paul718e3742002-12-13 20:15:29 +00002267
Paul Jakma5fc60512006-05-12 23:24:09 +00002268 *status = CMD_SUCCESS;
Christian Frankecd40b322013-09-30 12:27:51 +00002269 cmd_describe_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002270 return matchvec;
2271}
2272
paulb92938a2002-12-13 21:20:42 +00002273vector
2274cmd_describe_command (vector vline, struct vty *vty, int *status)
2275{
2276 vector ret;
2277
2278 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2279 {
2280 enum node_type onode;
2281 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002282 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002283
2284 onode = vty->node;
2285 vty->node = ENABLE_NODE;
2286 /* We can try it on enable node, cos' the vty is authenticated */
2287
2288 shifted_vline = vector_init (vector_count(vline));
2289 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002290 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002291 {
2292 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2293 }
2294
2295 ret = cmd_describe_command_real (shifted_vline, vty, status);
2296
2297 vector_free(shifted_vline);
2298 vty->node = onode;
2299 return ret;
2300 }
2301
2302
2303 return cmd_describe_command_real (vline, vty, status);
2304}
2305
2306
paul718e3742002-12-13 20:15:29 +00002307/* Check LCD of matched command. */
ajs274a4a42004-12-07 15:39:31 +00002308static int
paul718e3742002-12-13 20:15:29 +00002309cmd_lcd (char **matched)
2310{
2311 int i;
2312 int j;
2313 int lcd = -1;
2314 char *s1, *s2;
2315 char c1, c2;
2316
2317 if (matched[0] == NULL || matched[1] == NULL)
2318 return 0;
2319
2320 for (i = 1; matched[i] != NULL; i++)
2321 {
2322 s1 = matched[i - 1];
2323 s2 = matched[i];
2324
2325 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2326 if (c1 != c2)
2327 break;
2328
2329 if (lcd < 0)
2330 lcd = j;
2331 else
2332 {
2333 if (lcd > j)
2334 lcd = j;
2335 }
2336 }
2337 return lcd;
2338}
2339
Christian Frankecd40b322013-09-30 12:27:51 +00002340static int
2341cmd_complete_cmp(const void *a, const void *b)
2342{
2343 const char *first = *(char * const *)a;
2344 const char *second = *(char * const *)b;
2345
2346 if (!first)
2347 {
2348 if (!second)
2349 return 0;
2350 return 1;
2351 }
2352 if (!second)
2353 return -1;
2354
2355 return strcmp(first, second);
2356}
2357
2358static void
2359cmd_complete_sort(vector matchvec)
2360{
2361 qsort(matchvec->index, vector_active(matchvec),
2362 sizeof(void*), cmd_complete_cmp);
2363}
2364
paul718e3742002-12-13 20:15:29 +00002365/* Command line completion support. */
ajs274a4a42004-12-07 15:39:31 +00002366static char **
Lou Berger67290032016-01-12 13:41:46 -05002367cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib)
paul718e3742002-12-13 20:15:29 +00002368{
paulb8961472005-03-14 17:35:52 +00002369 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002370 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2371#define INIT_MATCHVEC_SIZE 10
2372 vector matchvec;
paulb8961472005-03-14 17:35:52 +00002373 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002374 char **match_str;
Christian Frankecd40b322013-09-30 12:27:51 +00002375 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002376 char *command;
2377 int lcd;
Christian Frankecd40b322013-09-30 12:27:51 +00002378 vector matches = NULL;
2379 vector match_vector;
paul718e3742002-12-13 20:15:29 +00002380
paul55468c82005-03-14 20:19:01 +00002381 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002382 {
Paul Jakmad2519962006-05-12 23:19:37 +00002383 vector_free (cmd_vector);
paulb8961472005-03-14 17:35:52 +00002384 *status = CMD_ERR_NO_MATCH;
2385 return NULL;
2386 }
2387 else
paul55468c82005-03-14 20:19:01 +00002388 index = vector_active (vline) - 1;
paulb8961472005-03-14 17:35:52 +00002389
Christian Frankecd40b322013-09-30 12:27:51 +00002390 /* First, filter by command string */
2391 for (i = 0; i <= index; i++)
2392 {
2393 command = vector_slot (vline, i);
2394 enum match_type match;
2395 int ret;
paul718e3742002-12-13 20:15:29 +00002396
Christian Frankecd40b322013-09-30 12:27:51 +00002397 if (matches)
2398 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002399
Christian Frankecd40b322013-09-30 12:27:51 +00002400 /* First try completion match, if there is exactly match return 1 */
2401 ret = cmd_vector_filter(cmd_vector,
2402 FILTER_RELAXED,
2403 vline, i,
2404 &match,
2405 &matches);
2406
2407 if (ret != CMD_SUCCESS)
2408 {
2409 vector_free(cmd_vector);
2410 cmd_matches_free(&matches);
2411 *status = ret;
2412 return NULL;
2413 }
2414
2415 /* Break here - the completion mustn't be checked to be non-ambiguous */
2416 if (i == index)
2417 break;
2418
2419 /* If there is exact match then filter ambiguous match else check
2420 ambiguousness. */
2421 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2422 if (ret == 1)
2423 {
2424 vector_free (cmd_vector);
2425 cmd_matches_free(&matches);
2426 *status = CMD_ERR_AMBIGUOUS;
2427 return NULL;
2428 }
2429 /*
paul909a2152005-03-14 17:41:45 +00002430 else if (ret == 2)
2431 {
2432 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002433 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002434 *status = CMD_ERR_NO_MATCH;
2435 return NULL;
2436 }
2437 */
Christian Frankecd40b322013-09-30 12:27:51 +00002438 }
paul909a2152005-03-14 17:41:45 +00002439
paul718e3742002-12-13 20:15:29 +00002440 /* Prepare match vector. */
2441 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2442
Christian Frankecd40b322013-09-30 12:27:51 +00002443 /* Build the possible list of continuations into a list of completions */
2444 for (i = 0; i < vector_active (matches); i++)
2445 if ((match_vector = vector_slot (matches, i)))
paul718e3742002-12-13 20:15:29 +00002446 {
hasso8c328f12004-10-05 21:01:23 +00002447 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002448 unsigned int j;
paul909a2152005-03-14 17:41:45 +00002449
Christian Frankecd40b322013-09-30 12:27:51 +00002450 for (j = 0; j < vector_active (match_vector); j++)
2451 if ((token = vector_slot (match_vector, j)))
Lou Berger67290032016-01-12 13:41:46 -05002452 {
2453 string = cmd_entry_function (vector_slot (vline, index), token);
2454 if (string && cmd_unique_string (matchvec, string))
2455 vector_set (matchvec, (islib != 0 ?
2456 XSTRDUP (MTYPE_TMP, string) :
2457 strdup (string) /* rl freed */));
2458 }
paul718e3742002-12-13 20:15:29 +00002459 }
2460
2461 /* We don't need cmd_vector any more. */
2462 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002463 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002464
2465 /* No matched command */
2466 if (vector_slot (matchvec, 0) == NULL)
2467 {
2468 vector_free (matchvec);
2469
2470 /* In case of 'command \t' pattern. Do you need '?' command at
2471 the end of the line. */
2472 if (vector_slot (vline, index) == '\0')
2473 *status = CMD_ERR_NOTHING_TODO;
2474 else
2475 *status = CMD_ERR_NO_MATCH;
2476 return NULL;
2477 }
2478
2479 /* Only one matched */
2480 if (vector_slot (matchvec, 1) == NULL)
2481 {
2482 match_str = (char **) matchvec->index;
2483 vector_only_wrapper_free (matchvec);
2484 *status = CMD_COMPLETE_FULL_MATCH;
2485 return match_str;
2486 }
2487 /* Make it sure last element is NULL. */
2488 vector_set (matchvec, NULL);
2489
2490 /* Check LCD of matched strings. */
2491 if (vector_slot (vline, index) != NULL)
2492 {
2493 lcd = cmd_lcd ((char **) matchvec->index);
2494
2495 if (lcd)
2496 {
2497 int len = strlen (vector_slot (vline, index));
paul909a2152005-03-14 17:41:45 +00002498
paul718e3742002-12-13 20:15:29 +00002499 if (len < lcd)
2500 {
2501 char *lcdstr;
paul909a2152005-03-14 17:41:45 +00002502
Lou Berger67290032016-01-12 13:41:46 -05002503 lcdstr = (islib != 0 ?
2504 XMALLOC (MTYPE_TMP, lcd + 1) :
2505 malloc(lcd + 1));
paul718e3742002-12-13 20:15:29 +00002506 memcpy (lcdstr, matchvec->index[0], lcd);
2507 lcdstr[lcd] = '\0';
2508
2509 /* match_str = (char **) &lcdstr; */
2510
2511 /* Free matchvec. */
paul55468c82005-03-14 20:19:01 +00002512 for (i = 0; i < vector_active (matchvec); i++)
Lou Berger67290032016-01-12 13:41:46 -05002513 {
2514 if (vector_slot (matchvec, i))
2515 {
2516 if (islib != 0)
2517 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
2518 else
2519 free (vector_slot (matchvec, i));
2520 }
2521 }
paul718e3742002-12-13 20:15:29 +00002522 vector_free (matchvec);
2523
paul909a2152005-03-14 17:41:45 +00002524 /* Make new matchvec. */
paul718e3742002-12-13 20:15:29 +00002525 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2526 vector_set (matchvec, lcdstr);
2527 match_str = (char **) matchvec->index;
2528 vector_only_wrapper_free (matchvec);
2529
2530 *status = CMD_COMPLETE_MATCH;
2531 return match_str;
2532 }
2533 }
2534 }
2535
2536 match_str = (char **) matchvec->index;
Christian Frankecd40b322013-09-30 12:27:51 +00002537 cmd_complete_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002538 vector_only_wrapper_free (matchvec);
2539 *status = CMD_COMPLETE_LIST_MATCH;
2540 return match_str;
2541}
2542
paulb92938a2002-12-13 21:20:42 +00002543char **
Lou Berger67290032016-01-12 13:41:46 -05002544cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)
paulb92938a2002-12-13 21:20:42 +00002545{
2546 char **ret;
2547
2548 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2549 {
2550 enum node_type onode;
2551 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002552 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002553
2554 onode = vty->node;
2555 vty->node = ENABLE_NODE;
2556 /* We can try it on enable node, cos' the vty is authenticated */
2557
2558 shifted_vline = vector_init (vector_count(vline));
2559 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002560 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002561 {
2562 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2563 }
2564
Lou Berger67290032016-01-12 13:41:46 -05002565 ret = cmd_complete_command_real (shifted_vline, vty, status, islib);
paulb92938a2002-12-13 21:20:42 +00002566
2567 vector_free(shifted_vline);
2568 vty->node = onode;
2569 return ret;
2570 }
2571
Lou Berger67290032016-01-12 13:41:46 -05002572 return cmd_complete_command_real (vline, vty, status, islib);
2573}
paulb92938a2002-12-13 21:20:42 +00002574
Lou Berger67290032016-01-12 13:41:46 -05002575char **
2576cmd_complete_command (vector vline, struct vty *vty, int *status)
2577{
2578 return cmd_complete_command_lib (vline, vty, status, 0);
paulb92938a2002-12-13 21:20:42 +00002579}
2580
2581/* return parent node */
2582/* MUST eventually converge on CONFIG_NODE */
hasso13bfca72005-01-23 21:42:25 +00002583enum node_type
ajs274a4a42004-12-07 15:39:31 +00002584node_parent ( enum node_type node )
paulb92938a2002-12-13 21:20:42 +00002585{
2586 enum node_type ret;
2587
paul9ab68122003-01-18 01:16:20 +00002588 assert (node > CONFIG_NODE);
2589
2590 switch (node)
2591 {
2592 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002593 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002594 case BGP_ENCAP_NODE:
2595 case BGP_ENCAPV6_NODE:
paul9ab68122003-01-18 01:16:20 +00002596 case BGP_IPV4_NODE:
2597 case BGP_IPV4M_NODE:
2598 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002599 case BGP_IPV6M_NODE:
paul9ab68122003-01-18 01:16:20 +00002600 ret = BGP_NODE;
2601 break;
2602 case KEYCHAIN_KEY_NODE:
2603 ret = KEYCHAIN_NODE;
2604 break;
2605 default:
2606 ret = CONFIG_NODE;
paulb92938a2002-12-13 21:20:42 +00002607 }
2608
2609 return ret;
2610}
2611
paul718e3742002-12-13 20:15:29 +00002612/* Execute command by argument vline vector. */
ajs274a4a42004-12-07 15:39:31 +00002613static int
Christian Frankecd40b322013-09-30 12:27:51 +00002614cmd_execute_command_real (vector vline,
2615 enum filter_type filter,
2616 struct vty *vty,
paulb8961472005-03-14 17:35:52 +00002617 struct cmd_element **cmd)
paul718e3742002-12-13 20:15:29 +00002618{
hasso8c328f12004-10-05 21:01:23 +00002619 unsigned int i;
2620 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002621 vector cmd_vector;
2622 struct cmd_element *cmd_element;
2623 struct cmd_element *matched_element;
2624 unsigned int matched_count, incomplete_count;
2625 int argc;
paul9035efa2004-10-10 11:56:56 +00002626 const char *argv[CMD_ARGC_MAX];
paul718e3742002-12-13 20:15:29 +00002627 enum match_type match = 0;
paul718e3742002-12-13 20:15:29 +00002628 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002629 int ret;
2630 vector matches;
paul718e3742002-12-13 20:15:29 +00002631
2632 /* Make copy of command elements. */
2633 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2634
paul55468c82005-03-14 20:19:01 +00002635 for (index = 0; index < vector_active (vline); index++)
Christian Frankecd40b322013-09-30 12:27:51 +00002636 {
2637 command = vector_slot (vline, index);
2638 ret = cmd_vector_filter(cmd_vector,
2639 filter,
2640 vline, index,
2641 &match,
2642 &matches);
paul718e3742002-12-13 20:15:29 +00002643
Christian Frankecd40b322013-09-30 12:27:51 +00002644 if (ret != CMD_SUCCESS)
2645 {
2646 cmd_matches_free(&matches);
2647 return ret;
2648 }
paul718e3742002-12-13 20:15:29 +00002649
Christian Frankecd40b322013-09-30 12:27:51 +00002650 if (match == vararg_match)
2651 {
2652 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002653 break;
Christian Frankecd40b322013-09-30 12:27:51 +00002654 }
paul718e3742002-12-13 20:15:29 +00002655
Christian Frankecd40b322013-09-30 12:27:51 +00002656 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2657 cmd_matches_free(&matches);
2658
2659 if (ret == 1)
2660 {
2661 vector_free(cmd_vector);
2662 return CMD_ERR_AMBIGUOUS;
2663 }
2664 else if (ret == 2)
2665 {
2666 vector_free(cmd_vector);
2667 return CMD_ERR_NO_MATCH;
2668 }
2669 }
paul718e3742002-12-13 20:15:29 +00002670
2671 /* Check matched count. */
2672 matched_element = NULL;
2673 matched_count = 0;
2674 incomplete_count = 0;
2675
paul55468c82005-03-14 20:19:01 +00002676 for (i = 0; i < vector_active (cmd_vector); i++)
paulb8961472005-03-14 17:35:52 +00002677 if ((cmd_element = vector_slot (cmd_vector, i)))
paul718e3742002-12-13 20:15:29 +00002678 {
Christian Frankecd40b322013-09-30 12:27:51 +00002679 if (cmd_is_complete(cmd_element, vline))
paul718e3742002-12-13 20:15:29 +00002680 {
2681 matched_element = cmd_element;
paul718e3742002-12-13 20:15:29 +00002682 matched_count++;
2683 }
2684 else
2685 {
2686 incomplete_count++;
2687 }
2688 }
paul909a2152005-03-14 17:41:45 +00002689
paul718e3742002-12-13 20:15:29 +00002690 /* Finish of using cmd_vector. */
2691 vector_free (cmd_vector);
2692
paul909a2152005-03-14 17:41:45 +00002693 /* To execute command, matched_count must be 1. */
2694 if (matched_count == 0)
paul718e3742002-12-13 20:15:29 +00002695 {
2696 if (incomplete_count)
2697 return CMD_ERR_INCOMPLETE;
2698 else
2699 return CMD_ERR_NO_MATCH;
2700 }
2701
paul909a2152005-03-14 17:41:45 +00002702 if (matched_count > 1)
paul718e3742002-12-13 20:15:29 +00002703 return CMD_ERR_AMBIGUOUS;
2704
Christian Frankecd40b322013-09-30 12:27:51 +00002705 ret = cmd_parse(matched_element, vline, &argc, argv);
2706 if (ret != CMD_SUCCESS)
2707 return ret;
paul718e3742002-12-13 20:15:29 +00002708
2709 /* For vtysh execution. */
2710 if (cmd)
2711 *cmd = matched_element;
2712
2713 if (matched_element->daemon)
2714 return CMD_SUCCESS_DAEMON;
2715
2716 /* Execute matched command. */
2717 return (*matched_element->func) (matched_element, vty, argc, argv);
2718}
2719
Christian Frankecd40b322013-09-30 12:27:51 +00002720/**
2721 * Execute a given command, handling things like "do ..." and checking
2722 * whether the given command might apply at a parent node if doesn't
2723 * apply for the current node.
2724 *
2725 * @param vline Command line input, vector of char* where each element is
2726 * one input token.
2727 * @param vty The vty context in which the command should be executed.
2728 * @param cmd Pointer where the struct cmd_element of the matched command
2729 * will be stored, if any. May be set to NULL if this info is
2730 * not needed.
2731 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2732 * @return The status of the command that has been executed or an error code
2733 * as to why no command could be executed.
2734 */
paulb92938a2002-12-13 21:20:42 +00002735int
hasso87d683b2005-01-16 23:31:54 +00002736cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2737 int vtysh) {
paul9ab68122003-01-18 01:16:20 +00002738 int ret, saved_ret, tried = 0;
2739 enum node_type onode, try_node;
2740
2741 onode = try_node = vty->node;
paulb92938a2002-12-13 21:20:42 +00002742
2743 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2744 {
2745 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002746 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002747
2748 vty->node = ENABLE_NODE;
2749 /* We can try it on enable node, cos' the vty is authenticated */
2750
2751 shifted_vline = vector_init (vector_count(vline));
2752 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002753 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002754 {
2755 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2756 }
2757
Christian Frankecd40b322013-09-30 12:27:51 +00002758 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002759
2760 vector_free(shifted_vline);
2761 vty->node = onode;
2762 return ret;
2763 }
2764
2765
Christian Frankecd40b322013-09-30 12:27:51 +00002766 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002767
hasso87d683b2005-01-16 23:31:54 +00002768 if (vtysh)
2769 return saved_ret;
2770
paulb92938a2002-12-13 21:20:42 +00002771 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
paul9ab68122003-01-18 01:16:20 +00002772 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
paulb92938a2002-12-13 21:20:42 +00002773 && vty->node > CONFIG_NODE )
2774 {
paul9ab68122003-01-18 01:16:20 +00002775 try_node = node_parent(try_node);
2776 vty->node = try_node;
Christian Frankecd40b322013-09-30 12:27:51 +00002777 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paul9ab68122003-01-18 01:16:20 +00002778 tried = 1;
2779 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
paulb92938a2002-12-13 21:20:42 +00002780 {
paul9ab68122003-01-18 01:16:20 +00002781 /* succesfull command, leave the node as is */
paulb92938a2002-12-13 21:20:42 +00002782 return ret;
2783 }
paulb92938a2002-12-13 21:20:42 +00002784 }
paul9ab68122003-01-18 01:16:20 +00002785 /* no command succeeded, reset the vty to the original node and
2786 return the error for this node */
2787 if ( tried )
2788 vty->node = onode;
2789 return saved_ret;
pauleda031f2003-01-18 00:39:19 +00002790}
2791
Christian Frankecd40b322013-09-30 12:27:51 +00002792/**
2793 * Execute a given command, matching it strictly against the current node.
2794 * This mode is used when reading config files.
2795 *
2796 * @param vline Command line input, vector of char* where each element is
2797 * one input token.
2798 * @param vty The vty context in which the command should be executed.
2799 * @param cmd Pointer where the struct cmd_element* of the matched command
2800 * will be stored, if any. May be set to NULL if this info is
2801 * not needed.
2802 * @return The status of the command that has been executed or an error code
2803 * as to why no command could be executed.
2804 */
paul718e3742002-12-13 20:15:29 +00002805int
paul909a2152005-03-14 17:41:45 +00002806cmd_execute_command_strict (vector vline, struct vty *vty,
paul718e3742002-12-13 20:15:29 +00002807 struct cmd_element **cmd)
2808{
Christian Frankecd40b322013-09-30 12:27:51 +00002809 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
paul718e3742002-12-13 20:15:29 +00002810}
2811
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002812/**
2813 * Parse one line of config, walking up the parse tree attempting to find a match
2814 *
2815 * @param vty The vty context in which the command should be executed.
2816 * @param cmd Pointer where the struct cmd_element* of the match command
2817 * will be stored, if any. May be set to NULL if this info is
2818 * not needed.
2819 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
2820 * or not.
2821 * @return The status of the command that has been executed or an error code
2822 * as to why no command could be executed.
2823 */
2824int
2825command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
2826{
2827 vector vline;
2828 int saved_node;
2829 int ret;
2830
2831 vline = cmd_make_strvec (vty->buf);
2832
2833 /* In case of comment line */
2834 if (vline == NULL)
2835 return CMD_SUCCESS;
2836
2837 /* Execute configuration command : this is strict match */
2838 ret = cmd_execute_command_strict (vline, vty, cmd);
2839
2840 saved_node = vty->node;
2841
2842 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2843 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2844 ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) {
2845 vty->node = node_parent(vty->node);
2846 ret = cmd_execute_command_strict (vline, vty, NULL);
2847 }
2848
2849 // If climbing the tree did not work then ignore the command and
2850 // stay at the same node
2851 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2852 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2853 ret != CMD_ERR_NOTHING_TODO)
2854 {
2855 vty->node = saved_node;
2856 }
2857
2858 cmd_free_strvec (vline);
2859
2860 return ret;
2861}
2862
paul718e3742002-12-13 20:15:29 +00002863/* Configration make from file. */
2864int
Steve Hillea555002009-07-28 16:36:14 -04002865config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
paul718e3742002-12-13 20:15:29 +00002866{
2867 int ret;
Steve Hillea555002009-07-28 16:36:14 -04002868 *line_num = 0;
paul718e3742002-12-13 20:15:29 +00002869
2870 while (fgets (vty->buf, VTY_BUFSIZ, fp))
2871 {
Steve Hillea555002009-07-28 16:36:14 -04002872 ++(*line_num);
paul718e3742002-12-13 20:15:29 +00002873
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002874 ret = command_config_read_one_line (vty, NULL, 0);
paul718e3742002-12-13 20:15:29 +00002875
hassoddd85ed2004-10-13 08:18:07 +00002876 if (ret != CMD_SUCCESS && ret != CMD_WARNING
2877 && ret != CMD_ERR_NOTHING_TODO)
paul718e3742002-12-13 20:15:29 +00002878 return ret;
2879 }
2880 return CMD_SUCCESS;
2881}
2882
2883/* Configration from terminal */
2884DEFUN (config_terminal,
2885 config_terminal_cmd,
2886 "configure terminal",
2887 "Configuration from vty interface\n"
2888 "Configuration terminal\n")
2889{
2890 if (vty_config_lock (vty))
2891 vty->node = CONFIG_NODE;
2892 else
2893 {
2894 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2895 return CMD_WARNING;
2896 }
2897 return CMD_SUCCESS;
2898}
2899
2900/* Enable command */
2901DEFUN (enable,
2902 config_enable_cmd,
2903 "enable",
2904 "Turn on privileged mode command\n")
2905{
2906 /* If enable password is NULL, change to ENABLE_NODE */
2907 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2908 vty->type == VTY_SHELL_SERV)
2909 vty->node = ENABLE_NODE;
2910 else
2911 vty->node = AUTH_ENABLE_NODE;
2912
2913 return CMD_SUCCESS;
2914}
2915
2916/* Disable command */
2917DEFUN (disable,
2918 config_disable_cmd,
2919 "disable",
2920 "Turn off privileged mode command\n")
2921{
2922 if (vty->node == ENABLE_NODE)
2923 vty->node = VIEW_NODE;
2924 return CMD_SUCCESS;
2925}
2926
2927/* Down vty node level. */
2928DEFUN (config_exit,
2929 config_exit_cmd,
2930 "exit",
2931 "Exit current mode and down to previous mode\n")
2932{
2933 switch (vty->node)
2934 {
2935 case VIEW_NODE:
2936 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002937 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002938 if (vty_shell (vty))
2939 exit (0);
2940 else
2941 vty->status = VTY_CLOSE;
2942 break;
2943 case CONFIG_NODE:
2944 vty->node = ENABLE_NODE;
2945 vty_config_unlock (vty);
2946 break;
2947 case INTERFACE_NODE:
2948 case ZEBRA_NODE:
2949 case BGP_NODE:
2950 case RIP_NODE:
2951 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002952 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002953 case OSPF_NODE:
2954 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002955 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002956 case KEYCHAIN_NODE:
2957 case MASC_NODE:
2958 case RMAP_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002959 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002960 case VTY_NODE:
2961 vty->node = CONFIG_NODE;
2962 break;
paul718e3742002-12-13 20:15:29 +00002963 case BGP_IPV4_NODE:
2964 case BGP_IPV4M_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002965 case BGP_VPNV4_NODE:
2966 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002967 case BGP_ENCAP_NODE:
2968 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00002969 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002970 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00002971 vty->node = BGP_NODE;
2972 break;
2973 case KEYCHAIN_KEY_NODE:
2974 vty->node = KEYCHAIN_NODE;
2975 break;
2976 default:
2977 break;
2978 }
2979 return CMD_SUCCESS;
2980}
2981
2982/* quit is alias of exit. */
2983ALIAS (config_exit,
2984 config_quit_cmd,
2985 "quit",
2986 "Exit current mode and down to previous mode\n")
2987
2988/* End of configuration. */
2989DEFUN (config_end,
2990 config_end_cmd,
2991 "end",
2992 "End current mode and change to enable mode.")
2993{
2994 switch (vty->node)
2995 {
2996 case VIEW_NODE:
2997 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002998 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002999 /* Nothing to do. */
3000 break;
3001 case CONFIG_NODE:
3002 case INTERFACE_NODE:
3003 case ZEBRA_NODE:
3004 case RIP_NODE:
3005 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01003006 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00003007 case BGP_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05003008 case BGP_ENCAP_NODE:
3009 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00003010 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05003011 case BGP_VPNV6_NODE:
paul718e3742002-12-13 20:15:29 +00003012 case BGP_IPV4_NODE:
3013 case BGP_IPV4M_NODE:
3014 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00003015 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00003016 case RMAP_NODE:
3017 case OSPF_NODE:
3018 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00003019 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00003020 case KEYCHAIN_NODE:
3021 case KEYCHAIN_KEY_NODE:
3022 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02003023 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00003024 case VTY_NODE:
3025 vty_config_unlock (vty);
3026 vty->node = ENABLE_NODE;
3027 break;
3028 default:
3029 break;
3030 }
3031 return CMD_SUCCESS;
3032}
3033
3034/* Show version. */
3035DEFUN (show_version,
3036 show_version_cmd,
3037 "show version",
3038 SHOW_STR
3039 "Displays zebra version\n")
3040{
hasso12f6ea22005-03-07 08:35:39 +00003041 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
3042 VTY_NEWLINE);
David Lamparter0be793e2012-11-27 01:34:56 +00003043 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
David Lamparter7abd8752014-11-22 10:43:29 -08003044 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
3045 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00003046
3047 return CMD_SUCCESS;
3048}
3049
3050/* Help display function for all node. */
3051DEFUN (config_help,
3052 config_help_cmd,
3053 "help",
3054 "Description of the interactive help system\n")
3055{
3056 vty_out (vty,
hasso6590f2c2004-10-19 20:40:08 +00003057 "Quagga VTY provides advanced help feature. When you need help,%s\
paul718e3742002-12-13 20:15:29 +00003058anytime at the command line please press '?'.%s\
3059%s\
3060If nothing matches, the help list will be empty and you must backup%s\
3061 until entering a '?' shows the available options.%s\
3062Two styles of help are provided:%s\
30631. Full help is available when you are ready to enter a%s\
3064command argument (e.g. 'show ?') and describes each possible%s\
3065argument.%s\
30662. Partial help is provided when an abbreviated argument is entered%s\
3067 and you want to know what arguments match the input%s\
3068 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3069 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3070 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
3071 return CMD_SUCCESS;
3072}
3073
3074/* Help display function for all node. */
3075DEFUN (config_list,
3076 config_list_cmd,
3077 "list",
3078 "Print command list\n")
3079{
hasso8c328f12004-10-05 21:01:23 +00003080 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003081 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
3082 struct cmd_element *cmd;
3083
paul55468c82005-03-14 20:19:01 +00003084 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
paul4275b1d2005-03-09 13:42:23 +00003085 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
3086 && !(cmd->attr == CMD_ATTR_DEPRECATED
3087 || cmd->attr == CMD_ATTR_HIDDEN))
paul718e3742002-12-13 20:15:29 +00003088 vty_out (vty, " %s%s", cmd->string,
3089 VTY_NEWLINE);
3090 return CMD_SUCCESS;
3091}
3092
3093/* Write current configuration into file. */
3094DEFUN (config_write_file,
3095 config_write_file_cmd,
3096 "write file",
3097 "Write running configuration to memory, network, or terminal\n"
3098 "Write to configuration file\n")
3099{
hasso8c328f12004-10-05 21:01:23 +00003100 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003101 int fd;
3102 struct cmd_node *node;
3103 char *config_file;
3104 char *config_file_tmp = NULL;
3105 char *config_file_sav = NULL;
paul05865c92005-10-26 05:49:54 +00003106 int ret = CMD_WARNING;
paul718e3742002-12-13 20:15:29 +00003107 struct vty *file_vty;
3108
3109 /* Check and see if we are operating under vtysh configuration */
3110 if (host.config == NULL)
3111 {
3112 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3113 VTY_NEWLINE);
3114 return CMD_WARNING;
3115 }
3116
3117 /* Get filename. */
3118 config_file = host.config;
3119
paul05865c92005-10-26 05:49:54 +00003120 config_file_sav =
3121 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
paul718e3742002-12-13 20:15:29 +00003122 strcpy (config_file_sav, config_file);
3123 strcat (config_file_sav, CONF_BACKUP_EXT);
3124
3125
paul05865c92005-10-26 05:49:54 +00003126 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
paul718e3742002-12-13 20:15:29 +00003127 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3128
3129 /* Open file to configuration write. */
3130 fd = mkstemp (config_file_tmp);
3131 if (fd < 0)
3132 {
3133 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3134 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003135 goto finished;
paul718e3742002-12-13 20:15:29 +00003136 }
3137
3138 /* Make vty for configuration file. */
3139 file_vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02003140 file_vty->wfd = fd;
paul718e3742002-12-13 20:15:29 +00003141 file_vty->type = VTY_FILE;
3142
3143 /* Config file header print. */
3144 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3145 vty_time_print (file_vty, 1);
3146 vty_out (file_vty, "!\n");
3147
paul55468c82005-03-14 20:19:01 +00003148 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003149 if ((node = vector_slot (cmdvec, i)) && node->func)
3150 {
3151 if ((*node->func) (file_vty))
3152 vty_out (file_vty, "!\n");
3153 }
3154 vty_close (file_vty);
3155
3156 if (unlink (config_file_sav) != 0)
3157 if (errno != ENOENT)
3158 {
3159 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3160 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003161 goto finished;
paul718e3742002-12-13 20:15:29 +00003162 }
3163 if (link (config_file, config_file_sav) != 0)
3164 {
3165 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3166 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003167 goto finished;
paul718e3742002-12-13 20:15:29 +00003168 }
3169 sync ();
3170 if (unlink (config_file) != 0)
3171 {
3172 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3173 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003174 goto finished;
paul718e3742002-12-13 20:15:29 +00003175 }
3176 if (link (config_file_tmp, config_file) != 0)
3177 {
3178 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3179 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003180 goto finished;
paul718e3742002-12-13 20:15:29 +00003181 }
paul718e3742002-12-13 20:15:29 +00003182 sync ();
3183
gdtaa593d52003-12-22 20:15:53 +00003184 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3185 {
3186 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
ajs6099b3b2004-11-20 02:06:59 +00003187 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003188 goto finished;
gdtaa593d52003-12-22 20:15:53 +00003189 }
3190
paul718e3742002-12-13 20:15:29 +00003191 vty_out (vty, "Configuration saved to %s%s", config_file,
3192 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003193 ret = CMD_SUCCESS;
3194
3195finished:
3196 unlink (config_file_tmp);
3197 XFREE (MTYPE_TMP, config_file_tmp);
3198 XFREE (MTYPE_TMP, config_file_sav);
3199 return ret;
paul718e3742002-12-13 20:15:29 +00003200}
3201
3202ALIAS (config_write_file,
3203 config_write_cmd,
3204 "write",
3205 "Write running configuration to memory, network, or terminal\n")
3206
3207ALIAS (config_write_file,
3208 config_write_memory_cmd,
3209 "write memory",
3210 "Write running configuration to memory, network, or terminal\n"
3211 "Write configuration to the file (same as write file)\n")
3212
3213ALIAS (config_write_file,
3214 copy_runningconfig_startupconfig_cmd,
3215 "copy running-config startup-config",
3216 "Copy configuration\n"
3217 "Copy running config to... \n"
3218 "Copy running config to startup config (same as write file)\n")
3219
3220/* Write current configuration into the terminal. */
3221DEFUN (config_write_terminal,
3222 config_write_terminal_cmd,
3223 "write terminal",
3224 "Write running configuration to memory, network, or terminal\n"
3225 "Write to terminal\n")
3226{
hasso8c328f12004-10-05 21:01:23 +00003227 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003228 struct cmd_node *node;
3229
3230 if (vty->type == VTY_SHELL_SERV)
3231 {
paul55468c82005-03-14 20:19:01 +00003232 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003233 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3234 {
3235 if ((*node->func) (vty))
3236 vty_out (vty, "!%s", VTY_NEWLINE);
3237 }
3238 }
3239 else
3240 {
3241 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3242 VTY_NEWLINE);
3243 vty_out (vty, "!%s", VTY_NEWLINE);
3244
paul55468c82005-03-14 20:19:01 +00003245 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003246 if ((node = vector_slot (cmdvec, i)) && node->func)
3247 {
3248 if ((*node->func) (vty))
3249 vty_out (vty, "!%s", VTY_NEWLINE);
3250 }
3251 vty_out (vty, "end%s",VTY_NEWLINE);
3252 }
3253 return CMD_SUCCESS;
3254}
3255
3256/* Write current configuration into the terminal. */
3257ALIAS (config_write_terminal,
3258 show_running_config_cmd,
3259 "show running-config",
3260 SHOW_STR
3261 "running configuration\n")
3262
3263/* Write startup configuration into the terminal. */
3264DEFUN (show_startup_config,
3265 show_startup_config_cmd,
3266 "show startup-config",
3267 SHOW_STR
3268 "Contentes of startup configuration\n")
3269{
3270 char buf[BUFSIZ];
3271 FILE *confp;
3272
3273 confp = fopen (host.config, "r");
3274 if (confp == NULL)
3275 {
3276 vty_out (vty, "Can't open configuration file [%s]%s",
3277 host.config, VTY_NEWLINE);
3278 return CMD_WARNING;
3279 }
3280
3281 while (fgets (buf, BUFSIZ, confp))
3282 {
3283 char *cp = buf;
3284
3285 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3286 cp++;
3287 *cp = '\0';
3288
3289 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3290 }
3291
3292 fclose (confp);
3293
3294 return CMD_SUCCESS;
3295}
3296
3297/* Hostname configuration */
3298DEFUN (config_hostname,
3299 hostname_cmd,
3300 "hostname WORD",
3301 "Set system's network name\n"
3302 "This system's network name\n")
3303{
3304 if (!isalpha((int) *argv[0]))
3305 {
3306 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3307 return CMD_WARNING;
3308 }
3309
3310 if (host.name)
paul05865c92005-10-26 05:49:54 +00003311 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003312
paul05865c92005-10-26 05:49:54 +00003313 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003314 return CMD_SUCCESS;
3315}
3316
3317DEFUN (config_no_hostname,
3318 no_hostname_cmd,
3319 "no hostname [HOSTNAME]",
3320 NO_STR
3321 "Reset system's network name\n"
3322 "Host name of this router\n")
3323{
3324 if (host.name)
paul05865c92005-10-26 05:49:54 +00003325 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003326 host.name = NULL;
3327 return CMD_SUCCESS;
3328}
3329
3330/* VTY interface password set. */
3331DEFUN (config_password, password_cmd,
3332 "password (8|) WORD",
3333 "Assign the terminal connection password\n"
3334 "Specifies a HIDDEN password will follow\n"
3335 "dummy string \n"
3336 "The HIDDEN line password string\n")
3337{
3338 /* Argument check. */
3339 if (argc == 0)
3340 {
3341 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3342 return CMD_WARNING;
3343 }
3344
3345 if (argc == 2)
3346 {
3347 if (*argv[0] == '8')
3348 {
3349 if (host.password)
paul05865c92005-10-26 05:49:54 +00003350 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003351 host.password = NULL;
3352 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003353 XFREE (MTYPE_HOST, host.password_encrypt);
3354 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003355 return CMD_SUCCESS;
3356 }
3357 else
3358 {
3359 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3360 return CMD_WARNING;
3361 }
3362 }
3363
3364 if (!isalnum ((int) *argv[0]))
3365 {
3366 vty_out (vty,
3367 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3368 return CMD_WARNING;
3369 }
3370
3371 if (host.password)
paul05865c92005-10-26 05:49:54 +00003372 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003373 host.password = NULL;
3374
3375 if (host.encrypt)
3376 {
3377 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003378 XFREE (MTYPE_HOST, host.password_encrypt);
3379 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003380 }
3381 else
paul05865c92005-10-26 05:49:54 +00003382 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003383
3384 return CMD_SUCCESS;
3385}
3386
3387ALIAS (config_password, password_text_cmd,
3388 "password LINE",
3389 "Assign the terminal connection password\n"
3390 "The UNENCRYPTED (cleartext) line password\n")
3391
3392/* VTY enable password set. */
3393DEFUN (config_enable_password, enable_password_cmd,
3394 "enable password (8|) WORD",
3395 "Modify enable password parameters\n"
3396 "Assign the privileged level password\n"
3397 "Specifies a HIDDEN password will follow\n"
3398 "dummy string \n"
3399 "The HIDDEN 'enable' password string\n")
3400{
3401 /* Argument check. */
3402 if (argc == 0)
3403 {
3404 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3405 return CMD_WARNING;
3406 }
3407
3408 /* Crypt type is specified. */
3409 if (argc == 2)
3410 {
3411 if (*argv[0] == '8')
3412 {
3413 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003414 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003415 host.enable = NULL;
3416
3417 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003418 XFREE (MTYPE_HOST, host.enable_encrypt);
3419 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003420
3421 return CMD_SUCCESS;
3422 }
3423 else
3424 {
3425 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3426 return CMD_WARNING;
3427 }
3428 }
3429
3430 if (!isalnum ((int) *argv[0]))
3431 {
3432 vty_out (vty,
3433 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3434 return CMD_WARNING;
3435 }
3436
3437 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003438 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003439 host.enable = NULL;
3440
3441 /* Plain password input. */
3442 if (host.encrypt)
3443 {
3444 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003445 XFREE (MTYPE_HOST, host.enable_encrypt);
3446 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003447 }
3448 else
paul05865c92005-10-26 05:49:54 +00003449 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003450
3451 return CMD_SUCCESS;
3452}
3453
3454ALIAS (config_enable_password,
3455 enable_password_text_cmd,
3456 "enable password LINE",
3457 "Modify enable password parameters\n"
3458 "Assign the privileged level password\n"
3459 "The UNENCRYPTED (cleartext) 'enable' password\n")
3460
3461/* VTY enable password delete. */
3462DEFUN (no_config_enable_password, no_enable_password_cmd,
3463 "no enable password",
3464 NO_STR
3465 "Modify enable password parameters\n"
3466 "Assign the privileged level password\n")
3467{
3468 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003469 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003470 host.enable = NULL;
3471
3472 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003473 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003474 host.enable_encrypt = NULL;
3475
3476 return CMD_SUCCESS;
3477}
3478
3479DEFUN (service_password_encrypt,
3480 service_password_encrypt_cmd,
3481 "service password-encryption",
3482 "Set up miscellaneous service\n"
3483 "Enable encrypted passwords\n")
3484{
3485 if (host.encrypt)
3486 return CMD_SUCCESS;
3487
3488 host.encrypt = 1;
3489
3490 if (host.password)
3491 {
3492 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003493 XFREE (MTYPE_HOST, host.password_encrypt);
3494 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
paul718e3742002-12-13 20:15:29 +00003495 }
3496 if (host.enable)
3497 {
3498 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003499 XFREE (MTYPE_HOST, host.enable_encrypt);
3500 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
paul718e3742002-12-13 20:15:29 +00003501 }
3502
3503 return CMD_SUCCESS;
3504}
3505
3506DEFUN (no_service_password_encrypt,
3507 no_service_password_encrypt_cmd,
3508 "no service password-encryption",
3509 NO_STR
3510 "Set up miscellaneous service\n"
3511 "Enable encrypted passwords\n")
3512{
3513 if (! host.encrypt)
3514 return CMD_SUCCESS;
3515
3516 host.encrypt = 0;
3517
3518 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003519 XFREE (MTYPE_HOST, host.password_encrypt);
paul718e3742002-12-13 20:15:29 +00003520 host.password_encrypt = NULL;
3521
3522 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003523 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003524 host.enable_encrypt = NULL;
3525
3526 return CMD_SUCCESS;
3527}
3528
3529DEFUN (config_terminal_length, config_terminal_length_cmd,
3530 "terminal length <0-512>",
3531 "Set terminal line parameters\n"
3532 "Set number of lines on a screen\n"
3533 "Number of lines on screen (0 for no pausing)\n")
3534{
3535 int lines;
3536 char *endptr = NULL;
3537
3538 lines = strtol (argv[0], &endptr, 10);
3539 if (lines < 0 || lines > 512 || *endptr != '\0')
3540 {
3541 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3542 return CMD_WARNING;
3543 }
3544 vty->lines = lines;
3545
3546 return CMD_SUCCESS;
3547}
3548
3549DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3550 "terminal no length",
3551 "Set terminal line parameters\n"
3552 NO_STR
3553 "Set number of lines on a screen\n")
3554{
3555 vty->lines = -1;
3556 return CMD_SUCCESS;
3557}
3558
3559DEFUN (service_terminal_length, service_terminal_length_cmd,
3560 "service terminal-length <0-512>",
3561 "Set up miscellaneous service\n"
3562 "System wide terminal length configuration\n"
3563 "Number of lines of VTY (0 means no line control)\n")
3564{
3565 int lines;
3566 char *endptr = NULL;
3567
3568 lines = strtol (argv[0], &endptr, 10);
3569 if (lines < 0 || lines > 512 || *endptr != '\0')
3570 {
3571 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3572 return CMD_WARNING;
3573 }
3574 host.lines = lines;
3575
3576 return CMD_SUCCESS;
3577}
3578
3579DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3580 "no service terminal-length [<0-512>]",
3581 NO_STR
3582 "Set up miscellaneous service\n"
3583 "System wide terminal length configuration\n"
3584 "Number of lines of VTY (0 means no line control)\n")
3585{
3586 host.lines = -1;
3587 return CMD_SUCCESS;
3588}
3589
ajs2885f722004-12-17 23:16:33 +00003590DEFUN_HIDDEN (do_echo,
3591 echo_cmd,
3592 "echo .MESSAGE",
3593 "Echo a message back to the vty\n"
3594 "The message to echo\n")
3595{
3596 char *message;
3597
ajsf6834d42005-01-28 20:28:35 +00003598 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3599 VTY_NEWLINE);
3600 if (message)
3601 XFREE(MTYPE_TMP, message);
ajs2885f722004-12-17 23:16:33 +00003602 return CMD_SUCCESS;
3603}
3604
ajs274a4a42004-12-07 15:39:31 +00003605DEFUN (config_logmsg,
3606 config_logmsg_cmd,
3607 "logmsg "LOG_LEVELS" .MESSAGE",
3608 "Send a message to enabled logging destinations\n"
3609 LOG_LEVEL_DESC
3610 "The message to send\n")
3611{
3612 int level;
3613 char *message;
3614
3615 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3616 return CMD_ERR_NO_MATCH;
3617
Christian Hammersfc951862011-03-23 13:07:55 +03003618 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
ajsf6834d42005-01-28 20:28:35 +00003619 if (message)
3620 XFREE(MTYPE_TMP, message);
ajs274a4a42004-12-07 15:39:31 +00003621 return CMD_SUCCESS;
3622}
3623
3624DEFUN (show_logging,
3625 show_logging_cmd,
3626 "show logging",
3627 SHOW_STR
3628 "Show current logging configuration\n")
3629{
3630 struct zlog *zl = zlog_default;
3631
3632 vty_out (vty, "Syslog logging: ");
3633 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3634 vty_out (vty, "disabled");
3635 else
3636 vty_out (vty, "level %s, facility %s, ident %s",
3637 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3638 facility_name(zl->facility), zl->ident);
3639 vty_out (vty, "%s", VTY_NEWLINE);
3640
3641 vty_out (vty, "Stdout logging: ");
3642 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3643 vty_out (vty, "disabled");
3644 else
3645 vty_out (vty, "level %s",
3646 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3647 vty_out (vty, "%s", VTY_NEWLINE);
3648
3649 vty_out (vty, "Monitor logging: ");
3650 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3651 vty_out (vty, "disabled");
3652 else
3653 vty_out (vty, "level %s",
3654 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3655 vty_out (vty, "%s", VTY_NEWLINE);
3656
3657 vty_out (vty, "File logging: ");
3658 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3659 !zl->fp)
3660 vty_out (vty, "disabled");
3661 else
3662 vty_out (vty, "level %s, filename %s",
3663 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3664 zl->filename);
3665 vty_out (vty, "%s", VTY_NEWLINE);
3666
3667 vty_out (vty, "Protocol name: %s%s",
3668 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3669 vty_out (vty, "Record priority: %s%s",
3670 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003671 vty_out (vty, "Timestamp precision: %d%s",
3672 zl->timestamp_precision, VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +00003673
3674 return CMD_SUCCESS;
3675}
3676
paul718e3742002-12-13 20:15:29 +00003677DEFUN (config_log_stdout,
3678 config_log_stdout_cmd,
3679 "log stdout",
3680 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003681 "Set stdout logging level\n")
paul718e3742002-12-13 20:15:29 +00003682{
ajs274a4a42004-12-07 15:39:31 +00003683 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3684 return CMD_SUCCESS;
3685}
3686
3687DEFUN (config_log_stdout_level,
3688 config_log_stdout_level_cmd,
3689 "log stdout "LOG_LEVELS,
3690 "Logging control\n"
3691 "Set stdout logging level\n"
3692 LOG_LEVEL_DESC)
3693{
3694 int level;
3695
3696 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3697 return CMD_ERR_NO_MATCH;
3698 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
paul718e3742002-12-13 20:15:29 +00003699 return CMD_SUCCESS;
3700}
3701
3702DEFUN (no_config_log_stdout,
3703 no_config_log_stdout_cmd,
ajs274a4a42004-12-07 15:39:31 +00003704 "no log stdout [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003705 NO_STR
3706 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003707 "Cancel logging to stdout\n"
3708 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003709{
ajs274a4a42004-12-07 15:39:31 +00003710 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003711 return CMD_SUCCESS;
3712}
3713
ajs274a4a42004-12-07 15:39:31 +00003714DEFUN (config_log_monitor,
3715 config_log_monitor_cmd,
3716 "log monitor",
paul718e3742002-12-13 20:15:29 +00003717 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003718 "Set terminal line (monitor) logging level\n")
3719{
3720 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3721 return CMD_SUCCESS;
3722}
3723
3724DEFUN (config_log_monitor_level,
3725 config_log_monitor_level_cmd,
3726 "log monitor "LOG_LEVELS,
3727 "Logging control\n"
3728 "Set terminal line (monitor) logging level\n"
3729 LOG_LEVEL_DESC)
3730{
3731 int level;
3732
3733 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3734 return CMD_ERR_NO_MATCH;
3735 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3736 return CMD_SUCCESS;
3737}
3738
3739DEFUN (no_config_log_monitor,
3740 no_config_log_monitor_cmd,
3741 "no log monitor [LEVEL]",
3742 NO_STR
3743 "Logging control\n"
3744 "Disable terminal line (monitor) logging\n"
3745 "Logging level\n")
3746{
3747 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3748 return CMD_SUCCESS;
3749}
3750
3751static int
3752set_log_file(struct vty *vty, const char *fname, int loglevel)
paul718e3742002-12-13 20:15:29 +00003753{
3754 int ret;
paul9035efa2004-10-10 11:56:56 +00003755 char *p = NULL;
3756 const char *fullpath;
3757
paul718e3742002-12-13 20:15:29 +00003758 /* Path detection. */
ajs274a4a42004-12-07 15:39:31 +00003759 if (! IS_DIRECTORY_SEP (*fname))
paul718e3742002-12-13 20:15:29 +00003760 {
paul9035efa2004-10-10 11:56:56 +00003761 char cwd[MAXPATHLEN+1];
3762 cwd[MAXPATHLEN] = '\0';
3763
3764 if (getcwd (cwd, MAXPATHLEN) == NULL)
3765 {
3766 zlog_err ("config_log_file: Unable to alloc mem!");
3767 return CMD_WARNING;
3768 }
3769
ajs274a4a42004-12-07 15:39:31 +00003770 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
paul9035efa2004-10-10 11:56:56 +00003771 == NULL)
3772 {
3773 zlog_err ("config_log_file: Unable to alloc mem!");
3774 return CMD_WARNING;
3775 }
ajs274a4a42004-12-07 15:39:31 +00003776 sprintf (p, "%s/%s", cwd, fname);
paul9035efa2004-10-10 11:56:56 +00003777 fullpath = p;
paul718e3742002-12-13 20:15:29 +00003778 }
3779 else
ajs274a4a42004-12-07 15:39:31 +00003780 fullpath = fname;
paul718e3742002-12-13 20:15:29 +00003781
ajs274a4a42004-12-07 15:39:31 +00003782 ret = zlog_set_file (NULL, fullpath, loglevel);
paul718e3742002-12-13 20:15:29 +00003783
paul9035efa2004-10-10 11:56:56 +00003784 if (p)
3785 XFREE (MTYPE_TMP, p);
3786
paul718e3742002-12-13 20:15:29 +00003787 if (!ret)
3788 {
ajs274a4a42004-12-07 15:39:31 +00003789 vty_out (vty, "can't open logfile %s\n", fname);
paul718e3742002-12-13 20:15:29 +00003790 return CMD_WARNING;
3791 }
3792
3793 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003794 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003795
paul05865c92005-10-26 05:49:54 +00003796 host.logfile = XSTRDUP (MTYPE_HOST, fname);
paul718e3742002-12-13 20:15:29 +00003797
3798 return CMD_SUCCESS;
3799}
3800
ajs274a4a42004-12-07 15:39:31 +00003801DEFUN (config_log_file,
3802 config_log_file_cmd,
3803 "log file FILENAME",
3804 "Logging control\n"
3805 "Logging to file\n"
3806 "Logging filename\n")
3807{
3808 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3809}
3810
3811DEFUN (config_log_file_level,
3812 config_log_file_level_cmd,
3813 "log file FILENAME "LOG_LEVELS,
3814 "Logging control\n"
3815 "Logging to file\n"
3816 "Logging filename\n"
3817 LOG_LEVEL_DESC)
3818{
3819 int level;
3820
3821 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3822 return CMD_ERR_NO_MATCH;
3823 return set_log_file(vty, argv[0], level);
3824}
3825
paul718e3742002-12-13 20:15:29 +00003826DEFUN (no_config_log_file,
3827 no_config_log_file_cmd,
3828 "no log file [FILENAME]",
3829 NO_STR
3830 "Logging control\n"
3831 "Cancel logging to file\n"
3832 "Logging file name\n")
3833{
3834 zlog_reset_file (NULL);
3835
3836 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003837 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003838
3839 host.logfile = NULL;
3840
3841 return CMD_SUCCESS;
3842}
3843
ajs274a4a42004-12-07 15:39:31 +00003844ALIAS (no_config_log_file,
3845 no_config_log_file_level_cmd,
3846 "no log file FILENAME LEVEL",
3847 NO_STR
3848 "Logging control\n"
3849 "Cancel logging to file\n"
3850 "Logging file name\n"
3851 "Logging level\n")
3852
paul718e3742002-12-13 20:15:29 +00003853DEFUN (config_log_syslog,
3854 config_log_syslog_cmd,
3855 "log syslog",
3856 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003857 "Set syslog logging level\n")
paul718e3742002-12-13 20:15:29 +00003858{
ajs274a4a42004-12-07 15:39:31 +00003859 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003860 return CMD_SUCCESS;
3861}
3862
ajs274a4a42004-12-07 15:39:31 +00003863DEFUN (config_log_syslog_level,
3864 config_log_syslog_level_cmd,
3865 "log syslog "LOG_LEVELS,
paul12ab19f2003-07-26 06:14:55 +00003866 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003867 "Set syslog logging level\n"
3868 LOG_LEVEL_DESC)
paul12ab19f2003-07-26 06:14:55 +00003869{
ajs274a4a42004-12-07 15:39:31 +00003870 int level;
paul12ab19f2003-07-26 06:14:55 +00003871
ajs274a4a42004-12-07 15:39:31 +00003872 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3873 return CMD_ERR_NO_MATCH;
3874 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3875 return CMD_SUCCESS;
3876}
paul12ab19f2003-07-26 06:14:55 +00003877
ajs274a4a42004-12-07 15:39:31 +00003878DEFUN_DEPRECATED (config_log_syslog_facility,
3879 config_log_syslog_facility_cmd,
3880 "log syslog facility "LOG_FACILITIES,
3881 "Logging control\n"
3882 "Logging goes to syslog\n"
3883 "(Deprecated) Facility parameter for syslog messages\n"
3884 LOG_FACILITY_DESC)
3885{
3886 int facility;
paul12ab19f2003-07-26 06:14:55 +00003887
ajs274a4a42004-12-07 15:39:31 +00003888 if ((facility = facility_match(argv[0])) < 0)
3889 return CMD_ERR_NO_MATCH;
3890
3891 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003892 zlog_default->facility = facility;
paul718e3742002-12-13 20:15:29 +00003893 return CMD_SUCCESS;
3894}
3895
3896DEFUN (no_config_log_syslog,
3897 no_config_log_syslog_cmd,
ajs274a4a42004-12-07 15:39:31 +00003898 "no log syslog [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003899 NO_STR
3900 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003901 "Cancel logging to syslog\n"
3902 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003903{
ajs274a4a42004-12-07 15:39:31 +00003904 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003905 return CMD_SUCCESS;
3906}
3907
paul12ab19f2003-07-26 06:14:55 +00003908ALIAS (no_config_log_syslog,
3909 no_config_log_syslog_facility_cmd,
ajs274a4a42004-12-07 15:39:31 +00003910 "no log syslog facility "LOG_FACILITIES,
paul12ab19f2003-07-26 06:14:55 +00003911 NO_STR
3912 "Logging control\n"
3913 "Logging goes to syslog\n"
3914 "Facility parameter for syslog messages\n"
ajs274a4a42004-12-07 15:39:31 +00003915 LOG_FACILITY_DESC)
paul12ab19f2003-07-26 06:14:55 +00003916
ajs274a4a42004-12-07 15:39:31 +00003917DEFUN (config_log_facility,
3918 config_log_facility_cmd,
3919 "log facility "LOG_FACILITIES,
paul718e3742002-12-13 20:15:29 +00003920 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003921 "Facility parameter for syslog messages\n"
3922 LOG_FACILITY_DESC)
paul718e3742002-12-13 20:15:29 +00003923{
ajs274a4a42004-12-07 15:39:31 +00003924 int facility;
3925
3926 if ((facility = facility_match(argv[0])) < 0)
3927 return CMD_ERR_NO_MATCH;
3928 zlog_default->facility = facility;
3929 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +00003930}
3931
ajs274a4a42004-12-07 15:39:31 +00003932DEFUN (no_config_log_facility,
3933 no_config_log_facility_cmd,
3934 "no log facility [FACILITY]",
paul718e3742002-12-13 20:15:29 +00003935 NO_STR
3936 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003937 "Reset syslog facility to default (daemon)\n"
3938 "Syslog facility\n")
paul718e3742002-12-13 20:15:29 +00003939{
ajs274a4a42004-12-07 15:39:31 +00003940 zlog_default->facility = LOG_DAEMON;
3941 return CMD_SUCCESS;
3942}
3943
3944DEFUN_DEPRECATED (config_log_trap,
3945 config_log_trap_cmd,
3946 "log trap "LOG_LEVELS,
3947 "Logging control\n"
3948 "(Deprecated) Set logging level and default for all destinations\n"
3949 LOG_LEVEL_DESC)
3950{
3951 int new_level ;
3952 int i;
3953
3954 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3955 return CMD_ERR_NO_MATCH;
3956
3957 zlog_default->default_lvl = new_level;
3958 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3959 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3960 zlog_default->maxlvl[i] = new_level;
3961 return CMD_SUCCESS;
3962}
3963
3964DEFUN_DEPRECATED (no_config_log_trap,
3965 no_config_log_trap_cmd,
3966 "no log trap [LEVEL]",
3967 NO_STR
3968 "Logging control\n"
3969 "Permit all logging information\n"
3970 "Logging level\n")
3971{
3972 zlog_default->default_lvl = LOG_DEBUG;
paul718e3742002-12-13 20:15:29 +00003973 return CMD_SUCCESS;
3974}
3975
3976DEFUN (config_log_record_priority,
3977 config_log_record_priority_cmd,
3978 "log record-priority",
3979 "Logging control\n"
3980 "Log the priority of the message within the message\n")
3981{
3982 zlog_default->record_priority = 1 ;
3983 return CMD_SUCCESS;
3984}
3985
3986DEFUN (no_config_log_record_priority,
3987 no_config_log_record_priority_cmd,
3988 "no log record-priority",
3989 NO_STR
3990 "Logging control\n"
3991 "Do not log the priority of the message within the message\n")
3992{
3993 zlog_default->record_priority = 0 ;
3994 return CMD_SUCCESS;
3995}
3996
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003997DEFUN (config_log_timestamp_precision,
3998 config_log_timestamp_precision_cmd,
3999 "log timestamp precision <0-6>",
4000 "Logging control\n"
4001 "Timestamp configuration\n"
4002 "Set the timestamp precision\n"
4003 "Number of subsecond digits\n")
4004{
4005 if (argc != 1)
4006 {
4007 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
4008 return CMD_WARNING;
4009 }
4010
4011 VTY_GET_INTEGER_RANGE("Timestamp Precision",
4012 zlog_default->timestamp_precision, argv[0], 0, 6);
4013 return CMD_SUCCESS;
4014}
4015
4016DEFUN (no_config_log_timestamp_precision,
4017 no_config_log_timestamp_precision_cmd,
4018 "no log timestamp precision",
4019 NO_STR
4020 "Logging control\n"
4021 "Timestamp configuration\n"
4022 "Reset the timestamp precision to the default value of 0\n")
4023{
4024 zlog_default->timestamp_precision = 0 ;
4025 return CMD_SUCCESS;
4026}
4027
paul3b0c5d92005-03-08 10:43:43 +00004028DEFUN (banner_motd_file,
4029 banner_motd_file_cmd,
4030 "banner motd file [FILE]",
4031 "Set banner\n"
4032 "Banner for motd\n"
4033 "Banner from a file\n"
4034 "Filename\n")
4035{
paulb45da6f2005-03-08 15:16:57 +00004036 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004037 XFREE (MTYPE_HOST, host.motdfile);
4038 host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
paulb45da6f2005-03-08 15:16:57 +00004039
paul3b0c5d92005-03-08 10:43:43 +00004040 return CMD_SUCCESS;
4041}
paul718e3742002-12-13 20:15:29 +00004042
4043DEFUN (banner_motd_default,
4044 banner_motd_default_cmd,
4045 "banner motd default",
4046 "Set banner string\n"
4047 "Strings for motd\n"
4048 "Default string\n")
4049{
4050 host.motd = default_motd;
4051 return CMD_SUCCESS;
4052}
4053
4054DEFUN (no_banner_motd,
4055 no_banner_motd_cmd,
4056 "no banner motd",
4057 NO_STR
4058 "Set banner string\n"
4059 "Strings for motd\n")
4060{
4061 host.motd = NULL;
paul22085182005-03-08 16:00:12 +00004062 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004063 XFREE (MTYPE_HOST, host.motdfile);
paul3b0c5d92005-03-08 10:43:43 +00004064 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004065 return CMD_SUCCESS;
4066}
4067
Lou Bergerf9ec4192016-01-12 13:41:48 -05004068DEFUN (show_commandtree,
4069 show_commandtree_cmd,
4070 "show commandtree",
4071 NO_STR
4072 "Show command tree\n")
4073{
4074 /* TBD */
4075 vector cmd_vector;
4076 unsigned int i;
4077
4078 vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE);
4079
4080 /* vector of all commands installed at this node */
4081 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
4082
4083 /* loop over all commands at this node */
4084 for (i = 0; i < vector_active(cmd_vector); ++i)
4085 {
4086 struct cmd_element *cmd_element;
4087
4088 /* A cmd_element (seems to be) is an individual command */
4089 if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
4090 continue;
4091
4092 vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE);
4093 }
4094
4095 vector_free (cmd_vector);
4096 return CMD_SUCCESS;
4097}
4098
paul718e3742002-12-13 20:15:29 +00004099/* Set config filename. Called from vty.c */
4100void
4101host_config_set (char *filename)
4102{
Chris Caputo228da422009-07-18 05:44:03 +00004103 if (host.config)
4104 XFREE (MTYPE_HOST, host.config);
paul05865c92005-10-26 05:49:54 +00004105 host.config = XSTRDUP (MTYPE_HOST, filename);
paul718e3742002-12-13 20:15:29 +00004106}
4107
4108void
4109install_default (enum node_type node)
4110{
4111 install_element (node, &config_exit_cmd);
4112 install_element (node, &config_quit_cmd);
4113 install_element (node, &config_end_cmd);
4114 install_element (node, &config_help_cmd);
4115 install_element (node, &config_list_cmd);
4116
4117 install_element (node, &config_write_terminal_cmd);
4118 install_element (node, &config_write_file_cmd);
4119 install_element (node, &config_write_memory_cmd);
4120 install_element (node, &config_write_cmd);
4121 install_element (node, &show_running_config_cmd);
4122}
4123
4124/* Initialize command interface. Install basic nodes and commands. */
4125void
4126cmd_init (int terminal)
4127{
Christian Frankecd40b322013-09-30 12:27:51 +00004128 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4129 token_cr.type = TOKEN_TERMINAL;
David Lamparter10bac802015-05-05 11:10:20 +02004130 token_cr.terminal = TERMINAL_LITERAL;
Christian Frankecd40b322013-09-30 12:27:51 +00004131 token_cr.cmd = command_cr;
4132 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
Chris Caputo228da422009-07-18 05:44:03 +00004133
paul718e3742002-12-13 20:15:29 +00004134 /* Allocate initial top vector of commands. */
4135 cmdvec = vector_init (VECTOR_MIN_SIZE);
4136
4137 /* Default host value settings. */
4138 host.name = NULL;
4139 host.password = NULL;
4140 host.enable = NULL;
4141 host.logfile = NULL;
4142 host.config = NULL;
4143 host.lines = -1;
4144 host.motd = default_motd;
paul3b0c5d92005-03-08 10:43:43 +00004145 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004146
4147 /* Install top nodes. */
4148 install_node (&view_node, NULL);
4149 install_node (&enable_node, NULL);
4150 install_node (&auth_node, NULL);
4151 install_node (&auth_enable_node, NULL);
Paul Jakma62687ff2008-08-23 14:27:06 +01004152 install_node (&restricted_node, NULL);
paul718e3742002-12-13 20:15:29 +00004153 install_node (&config_node, config_write_host);
4154
4155 /* Each node's basic commands. */
4156 install_element (VIEW_NODE, &show_version_cmd);
4157 if (terminal)
4158 {
4159 install_element (VIEW_NODE, &config_list_cmd);
4160 install_element (VIEW_NODE, &config_exit_cmd);
4161 install_element (VIEW_NODE, &config_quit_cmd);
4162 install_element (VIEW_NODE, &config_help_cmd);
4163 install_element (VIEW_NODE, &config_enable_cmd);
4164 install_element (VIEW_NODE, &config_terminal_length_cmd);
4165 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004166 install_element (VIEW_NODE, &show_logging_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004167 install_element (VIEW_NODE, &show_commandtree_cmd);
ajs2885f722004-12-17 23:16:33 +00004168 install_element (VIEW_NODE, &echo_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004169
4170 install_element (RESTRICTED_NODE, &config_list_cmd);
4171 install_element (RESTRICTED_NODE, &config_exit_cmd);
4172 install_element (RESTRICTED_NODE, &config_quit_cmd);
4173 install_element (RESTRICTED_NODE, &config_help_cmd);
4174 install_element (RESTRICTED_NODE, &config_enable_cmd);
4175 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4176 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004177 install_element (RESTRICTED_NODE, &show_commandtree_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004178 install_element (RESTRICTED_NODE, &echo_cmd);
paul718e3742002-12-13 20:15:29 +00004179 }
4180
4181 if (terminal)
4182 {
4183 install_default (ENABLE_NODE);
4184 install_element (ENABLE_NODE, &config_disable_cmd);
4185 install_element (ENABLE_NODE, &config_terminal_cmd);
4186 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4187 }
4188 install_element (ENABLE_NODE, &show_startup_config_cmd);
4189 install_element (ENABLE_NODE, &show_version_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004190 install_element (ENABLE_NODE, &show_commandtree_cmd);
paul718e3742002-12-13 20:15:29 +00004191
4192 if (terminal)
paul718e3742002-12-13 20:15:29 +00004193 {
hassoe7168df2004-10-03 20:11:32 +00004194 install_element (ENABLE_NODE, &config_terminal_length_cmd);
4195 install_element (ENABLE_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004196 install_element (ENABLE_NODE, &show_logging_cmd);
ajs2885f722004-12-17 23:16:33 +00004197 install_element (ENABLE_NODE, &echo_cmd);
ajs274a4a42004-12-07 15:39:31 +00004198 install_element (ENABLE_NODE, &config_logmsg_cmd);
hassoe7168df2004-10-03 20:11:32 +00004199
4200 install_default (CONFIG_NODE);
hassoea8e9d92004-10-07 21:32:14 +00004201 }
4202
4203 install_element (CONFIG_NODE, &hostname_cmd);
4204 install_element (CONFIG_NODE, &no_hostname_cmd);
hassoe7168df2004-10-03 20:11:32 +00004205
hassoea8e9d92004-10-07 21:32:14 +00004206 if (terminal)
4207 {
hassoe7168df2004-10-03 20:11:32 +00004208 install_element (CONFIG_NODE, &password_cmd);
4209 install_element (CONFIG_NODE, &password_text_cmd);
4210 install_element (CONFIG_NODE, &enable_password_cmd);
4211 install_element (CONFIG_NODE, &enable_password_text_cmd);
4212 install_element (CONFIG_NODE, &no_enable_password_cmd);
4213
paul718e3742002-12-13 20:15:29 +00004214 install_element (CONFIG_NODE, &config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004215 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
paul718e3742002-12-13 20:15:29 +00004216 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004217 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4218 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4219 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00004220 install_element (CONFIG_NODE, &config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004221 install_element (CONFIG_NODE, &config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004222 install_element (CONFIG_NODE, &no_config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004223 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004224 install_element (CONFIG_NODE, &config_log_syslog_cmd);
ajs274a4a42004-12-07 15:39:31 +00004225 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
paul12ab19f2003-07-26 06:14:55 +00004226 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004227 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
paul12ab19f2003-07-26 06:14:55 +00004228 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
ajs274a4a42004-12-07 15:39:31 +00004229 install_element (CONFIG_NODE, &config_log_facility_cmd);
4230 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004231 install_element (CONFIG_NODE, &config_log_trap_cmd);
4232 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4233 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4234 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004235 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4236 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
paul718e3742002-12-13 20:15:29 +00004237 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4238 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4239 install_element (CONFIG_NODE, &banner_motd_default_cmd);
paul3b0c5d92005-03-08 10:43:43 +00004240 install_element (CONFIG_NODE, &banner_motd_file_cmd);
paul718e3742002-12-13 20:15:29 +00004241 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4242 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4243 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
paul718e3742002-12-13 20:15:29 +00004244
paul354d1192005-04-25 16:26:42 +00004245 install_element (VIEW_NODE, &show_thread_cpu_cmd);
4246 install_element (ENABLE_NODE, &show_thread_cpu_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004247 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
Paul Jakmae276eb82010-01-09 16:15:00 +00004248
4249 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
paul354d1192005-04-25 16:26:42 +00004250 install_element (VIEW_NODE, &show_work_queues_cmd);
4251 install_element (ENABLE_NODE, &show_work_queues_cmd);
paul9ab68122003-01-18 01:16:20 +00004252 }
Lou Bergerf9ec4192016-01-12 13:41:48 -05004253 install_element (CONFIG_NODE, &show_commandtree_cmd);
Donald Sharpf31bab42015-06-19 19:26:19 -04004254 srandom(time(NULL));
paul718e3742002-12-13 20:15:29 +00004255}
Chris Caputo228da422009-07-18 05:44:03 +00004256
Christian Frankecd40b322013-09-30 12:27:51 +00004257static void
4258cmd_terminate_token(struct cmd_token *token)
4259{
4260 unsigned int i, j;
4261 vector keyword_vect;
4262
4263 if (token->multiple)
4264 {
4265 for (i = 0; i < vector_active(token->multiple); i++)
4266 cmd_terminate_token(vector_slot(token->multiple, i));
4267 vector_free(token->multiple);
4268 token->multiple = NULL;
4269 }
4270
4271 if (token->keyword)
4272 {
4273 for (i = 0; i < vector_active(token->keyword); i++)
4274 {
4275 keyword_vect = vector_slot(token->keyword, i);
4276 for (j = 0; j < vector_active(keyword_vect); j++)
4277 cmd_terminate_token(vector_slot(keyword_vect, j));
4278 vector_free(keyword_vect);
4279 }
4280 vector_free(token->keyword);
4281 token->keyword = NULL;
4282 }
4283
4284 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4285 XFREE(MTYPE_CMD_TOKENS, token->desc);
4286
4287 XFREE(MTYPE_CMD_TOKENS, token);
4288}
4289
4290static void
4291cmd_terminate_element(struct cmd_element *cmd)
4292{
4293 unsigned int i;
4294
4295 if (cmd->tokens == NULL)
4296 return;
4297
4298 for (i = 0; i < vector_active(cmd->tokens); i++)
4299 cmd_terminate_token(vector_slot(cmd->tokens, i));
4300
4301 vector_free(cmd->tokens);
4302 cmd->tokens = NULL;
4303}
4304
Chris Caputo228da422009-07-18 05:44:03 +00004305void
4306cmd_terminate ()
4307{
Christian Frankecd40b322013-09-30 12:27:51 +00004308 unsigned int i, j;
Chris Caputo228da422009-07-18 05:44:03 +00004309 struct cmd_node *cmd_node;
4310 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00004311 vector cmd_node_v;
Chris Caputo228da422009-07-18 05:44:03 +00004312
4313 if (cmdvec)
4314 {
4315 for (i = 0; i < vector_active (cmdvec); i++)
4316 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4317 {
4318 cmd_node_v = cmd_node->cmd_vector;
4319
4320 for (j = 0; j < vector_active (cmd_node_v); j++)
Christian Frankecd40b322013-09-30 12:27:51 +00004321 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4322 cmd_terminate_element(cmd_element);
Chris Caputo228da422009-07-18 05:44:03 +00004323
4324 vector_free (cmd_node_v);
4325 }
4326
4327 vector_free (cmdvec);
4328 cmdvec = NULL;
4329 }
4330
4331 if (command_cr)
Christian Frankecd40b322013-09-30 12:27:51 +00004332 XFREE(MTYPE_CMD_TOKENS, command_cr);
4333 if (token_cr.desc)
4334 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
Chris Caputo228da422009-07-18 05:44:03 +00004335 if (host.name)
4336 XFREE (MTYPE_HOST, host.name);
4337 if (host.password)
4338 XFREE (MTYPE_HOST, host.password);
4339 if (host.password_encrypt)
4340 XFREE (MTYPE_HOST, host.password_encrypt);
4341 if (host.enable)
4342 XFREE (MTYPE_HOST, host.enable);
4343 if (host.enable_encrypt)
4344 XFREE (MTYPE_HOST, host.enable_encrypt);
4345 if (host.logfile)
4346 XFREE (MTYPE_HOST, host.logfile);
4347 if (host.motdfile)
4348 XFREE (MTYPE_HOST, host.motdfile);
4349 if (host.config)
4350 XFREE (MTYPE_HOST, host.config);
4351}