blob: 12e8bb100704c575fe662cb402816229480a5b9a [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);
Donald Sharpb9ac2f32016-03-11 14:27:12 -0500625
626 if (ntype == VIEW_NODE)
627 install_element (ENABLE_NODE, cmd);
paul718e3742002-12-13 20:15:29 +0000628}
629
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300630static const unsigned char itoa64[] =
paul718e3742002-12-13 20:15:29 +0000631"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
632
ajs274a4a42004-12-07 15:39:31 +0000633static void
paul718e3742002-12-13 20:15:29 +0000634to64(char *s, long v, int n)
635{
636 while (--n >= 0)
637 {
638 *s++ = itoa64[v&0x3f];
639 v >>= 6;
640 }
641}
642
ajs274a4a42004-12-07 15:39:31 +0000643static char *
644zencrypt (const char *passwd)
paul718e3742002-12-13 20:15:29 +0000645{
646 char salt[6];
647 struct timeval tv;
648 char *crypt (const char *, const char *);
649
650 gettimeofday(&tv,0);
651
652 to64(&salt[0], random(), 3);
653 to64(&salt[3], tv.tv_usec, 3);
654 salt[5] = '\0';
655
656 return crypt (passwd, salt);
657}
658
659/* This function write configuration of this host. */
ajs274a4a42004-12-07 15:39:31 +0000660static int
paul718e3742002-12-13 20:15:29 +0000661config_write_host (struct vty *vty)
662{
663 if (host.name)
664 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
665
666 if (host.encrypt)
667 {
668 if (host.password_encrypt)
669 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
670 if (host.enable_encrypt)
671 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
672 }
673 else
674 {
675 if (host.password)
676 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
677 if (host.enable)
678 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
679 }
680
ajs274a4a42004-12-07 15:39:31 +0000681 if (zlog_default->default_lvl != LOG_DEBUG)
ajs82146b82004-12-07 17:15:55 +0000682 {
683 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
684 VTY_NEWLINE);
685 vty_out (vty, "log trap %s%s",
686 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
687 }
paul718e3742002-12-13 20:15:29 +0000688
ajs274a4a42004-12-07 15:39:31 +0000689 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
paul12ab19f2003-07-26 06:14:55 +0000690 {
ajs274a4a42004-12-07 15:39:31 +0000691 vty_out (vty, "log file %s", host.logfile);
692 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
693 vty_out (vty, " %s",
694 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
paul12ab19f2003-07-26 06:14:55 +0000695 vty_out (vty, "%s", VTY_NEWLINE);
696 }
ajs274a4a42004-12-07 15:39:31 +0000697
698 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
699 {
700 vty_out (vty, "log stdout");
701 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
702 vty_out (vty, " %s",
703 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
704 vty_out (vty, "%s", VTY_NEWLINE);
705 }
706
707 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
708 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
709 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
710 vty_out(vty,"log monitor %s%s",
711 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
712
713 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
714 {
715 vty_out (vty, "log syslog");
716 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
717 vty_out (vty, " %s",
718 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
719 vty_out (vty, "%s", VTY_NEWLINE);
720 }
721
722 if (zlog_default->facility != LOG_DAEMON)
723 vty_out (vty, "log facility %s%s",
724 facility_name(zlog_default->facility), VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000725
726 if (zlog_default->record_priority == 1)
727 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
728
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000729 if (zlog_default->timestamp_precision > 0)
730 vty_out (vty, "log timestamp precision %d%s",
731 zlog_default->timestamp_precision, VTY_NEWLINE);
732
paul718e3742002-12-13 20:15:29 +0000733 if (host.advanced)
734 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
735
736 if (host.encrypt)
737 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
738
739 if (host.lines >= 0)
740 vty_out (vty, "service terminal-length %d%s", host.lines,
741 VTY_NEWLINE);
742
paul3b0c5d92005-03-08 10:43:43 +0000743 if (host.motdfile)
744 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
745 else if (! host.motd)
paul718e3742002-12-13 20:15:29 +0000746 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
747
748 return 1;
749}
750
751/* Utility function for getting command vector. */
ajs274a4a42004-12-07 15:39:31 +0000752static vector
paul718e3742002-12-13 20:15:29 +0000753cmd_node_vector (vector v, enum node_type ntype)
754{
755 struct cmd_node *cnode = vector_slot (v, ntype);
756 return cnode->cmd_vector;
757}
758
ajs274a4a42004-12-07 15:39:31 +0000759#if 0
760/* Filter command vector by symbol. This function is not actually used;
761 * should it be deleted? */
762static int
paul718e3742002-12-13 20:15:29 +0000763cmd_filter_by_symbol (char *command, char *symbol)
764{
765 int i, lim;
766
767 if (strcmp (symbol, "IPV4_ADDRESS") == 0)
768 {
769 i = 0;
770 lim = strlen (command);
771 while (i < lim)
772 {
773 if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
774 return 1;
775 i++;
776 }
777 return 0;
778 }
779 if (strcmp (symbol, "STRING") == 0)
780 {
781 i = 0;
782 lim = strlen (command);
783 while (i < lim)
784 {
785 if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
786 return 1;
787 i++;
788 }
789 return 0;
790 }
791 if (strcmp (symbol, "IFNAME") == 0)
792 {
793 i = 0;
794 lim = strlen (command);
795 while (i < lim)
796 {
797 if (! isalnum ((int) command[i]))
798 return 1;
799 i++;
800 }
801 return 0;
802 }
803 return 0;
804}
ajs274a4a42004-12-07 15:39:31 +0000805#endif
paul718e3742002-12-13 20:15:29 +0000806
807/* Completion match types. */
808enum match_type
809{
810 no_match,
811 extend_match,
812 ipv4_prefix_match,
813 ipv4_match,
814 ipv6_prefix_match,
815 ipv6_match,
816 range_match,
817 vararg_match,
818 partly_match,
819 exact_match
820};
821
ajs274a4a42004-12-07 15:39:31 +0000822static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000823cmd_ipv4_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000824{
hasso8c328f12004-10-05 21:01:23 +0000825 const char *sp;
paul718e3742002-12-13 20:15:29 +0000826 int dots = 0, nums = 0;
827 char buf[4];
828
829 if (str == NULL)
830 return partly_match;
831
832 for (;;)
833 {
834 memset (buf, 0, sizeof (buf));
835 sp = str;
836 while (*str != '\0')
837 {
838 if (*str == '.')
839 {
840 if (dots >= 3)
841 return no_match;
842
843 if (*(str + 1) == '.')
844 return no_match;
845
846 if (*(str + 1) == '\0')
847 return partly_match;
848
849 dots++;
850 break;
851 }
852 if (!isdigit ((int) *str))
853 return no_match;
854
855 str++;
856 }
857
858 if (str - sp > 3)
859 return no_match;
860
861 strncpy (buf, sp, str - sp);
862 if (atoi (buf) > 255)
863 return no_match;
864
865 nums++;
866
867 if (*str == '\0')
868 break;
869
870 str++;
871 }
872
873 if (nums < 4)
874 return partly_match;
875
876 return exact_match;
877}
878
ajs274a4a42004-12-07 15:39:31 +0000879static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000880cmd_ipv4_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000881{
hasso8c328f12004-10-05 21:01:23 +0000882 const char *sp;
paul718e3742002-12-13 20:15:29 +0000883 int dots = 0;
884 char buf[4];
885
886 if (str == NULL)
887 return partly_match;
888
889 for (;;)
890 {
891 memset (buf, 0, sizeof (buf));
892 sp = str;
893 while (*str != '\0' && *str != '/')
894 {
895 if (*str == '.')
896 {
897 if (dots == 3)
898 return no_match;
899
900 if (*(str + 1) == '.' || *(str + 1) == '/')
901 return no_match;
902
903 if (*(str + 1) == '\0')
904 return partly_match;
905
906 dots++;
907 break;
908 }
909
910 if (!isdigit ((int) *str))
911 return no_match;
912
913 str++;
914 }
915
916 if (str - sp > 3)
917 return no_match;
918
919 strncpy (buf, sp, str - sp);
920 if (atoi (buf) > 255)
921 return no_match;
922
923 if (dots == 3)
924 {
925 if (*str == '/')
926 {
927 if (*(str + 1) == '\0')
928 return partly_match;
929
930 str++;
931 break;
932 }
933 else if (*str == '\0')
934 return partly_match;
935 }
936
937 if (*str == '\0')
938 return partly_match;
939
940 str++;
941 }
942
943 sp = str;
944 while (*str != '\0')
945 {
946 if (!isdigit ((int) *str))
947 return no_match;
948
949 str++;
950 }
951
952 if (atoi (sp) > 32)
953 return no_match;
954
955 return exact_match;
956}
957
958#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
959#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
960#define STATE_START 1
961#define STATE_COLON 2
962#define STATE_DOUBLE 3
963#define STATE_ADDR 4
964#define STATE_DOT 5
965#define STATE_SLASH 6
966#define STATE_MASK 7
967
paul22e0a9e2003-07-11 17:55:46 +0000968#ifdef HAVE_IPV6
969
ajs274a4a42004-12-07 15:39:31 +0000970static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000971cmd_ipv6_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000972{
hasso726f9b22003-05-25 21:04:54 +0000973 struct sockaddr_in6 sin6_dummy;
974 int ret;
paul718e3742002-12-13 20:15:29 +0000975
976 if (str == NULL)
977 return partly_match;
978
979 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
980 return no_match;
981
hasso726f9b22003-05-25 21:04:54 +0000982 /* use inet_pton that has a better support,
983 * for example inet_pton can support the automatic addresses:
984 * ::1.2.3.4
985 */
986 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
987
988 if (ret == 1)
989 return exact_match;
990
Roman Hoog Antink7c9c6ae2012-05-09 06:35:34 +0000991 return no_match;
paul718e3742002-12-13 20:15:29 +0000992}
993
ajs274a4a42004-12-07 15:39:31 +0000994static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000995cmd_ipv6_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000996{
997 int state = STATE_START;
998 int colons = 0, nums = 0, double_colon = 0;
999 int mask;
hasso8c328f12004-10-05 21:01:23 +00001000 const char *sp = NULL;
paul718e3742002-12-13 20:15:29 +00001001 char *endptr = NULL;
1002
1003 if (str == NULL)
1004 return partly_match;
1005
1006 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
1007 return no_match;
1008
1009 while (*str != '\0' && state != STATE_MASK)
1010 {
1011 switch (state)
1012 {
1013 case STATE_START:
1014 if (*str == ':')
1015 {
1016 if (*(str + 1) != ':' && *(str + 1) != '\0')
1017 return no_match;
1018 colons--;
1019 state = STATE_COLON;
1020 }
1021 else
1022 {
1023 sp = str;
1024 state = STATE_ADDR;
1025 }
1026
1027 continue;
1028 case STATE_COLON:
1029 colons++;
1030 if (*(str + 1) == '/')
1031 return no_match;
1032 else if (*(str + 1) == ':')
1033 state = STATE_DOUBLE;
1034 else
1035 {
1036 sp = str + 1;
1037 state = STATE_ADDR;
1038 }
1039 break;
1040 case STATE_DOUBLE:
1041 if (double_colon)
1042 return no_match;
1043
1044 if (*(str + 1) == ':')
1045 return no_match;
1046 else
1047 {
1048 if (*(str + 1) != '\0' && *(str + 1) != '/')
1049 colons++;
1050 sp = str + 1;
1051
1052 if (*(str + 1) == '/')
1053 state = STATE_SLASH;
1054 else
1055 state = STATE_ADDR;
1056 }
1057
1058 double_colon++;
1059 nums += 1;
1060 break;
1061 case STATE_ADDR:
1062 if (*(str + 1) == ':' || *(str + 1) == '.'
1063 || *(str + 1) == '\0' || *(str + 1) == '/')
1064 {
1065 if (str - sp > 3)
1066 return no_match;
1067
1068 for (; sp <= str; sp++)
1069 if (*sp == '/')
1070 return no_match;
1071
1072 nums++;
1073
1074 if (*(str + 1) == ':')
1075 state = STATE_COLON;
1076 else if (*(str + 1) == '.')
David Lamparteraa5cf242012-07-19 16:11:50 +02001077 {
1078 if (colons || double_colon)
1079 state = STATE_DOT;
1080 else
1081 return no_match;
1082 }
paul718e3742002-12-13 20:15:29 +00001083 else if (*(str + 1) == '/')
1084 state = STATE_SLASH;
1085 }
1086 break;
1087 case STATE_DOT:
1088 state = STATE_ADDR;
1089 break;
1090 case STATE_SLASH:
1091 if (*(str + 1) == '\0')
1092 return partly_match;
1093
1094 state = STATE_MASK;
1095 break;
1096 default:
1097 break;
1098 }
1099
1100 if (nums > 11)
1101 return no_match;
1102
1103 if (colons > 7)
1104 return no_match;
1105
1106 str++;
1107 }
1108
1109 if (state < STATE_MASK)
1110 return partly_match;
1111
1112 mask = strtol (str, &endptr, 10);
1113 if (*endptr != '\0')
1114 return no_match;
1115
1116 if (mask < 0 || mask > 128)
1117 return no_match;
1118
1119/* I don't know why mask < 13 makes command match partly.
1120 Forgive me to make this comments. I Want to set static default route
1121 because of lack of function to originate default in ospf6d; sorry
1122 yasu
1123 if (mask < 13)
1124 return partly_match;
1125*/
1126
1127 return exact_match;
1128}
1129
paul22e0a9e2003-07-11 17:55:46 +00001130#endif /* HAVE_IPV6 */
1131
paul718e3742002-12-13 20:15:29 +00001132#define DECIMAL_STRLEN_MAX 10
1133
ajs274a4a42004-12-07 15:39:31 +00001134static int
hasso8c328f12004-10-05 21:01:23 +00001135cmd_range_match (const char *range, const char *str)
paul718e3742002-12-13 20:15:29 +00001136{
1137 char *p;
1138 char buf[DECIMAL_STRLEN_MAX + 1];
1139 char *endptr = NULL;
1140 unsigned long min, max, val;
1141
1142 if (str == NULL)
1143 return 1;
1144
1145 val = strtoul (str, &endptr, 10);
1146 if (*endptr != '\0')
1147 return 0;
1148
1149 range++;
1150 p = strchr (range, '-');
1151 if (p == NULL)
1152 return 0;
1153 if (p - range > DECIMAL_STRLEN_MAX)
1154 return 0;
1155 strncpy (buf, range, p - range);
1156 buf[p - range] = '\0';
1157 min = strtoul (buf, &endptr, 10);
1158 if (*endptr != '\0')
1159 return 0;
1160
1161 range = p + 1;
1162 p = strchr (range, '>');
1163 if (p == NULL)
1164 return 0;
1165 if (p - range > DECIMAL_STRLEN_MAX)
1166 return 0;
1167 strncpy (buf, range, p - range);
1168 buf[p - range] = '\0';
1169 max = strtoul (buf, &endptr, 10);
1170 if (*endptr != '\0')
1171 return 0;
1172
1173 if (val < min || val > max)
1174 return 0;
1175
1176 return 1;
1177}
1178
ajs274a4a42004-12-07 15:39:31 +00001179static enum match_type
Christian Frankecd40b322013-09-30 12:27:51 +00001180cmd_word_match(struct cmd_token *token,
1181 enum filter_type filter,
1182 const char *word)
paul718e3742002-12-13 20:15:29 +00001183{
hasso8c328f12004-10-05 21:01:23 +00001184 const char *str;
paul718e3742002-12-13 20:15:29 +00001185 enum match_type match_type;
paul909a2152005-03-14 17:41:45 +00001186
Christian Frankecd40b322013-09-30 12:27:51 +00001187 str = token->cmd;
paul718e3742002-12-13 20:15:29 +00001188
Christian Frankecd40b322013-09-30 12:27:51 +00001189 if (filter == FILTER_RELAXED)
1190 if (!word || !strlen(word))
1191 return partly_match;
paul718e3742002-12-13 20:15:29 +00001192
Christian Frankecd40b322013-09-30 12:27:51 +00001193 if (!word)
1194 return no_match;
paul909a2152005-03-14 17:41:45 +00001195
David Lamparter10bac802015-05-05 11:10:20 +02001196 switch (token->terminal)
Christian Frankecd40b322013-09-30 12:27:51 +00001197 {
David Lamparter10bac802015-05-05 11:10:20 +02001198 case TERMINAL_VARARG:
1199 return vararg_match;
1200
1201 case TERMINAL_RANGE:
1202 if (cmd_range_match(str, word))
1203 return range_match;
1204 break;
1205
1206 case TERMINAL_IPV6:
1207 match_type = cmd_ipv6_match(word);
1208 if ((filter == FILTER_RELAXED && match_type != no_match)
Christian Frankecd40b322013-09-30 12:27:51 +00001209 || (filter == FILTER_STRICT && match_type == exact_match))
David Lamparter10bac802015-05-05 11:10:20 +02001210 return ipv6_match;
1211 break;
1212
1213 case TERMINAL_IPV6_PREFIX:
1214 match_type = cmd_ipv6_prefix_match(word);
1215 if ((filter == FILTER_RELAXED && match_type != no_match)
1216 || (filter == FILTER_STRICT && match_type == exact_match))
1217 return ipv6_prefix_match;
1218 break;
1219
1220 case TERMINAL_IPV4:
1221 match_type = cmd_ipv4_match(word);
1222 if ((filter == FILTER_RELAXED && match_type != no_match)
1223 || (filter == FILTER_STRICT && match_type == exact_match))
1224 return ipv4_match;
1225 break;
1226
1227 case TERMINAL_IPV4_PREFIX:
1228 match_type = cmd_ipv4_prefix_match(word);
1229 if ((filter == FILTER_RELAXED && match_type != no_match)
1230 || (filter == FILTER_STRICT && match_type == exact_match))
1231 return ipv4_prefix_match;
1232 break;
1233
1234 case TERMINAL_OPTION:
1235 case TERMINAL_VARIABLE:
1236 return extend_match;
1237
1238 case TERMINAL_LITERAL:
1239 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1240 {
1241 if (!strcmp(str, word))
1242 return exact_match;
1243 return partly_match;
1244 }
1245 if (filter == FILTER_STRICT && !strcmp(str, word))
1246 return exact_match;
1247 break;
1248
1249 default:
1250 assert (0);
Christian Frankecd40b322013-09-30 12:27:51 +00001251 }
paul718e3742002-12-13 20:15:29 +00001252
Christian Frankecd40b322013-09-30 12:27:51 +00001253 return no_match;
paul718e3742002-12-13 20:15:29 +00001254}
1255
Christian Frankecd40b322013-09-30 12:27:51 +00001256struct cmd_matcher
1257{
1258 struct cmd_element *cmd; /* The command element the matcher is using */
1259 enum filter_type filter; /* Whether to use strict or relaxed matching */
1260 vector vline; /* The tokenized commandline which is to be matched */
1261 unsigned int index; /* The index up to which matching should be done */
1262
1263 /* If set, construct a list of matches at the position given by index */
1264 enum match_type *match_type;
1265 vector *match;
1266
1267 unsigned int word_index; /* iterating over vline */
1268};
1269
1270static int
1271push_argument(int *argc, const char **argv, const char *arg)
1272{
1273 if (!arg || !strlen(arg))
1274 arg = NULL;
1275
1276 if (!argc || !argv)
1277 return 0;
1278
1279 if (*argc >= CMD_ARGC_MAX)
1280 return -1;
1281
1282 argv[(*argc)++] = arg;
1283 return 0;
1284}
1285
1286static void
1287cmd_matcher_record_match(struct cmd_matcher *matcher,
1288 enum match_type match_type,
1289 struct cmd_token *token)
1290{
1291 if (matcher->word_index != matcher->index)
1292 return;
1293
1294 if (matcher->match)
1295 {
1296 if (!*matcher->match)
1297 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1298 vector_set(*matcher->match, token);
1299 }
1300
1301 if (matcher->match_type)
1302 {
1303 if (match_type > *matcher->match_type)
1304 *matcher->match_type = match_type;
1305 }
1306}
1307
1308static int
1309cmd_matcher_words_left(struct cmd_matcher *matcher)
1310{
1311 return matcher->word_index < vector_active(matcher->vline);
1312}
1313
1314static const char*
1315cmd_matcher_get_word(struct cmd_matcher *matcher)
1316{
1317 assert(cmd_matcher_words_left(matcher));
1318
1319 return vector_slot(matcher->vline, matcher->word_index);
1320}
1321
1322static enum matcher_rv
1323cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1324 struct cmd_token *token,
1325 int *argc, const char **argv)
1326{
1327 const char *word;
1328 enum match_type word_match;
1329
1330 assert(token->type == TOKEN_TERMINAL);
1331
1332 if (!cmd_matcher_words_left(matcher))
1333 {
David Lamparter10bac802015-05-05 11:10:20 +02001334 if (token->terminal == TERMINAL_OPTION)
Christian Frankecd40b322013-09-30 12:27:51 +00001335 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1336 else
1337 return MATCHER_INCOMPLETE;
1338 }
1339
1340 word = cmd_matcher_get_word(matcher);
1341 word_match = cmd_word_match(token, matcher->filter, word);
1342 if (word_match == no_match)
1343 return MATCHER_NO_MATCH;
1344
1345 /* We have to record the input word as argument if it matched
1346 * against a variable. */
David Lamparter14162932015-05-12 17:18:04 +02001347 if (TERMINAL_RECORD (token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001348 {
1349 if (push_argument(argc, argv, word))
1350 return MATCHER_EXCEED_ARGC_MAX;
1351 }
1352
1353 cmd_matcher_record_match(matcher, word_match, token);
1354
1355 matcher->word_index++;
1356
1357 /* A vararg token should consume all left over words as arguments */
David Lamparter10bac802015-05-05 11:10:20 +02001358 if (token->terminal == TERMINAL_VARARG)
Christian Frankecd40b322013-09-30 12:27:51 +00001359 while (cmd_matcher_words_left(matcher))
1360 {
1361 word = cmd_matcher_get_word(matcher);
1362 if (word && strlen(word))
1363 push_argument(argc, argv, word);
1364 matcher->word_index++;
1365 }
1366
1367 return MATCHER_OK;
1368}
1369
1370static enum matcher_rv
1371cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1372 struct cmd_token *token,
1373 int *argc, const char **argv)
1374{
1375 enum match_type multiple_match;
1376 unsigned int multiple_index;
1377 const char *word;
David Lamparterab90fc02015-03-03 09:07:25 +01001378 const char *arg = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001379 struct cmd_token *word_token;
1380 enum match_type word_match;
1381
1382 assert(token->type == TOKEN_MULTIPLE);
1383
1384 multiple_match = no_match;
1385
1386 if (!cmd_matcher_words_left(matcher))
1387 return MATCHER_INCOMPLETE;
1388
1389 word = cmd_matcher_get_word(matcher);
1390 for (multiple_index = 0;
1391 multiple_index < vector_active(token->multiple);
1392 multiple_index++)
1393 {
1394 word_token = vector_slot(token->multiple, multiple_index);
1395
1396 word_match = cmd_word_match(word_token, matcher->filter, word);
1397 if (word_match == no_match)
1398 continue;
1399
1400 cmd_matcher_record_match(matcher, word_match, word_token);
1401
1402 if (word_match > multiple_match)
1403 {
1404 multiple_match = word_match;
1405 arg = word;
1406 }
1407 /* To mimic the behavior of the old command implementation, we
1408 * tolerate any ambiguities here :/ */
1409 }
1410
1411 matcher->word_index++;
1412
1413 if (multiple_match == no_match)
1414 return MATCHER_NO_MATCH;
1415
1416 if (push_argument(argc, argv, arg))
1417 return MATCHER_EXCEED_ARGC_MAX;
1418
1419 return MATCHER_OK;
1420}
1421
1422static enum matcher_rv
1423cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1424 struct cmd_token *token,
1425 vector args_vector)
paul718e3742002-12-13 20:15:29 +00001426{
hasso8c328f12004-10-05 21:01:23 +00001427 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00001428 unsigned long keyword_mask;
1429 unsigned int keyword_found;
1430 enum match_type keyword_match;
1431 enum match_type word_match;
1432 vector keyword_vector;
1433 struct cmd_token *word_token;
1434 const char *word;
1435 int keyword_argc;
1436 const char **keyword_argv;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001437 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001438
1439 keyword_mask = 0;
1440 while (1)
1441 {
1442 if (!cmd_matcher_words_left(matcher))
1443 return MATCHER_OK;
1444
1445 word = cmd_matcher_get_word(matcher);
1446
1447 keyword_found = -1;
1448 keyword_match = no_match;
1449 for (i = 0; i < vector_active(token->keyword); i++)
1450 {
1451 if (keyword_mask & (1 << i))
1452 continue;
1453
1454 keyword_vector = vector_slot(token->keyword, i);
1455 word_token = vector_slot(keyword_vector, 0);
1456
1457 word_match = cmd_word_match(word_token, matcher->filter, word);
1458 if (word_match == no_match)
1459 continue;
1460
1461 cmd_matcher_record_match(matcher, word_match, word_token);
1462
1463 if (word_match > keyword_match)
1464 {
1465 keyword_match = word_match;
1466 keyword_found = i;
1467 }
1468 else if (word_match == keyword_match)
1469 {
1470 if (matcher->word_index != matcher->index || args_vector)
1471 return MATCHER_AMBIGUOUS;
1472 }
1473 }
1474
1475 if (keyword_found == (unsigned int)-1)
1476 return MATCHER_NO_MATCH;
1477
1478 matcher->word_index++;
1479
1480 if (matcher->word_index > matcher->index)
1481 return MATCHER_OK;
1482
1483 keyword_mask |= (1 << keyword_found);
1484
1485 if (args_vector)
1486 {
1487 keyword_argc = 0;
1488 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1489 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1490 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1491 keyword_argv[i] = (void*)-1;
1492 vector_set_index(args_vector, keyword_found, keyword_argv);
1493 }
1494 else
1495 {
1496 keyword_argv = NULL;
1497 }
1498
1499 keyword_vector = vector_slot(token->keyword, keyword_found);
1500 /* the keyword itself is at 0. We are only interested in the arguments,
1501 * so start counting at 1. */
1502 for (i = 1; i < vector_active(keyword_vector); i++)
1503 {
1504 word_token = vector_slot(keyword_vector, i);
1505
1506 switch (word_token->type)
1507 {
1508 case TOKEN_TERMINAL:
1509 rv = cmd_matcher_match_terminal(matcher, word_token,
1510 &keyword_argc, keyword_argv);
1511 break;
1512 case TOKEN_MULTIPLE:
1513 rv = cmd_matcher_match_multiple(matcher, word_token,
1514 &keyword_argc, keyword_argv);
1515 break;
1516 case TOKEN_KEYWORD:
1517 assert(!"Keywords should never be nested.");
1518 break;
1519 }
1520
1521 if (MATCHER_ERROR(rv))
1522 return rv;
1523
1524 if (matcher->word_index > matcher->index)
1525 return MATCHER_OK;
1526 }
1527 }
1528 /* not reached */
1529}
1530
1531static enum matcher_rv
1532cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1533 struct cmd_token *token,
1534 int *argc, const char **argv,
1535 vector keyword_args_vector)
1536{
1537 unsigned int i, j;
1538 const char **keyword_args;
1539 vector keyword_vector;
1540 struct cmd_token *word_token;
1541 const char *arg;
1542 enum matcher_rv rv;
1543
1544 rv = MATCHER_OK;
1545
1546 if (keyword_args_vector == NULL)
1547 return rv;
1548
1549 for (i = 0; i < vector_active(token->keyword); i++)
1550 {
1551 keyword_vector = vector_slot(token->keyword, i);
1552 keyword_args = vector_lookup(keyword_args_vector, i);
1553
1554 if (vector_active(keyword_vector) == 1)
1555 {
1556 /* this is a keyword without arguments */
1557 if (keyword_args)
1558 {
1559 word_token = vector_slot(keyword_vector, 0);
1560 arg = word_token->cmd;
1561 }
1562 else
1563 {
1564 arg = NULL;
1565 }
1566
1567 if (push_argument(argc, argv, arg))
1568 rv = MATCHER_EXCEED_ARGC_MAX;
1569 }
1570 else
1571 {
1572 /* this is a keyword with arguments */
1573 if (keyword_args)
1574 {
1575 /* the keyword was present, so just fill in the arguments */
1576 for (j = 0; keyword_args[j] != (void*)-1; j++)
1577 if (push_argument(argc, argv, keyword_args[j]))
1578 rv = MATCHER_EXCEED_ARGC_MAX;
1579 XFREE(MTYPE_TMP, keyword_args);
1580 }
1581 else
1582 {
1583 /* the keyword was not present, insert NULL for the arguments
1584 * the keyword would have taken. */
1585 for (j = 1; j < vector_active(keyword_vector); j++)
1586 {
1587 word_token = vector_slot(keyword_vector, j);
1588 if ((word_token->type == TOKEN_TERMINAL
David Lamparter14162932015-05-12 17:18:04 +02001589 && TERMINAL_RECORD (word_token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001590 || word_token->type == TOKEN_MULTIPLE)
1591 {
1592 if (push_argument(argc, argv, NULL))
1593 rv = MATCHER_EXCEED_ARGC_MAX;
1594 }
1595 }
1596 }
1597 }
1598 }
1599 vector_free(keyword_args_vector);
1600 return rv;
1601}
1602
1603static enum matcher_rv
1604cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1605 struct cmd_token *token,
1606 int *argc, const char **argv)
1607{
1608 vector keyword_args_vector;
1609 enum matcher_rv reader_rv;
1610 enum matcher_rv builder_rv;
1611
1612 assert(token->type == TOKEN_KEYWORD);
1613
1614 if (argc && argv)
1615 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1616 else
1617 keyword_args_vector = NULL;
1618
1619 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1620 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1621 argv, keyword_args_vector);
1622 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1623
1624 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1625 return builder_rv;
1626
1627 return reader_rv;
1628}
1629
1630static void
1631cmd_matcher_init(struct cmd_matcher *matcher,
1632 struct cmd_element *cmd,
1633 enum filter_type filter,
1634 vector vline,
1635 unsigned int index,
1636 enum match_type *match_type,
1637 vector *match)
1638{
1639 memset(matcher, 0, sizeof(*matcher));
1640
1641 matcher->cmd = cmd;
1642 matcher->filter = filter;
1643 matcher->vline = vline;
1644 matcher->index = index;
1645
1646 matcher->match_type = match_type;
1647 if (matcher->match_type)
1648 *matcher->match_type = no_match;
1649 matcher->match = match;
1650
1651 matcher->word_index = 0;
1652}
1653
1654static enum matcher_rv
1655cmd_element_match(struct cmd_element *cmd_element,
1656 enum filter_type filter,
1657 vector vline,
1658 unsigned int index,
1659 enum match_type *match_type,
1660 vector *match,
1661 int *argc,
1662 const char **argv)
1663{
1664 struct cmd_matcher matcher;
1665 unsigned int token_index;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001666 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001667
1668 cmd_matcher_init(&matcher, cmd_element, filter,
1669 vline, index, match_type, match);
1670
1671 if (argc != NULL)
1672 *argc = 0;
1673
1674 for (token_index = 0;
1675 token_index < vector_active(cmd_element->tokens);
1676 token_index++)
1677 {
1678 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1679
1680 switch (token->type)
1681 {
1682 case TOKEN_TERMINAL:
1683 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1684 break;
1685 case TOKEN_MULTIPLE:
1686 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1687 break;
1688 case TOKEN_KEYWORD:
1689 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1690 }
1691
1692 if (MATCHER_ERROR(rv))
1693 return rv;
1694
1695 if (matcher.word_index > index)
1696 return MATCHER_OK;
1697 }
1698
1699 /* return MATCHER_COMPLETE if all words were consumed */
1700 if (matcher.word_index >= vector_active(vline))
1701 return MATCHER_COMPLETE;
1702
1703 /* return MATCHER_COMPLETE also if only an empty word is left. */
1704 if (matcher.word_index == vector_active(vline) - 1
1705 && (!vector_slot(vline, matcher.word_index)
1706 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1707 return MATCHER_COMPLETE;
1708
1709 return MATCHER_NO_MATCH; /* command is too long to match */
1710}
1711
1712/**
1713 * Filter a given vector of commands against a given commandline and
1714 * calculate possible completions.
1715 *
1716 * @param commands A vector of struct cmd_element*. Commands that don't
1717 * match against the given command line will be overwritten
1718 * with NULL in that vector.
1719 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1720 * determines how incomplete commands are handled, compare with
1721 * cmd_word_match for details.
1722 * @param vline A vector of char* containing the tokenized commandline.
1723 * @param index Only match up to the given token of the commandline.
1724 * @param match_type Record the type of the best match here.
1725 * @param matches Record the matches here. For each cmd_element in the commands
1726 * vector, a match vector will be created in the matches vector.
1727 * That vector will contain all struct command_token* of the
1728 * cmd_element which matched against the given vline at the given
1729 * index.
1730 * @return A code specifying if an error occured. If all went right, it's
1731 * CMD_SUCCESS.
1732 */
1733static int
1734cmd_vector_filter(vector commands,
1735 enum filter_type filter,
1736 vector vline,
1737 unsigned int index,
1738 enum match_type *match_type,
1739 vector *matches)
1740{
1741 unsigned int i;
paul718e3742002-12-13 20:15:29 +00001742 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00001743 enum match_type best_match;
1744 enum match_type element_match;
1745 enum matcher_rv matcher_rv;
paul909a2152005-03-14 17:41:45 +00001746
Christian Frankecd40b322013-09-30 12:27:51 +00001747 best_match = no_match;
1748 *matches = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +00001749
Christian Frankecd40b322013-09-30 12:27:51 +00001750 for (i = 0; i < vector_active (commands); i++)
1751 if ((cmd_element = vector_slot (commands, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001752 {
Christian Frankecd40b322013-09-30 12:27:51 +00001753 vector_set_index(*matches, i, NULL);
1754 matcher_rv = cmd_element_match(cmd_element, filter,
1755 vline, index,
1756 &element_match,
1757 (vector*)&vector_slot(*matches, i),
1758 NULL, NULL);
1759 if (MATCHER_ERROR(matcher_rv))
1760 {
1761 vector_slot(commands, i) = NULL;
1762 if (matcher_rv == MATCHER_AMBIGUOUS)
1763 return CMD_ERR_AMBIGUOUS;
1764 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1765 return CMD_ERR_EXEED_ARGC_MAX;
1766 }
1767 else if (element_match > best_match)
1768 {
1769 best_match = element_match;
1770 }
paul718e3742002-12-13 20:15:29 +00001771 }
Christian Frankecd40b322013-09-30 12:27:51 +00001772 *match_type = best_match;
1773 return CMD_SUCCESS;
1774}
1775
1776/**
1777 * Check whether a given commandline is complete if used for a specific
1778 * cmd_element.
1779 *
1780 * @param cmd_element A cmd_element against which the commandline should be
1781 * checked.
1782 * @param vline The tokenized commandline.
1783 * @return 1 if the given commandline is complete, 0 otherwise.
1784 */
1785static int
1786cmd_is_complete(struct cmd_element *cmd_element,
1787 vector vline)
1788{
1789 enum matcher_rv rv;
1790
1791 rv = cmd_element_match(cmd_element,
1792 FILTER_RELAXED,
1793 vline, -1,
1794 NULL, NULL,
1795 NULL, NULL);
1796 return (rv == MATCHER_COMPLETE);
1797}
1798
1799/**
1800 * Parse a given commandline and construct a list of arguments for the
1801 * given command_element.
1802 *
1803 * @param cmd_element The cmd_element for which we want to construct arguments.
1804 * @param vline The tokenized commandline.
1805 * @param argc Where to store the argument count.
1806 * @param argv Where to store the argument list. Should be at least
1807 * CMD_ARGC_MAX elements long.
1808 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1809 */
1810static int
1811cmd_parse(struct cmd_element *cmd_element,
1812 vector vline,
1813 int *argc, const char **argv)
1814{
1815 enum matcher_rv rv = cmd_element_match(cmd_element,
1816 FILTER_RELAXED,
1817 vline, -1,
1818 NULL, NULL,
1819 argc, argv);
1820 switch (rv)
1821 {
1822 case MATCHER_COMPLETE:
1823 return CMD_SUCCESS;
1824
1825 case MATCHER_NO_MATCH:
1826 return CMD_ERR_NO_MATCH;
1827
1828 case MATCHER_AMBIGUOUS:
1829 return CMD_ERR_AMBIGUOUS;
1830
1831 case MATCHER_EXCEED_ARGC_MAX:
1832 return CMD_ERR_EXEED_ARGC_MAX;
1833
1834 default:
1835 return CMD_ERR_INCOMPLETE;
1836 }
paul718e3742002-12-13 20:15:29 +00001837}
1838
1839/* Check ambiguous match */
ajs274a4a42004-12-07 15:39:31 +00001840static int
Christian Frankecd40b322013-09-30 12:27:51 +00001841is_cmd_ambiguous (vector cmd_vector,
1842 const char *command,
1843 vector matches,
1844 enum match_type type)
paul718e3742002-12-13 20:15:29 +00001845{
hasso8c328f12004-10-05 21:01:23 +00001846 unsigned int i;
1847 unsigned int j;
1848 const char *str = NULL;
hasso8c328f12004-10-05 21:01:23 +00001849 const char *matched = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001850 vector match_vector;
1851 struct cmd_token *cmd_token;
paul909a2152005-03-14 17:41:45 +00001852
Christian Frankecd40b322013-09-30 12:27:51 +00001853 if (command == NULL)
1854 command = "";
1855
1856 for (i = 0; i < vector_active (matches); i++)
1857 if ((match_vector = vector_slot (matches, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001858 {
1859 int match = 0;
1860
Christian Frankecd40b322013-09-30 12:27:51 +00001861 for (j = 0; j < vector_active (match_vector); j++)
1862 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
paul909a2152005-03-14 17:41:45 +00001863 {
1864 enum match_type ret;
Christian Frankecd40b322013-09-30 12:27:51 +00001865
1866 assert(cmd_token->type == TOKEN_TERMINAL);
1867 if (cmd_token->type != TOKEN_TERMINAL)
1868 continue;
1869
1870 str = cmd_token->cmd;
paul718e3742002-12-13 20:15:29 +00001871
paul909a2152005-03-14 17:41:45 +00001872 switch (type)
1873 {
1874 case exact_match:
David Lamparter14162932015-05-12 17:18:04 +02001875 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001876 && strcmp (command, str) == 0)
1877 match++;
1878 break;
1879 case partly_match:
David Lamparter14162932015-05-12 17:18:04 +02001880 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001881 && strncmp (command, str, strlen (command)) == 0)
1882 {
1883 if (matched && strcmp (matched, str) != 0)
1884 return 1; /* There is ambiguous match. */
1885 else
1886 matched = str;
1887 match++;
1888 }
1889 break;
1890 case range_match:
1891 if (cmd_range_match (str, command))
1892 {
1893 if (matched && strcmp (matched, str) != 0)
1894 return 1;
1895 else
1896 matched = str;
1897 match++;
1898 }
1899 break;
1900#ifdef HAVE_IPV6
1901 case ipv6_match:
David Lamparter10bac802015-05-05 11:10:20 +02001902 if (cmd_token->terminal == TERMINAL_IPV6)
paul909a2152005-03-14 17:41:45 +00001903 match++;
1904 break;
1905 case ipv6_prefix_match:
1906 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1907 {
1908 if (ret == partly_match)
1909 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001910
paul909a2152005-03-14 17:41:45 +00001911 match++;
1912 }
1913 break;
1914#endif /* HAVE_IPV6 */
1915 case ipv4_match:
David Lamparter10bac802015-05-05 11:10:20 +02001916 if (cmd_token->terminal == TERMINAL_IPV4)
paul718e3742002-12-13 20:15:29 +00001917 match++;
paul909a2152005-03-14 17:41:45 +00001918 break;
1919 case ipv4_prefix_match:
1920 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1921 {
1922 if (ret == partly_match)
1923 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001924
paul909a2152005-03-14 17:41:45 +00001925 match++;
1926 }
1927 break;
1928 case extend_match:
David Lamparter14162932015-05-12 17:18:04 +02001929 if (TERMINAL_RECORD (cmd_token->terminal))
paul718e3742002-12-13 20:15:29 +00001930 match++;
paul909a2152005-03-14 17:41:45 +00001931 break;
1932 case no_match:
1933 default:
1934 break;
1935 }
1936 }
1937 if (!match)
Christian Frankecd40b322013-09-30 12:27:51 +00001938 vector_slot (cmd_vector, i) = NULL;
paul718e3742002-12-13 20:15:29 +00001939 }
1940 return 0;
1941}
1942
1943/* If src matches dst return dst string, otherwise return NULL */
ajs274a4a42004-12-07 15:39:31 +00001944static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001945cmd_entry_function (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001946{
David Lamparter10bac802015-05-05 11:10:20 +02001947 const char *dst = token->cmd;
1948
paul718e3742002-12-13 20:15:29 +00001949 /* Skip variable arguments. */
David Lamparter14162932015-05-12 17:18:04 +02001950 if (TERMINAL_RECORD (token->terminal))
1951 return NULL;
paul718e3742002-12-13 20:15:29 +00001952
1953 /* In case of 'command \t', given src is NULL string. */
1954 if (src == NULL)
1955 return dst;
1956
1957 /* Matched with input string. */
1958 if (strncmp (src, dst, strlen (src)) == 0)
1959 return dst;
1960
1961 return NULL;
1962}
1963
1964/* If src matches dst return dst string, otherwise return NULL */
1965/* This version will return the dst string always if it is
1966 CMD_VARIABLE for '?' key processing */
ajs274a4a42004-12-07 15:39:31 +00001967static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001968cmd_entry_function_desc (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001969{
David Lamparter10bac802015-05-05 11:10:20 +02001970 const char *dst = token->cmd;
paul718e3742002-12-13 20:15:29 +00001971
David Lamparter10bac802015-05-05 11:10:20 +02001972 switch (token->terminal)
paul718e3742002-12-13 20:15:29 +00001973 {
David Lamparter10bac802015-05-05 11:10:20 +02001974 case TERMINAL_VARARG:
1975 return dst;
1976
1977 case TERMINAL_RANGE:
1978 if (cmd_range_match (dst, src))
1979 return dst;
1980 else
1981 return NULL;
1982
1983 case TERMINAL_IPV6:
1984 if (cmd_ipv6_match (src))
1985 return dst;
1986 else
1987 return NULL;
1988
1989 case TERMINAL_IPV6_PREFIX:
1990 if (cmd_ipv6_prefix_match (src))
1991 return dst;
1992 else
1993 return NULL;
1994
1995 case TERMINAL_IPV4:
1996 if (cmd_ipv4_match (src))
1997 return dst;
1998 else
1999 return NULL;
2000
2001 case TERMINAL_IPV4_PREFIX:
2002 if (cmd_ipv4_prefix_match (src))
2003 return dst;
2004 else
2005 return NULL;
2006
2007 /* Optional or variable commands always match on '?' */
2008 case TERMINAL_OPTION:
2009 case TERMINAL_VARIABLE:
2010 return dst;
2011
2012 case TERMINAL_LITERAL:
2013 /* In case of 'command \t', given src is NULL string. */
2014 if (src == NULL)
2015 return dst;
2016
2017 if (strncmp (src, dst, strlen (src)) == 0)
2018 return dst;
2019 else
2020 return NULL;
2021
2022 default:
2023 assert(0);
David Lamparterf1fc3272015-05-13 12:44:50 +02002024 return NULL;
paul718e3742002-12-13 20:15:29 +00002025 }
paul718e3742002-12-13 20:15:29 +00002026}
2027
Christian Frankecd40b322013-09-30 12:27:51 +00002028/**
2029 * Check whether a string is already present in a vector of strings.
2030 * @param v A vector of char*.
2031 * @param str A char*.
2032 * @return 0 if str is already present in the vector, 1 otherwise.
2033 */
ajs274a4a42004-12-07 15:39:31 +00002034static int
hasso8c328f12004-10-05 21:01:23 +00002035cmd_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002036{
hasso8c328f12004-10-05 21:01:23 +00002037 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002038 char *match;
2039
paul55468c82005-03-14 20:19:01 +00002040 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +00002041 if ((match = vector_slot (v, i)) != NULL)
2042 if (strcmp (match, str) == 0)
2043 return 0;
2044 return 1;
2045}
2046
Christian Frankecd40b322013-09-30 12:27:51 +00002047/**
2048 * Check whether a struct cmd_token matching a given string is already
2049 * present in a vector of struct cmd_token.
2050 * @param v A vector of struct cmd_token*.
2051 * @param str A char* which should be searched for.
2052 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2053 * 1 otherwise.
2054 */
ajs274a4a42004-12-07 15:39:31 +00002055static int
hasso8c328f12004-10-05 21:01:23 +00002056desc_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002057{
hasso8c328f12004-10-05 21:01:23 +00002058 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00002059 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002060
paul55468c82005-03-14 20:19:01 +00002061 for (i = 0; i < vector_active (v); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00002062 if ((token = vector_slot (v, i)) != NULL)
2063 if (strcmp (token->cmd, str) == 0)
2064 return 0;
2065 return 1;
paul718e3742002-12-13 20:15:29 +00002066}
2067
ajs274a4a42004-12-07 15:39:31 +00002068static int
paulb92938a2002-12-13 21:20:42 +00002069cmd_try_do_shortcut (enum node_type node, char* first_word) {
2070 if ( first_word != NULL &&
2071 node != AUTH_NODE &&
2072 node != VIEW_NODE &&
2073 node != AUTH_ENABLE_NODE &&
2074 node != ENABLE_NODE &&
Paul Jakma62687ff2008-08-23 14:27:06 +01002075 node != RESTRICTED_NODE &&
paulb92938a2002-12-13 21:20:42 +00002076 0 == strcmp( "do", first_word ) )
2077 return 1;
2078 return 0;
2079}
2080
Christian Frankecd40b322013-09-30 12:27:51 +00002081static void
2082cmd_matches_free(vector *matches)
2083{
2084 unsigned int i;
2085 vector cmd_matches;
2086
2087 for (i = 0; i < vector_active(*matches); i++)
2088 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2089 vector_free(cmd_matches);
2090 vector_free(*matches);
2091 *matches = NULL;
2092}
2093
2094static int
2095cmd_describe_cmp(const void *a, const void *b)
2096{
2097 const struct cmd_token *first = *(struct cmd_token * const *)a;
2098 const struct cmd_token *second = *(struct cmd_token * const *)b;
2099
2100 return strcmp(first->cmd, second->cmd);
2101}
2102
2103static void
2104cmd_describe_sort(vector matchvec)
2105{
2106 qsort(matchvec->index, vector_active(matchvec),
2107 sizeof(void*), cmd_describe_cmp);
2108}
2109
paul718e3742002-12-13 20:15:29 +00002110/* '?' describe command support. */
ajs274a4a42004-12-07 15:39:31 +00002111static vector
paulb92938a2002-12-13 21:20:42 +00002112cmd_describe_command_real (vector vline, struct vty *vty, int *status)
paul718e3742002-12-13 20:15:29 +00002113{
paulb8961472005-03-14 17:35:52 +00002114 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002115 vector cmd_vector;
2116#define INIT_MATCHVEC_SIZE 10
2117 vector matchvec;
2118 struct cmd_element *cmd_element;
paulb8961472005-03-14 17:35:52 +00002119 unsigned int index;
paul54aba542003-08-21 20:28:24 +00002120 int ret;
2121 enum match_type match;
2122 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002123 vector matches = NULL;
2124 vector match_vector;
Donald Sharpf7332802015-07-17 22:36:57 -04002125 uint32_t command_found = 0;
2126 const char *last_word;
paul718e3742002-12-13 20:15:29 +00002127
2128 /* Set index. */
paul55468c82005-03-14 20:19:01 +00002129 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002130 {
2131 *status = CMD_ERR_NO_MATCH;
2132 return NULL;
2133 }
Christian Frankecd40b322013-09-30 12:27:51 +00002134
2135 index = vector_active (vline) - 1;
2136
paul718e3742002-12-13 20:15:29 +00002137 /* Make copy vector of current node's command vector. */
2138 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2139
2140 /* Prepare match vector */
2141 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2142
Christian Frankecd40b322013-09-30 12:27:51 +00002143 /* Filter commands and build a list how they could possibly continue. */
2144 for (i = 0; i <= index; i++)
2145 {
2146 command = vector_slot (vline, i);
paul718e3742002-12-13 20:15:29 +00002147
Christian Frankecd40b322013-09-30 12:27:51 +00002148 if (matches)
2149 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002150
Christian Frankecd40b322013-09-30 12:27:51 +00002151 ret = cmd_vector_filter(cmd_vector,
2152 FILTER_RELAXED,
2153 vline, i,
2154 &match,
2155 &matches);
paul718e3742002-12-13 20:15:29 +00002156
Christian Frankecd40b322013-09-30 12:27:51 +00002157 if (ret != CMD_SUCCESS)
2158 {
2159 vector_free (cmd_vector);
2160 vector_free (matchvec);
2161 cmd_matches_free(&matches);
2162 *status = ret;
2163 return NULL;
2164 }
paul718e3742002-12-13 20:15:29 +00002165
Christian Frankecd40b322013-09-30 12:27:51 +00002166 /* The last match may well be ambigious, so break here */
2167 if (i == index)
2168 break;
paul718e3742002-12-13 20:15:29 +00002169
Christian Frankecd40b322013-09-30 12:27:51 +00002170 if (match == vararg_match)
2171 {
2172 /* We found a vararg match - so we can throw out the current matches here
2173 * and don't need to continue checking the command input */
2174 unsigned int j, k;
2175
2176 for (j = 0; j < vector_active (matches); j++)
2177 if ((match_vector = vector_slot (matches, j)) != NULL)
2178 for (k = 0; k < vector_active (match_vector); k++)
2179 {
2180 struct cmd_token *token = vector_slot (match_vector, k);
2181 vector_set (matchvec, token);
2182 }
2183
2184 *status = CMD_SUCCESS;
2185 vector_set(matchvec, &token_cr);
2186 vector_free (cmd_vector);
2187 cmd_matches_free(&matches);
2188 cmd_describe_sort(matchvec);
2189 return matchvec;
2190 }
2191
2192 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2193 if (ret == 1)
2194 {
2195 vector_free (cmd_vector);
2196 vector_free (matchvec);
2197 cmd_matches_free(&matches);
2198 *status = CMD_ERR_AMBIGUOUS;
2199 return NULL;
2200 }
2201 else if (ret == 2)
2202 {
2203 vector_free (cmd_vector);
2204 vector_free (matchvec);
2205 cmd_matches_free(&matches);
2206 *status = CMD_ERR_NO_MATCH;
2207 return NULL;
2208 }
2209 }
paul54aba542003-08-21 20:28:24 +00002210
paul718e3742002-12-13 20:15:29 +00002211 /* Make description vector. */
Christian Frankecd40b322013-09-30 12:27:51 +00002212 for (i = 0; i < vector_active (matches); i++)
Donald Sharpf7332802015-07-17 22:36:57 -04002213 {
2214 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
2215 {
2216 unsigned int j;
2217 vector vline_trimmed;
paul718e3742002-12-13 20:15:29 +00002218
Donald Sharpf7332802015-07-17 22:36:57 -04002219 command_found++;
2220 last_word = vector_slot(vline, vector_active(vline) - 1);
2221 if (last_word == NULL || !strlen(last_word))
2222 {
2223 vline_trimmed = vector_copy(vline);
2224 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
paul718e3742002-12-13 20:15:29 +00002225
Donald Sharpf7332802015-07-17 22:36:57 -04002226 if (cmd_is_complete(cmd_element, vline_trimmed)
2227 && desc_unique_string(matchvec, command_cr))
2228 {
2229 if (match != vararg_match)
2230 vector_set(matchvec, &token_cr);
2231 }
Chris Caputo228da422009-07-18 05:44:03 +00002232
Donald Sharpf7332802015-07-17 22:36:57 -04002233 vector_free(vline_trimmed);
2234 }
Christian Frankecd40b322013-09-30 12:27:51 +00002235
Donald Sharpf7332802015-07-17 22:36:57 -04002236 match_vector = vector_slot (matches, i);
2237 if (match_vector)
2238 {
2239 for (j = 0; j < vector_active(match_vector); j++)
2240 {
2241 struct cmd_token *token = vector_slot(match_vector, j);
2242 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002243
Donald Sharpf7332802015-07-17 22:36:57 -04002244 string = cmd_entry_function_desc(command, token);
2245 if (string && desc_unique_string(matchvec, string))
2246 vector_set(matchvec, token);
2247 }
2248 }
2249 }
2250 }
2251
2252 /*
2253 * We can get into this situation when the command is complete
2254 * but the last part of the command is an optional piece of
2255 * the cli.
2256 */
2257 last_word = vector_slot(vline, vector_active(vline) - 1);
2258 if (command_found == 0 && (last_word == NULL || !strlen(last_word)))
2259 vector_set(matchvec, &token_cr);
2260
paul718e3742002-12-13 20:15:29 +00002261 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002262 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002263
2264 if (vector_slot (matchvec, 0) == NULL)
2265 {
2266 vector_free (matchvec);
paul909a2152005-03-14 17:41:45 +00002267 *status = CMD_ERR_NO_MATCH;
Paul Jakma5fc60512006-05-12 23:24:09 +00002268 return NULL;
paul718e3742002-12-13 20:15:29 +00002269 }
paul718e3742002-12-13 20:15:29 +00002270
Paul Jakma5fc60512006-05-12 23:24:09 +00002271 *status = CMD_SUCCESS;
Christian Frankecd40b322013-09-30 12:27:51 +00002272 cmd_describe_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002273 return matchvec;
2274}
2275
paulb92938a2002-12-13 21:20:42 +00002276vector
2277cmd_describe_command (vector vline, struct vty *vty, int *status)
2278{
2279 vector ret;
2280
2281 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2282 {
2283 enum node_type onode;
2284 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002285 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002286
2287 onode = vty->node;
2288 vty->node = ENABLE_NODE;
2289 /* We can try it on enable node, cos' the vty is authenticated */
2290
2291 shifted_vline = vector_init (vector_count(vline));
2292 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002293 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002294 {
2295 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2296 }
2297
2298 ret = cmd_describe_command_real (shifted_vline, vty, status);
2299
2300 vector_free(shifted_vline);
2301 vty->node = onode;
2302 return ret;
2303 }
2304
2305
2306 return cmd_describe_command_real (vline, vty, status);
2307}
2308
2309
paul718e3742002-12-13 20:15:29 +00002310/* Check LCD of matched command. */
ajs274a4a42004-12-07 15:39:31 +00002311static int
paul718e3742002-12-13 20:15:29 +00002312cmd_lcd (char **matched)
2313{
2314 int i;
2315 int j;
2316 int lcd = -1;
2317 char *s1, *s2;
2318 char c1, c2;
2319
2320 if (matched[0] == NULL || matched[1] == NULL)
2321 return 0;
2322
2323 for (i = 1; matched[i] != NULL; i++)
2324 {
2325 s1 = matched[i - 1];
2326 s2 = matched[i];
2327
2328 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2329 if (c1 != c2)
2330 break;
2331
2332 if (lcd < 0)
2333 lcd = j;
2334 else
2335 {
2336 if (lcd > j)
2337 lcd = j;
2338 }
2339 }
2340 return lcd;
2341}
2342
Christian Frankecd40b322013-09-30 12:27:51 +00002343static int
2344cmd_complete_cmp(const void *a, const void *b)
2345{
2346 const char *first = *(char * const *)a;
2347 const char *second = *(char * const *)b;
2348
2349 if (!first)
2350 {
2351 if (!second)
2352 return 0;
2353 return 1;
2354 }
2355 if (!second)
2356 return -1;
2357
2358 return strcmp(first, second);
2359}
2360
2361static void
2362cmd_complete_sort(vector matchvec)
2363{
2364 qsort(matchvec->index, vector_active(matchvec),
2365 sizeof(void*), cmd_complete_cmp);
2366}
2367
paul718e3742002-12-13 20:15:29 +00002368/* Command line completion support. */
ajs274a4a42004-12-07 15:39:31 +00002369static char **
Lou Berger67290032016-01-12 13:41:46 -05002370cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib)
paul718e3742002-12-13 20:15:29 +00002371{
paulb8961472005-03-14 17:35:52 +00002372 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002373 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2374#define INIT_MATCHVEC_SIZE 10
2375 vector matchvec;
paulb8961472005-03-14 17:35:52 +00002376 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002377 char **match_str;
Christian Frankecd40b322013-09-30 12:27:51 +00002378 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002379 char *command;
2380 int lcd;
Christian Frankecd40b322013-09-30 12:27:51 +00002381 vector matches = NULL;
2382 vector match_vector;
paul718e3742002-12-13 20:15:29 +00002383
paul55468c82005-03-14 20:19:01 +00002384 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002385 {
Paul Jakmad2519962006-05-12 23:19:37 +00002386 vector_free (cmd_vector);
paulb8961472005-03-14 17:35:52 +00002387 *status = CMD_ERR_NO_MATCH;
2388 return NULL;
2389 }
2390 else
paul55468c82005-03-14 20:19:01 +00002391 index = vector_active (vline) - 1;
paulb8961472005-03-14 17:35:52 +00002392
Christian Frankecd40b322013-09-30 12:27:51 +00002393 /* First, filter by command string */
2394 for (i = 0; i <= index; i++)
2395 {
2396 command = vector_slot (vline, i);
2397 enum match_type match;
2398 int ret;
paul718e3742002-12-13 20:15:29 +00002399
Christian Frankecd40b322013-09-30 12:27:51 +00002400 if (matches)
2401 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002402
Christian Frankecd40b322013-09-30 12:27:51 +00002403 /* First try completion match, if there is exactly match return 1 */
2404 ret = cmd_vector_filter(cmd_vector,
2405 FILTER_RELAXED,
2406 vline, i,
2407 &match,
2408 &matches);
2409
2410 if (ret != CMD_SUCCESS)
2411 {
2412 vector_free(cmd_vector);
2413 cmd_matches_free(&matches);
2414 *status = ret;
2415 return NULL;
2416 }
2417
2418 /* Break here - the completion mustn't be checked to be non-ambiguous */
2419 if (i == index)
2420 break;
2421
2422 /* If there is exact match then filter ambiguous match else check
2423 ambiguousness. */
2424 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2425 if (ret == 1)
2426 {
2427 vector_free (cmd_vector);
2428 cmd_matches_free(&matches);
2429 *status = CMD_ERR_AMBIGUOUS;
2430 return NULL;
2431 }
2432 /*
paul909a2152005-03-14 17:41:45 +00002433 else if (ret == 2)
2434 {
2435 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002436 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002437 *status = CMD_ERR_NO_MATCH;
2438 return NULL;
2439 }
2440 */
Christian Frankecd40b322013-09-30 12:27:51 +00002441 }
paul909a2152005-03-14 17:41:45 +00002442
paul718e3742002-12-13 20:15:29 +00002443 /* Prepare match vector. */
2444 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2445
Christian Frankecd40b322013-09-30 12:27:51 +00002446 /* Build the possible list of continuations into a list of completions */
2447 for (i = 0; i < vector_active (matches); i++)
2448 if ((match_vector = vector_slot (matches, i)))
paul718e3742002-12-13 20:15:29 +00002449 {
hasso8c328f12004-10-05 21:01:23 +00002450 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002451 unsigned int j;
paul909a2152005-03-14 17:41:45 +00002452
Christian Frankecd40b322013-09-30 12:27:51 +00002453 for (j = 0; j < vector_active (match_vector); j++)
2454 if ((token = vector_slot (match_vector, j)))
Lou Berger67290032016-01-12 13:41:46 -05002455 {
2456 string = cmd_entry_function (vector_slot (vline, index), token);
2457 if (string && cmd_unique_string (matchvec, string))
2458 vector_set (matchvec, (islib != 0 ?
2459 XSTRDUP (MTYPE_TMP, string) :
2460 strdup (string) /* rl freed */));
2461 }
paul718e3742002-12-13 20:15:29 +00002462 }
2463
2464 /* We don't need cmd_vector any more. */
2465 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002466 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002467
2468 /* No matched command */
2469 if (vector_slot (matchvec, 0) == NULL)
2470 {
2471 vector_free (matchvec);
2472
2473 /* In case of 'command \t' pattern. Do you need '?' command at
2474 the end of the line. */
2475 if (vector_slot (vline, index) == '\0')
2476 *status = CMD_ERR_NOTHING_TODO;
2477 else
2478 *status = CMD_ERR_NO_MATCH;
2479 return NULL;
2480 }
2481
2482 /* Only one matched */
2483 if (vector_slot (matchvec, 1) == NULL)
2484 {
2485 match_str = (char **) matchvec->index;
2486 vector_only_wrapper_free (matchvec);
2487 *status = CMD_COMPLETE_FULL_MATCH;
2488 return match_str;
2489 }
2490 /* Make it sure last element is NULL. */
2491 vector_set (matchvec, NULL);
2492
2493 /* Check LCD of matched strings. */
2494 if (vector_slot (vline, index) != NULL)
2495 {
2496 lcd = cmd_lcd ((char **) matchvec->index);
2497
2498 if (lcd)
2499 {
2500 int len = strlen (vector_slot (vline, index));
paul909a2152005-03-14 17:41:45 +00002501
paul718e3742002-12-13 20:15:29 +00002502 if (len < lcd)
2503 {
2504 char *lcdstr;
paul909a2152005-03-14 17:41:45 +00002505
Lou Berger67290032016-01-12 13:41:46 -05002506 lcdstr = (islib != 0 ?
2507 XMALLOC (MTYPE_TMP, lcd + 1) :
2508 malloc(lcd + 1));
paul718e3742002-12-13 20:15:29 +00002509 memcpy (lcdstr, matchvec->index[0], lcd);
2510 lcdstr[lcd] = '\0';
2511
2512 /* match_str = (char **) &lcdstr; */
2513
2514 /* Free matchvec. */
paul55468c82005-03-14 20:19:01 +00002515 for (i = 0; i < vector_active (matchvec); i++)
Lou Berger67290032016-01-12 13:41:46 -05002516 {
2517 if (vector_slot (matchvec, i))
2518 {
2519 if (islib != 0)
2520 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
2521 else
2522 free (vector_slot (matchvec, i));
2523 }
2524 }
paul718e3742002-12-13 20:15:29 +00002525 vector_free (matchvec);
2526
paul909a2152005-03-14 17:41:45 +00002527 /* Make new matchvec. */
paul718e3742002-12-13 20:15:29 +00002528 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2529 vector_set (matchvec, lcdstr);
2530 match_str = (char **) matchvec->index;
2531 vector_only_wrapper_free (matchvec);
2532
2533 *status = CMD_COMPLETE_MATCH;
2534 return match_str;
2535 }
2536 }
2537 }
2538
2539 match_str = (char **) matchvec->index;
Christian Frankecd40b322013-09-30 12:27:51 +00002540 cmd_complete_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002541 vector_only_wrapper_free (matchvec);
2542 *status = CMD_COMPLETE_LIST_MATCH;
2543 return match_str;
2544}
2545
paulb92938a2002-12-13 21:20:42 +00002546char **
Lou Berger67290032016-01-12 13:41:46 -05002547cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)
paulb92938a2002-12-13 21:20:42 +00002548{
2549 char **ret;
2550
2551 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2552 {
2553 enum node_type onode;
2554 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002555 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002556
2557 onode = vty->node;
2558 vty->node = ENABLE_NODE;
2559 /* We can try it on enable node, cos' the vty is authenticated */
2560
2561 shifted_vline = vector_init (vector_count(vline));
2562 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002563 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002564 {
2565 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2566 }
2567
Lou Berger67290032016-01-12 13:41:46 -05002568 ret = cmd_complete_command_real (shifted_vline, vty, status, islib);
paulb92938a2002-12-13 21:20:42 +00002569
2570 vector_free(shifted_vline);
2571 vty->node = onode;
2572 return ret;
2573 }
2574
Lou Berger67290032016-01-12 13:41:46 -05002575 return cmd_complete_command_real (vline, vty, status, islib);
2576}
paulb92938a2002-12-13 21:20:42 +00002577
Lou Berger67290032016-01-12 13:41:46 -05002578char **
2579cmd_complete_command (vector vline, struct vty *vty, int *status)
2580{
2581 return cmd_complete_command_lib (vline, vty, status, 0);
paulb92938a2002-12-13 21:20:42 +00002582}
2583
2584/* return parent node */
2585/* MUST eventually converge on CONFIG_NODE */
hasso13bfca72005-01-23 21:42:25 +00002586enum node_type
ajs274a4a42004-12-07 15:39:31 +00002587node_parent ( enum node_type node )
paulb92938a2002-12-13 21:20:42 +00002588{
2589 enum node_type ret;
2590
paul9ab68122003-01-18 01:16:20 +00002591 assert (node > CONFIG_NODE);
2592
2593 switch (node)
2594 {
2595 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002596 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002597 case BGP_ENCAP_NODE:
2598 case BGP_ENCAPV6_NODE:
paul9ab68122003-01-18 01:16:20 +00002599 case BGP_IPV4_NODE:
2600 case BGP_IPV4M_NODE:
2601 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002602 case BGP_IPV6M_NODE:
paul9ab68122003-01-18 01:16:20 +00002603 ret = BGP_NODE;
2604 break;
2605 case KEYCHAIN_KEY_NODE:
2606 ret = KEYCHAIN_NODE;
2607 break;
2608 default:
2609 ret = CONFIG_NODE;
paulb92938a2002-12-13 21:20:42 +00002610 }
2611
2612 return ret;
2613}
2614
paul718e3742002-12-13 20:15:29 +00002615/* Execute command by argument vline vector. */
ajs274a4a42004-12-07 15:39:31 +00002616static int
Christian Frankecd40b322013-09-30 12:27:51 +00002617cmd_execute_command_real (vector vline,
2618 enum filter_type filter,
2619 struct vty *vty,
paulb8961472005-03-14 17:35:52 +00002620 struct cmd_element **cmd)
paul718e3742002-12-13 20:15:29 +00002621{
hasso8c328f12004-10-05 21:01:23 +00002622 unsigned int i;
2623 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002624 vector cmd_vector;
2625 struct cmd_element *cmd_element;
2626 struct cmd_element *matched_element;
2627 unsigned int matched_count, incomplete_count;
2628 int argc;
paul9035efa2004-10-10 11:56:56 +00002629 const char *argv[CMD_ARGC_MAX];
paul718e3742002-12-13 20:15:29 +00002630 enum match_type match = 0;
paul718e3742002-12-13 20:15:29 +00002631 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002632 int ret;
2633 vector matches;
paul718e3742002-12-13 20:15:29 +00002634
2635 /* Make copy of command elements. */
2636 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2637
paul55468c82005-03-14 20:19:01 +00002638 for (index = 0; index < vector_active (vline); index++)
Christian Frankecd40b322013-09-30 12:27:51 +00002639 {
2640 command = vector_slot (vline, index);
2641 ret = cmd_vector_filter(cmd_vector,
2642 filter,
2643 vline, index,
2644 &match,
2645 &matches);
paul718e3742002-12-13 20:15:29 +00002646
Christian Frankecd40b322013-09-30 12:27:51 +00002647 if (ret != CMD_SUCCESS)
2648 {
2649 cmd_matches_free(&matches);
2650 return ret;
2651 }
paul718e3742002-12-13 20:15:29 +00002652
Christian Frankecd40b322013-09-30 12:27:51 +00002653 if (match == vararg_match)
2654 {
2655 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002656 break;
Christian Frankecd40b322013-09-30 12:27:51 +00002657 }
paul718e3742002-12-13 20:15:29 +00002658
Christian Frankecd40b322013-09-30 12:27:51 +00002659 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2660 cmd_matches_free(&matches);
2661
2662 if (ret == 1)
2663 {
2664 vector_free(cmd_vector);
2665 return CMD_ERR_AMBIGUOUS;
2666 }
2667 else if (ret == 2)
2668 {
2669 vector_free(cmd_vector);
2670 return CMD_ERR_NO_MATCH;
2671 }
2672 }
paul718e3742002-12-13 20:15:29 +00002673
2674 /* Check matched count. */
2675 matched_element = NULL;
2676 matched_count = 0;
2677 incomplete_count = 0;
2678
paul55468c82005-03-14 20:19:01 +00002679 for (i = 0; i < vector_active (cmd_vector); i++)
paulb8961472005-03-14 17:35:52 +00002680 if ((cmd_element = vector_slot (cmd_vector, i)))
paul718e3742002-12-13 20:15:29 +00002681 {
Christian Frankecd40b322013-09-30 12:27:51 +00002682 if (cmd_is_complete(cmd_element, vline))
paul718e3742002-12-13 20:15:29 +00002683 {
2684 matched_element = cmd_element;
paul718e3742002-12-13 20:15:29 +00002685 matched_count++;
2686 }
2687 else
2688 {
2689 incomplete_count++;
2690 }
2691 }
paul909a2152005-03-14 17:41:45 +00002692
paul718e3742002-12-13 20:15:29 +00002693 /* Finish of using cmd_vector. */
2694 vector_free (cmd_vector);
2695
paul909a2152005-03-14 17:41:45 +00002696 /* To execute command, matched_count must be 1. */
2697 if (matched_count == 0)
paul718e3742002-12-13 20:15:29 +00002698 {
2699 if (incomplete_count)
2700 return CMD_ERR_INCOMPLETE;
2701 else
2702 return CMD_ERR_NO_MATCH;
2703 }
2704
paul909a2152005-03-14 17:41:45 +00002705 if (matched_count > 1)
paul718e3742002-12-13 20:15:29 +00002706 return CMD_ERR_AMBIGUOUS;
2707
Christian Frankecd40b322013-09-30 12:27:51 +00002708 ret = cmd_parse(matched_element, vline, &argc, argv);
2709 if (ret != CMD_SUCCESS)
2710 return ret;
paul718e3742002-12-13 20:15:29 +00002711
2712 /* For vtysh execution. */
2713 if (cmd)
2714 *cmd = matched_element;
2715
2716 if (matched_element->daemon)
2717 return CMD_SUCCESS_DAEMON;
2718
2719 /* Execute matched command. */
2720 return (*matched_element->func) (matched_element, vty, argc, argv);
2721}
2722
Christian Frankecd40b322013-09-30 12:27:51 +00002723/**
2724 * Execute a given command, handling things like "do ..." and checking
2725 * whether the given command might apply at a parent node if doesn't
2726 * apply for the current node.
2727 *
2728 * @param vline Command line input, vector of char* where each element is
2729 * one input token.
2730 * @param vty The vty context in which the command should be executed.
2731 * @param cmd Pointer where the struct cmd_element of the matched command
2732 * will be stored, if any. May be set to NULL if this info is
2733 * not needed.
2734 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2735 * @return The status of the command that has been executed or an error code
2736 * as to why no command could be executed.
2737 */
paulb92938a2002-12-13 21:20:42 +00002738int
hasso87d683b2005-01-16 23:31:54 +00002739cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2740 int vtysh) {
paul9ab68122003-01-18 01:16:20 +00002741 int ret, saved_ret, tried = 0;
2742 enum node_type onode, try_node;
2743
2744 onode = try_node = vty->node;
paulb92938a2002-12-13 21:20:42 +00002745
2746 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2747 {
2748 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002749 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002750
2751 vty->node = ENABLE_NODE;
2752 /* We can try it on enable node, cos' the vty is authenticated */
2753
2754 shifted_vline = vector_init (vector_count(vline));
2755 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002756 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002757 {
2758 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2759 }
2760
Christian Frankecd40b322013-09-30 12:27:51 +00002761 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002762
2763 vector_free(shifted_vline);
2764 vty->node = onode;
2765 return ret;
2766 }
2767
2768
Christian Frankecd40b322013-09-30 12:27:51 +00002769 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002770
hasso87d683b2005-01-16 23:31:54 +00002771 if (vtysh)
2772 return saved_ret;
2773
paulb92938a2002-12-13 21:20:42 +00002774 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
paul9ab68122003-01-18 01:16:20 +00002775 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
paulb92938a2002-12-13 21:20:42 +00002776 && vty->node > CONFIG_NODE )
2777 {
paul9ab68122003-01-18 01:16:20 +00002778 try_node = node_parent(try_node);
2779 vty->node = try_node;
Christian Frankecd40b322013-09-30 12:27:51 +00002780 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paul9ab68122003-01-18 01:16:20 +00002781 tried = 1;
2782 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
paulb92938a2002-12-13 21:20:42 +00002783 {
paul9ab68122003-01-18 01:16:20 +00002784 /* succesfull command, leave the node as is */
paulb92938a2002-12-13 21:20:42 +00002785 return ret;
2786 }
paulb92938a2002-12-13 21:20:42 +00002787 }
paul9ab68122003-01-18 01:16:20 +00002788 /* no command succeeded, reset the vty to the original node and
2789 return the error for this node */
2790 if ( tried )
2791 vty->node = onode;
2792 return saved_ret;
pauleda031f2003-01-18 00:39:19 +00002793}
2794
Christian Frankecd40b322013-09-30 12:27:51 +00002795/**
2796 * Execute a given command, matching it strictly against the current node.
2797 * This mode is used when reading config files.
2798 *
2799 * @param vline Command line input, vector of char* where each element is
2800 * one input token.
2801 * @param vty The vty context in which the command should be executed.
2802 * @param cmd Pointer where the struct cmd_element* of the matched command
2803 * will be stored, if any. May be set to NULL if this info is
2804 * not needed.
2805 * @return The status of the command that has been executed or an error code
2806 * as to why no command could be executed.
2807 */
paul718e3742002-12-13 20:15:29 +00002808int
paul909a2152005-03-14 17:41:45 +00002809cmd_execute_command_strict (vector vline, struct vty *vty,
paul718e3742002-12-13 20:15:29 +00002810 struct cmd_element **cmd)
2811{
Christian Frankecd40b322013-09-30 12:27:51 +00002812 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
paul718e3742002-12-13 20:15:29 +00002813}
2814
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002815/**
2816 * Parse one line of config, walking up the parse tree attempting to find a match
2817 *
2818 * @param vty The vty context in which the command should be executed.
2819 * @param cmd Pointer where the struct cmd_element* of the match command
2820 * will be stored, if any. May be set to NULL if this info is
2821 * not needed.
2822 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
2823 * or not.
2824 * @return The status of the command that has been executed or an error code
2825 * as to why no command could be executed.
2826 */
2827int
2828command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
2829{
2830 vector vline;
2831 int saved_node;
2832 int ret;
2833
2834 vline = cmd_make_strvec (vty->buf);
2835
2836 /* In case of comment line */
2837 if (vline == NULL)
2838 return CMD_SUCCESS;
2839
2840 /* Execute configuration command : this is strict match */
2841 ret = cmd_execute_command_strict (vline, vty, cmd);
2842
2843 saved_node = vty->node;
2844
2845 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2846 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2847 ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) {
2848 vty->node = node_parent(vty->node);
2849 ret = cmd_execute_command_strict (vline, vty, NULL);
2850 }
2851
2852 // If climbing the tree did not work then ignore the command and
2853 // stay at the same node
2854 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2855 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2856 ret != CMD_ERR_NOTHING_TODO)
2857 {
2858 vty->node = saved_node;
2859 }
2860
2861 cmd_free_strvec (vline);
2862
2863 return ret;
2864}
2865
paul718e3742002-12-13 20:15:29 +00002866/* Configration make from file. */
2867int
Steve Hillea555002009-07-28 16:36:14 -04002868config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
paul718e3742002-12-13 20:15:29 +00002869{
2870 int ret;
Steve Hillea555002009-07-28 16:36:14 -04002871 *line_num = 0;
paul718e3742002-12-13 20:15:29 +00002872
2873 while (fgets (vty->buf, VTY_BUFSIZ, fp))
2874 {
Steve Hillea555002009-07-28 16:36:14 -04002875 ++(*line_num);
paul718e3742002-12-13 20:15:29 +00002876
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002877 ret = command_config_read_one_line (vty, NULL, 0);
paul718e3742002-12-13 20:15:29 +00002878
hassoddd85ed2004-10-13 08:18:07 +00002879 if (ret != CMD_SUCCESS && ret != CMD_WARNING
2880 && ret != CMD_ERR_NOTHING_TODO)
paul718e3742002-12-13 20:15:29 +00002881 return ret;
2882 }
2883 return CMD_SUCCESS;
2884}
2885
2886/* Configration from terminal */
2887DEFUN (config_terminal,
2888 config_terminal_cmd,
2889 "configure terminal",
2890 "Configuration from vty interface\n"
2891 "Configuration terminal\n")
2892{
2893 if (vty_config_lock (vty))
2894 vty->node = CONFIG_NODE;
2895 else
2896 {
2897 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2898 return CMD_WARNING;
2899 }
2900 return CMD_SUCCESS;
2901}
2902
2903/* Enable command */
2904DEFUN (enable,
2905 config_enable_cmd,
2906 "enable",
2907 "Turn on privileged mode command\n")
2908{
2909 /* If enable password is NULL, change to ENABLE_NODE */
2910 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2911 vty->type == VTY_SHELL_SERV)
2912 vty->node = ENABLE_NODE;
2913 else
2914 vty->node = AUTH_ENABLE_NODE;
2915
2916 return CMD_SUCCESS;
2917}
2918
2919/* Disable command */
2920DEFUN (disable,
2921 config_disable_cmd,
2922 "disable",
2923 "Turn off privileged mode command\n")
2924{
2925 if (vty->node == ENABLE_NODE)
2926 vty->node = VIEW_NODE;
2927 return CMD_SUCCESS;
2928}
2929
2930/* Down vty node level. */
2931DEFUN (config_exit,
2932 config_exit_cmd,
2933 "exit",
2934 "Exit current mode and down to previous mode\n")
2935{
2936 switch (vty->node)
2937 {
2938 case VIEW_NODE:
2939 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002940 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002941 if (vty_shell (vty))
2942 exit (0);
2943 else
2944 vty->status = VTY_CLOSE;
2945 break;
2946 case CONFIG_NODE:
2947 vty->node = ENABLE_NODE;
2948 vty_config_unlock (vty);
2949 break;
2950 case INTERFACE_NODE:
2951 case ZEBRA_NODE:
2952 case BGP_NODE:
2953 case RIP_NODE:
2954 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002955 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002956 case OSPF_NODE:
2957 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002958 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002959 case KEYCHAIN_NODE:
2960 case MASC_NODE:
2961 case RMAP_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002962 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002963 case VTY_NODE:
2964 vty->node = CONFIG_NODE;
2965 break;
paul718e3742002-12-13 20:15:29 +00002966 case BGP_IPV4_NODE:
2967 case BGP_IPV4M_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002968 case BGP_VPNV4_NODE:
2969 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002970 case BGP_ENCAP_NODE:
2971 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00002972 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002973 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00002974 vty->node = BGP_NODE;
2975 break;
2976 case KEYCHAIN_KEY_NODE:
2977 vty->node = KEYCHAIN_NODE;
2978 break;
2979 default:
2980 break;
2981 }
2982 return CMD_SUCCESS;
2983}
2984
2985/* quit is alias of exit. */
2986ALIAS (config_exit,
2987 config_quit_cmd,
2988 "quit",
2989 "Exit current mode and down to previous mode\n")
2990
2991/* End of configuration. */
2992DEFUN (config_end,
2993 config_end_cmd,
2994 "end",
2995 "End current mode and change to enable mode.")
2996{
2997 switch (vty->node)
2998 {
2999 case VIEW_NODE:
3000 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01003001 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00003002 /* Nothing to do. */
3003 break;
3004 case CONFIG_NODE:
3005 case INTERFACE_NODE:
3006 case ZEBRA_NODE:
3007 case RIP_NODE:
3008 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01003009 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00003010 case BGP_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05003011 case BGP_ENCAP_NODE:
3012 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00003013 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05003014 case BGP_VPNV6_NODE:
paul718e3742002-12-13 20:15:29 +00003015 case BGP_IPV4_NODE:
3016 case BGP_IPV4M_NODE:
3017 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00003018 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00003019 case RMAP_NODE:
3020 case OSPF_NODE:
3021 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00003022 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00003023 case KEYCHAIN_NODE:
3024 case KEYCHAIN_KEY_NODE:
3025 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02003026 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00003027 case VTY_NODE:
3028 vty_config_unlock (vty);
3029 vty->node = ENABLE_NODE;
3030 break;
3031 default:
3032 break;
3033 }
3034 return CMD_SUCCESS;
3035}
3036
3037/* Show version. */
3038DEFUN (show_version,
3039 show_version_cmd,
3040 "show version",
3041 SHOW_STR
3042 "Displays zebra version\n")
3043{
hasso12f6ea22005-03-07 08:35:39 +00003044 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
3045 VTY_NEWLINE);
David Lamparter0be793e2012-11-27 01:34:56 +00003046 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
David Lamparter7abd8752014-11-22 10:43:29 -08003047 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
3048 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00003049
3050 return CMD_SUCCESS;
3051}
3052
3053/* Help display function for all node. */
3054DEFUN (config_help,
3055 config_help_cmd,
3056 "help",
3057 "Description of the interactive help system\n")
3058{
3059 vty_out (vty,
hasso6590f2c2004-10-19 20:40:08 +00003060 "Quagga VTY provides advanced help feature. When you need help,%s\
paul718e3742002-12-13 20:15:29 +00003061anytime at the command line please press '?'.%s\
3062%s\
3063If nothing matches, the help list will be empty and you must backup%s\
3064 until entering a '?' shows the available options.%s\
3065Two styles of help are provided:%s\
30661. Full help is available when you are ready to enter a%s\
3067command argument (e.g. 'show ?') and describes each possible%s\
3068argument.%s\
30692. Partial help is provided when an abbreviated argument is entered%s\
3070 and you want to know what arguments match the input%s\
3071 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3072 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3073 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
3074 return CMD_SUCCESS;
3075}
3076
3077/* Help display function for all node. */
3078DEFUN (config_list,
3079 config_list_cmd,
3080 "list",
3081 "Print command list\n")
3082{
hasso8c328f12004-10-05 21:01:23 +00003083 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003084 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
3085 struct cmd_element *cmd;
3086
paul55468c82005-03-14 20:19:01 +00003087 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
paul4275b1d2005-03-09 13:42:23 +00003088 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
3089 && !(cmd->attr == CMD_ATTR_DEPRECATED
3090 || cmd->attr == CMD_ATTR_HIDDEN))
paul718e3742002-12-13 20:15:29 +00003091 vty_out (vty, " %s%s", cmd->string,
3092 VTY_NEWLINE);
3093 return CMD_SUCCESS;
3094}
3095
3096/* Write current configuration into file. */
3097DEFUN (config_write_file,
3098 config_write_file_cmd,
3099 "write file",
3100 "Write running configuration to memory, network, or terminal\n"
3101 "Write to configuration file\n")
3102{
hasso8c328f12004-10-05 21:01:23 +00003103 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003104 int fd;
3105 struct cmd_node *node;
3106 char *config_file;
3107 char *config_file_tmp = NULL;
3108 char *config_file_sav = NULL;
paul05865c92005-10-26 05:49:54 +00003109 int ret = CMD_WARNING;
paul718e3742002-12-13 20:15:29 +00003110 struct vty *file_vty;
3111
3112 /* Check and see if we are operating under vtysh configuration */
3113 if (host.config == NULL)
3114 {
3115 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3116 VTY_NEWLINE);
3117 return CMD_WARNING;
3118 }
3119
3120 /* Get filename. */
3121 config_file = host.config;
3122
paul05865c92005-10-26 05:49:54 +00003123 config_file_sav =
3124 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
paul718e3742002-12-13 20:15:29 +00003125 strcpy (config_file_sav, config_file);
3126 strcat (config_file_sav, CONF_BACKUP_EXT);
3127
3128
paul05865c92005-10-26 05:49:54 +00003129 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
paul718e3742002-12-13 20:15:29 +00003130 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3131
3132 /* Open file to configuration write. */
3133 fd = mkstemp (config_file_tmp);
3134 if (fd < 0)
3135 {
3136 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3137 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003138 goto finished;
paul718e3742002-12-13 20:15:29 +00003139 }
3140
3141 /* Make vty for configuration file. */
3142 file_vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02003143 file_vty->wfd = fd;
paul718e3742002-12-13 20:15:29 +00003144 file_vty->type = VTY_FILE;
3145
3146 /* Config file header print. */
3147 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3148 vty_time_print (file_vty, 1);
3149 vty_out (file_vty, "!\n");
3150
paul55468c82005-03-14 20:19:01 +00003151 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003152 if ((node = vector_slot (cmdvec, i)) && node->func)
3153 {
3154 if ((*node->func) (file_vty))
3155 vty_out (file_vty, "!\n");
3156 }
3157 vty_close (file_vty);
3158
3159 if (unlink (config_file_sav) != 0)
3160 if (errno != ENOENT)
3161 {
3162 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3163 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003164 goto finished;
paul718e3742002-12-13 20:15:29 +00003165 }
3166 if (link (config_file, config_file_sav) != 0)
3167 {
3168 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3169 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003170 goto finished;
paul718e3742002-12-13 20:15:29 +00003171 }
3172 sync ();
3173 if (unlink (config_file) != 0)
3174 {
3175 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3176 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003177 goto finished;
paul718e3742002-12-13 20:15:29 +00003178 }
3179 if (link (config_file_tmp, config_file) != 0)
3180 {
3181 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3182 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003183 goto finished;
paul718e3742002-12-13 20:15:29 +00003184 }
paul718e3742002-12-13 20:15:29 +00003185 sync ();
3186
gdtaa593d52003-12-22 20:15:53 +00003187 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3188 {
3189 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
ajs6099b3b2004-11-20 02:06:59 +00003190 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003191 goto finished;
gdtaa593d52003-12-22 20:15:53 +00003192 }
3193
paul718e3742002-12-13 20:15:29 +00003194 vty_out (vty, "Configuration saved to %s%s", config_file,
3195 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003196 ret = CMD_SUCCESS;
3197
3198finished:
3199 unlink (config_file_tmp);
3200 XFREE (MTYPE_TMP, config_file_tmp);
3201 XFREE (MTYPE_TMP, config_file_sav);
3202 return ret;
paul718e3742002-12-13 20:15:29 +00003203}
3204
3205ALIAS (config_write_file,
3206 config_write_cmd,
3207 "write",
3208 "Write running configuration to memory, network, or terminal\n")
3209
3210ALIAS (config_write_file,
3211 config_write_memory_cmd,
3212 "write memory",
3213 "Write running configuration to memory, network, or terminal\n"
3214 "Write configuration to the file (same as write file)\n")
3215
3216ALIAS (config_write_file,
3217 copy_runningconfig_startupconfig_cmd,
3218 "copy running-config startup-config",
3219 "Copy configuration\n"
3220 "Copy running config to... \n"
3221 "Copy running config to startup config (same as write file)\n")
3222
3223/* Write current configuration into the terminal. */
3224DEFUN (config_write_terminal,
3225 config_write_terminal_cmd,
3226 "write terminal",
3227 "Write running configuration to memory, network, or terminal\n"
3228 "Write to terminal\n")
3229{
hasso8c328f12004-10-05 21:01:23 +00003230 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003231 struct cmd_node *node;
3232
3233 if (vty->type == VTY_SHELL_SERV)
3234 {
paul55468c82005-03-14 20:19:01 +00003235 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003236 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3237 {
3238 if ((*node->func) (vty))
3239 vty_out (vty, "!%s", VTY_NEWLINE);
3240 }
3241 }
3242 else
3243 {
3244 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3245 VTY_NEWLINE);
3246 vty_out (vty, "!%s", VTY_NEWLINE);
3247
paul55468c82005-03-14 20:19:01 +00003248 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003249 if ((node = vector_slot (cmdvec, i)) && node->func)
3250 {
3251 if ((*node->func) (vty))
3252 vty_out (vty, "!%s", VTY_NEWLINE);
3253 }
3254 vty_out (vty, "end%s",VTY_NEWLINE);
3255 }
3256 return CMD_SUCCESS;
3257}
3258
3259/* Write current configuration into the terminal. */
3260ALIAS (config_write_terminal,
3261 show_running_config_cmd,
3262 "show running-config",
3263 SHOW_STR
3264 "running configuration\n")
3265
3266/* Write startup configuration into the terminal. */
3267DEFUN (show_startup_config,
3268 show_startup_config_cmd,
3269 "show startup-config",
3270 SHOW_STR
3271 "Contentes of startup configuration\n")
3272{
3273 char buf[BUFSIZ];
3274 FILE *confp;
3275
3276 confp = fopen (host.config, "r");
3277 if (confp == NULL)
3278 {
3279 vty_out (vty, "Can't open configuration file [%s]%s",
3280 host.config, VTY_NEWLINE);
3281 return CMD_WARNING;
3282 }
3283
3284 while (fgets (buf, BUFSIZ, confp))
3285 {
3286 char *cp = buf;
3287
3288 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3289 cp++;
3290 *cp = '\0';
3291
3292 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3293 }
3294
3295 fclose (confp);
3296
3297 return CMD_SUCCESS;
3298}
3299
3300/* Hostname configuration */
3301DEFUN (config_hostname,
3302 hostname_cmd,
3303 "hostname WORD",
3304 "Set system's network name\n"
3305 "This system's network name\n")
3306{
3307 if (!isalpha((int) *argv[0]))
3308 {
3309 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3310 return CMD_WARNING;
3311 }
3312
3313 if (host.name)
paul05865c92005-10-26 05:49:54 +00003314 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003315
paul05865c92005-10-26 05:49:54 +00003316 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003317 return CMD_SUCCESS;
3318}
3319
3320DEFUN (config_no_hostname,
3321 no_hostname_cmd,
3322 "no hostname [HOSTNAME]",
3323 NO_STR
3324 "Reset system's network name\n"
3325 "Host name of this router\n")
3326{
3327 if (host.name)
paul05865c92005-10-26 05:49:54 +00003328 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003329 host.name = NULL;
3330 return CMD_SUCCESS;
3331}
3332
3333/* VTY interface password set. */
3334DEFUN (config_password, password_cmd,
3335 "password (8|) WORD",
3336 "Assign the terminal connection password\n"
3337 "Specifies a HIDDEN password will follow\n"
3338 "dummy string \n"
3339 "The HIDDEN line password string\n")
3340{
3341 /* Argument check. */
3342 if (argc == 0)
3343 {
3344 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3345 return CMD_WARNING;
3346 }
3347
3348 if (argc == 2)
3349 {
3350 if (*argv[0] == '8')
3351 {
3352 if (host.password)
paul05865c92005-10-26 05:49:54 +00003353 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003354 host.password = NULL;
3355 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003356 XFREE (MTYPE_HOST, host.password_encrypt);
3357 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003358 return CMD_SUCCESS;
3359 }
3360 else
3361 {
3362 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3363 return CMD_WARNING;
3364 }
3365 }
3366
3367 if (!isalnum ((int) *argv[0]))
3368 {
3369 vty_out (vty,
3370 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3371 return CMD_WARNING;
3372 }
3373
3374 if (host.password)
paul05865c92005-10-26 05:49:54 +00003375 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003376 host.password = NULL;
3377
3378 if (host.encrypt)
3379 {
3380 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003381 XFREE (MTYPE_HOST, host.password_encrypt);
3382 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003383 }
3384 else
paul05865c92005-10-26 05:49:54 +00003385 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003386
3387 return CMD_SUCCESS;
3388}
3389
3390ALIAS (config_password, password_text_cmd,
3391 "password LINE",
3392 "Assign the terminal connection password\n"
3393 "The UNENCRYPTED (cleartext) line password\n")
3394
3395/* VTY enable password set. */
3396DEFUN (config_enable_password, enable_password_cmd,
3397 "enable password (8|) WORD",
3398 "Modify enable password parameters\n"
3399 "Assign the privileged level password\n"
3400 "Specifies a HIDDEN password will follow\n"
3401 "dummy string \n"
3402 "The HIDDEN 'enable' password string\n")
3403{
3404 /* Argument check. */
3405 if (argc == 0)
3406 {
3407 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3408 return CMD_WARNING;
3409 }
3410
3411 /* Crypt type is specified. */
3412 if (argc == 2)
3413 {
3414 if (*argv[0] == '8')
3415 {
3416 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003417 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003418 host.enable = NULL;
3419
3420 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003421 XFREE (MTYPE_HOST, host.enable_encrypt);
3422 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003423
3424 return CMD_SUCCESS;
3425 }
3426 else
3427 {
3428 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3429 return CMD_WARNING;
3430 }
3431 }
3432
3433 if (!isalnum ((int) *argv[0]))
3434 {
3435 vty_out (vty,
3436 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3437 return CMD_WARNING;
3438 }
3439
3440 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003441 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003442 host.enable = NULL;
3443
3444 /* Plain password input. */
3445 if (host.encrypt)
3446 {
3447 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003448 XFREE (MTYPE_HOST, host.enable_encrypt);
3449 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003450 }
3451 else
paul05865c92005-10-26 05:49:54 +00003452 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003453
3454 return CMD_SUCCESS;
3455}
3456
3457ALIAS (config_enable_password,
3458 enable_password_text_cmd,
3459 "enable password LINE",
3460 "Modify enable password parameters\n"
3461 "Assign the privileged level password\n"
3462 "The UNENCRYPTED (cleartext) 'enable' password\n")
3463
3464/* VTY enable password delete. */
3465DEFUN (no_config_enable_password, no_enable_password_cmd,
3466 "no enable password",
3467 NO_STR
3468 "Modify enable password parameters\n"
3469 "Assign the privileged level password\n")
3470{
3471 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003472 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003473 host.enable = NULL;
3474
3475 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003476 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003477 host.enable_encrypt = NULL;
3478
3479 return CMD_SUCCESS;
3480}
3481
3482DEFUN (service_password_encrypt,
3483 service_password_encrypt_cmd,
3484 "service password-encryption",
3485 "Set up miscellaneous service\n"
3486 "Enable encrypted passwords\n")
3487{
3488 if (host.encrypt)
3489 return CMD_SUCCESS;
3490
3491 host.encrypt = 1;
3492
3493 if (host.password)
3494 {
3495 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003496 XFREE (MTYPE_HOST, host.password_encrypt);
3497 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
paul718e3742002-12-13 20:15:29 +00003498 }
3499 if (host.enable)
3500 {
3501 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003502 XFREE (MTYPE_HOST, host.enable_encrypt);
3503 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
paul718e3742002-12-13 20:15:29 +00003504 }
3505
3506 return CMD_SUCCESS;
3507}
3508
3509DEFUN (no_service_password_encrypt,
3510 no_service_password_encrypt_cmd,
3511 "no service password-encryption",
3512 NO_STR
3513 "Set up miscellaneous service\n"
3514 "Enable encrypted passwords\n")
3515{
3516 if (! host.encrypt)
3517 return CMD_SUCCESS;
3518
3519 host.encrypt = 0;
3520
3521 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003522 XFREE (MTYPE_HOST, host.password_encrypt);
paul718e3742002-12-13 20:15:29 +00003523 host.password_encrypt = NULL;
3524
3525 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003526 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003527 host.enable_encrypt = NULL;
3528
3529 return CMD_SUCCESS;
3530}
3531
3532DEFUN (config_terminal_length, config_terminal_length_cmd,
3533 "terminal length <0-512>",
3534 "Set terminal line parameters\n"
3535 "Set number of lines on a screen\n"
3536 "Number of lines on screen (0 for no pausing)\n")
3537{
3538 int lines;
3539 char *endptr = NULL;
3540
3541 lines = strtol (argv[0], &endptr, 10);
3542 if (lines < 0 || lines > 512 || *endptr != '\0')
3543 {
3544 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3545 return CMD_WARNING;
3546 }
3547 vty->lines = lines;
3548
3549 return CMD_SUCCESS;
3550}
3551
3552DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3553 "terminal no length",
3554 "Set terminal line parameters\n"
3555 NO_STR
3556 "Set number of lines on a screen\n")
3557{
3558 vty->lines = -1;
3559 return CMD_SUCCESS;
3560}
3561
3562DEFUN (service_terminal_length, service_terminal_length_cmd,
3563 "service terminal-length <0-512>",
3564 "Set up miscellaneous service\n"
3565 "System wide terminal length configuration\n"
3566 "Number of lines of VTY (0 means no line control)\n")
3567{
3568 int lines;
3569 char *endptr = NULL;
3570
3571 lines = strtol (argv[0], &endptr, 10);
3572 if (lines < 0 || lines > 512 || *endptr != '\0')
3573 {
3574 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3575 return CMD_WARNING;
3576 }
3577 host.lines = lines;
3578
3579 return CMD_SUCCESS;
3580}
3581
3582DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3583 "no service terminal-length [<0-512>]",
3584 NO_STR
3585 "Set up miscellaneous service\n"
3586 "System wide terminal length configuration\n"
3587 "Number of lines of VTY (0 means no line control)\n")
3588{
3589 host.lines = -1;
3590 return CMD_SUCCESS;
3591}
3592
ajs2885f722004-12-17 23:16:33 +00003593DEFUN_HIDDEN (do_echo,
3594 echo_cmd,
3595 "echo .MESSAGE",
3596 "Echo a message back to the vty\n"
3597 "The message to echo\n")
3598{
3599 char *message;
3600
ajsf6834d42005-01-28 20:28:35 +00003601 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3602 VTY_NEWLINE);
3603 if (message)
3604 XFREE(MTYPE_TMP, message);
ajs2885f722004-12-17 23:16:33 +00003605 return CMD_SUCCESS;
3606}
3607
ajs274a4a42004-12-07 15:39:31 +00003608DEFUN (config_logmsg,
3609 config_logmsg_cmd,
3610 "logmsg "LOG_LEVELS" .MESSAGE",
3611 "Send a message to enabled logging destinations\n"
3612 LOG_LEVEL_DESC
3613 "The message to send\n")
3614{
3615 int level;
3616 char *message;
3617
3618 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3619 return CMD_ERR_NO_MATCH;
3620
Christian Hammersfc951862011-03-23 13:07:55 +03003621 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
ajsf6834d42005-01-28 20:28:35 +00003622 if (message)
3623 XFREE(MTYPE_TMP, message);
ajs274a4a42004-12-07 15:39:31 +00003624 return CMD_SUCCESS;
3625}
3626
3627DEFUN (show_logging,
3628 show_logging_cmd,
3629 "show logging",
3630 SHOW_STR
3631 "Show current logging configuration\n")
3632{
3633 struct zlog *zl = zlog_default;
3634
3635 vty_out (vty, "Syslog logging: ");
3636 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3637 vty_out (vty, "disabled");
3638 else
3639 vty_out (vty, "level %s, facility %s, ident %s",
3640 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3641 facility_name(zl->facility), zl->ident);
3642 vty_out (vty, "%s", VTY_NEWLINE);
3643
3644 vty_out (vty, "Stdout logging: ");
3645 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3646 vty_out (vty, "disabled");
3647 else
3648 vty_out (vty, "level %s",
3649 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3650 vty_out (vty, "%s", VTY_NEWLINE);
3651
3652 vty_out (vty, "Monitor logging: ");
3653 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3654 vty_out (vty, "disabled");
3655 else
3656 vty_out (vty, "level %s",
3657 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3658 vty_out (vty, "%s", VTY_NEWLINE);
3659
3660 vty_out (vty, "File logging: ");
3661 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3662 !zl->fp)
3663 vty_out (vty, "disabled");
3664 else
3665 vty_out (vty, "level %s, filename %s",
3666 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3667 zl->filename);
3668 vty_out (vty, "%s", VTY_NEWLINE);
3669
3670 vty_out (vty, "Protocol name: %s%s",
3671 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3672 vty_out (vty, "Record priority: %s%s",
3673 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003674 vty_out (vty, "Timestamp precision: %d%s",
3675 zl->timestamp_precision, VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +00003676
3677 return CMD_SUCCESS;
3678}
3679
paul718e3742002-12-13 20:15:29 +00003680DEFUN (config_log_stdout,
3681 config_log_stdout_cmd,
3682 "log stdout",
3683 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003684 "Set stdout logging level\n")
paul718e3742002-12-13 20:15:29 +00003685{
ajs274a4a42004-12-07 15:39:31 +00003686 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3687 return CMD_SUCCESS;
3688}
3689
3690DEFUN (config_log_stdout_level,
3691 config_log_stdout_level_cmd,
3692 "log stdout "LOG_LEVELS,
3693 "Logging control\n"
3694 "Set stdout logging level\n"
3695 LOG_LEVEL_DESC)
3696{
3697 int level;
3698
3699 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3700 return CMD_ERR_NO_MATCH;
3701 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
paul718e3742002-12-13 20:15:29 +00003702 return CMD_SUCCESS;
3703}
3704
3705DEFUN (no_config_log_stdout,
3706 no_config_log_stdout_cmd,
ajs274a4a42004-12-07 15:39:31 +00003707 "no log stdout [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003708 NO_STR
3709 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003710 "Cancel logging to stdout\n"
3711 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003712{
ajs274a4a42004-12-07 15:39:31 +00003713 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003714 return CMD_SUCCESS;
3715}
3716
ajs274a4a42004-12-07 15:39:31 +00003717DEFUN (config_log_monitor,
3718 config_log_monitor_cmd,
3719 "log monitor",
paul718e3742002-12-13 20:15:29 +00003720 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003721 "Set terminal line (monitor) logging level\n")
3722{
3723 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3724 return CMD_SUCCESS;
3725}
3726
3727DEFUN (config_log_monitor_level,
3728 config_log_monitor_level_cmd,
3729 "log monitor "LOG_LEVELS,
3730 "Logging control\n"
3731 "Set terminal line (monitor) logging level\n"
3732 LOG_LEVEL_DESC)
3733{
3734 int level;
3735
3736 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3737 return CMD_ERR_NO_MATCH;
3738 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3739 return CMD_SUCCESS;
3740}
3741
3742DEFUN (no_config_log_monitor,
3743 no_config_log_monitor_cmd,
3744 "no log monitor [LEVEL]",
3745 NO_STR
3746 "Logging control\n"
3747 "Disable terminal line (monitor) logging\n"
3748 "Logging level\n")
3749{
3750 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3751 return CMD_SUCCESS;
3752}
3753
3754static int
3755set_log_file(struct vty *vty, const char *fname, int loglevel)
paul718e3742002-12-13 20:15:29 +00003756{
3757 int ret;
paul9035efa2004-10-10 11:56:56 +00003758 char *p = NULL;
3759 const char *fullpath;
3760
paul718e3742002-12-13 20:15:29 +00003761 /* Path detection. */
ajs274a4a42004-12-07 15:39:31 +00003762 if (! IS_DIRECTORY_SEP (*fname))
paul718e3742002-12-13 20:15:29 +00003763 {
paul9035efa2004-10-10 11:56:56 +00003764 char cwd[MAXPATHLEN+1];
3765 cwd[MAXPATHLEN] = '\0';
3766
3767 if (getcwd (cwd, MAXPATHLEN) == NULL)
3768 {
3769 zlog_err ("config_log_file: Unable to alloc mem!");
3770 return CMD_WARNING;
3771 }
3772
ajs274a4a42004-12-07 15:39:31 +00003773 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
paul9035efa2004-10-10 11:56:56 +00003774 == NULL)
3775 {
3776 zlog_err ("config_log_file: Unable to alloc mem!");
3777 return CMD_WARNING;
3778 }
ajs274a4a42004-12-07 15:39:31 +00003779 sprintf (p, "%s/%s", cwd, fname);
paul9035efa2004-10-10 11:56:56 +00003780 fullpath = p;
paul718e3742002-12-13 20:15:29 +00003781 }
3782 else
ajs274a4a42004-12-07 15:39:31 +00003783 fullpath = fname;
paul718e3742002-12-13 20:15:29 +00003784
ajs274a4a42004-12-07 15:39:31 +00003785 ret = zlog_set_file (NULL, fullpath, loglevel);
paul718e3742002-12-13 20:15:29 +00003786
paul9035efa2004-10-10 11:56:56 +00003787 if (p)
3788 XFREE (MTYPE_TMP, p);
3789
paul718e3742002-12-13 20:15:29 +00003790 if (!ret)
3791 {
ajs274a4a42004-12-07 15:39:31 +00003792 vty_out (vty, "can't open logfile %s\n", fname);
paul718e3742002-12-13 20:15:29 +00003793 return CMD_WARNING;
3794 }
3795
3796 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003797 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003798
paul05865c92005-10-26 05:49:54 +00003799 host.logfile = XSTRDUP (MTYPE_HOST, fname);
paul718e3742002-12-13 20:15:29 +00003800
3801 return CMD_SUCCESS;
3802}
3803
ajs274a4a42004-12-07 15:39:31 +00003804DEFUN (config_log_file,
3805 config_log_file_cmd,
3806 "log file FILENAME",
3807 "Logging control\n"
3808 "Logging to file\n"
3809 "Logging filename\n")
3810{
3811 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3812}
3813
3814DEFUN (config_log_file_level,
3815 config_log_file_level_cmd,
3816 "log file FILENAME "LOG_LEVELS,
3817 "Logging control\n"
3818 "Logging to file\n"
3819 "Logging filename\n"
3820 LOG_LEVEL_DESC)
3821{
3822 int level;
3823
3824 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3825 return CMD_ERR_NO_MATCH;
3826 return set_log_file(vty, argv[0], level);
3827}
3828
paul718e3742002-12-13 20:15:29 +00003829DEFUN (no_config_log_file,
3830 no_config_log_file_cmd,
3831 "no log file [FILENAME]",
3832 NO_STR
3833 "Logging control\n"
3834 "Cancel logging to file\n"
3835 "Logging file name\n")
3836{
3837 zlog_reset_file (NULL);
3838
3839 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003840 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003841
3842 host.logfile = NULL;
3843
3844 return CMD_SUCCESS;
3845}
3846
ajs274a4a42004-12-07 15:39:31 +00003847ALIAS (no_config_log_file,
3848 no_config_log_file_level_cmd,
3849 "no log file FILENAME LEVEL",
3850 NO_STR
3851 "Logging control\n"
3852 "Cancel logging to file\n"
3853 "Logging file name\n"
3854 "Logging level\n")
3855
paul718e3742002-12-13 20:15:29 +00003856DEFUN (config_log_syslog,
3857 config_log_syslog_cmd,
3858 "log syslog",
3859 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003860 "Set syslog logging level\n")
paul718e3742002-12-13 20:15:29 +00003861{
ajs274a4a42004-12-07 15:39:31 +00003862 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003863 return CMD_SUCCESS;
3864}
3865
ajs274a4a42004-12-07 15:39:31 +00003866DEFUN (config_log_syslog_level,
3867 config_log_syslog_level_cmd,
3868 "log syslog "LOG_LEVELS,
paul12ab19f2003-07-26 06:14:55 +00003869 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003870 "Set syslog logging level\n"
3871 LOG_LEVEL_DESC)
paul12ab19f2003-07-26 06:14:55 +00003872{
ajs274a4a42004-12-07 15:39:31 +00003873 int level;
paul12ab19f2003-07-26 06:14:55 +00003874
ajs274a4a42004-12-07 15:39:31 +00003875 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3876 return CMD_ERR_NO_MATCH;
3877 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3878 return CMD_SUCCESS;
3879}
paul12ab19f2003-07-26 06:14:55 +00003880
ajs274a4a42004-12-07 15:39:31 +00003881DEFUN_DEPRECATED (config_log_syslog_facility,
3882 config_log_syslog_facility_cmd,
3883 "log syslog facility "LOG_FACILITIES,
3884 "Logging control\n"
3885 "Logging goes to syslog\n"
3886 "(Deprecated) Facility parameter for syslog messages\n"
3887 LOG_FACILITY_DESC)
3888{
3889 int facility;
paul12ab19f2003-07-26 06:14:55 +00003890
ajs274a4a42004-12-07 15:39:31 +00003891 if ((facility = facility_match(argv[0])) < 0)
3892 return CMD_ERR_NO_MATCH;
3893
3894 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003895 zlog_default->facility = facility;
paul718e3742002-12-13 20:15:29 +00003896 return CMD_SUCCESS;
3897}
3898
3899DEFUN (no_config_log_syslog,
3900 no_config_log_syslog_cmd,
ajs274a4a42004-12-07 15:39:31 +00003901 "no log syslog [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003902 NO_STR
3903 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003904 "Cancel logging to syslog\n"
3905 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003906{
ajs274a4a42004-12-07 15:39:31 +00003907 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003908 return CMD_SUCCESS;
3909}
3910
paul12ab19f2003-07-26 06:14:55 +00003911ALIAS (no_config_log_syslog,
3912 no_config_log_syslog_facility_cmd,
ajs274a4a42004-12-07 15:39:31 +00003913 "no log syslog facility "LOG_FACILITIES,
paul12ab19f2003-07-26 06:14:55 +00003914 NO_STR
3915 "Logging control\n"
3916 "Logging goes to syslog\n"
3917 "Facility parameter for syslog messages\n"
ajs274a4a42004-12-07 15:39:31 +00003918 LOG_FACILITY_DESC)
paul12ab19f2003-07-26 06:14:55 +00003919
ajs274a4a42004-12-07 15:39:31 +00003920DEFUN (config_log_facility,
3921 config_log_facility_cmd,
3922 "log facility "LOG_FACILITIES,
paul718e3742002-12-13 20:15:29 +00003923 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003924 "Facility parameter for syslog messages\n"
3925 LOG_FACILITY_DESC)
paul718e3742002-12-13 20:15:29 +00003926{
ajs274a4a42004-12-07 15:39:31 +00003927 int facility;
3928
3929 if ((facility = facility_match(argv[0])) < 0)
3930 return CMD_ERR_NO_MATCH;
3931 zlog_default->facility = facility;
3932 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +00003933}
3934
ajs274a4a42004-12-07 15:39:31 +00003935DEFUN (no_config_log_facility,
3936 no_config_log_facility_cmd,
3937 "no log facility [FACILITY]",
paul718e3742002-12-13 20:15:29 +00003938 NO_STR
3939 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003940 "Reset syslog facility to default (daemon)\n"
3941 "Syslog facility\n")
paul718e3742002-12-13 20:15:29 +00003942{
ajs274a4a42004-12-07 15:39:31 +00003943 zlog_default->facility = LOG_DAEMON;
3944 return CMD_SUCCESS;
3945}
3946
3947DEFUN_DEPRECATED (config_log_trap,
3948 config_log_trap_cmd,
3949 "log trap "LOG_LEVELS,
3950 "Logging control\n"
3951 "(Deprecated) Set logging level and default for all destinations\n"
3952 LOG_LEVEL_DESC)
3953{
3954 int new_level ;
3955 int i;
3956
3957 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3958 return CMD_ERR_NO_MATCH;
3959
3960 zlog_default->default_lvl = new_level;
3961 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3962 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3963 zlog_default->maxlvl[i] = new_level;
3964 return CMD_SUCCESS;
3965}
3966
3967DEFUN_DEPRECATED (no_config_log_trap,
3968 no_config_log_trap_cmd,
3969 "no log trap [LEVEL]",
3970 NO_STR
3971 "Logging control\n"
3972 "Permit all logging information\n"
3973 "Logging level\n")
3974{
3975 zlog_default->default_lvl = LOG_DEBUG;
paul718e3742002-12-13 20:15:29 +00003976 return CMD_SUCCESS;
3977}
3978
3979DEFUN (config_log_record_priority,
3980 config_log_record_priority_cmd,
3981 "log record-priority",
3982 "Logging control\n"
3983 "Log the priority of the message within the message\n")
3984{
3985 zlog_default->record_priority = 1 ;
3986 return CMD_SUCCESS;
3987}
3988
3989DEFUN (no_config_log_record_priority,
3990 no_config_log_record_priority_cmd,
3991 "no log record-priority",
3992 NO_STR
3993 "Logging control\n"
3994 "Do not log the priority of the message within the message\n")
3995{
3996 zlog_default->record_priority = 0 ;
3997 return CMD_SUCCESS;
3998}
3999
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004000DEFUN (config_log_timestamp_precision,
4001 config_log_timestamp_precision_cmd,
4002 "log timestamp precision <0-6>",
4003 "Logging control\n"
4004 "Timestamp configuration\n"
4005 "Set the timestamp precision\n"
4006 "Number of subsecond digits\n")
4007{
4008 if (argc != 1)
4009 {
4010 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
4011 return CMD_WARNING;
4012 }
4013
4014 VTY_GET_INTEGER_RANGE("Timestamp Precision",
4015 zlog_default->timestamp_precision, argv[0], 0, 6);
4016 return CMD_SUCCESS;
4017}
4018
4019DEFUN (no_config_log_timestamp_precision,
4020 no_config_log_timestamp_precision_cmd,
4021 "no log timestamp precision",
4022 NO_STR
4023 "Logging control\n"
4024 "Timestamp configuration\n"
4025 "Reset the timestamp precision to the default value of 0\n")
4026{
4027 zlog_default->timestamp_precision = 0 ;
4028 return CMD_SUCCESS;
4029}
4030
paul3b0c5d92005-03-08 10:43:43 +00004031DEFUN (banner_motd_file,
4032 banner_motd_file_cmd,
4033 "banner motd file [FILE]",
4034 "Set banner\n"
4035 "Banner for motd\n"
4036 "Banner from a file\n"
4037 "Filename\n")
4038{
paulb45da6f2005-03-08 15:16:57 +00004039 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004040 XFREE (MTYPE_HOST, host.motdfile);
4041 host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
paulb45da6f2005-03-08 15:16:57 +00004042
paul3b0c5d92005-03-08 10:43:43 +00004043 return CMD_SUCCESS;
4044}
paul718e3742002-12-13 20:15:29 +00004045
4046DEFUN (banner_motd_default,
4047 banner_motd_default_cmd,
4048 "banner motd default",
4049 "Set banner string\n"
4050 "Strings for motd\n"
4051 "Default string\n")
4052{
4053 host.motd = default_motd;
4054 return CMD_SUCCESS;
4055}
4056
4057DEFUN (no_banner_motd,
4058 no_banner_motd_cmd,
4059 "no banner motd",
4060 NO_STR
4061 "Set banner string\n"
4062 "Strings for motd\n")
4063{
4064 host.motd = NULL;
paul22085182005-03-08 16:00:12 +00004065 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004066 XFREE (MTYPE_HOST, host.motdfile);
paul3b0c5d92005-03-08 10:43:43 +00004067 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004068 return CMD_SUCCESS;
4069}
4070
Lou Bergerf9ec4192016-01-12 13:41:48 -05004071DEFUN (show_commandtree,
4072 show_commandtree_cmd,
4073 "show commandtree",
4074 NO_STR
4075 "Show command tree\n")
4076{
4077 /* TBD */
4078 vector cmd_vector;
4079 unsigned int i;
4080
4081 vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE);
4082
4083 /* vector of all commands installed at this node */
4084 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
4085
4086 /* loop over all commands at this node */
4087 for (i = 0; i < vector_active(cmd_vector); ++i)
4088 {
4089 struct cmd_element *cmd_element;
4090
4091 /* A cmd_element (seems to be) is an individual command */
4092 if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
4093 continue;
4094
4095 vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE);
4096 }
4097
4098 vector_free (cmd_vector);
4099 return CMD_SUCCESS;
4100}
4101
paul718e3742002-12-13 20:15:29 +00004102/* Set config filename. Called from vty.c */
4103void
4104host_config_set (char *filename)
4105{
Chris Caputo228da422009-07-18 05:44:03 +00004106 if (host.config)
4107 XFREE (MTYPE_HOST, host.config);
paul05865c92005-10-26 05:49:54 +00004108 host.config = XSTRDUP (MTYPE_HOST, filename);
paul718e3742002-12-13 20:15:29 +00004109}
4110
Christian Franke41de6292016-05-03 19:59:41 +02004111const char *
4112host_config_get (void)
4113{
4114 return host.config;
4115}
4116
paul718e3742002-12-13 20:15:29 +00004117void
4118install_default (enum node_type node)
4119{
4120 install_element (node, &config_exit_cmd);
4121 install_element (node, &config_quit_cmd);
4122 install_element (node, &config_end_cmd);
4123 install_element (node, &config_help_cmd);
4124 install_element (node, &config_list_cmd);
4125
4126 install_element (node, &config_write_terminal_cmd);
4127 install_element (node, &config_write_file_cmd);
4128 install_element (node, &config_write_memory_cmd);
4129 install_element (node, &config_write_cmd);
4130 install_element (node, &show_running_config_cmd);
4131}
4132
4133/* Initialize command interface. Install basic nodes and commands. */
4134void
4135cmd_init (int terminal)
4136{
Christian Frankecd40b322013-09-30 12:27:51 +00004137 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4138 token_cr.type = TOKEN_TERMINAL;
David Lamparter10bac802015-05-05 11:10:20 +02004139 token_cr.terminal = TERMINAL_LITERAL;
Christian Frankecd40b322013-09-30 12:27:51 +00004140 token_cr.cmd = command_cr;
4141 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
Chris Caputo228da422009-07-18 05:44:03 +00004142
paul718e3742002-12-13 20:15:29 +00004143 /* Allocate initial top vector of commands. */
4144 cmdvec = vector_init (VECTOR_MIN_SIZE);
4145
4146 /* Default host value settings. */
4147 host.name = NULL;
4148 host.password = NULL;
4149 host.enable = NULL;
4150 host.logfile = NULL;
4151 host.config = NULL;
4152 host.lines = -1;
4153 host.motd = default_motd;
paul3b0c5d92005-03-08 10:43:43 +00004154 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004155
4156 /* Install top nodes. */
4157 install_node (&view_node, NULL);
4158 install_node (&enable_node, NULL);
4159 install_node (&auth_node, NULL);
4160 install_node (&auth_enable_node, NULL);
Paul Jakma62687ff2008-08-23 14:27:06 +01004161 install_node (&restricted_node, NULL);
paul718e3742002-12-13 20:15:29 +00004162 install_node (&config_node, config_write_host);
4163
4164 /* Each node's basic commands. */
4165 install_element (VIEW_NODE, &show_version_cmd);
4166 if (terminal)
4167 {
4168 install_element (VIEW_NODE, &config_list_cmd);
4169 install_element (VIEW_NODE, &config_exit_cmd);
4170 install_element (VIEW_NODE, &config_quit_cmd);
4171 install_element (VIEW_NODE, &config_help_cmd);
4172 install_element (VIEW_NODE, &config_enable_cmd);
4173 install_element (VIEW_NODE, &config_terminal_length_cmd);
4174 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004175 install_element (VIEW_NODE, &show_logging_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004176 install_element (VIEW_NODE, &show_commandtree_cmd);
ajs2885f722004-12-17 23:16:33 +00004177 install_element (VIEW_NODE, &echo_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004178
4179 install_element (RESTRICTED_NODE, &config_list_cmd);
4180 install_element (RESTRICTED_NODE, &config_exit_cmd);
4181 install_element (RESTRICTED_NODE, &config_quit_cmd);
4182 install_element (RESTRICTED_NODE, &config_help_cmd);
4183 install_element (RESTRICTED_NODE, &config_enable_cmd);
4184 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4185 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004186 install_element (RESTRICTED_NODE, &show_commandtree_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004187 install_element (RESTRICTED_NODE, &echo_cmd);
paul718e3742002-12-13 20:15:29 +00004188 }
4189
4190 if (terminal)
4191 {
4192 install_default (ENABLE_NODE);
4193 install_element (ENABLE_NODE, &config_disable_cmd);
4194 install_element (ENABLE_NODE, &config_terminal_cmd);
4195 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4196 }
4197 install_element (ENABLE_NODE, &show_startup_config_cmd);
4198 install_element (ENABLE_NODE, &show_version_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004199 install_element (ENABLE_NODE, &show_commandtree_cmd);
paul718e3742002-12-13 20:15:29 +00004200
4201 if (terminal)
paul718e3742002-12-13 20:15:29 +00004202 {
hassoe7168df2004-10-03 20:11:32 +00004203 install_element (ENABLE_NODE, &config_terminal_length_cmd);
4204 install_element (ENABLE_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004205 install_element (ENABLE_NODE, &show_logging_cmd);
ajs2885f722004-12-17 23:16:33 +00004206 install_element (ENABLE_NODE, &echo_cmd);
ajs274a4a42004-12-07 15:39:31 +00004207 install_element (ENABLE_NODE, &config_logmsg_cmd);
hassoe7168df2004-10-03 20:11:32 +00004208
4209 install_default (CONFIG_NODE);
hassoea8e9d92004-10-07 21:32:14 +00004210 }
4211
4212 install_element (CONFIG_NODE, &hostname_cmd);
4213 install_element (CONFIG_NODE, &no_hostname_cmd);
hassoe7168df2004-10-03 20:11:32 +00004214
hassoea8e9d92004-10-07 21:32:14 +00004215 if (terminal)
4216 {
hassoe7168df2004-10-03 20:11:32 +00004217 install_element (CONFIG_NODE, &password_cmd);
4218 install_element (CONFIG_NODE, &password_text_cmd);
4219 install_element (CONFIG_NODE, &enable_password_cmd);
4220 install_element (CONFIG_NODE, &enable_password_text_cmd);
4221 install_element (CONFIG_NODE, &no_enable_password_cmd);
4222
paul718e3742002-12-13 20:15:29 +00004223 install_element (CONFIG_NODE, &config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004224 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
paul718e3742002-12-13 20:15:29 +00004225 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004226 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4227 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4228 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00004229 install_element (CONFIG_NODE, &config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004230 install_element (CONFIG_NODE, &config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004231 install_element (CONFIG_NODE, &no_config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004232 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004233 install_element (CONFIG_NODE, &config_log_syslog_cmd);
ajs274a4a42004-12-07 15:39:31 +00004234 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
paul12ab19f2003-07-26 06:14:55 +00004235 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004236 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
paul12ab19f2003-07-26 06:14:55 +00004237 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
ajs274a4a42004-12-07 15:39:31 +00004238 install_element (CONFIG_NODE, &config_log_facility_cmd);
4239 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004240 install_element (CONFIG_NODE, &config_log_trap_cmd);
4241 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4242 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4243 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004244 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4245 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
paul718e3742002-12-13 20:15:29 +00004246 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4247 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4248 install_element (CONFIG_NODE, &banner_motd_default_cmd);
paul3b0c5d92005-03-08 10:43:43 +00004249 install_element (CONFIG_NODE, &banner_motd_file_cmd);
paul718e3742002-12-13 20:15:29 +00004250 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4251 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4252 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
paul718e3742002-12-13 20:15:29 +00004253
paul354d1192005-04-25 16:26:42 +00004254 install_element (VIEW_NODE, &show_thread_cpu_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004255 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
Paul Jakmae276eb82010-01-09 16:15:00 +00004256
4257 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
paul354d1192005-04-25 16:26:42 +00004258 install_element (VIEW_NODE, &show_work_queues_cmd);
paul9ab68122003-01-18 01:16:20 +00004259 }
Lou Bergerf9ec4192016-01-12 13:41:48 -05004260 install_element (CONFIG_NODE, &show_commandtree_cmd);
Donald Sharpf31bab42015-06-19 19:26:19 -04004261 srandom(time(NULL));
paul718e3742002-12-13 20:15:29 +00004262}
Chris Caputo228da422009-07-18 05:44:03 +00004263
Christian Frankecd40b322013-09-30 12:27:51 +00004264static void
4265cmd_terminate_token(struct cmd_token *token)
4266{
4267 unsigned int i, j;
4268 vector keyword_vect;
4269
4270 if (token->multiple)
4271 {
4272 for (i = 0; i < vector_active(token->multiple); i++)
4273 cmd_terminate_token(vector_slot(token->multiple, i));
4274 vector_free(token->multiple);
4275 token->multiple = NULL;
4276 }
4277
4278 if (token->keyword)
4279 {
4280 for (i = 0; i < vector_active(token->keyword); i++)
4281 {
4282 keyword_vect = vector_slot(token->keyword, i);
4283 for (j = 0; j < vector_active(keyword_vect); j++)
4284 cmd_terminate_token(vector_slot(keyword_vect, j));
4285 vector_free(keyword_vect);
4286 }
4287 vector_free(token->keyword);
4288 token->keyword = NULL;
4289 }
4290
4291 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4292 XFREE(MTYPE_CMD_TOKENS, token->desc);
4293
4294 XFREE(MTYPE_CMD_TOKENS, token);
4295}
4296
4297static void
4298cmd_terminate_element(struct cmd_element *cmd)
4299{
4300 unsigned int i;
4301
4302 if (cmd->tokens == NULL)
4303 return;
4304
4305 for (i = 0; i < vector_active(cmd->tokens); i++)
4306 cmd_terminate_token(vector_slot(cmd->tokens, i));
4307
4308 vector_free(cmd->tokens);
4309 cmd->tokens = NULL;
4310}
4311
Chris Caputo228da422009-07-18 05:44:03 +00004312void
4313cmd_terminate ()
4314{
Christian Frankecd40b322013-09-30 12:27:51 +00004315 unsigned int i, j;
Chris Caputo228da422009-07-18 05:44:03 +00004316 struct cmd_node *cmd_node;
4317 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00004318 vector cmd_node_v;
Chris Caputo228da422009-07-18 05:44:03 +00004319
4320 if (cmdvec)
4321 {
4322 for (i = 0; i < vector_active (cmdvec); i++)
4323 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4324 {
4325 cmd_node_v = cmd_node->cmd_vector;
4326
4327 for (j = 0; j < vector_active (cmd_node_v); j++)
Christian Frankecd40b322013-09-30 12:27:51 +00004328 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4329 cmd_terminate_element(cmd_element);
Chris Caputo228da422009-07-18 05:44:03 +00004330
4331 vector_free (cmd_node_v);
4332 }
4333
4334 vector_free (cmdvec);
4335 cmdvec = NULL;
4336 }
4337
4338 if (command_cr)
Christian Frankecd40b322013-09-30 12:27:51 +00004339 XFREE(MTYPE_CMD_TOKENS, command_cr);
4340 if (token_cr.desc)
4341 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
Chris Caputo228da422009-07-18 05:44:03 +00004342 if (host.name)
4343 XFREE (MTYPE_HOST, host.name);
4344 if (host.password)
4345 XFREE (MTYPE_HOST, host.password);
4346 if (host.password_encrypt)
4347 XFREE (MTYPE_HOST, host.password_encrypt);
4348 if (host.enable)
4349 XFREE (MTYPE_HOST, host.enable);
4350 if (host.enable_encrypt)
4351 XFREE (MTYPE_HOST, host.enable_encrypt);
4352 if (host.logfile)
4353 XFREE (MTYPE_HOST, host.logfile);
4354 if (host.motdfile)
4355 XFREE (MTYPE_HOST, host.motdfile);
4356 if (host.config)
4357 XFREE (MTYPE_HOST, host.config);
4358}