blob: 662f8a3d8be3a0ec6622c66ae282651de45ae249 [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
Paul Jakma92193662016-06-16 15:53:26 +0100215static unsigned int
216cmd_hash_key (void *p)
217{
218 return (uintptr_t) p;
219}
220
221static int
222cmd_hash_cmp (const void *a, const void *b)
223{
224 return a == b;
225}
226
paul718e3742002-12-13 20:15:29 +0000227/* Install top node of command vector. */
228void
229install_node (struct cmd_node *node,
230 int (*func) (struct vty *))
231{
232 vector_set_index (cmdvec, node->node, node);
233 node->func = func;
234 node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
Paul Jakma92193662016-06-16 15:53:26 +0100235 node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp);
paul718e3742002-12-13 20:15:29 +0000236}
237
paul718e3742002-12-13 20:15:29 +0000238/* Breaking up string into each command piece. I assume given
239 character is separated by a space character. Return value is a
240 vector which includes char ** data element. */
241vector
hassoea8e9d92004-10-07 21:32:14 +0000242cmd_make_strvec (const char *string)
paul718e3742002-12-13 20:15:29 +0000243{
hassoea8e9d92004-10-07 21:32:14 +0000244 const char *cp, *start;
245 char *token;
paul718e3742002-12-13 20:15:29 +0000246 int strlen;
247 vector strvec;
248
249 if (string == NULL)
250 return NULL;
251
252 cp = string;
253
254 /* Skip white spaces. */
255 while (isspace ((int) *cp) && *cp != '\0')
256 cp++;
257
258 /* Return if there is only white spaces */
259 if (*cp == '\0')
260 return NULL;
261
262 if (*cp == '!' || *cp == '#')
263 return NULL;
264
265 /* Prepare return vector. */
266 strvec = vector_init (VECTOR_MIN_SIZE);
267
268 /* Copy each command piece and set into vector. */
269 while (1)
270 {
271 start = cp;
272 while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
273 *cp != '\0')
274 cp++;
275 strlen = cp - start;
276 token = XMALLOC (MTYPE_STRVEC, strlen + 1);
277 memcpy (token, start, strlen);
278 *(token + strlen) = '\0';
279 vector_set (strvec, token);
280
281 while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
282 *cp != '\0')
283 cp++;
284
285 if (*cp == '\0')
286 return strvec;
287 }
288}
289
290/* Free allocated string vector. */
291void
292cmd_free_strvec (vector v)
293{
hasso8c328f12004-10-05 21:01:23 +0000294 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000295 char *cp;
296
297 if (!v)
298 return;
299
paul55468c82005-03-14 20:19:01 +0000300 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +0000301 if ((cp = vector_slot (v, i)) != NULL)
302 XFREE (MTYPE_STRVEC, cp);
303
304 vector_free (v);
305}
306
Christian Frankecd40b322013-09-30 12:27:51 +0000307struct format_parser_state
308{
309 vector topvect; /* Top level vector */
310 vector intvect; /* Intermediate level vector, used when there's
311 * a multiple in a keyword. */
312 vector curvect; /* current vector where read tokens should be
313 appended. */
314
315 const char *string; /* pointer to command string, not modified */
316 const char *cp; /* pointer in command string, moved along while
317 parsing */
318 const char *dp; /* pointer in description string, moved along while
319 parsing */
320
321 int in_keyword; /* flag to remember if we are in a keyword group */
322 int in_multiple; /* flag to remember if we are in a multiple group */
323 int just_read_word; /* flag to remember if the last thing we red was a
324 * real word and not some abstract token */
325};
326
327static void
328format_parser_error(struct format_parser_state *state, const char *message)
329{
330 int offset = state->cp - state->string + 1;
331
332 fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string);
333 fprintf(stderr, " %*c\n", offset, '^');
334 fprintf(stderr, "%s at offset %d.\n", message, offset);
335 fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n");
336 exit(1);
337}
338
ajs274a4a42004-12-07 15:39:31 +0000339static char *
Christian Frankecd40b322013-09-30 12:27:51 +0000340format_parser_desc_str(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000341{
hasso6ad96ea2004-10-07 19:33:46 +0000342 const char *cp, *start;
343 char *token;
paul718e3742002-12-13 20:15:29 +0000344 int strlen;
Christian Frankecd40b322013-09-30 12:27:51 +0000345
346 cp = state->dp;
paul718e3742002-12-13 20:15:29 +0000347
348 if (cp == NULL)
349 return NULL;
350
351 /* Skip white spaces. */
352 while (isspace ((int) *cp) && *cp != '\0')
353 cp++;
354
355 /* Return if there is only white spaces */
356 if (*cp == '\0')
357 return NULL;
358
359 start = cp;
360
361 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
362 cp++;
363
364 strlen = cp - start;
Christian Frankecd40b322013-09-30 12:27:51 +0000365 token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1);
paul718e3742002-12-13 20:15:29 +0000366 memcpy (token, start, strlen);
367 *(token + strlen) = '\0';
368
Christian Frankecd40b322013-09-30 12:27:51 +0000369 state->dp = cp;
paul718e3742002-12-13 20:15:29 +0000370
371 return token;
372}
373
Christian Frankecd40b322013-09-30 12:27:51 +0000374static void
375format_parser_begin_keyword(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000376{
Christian Frankecd40b322013-09-30 12:27:51 +0000377 struct cmd_token *token;
378 vector keyword_vect;
paul718e3742002-12-13 20:15:29 +0000379
Christian Frankecd40b322013-09-30 12:27:51 +0000380 if (state->in_keyword
381 || state->in_multiple)
382 format_parser_error(state, "Unexpected '{'");
paul718e3742002-12-13 20:15:29 +0000383
Christian Frankecd40b322013-09-30 12:27:51 +0000384 state->cp++;
385 state->in_keyword = 1;
paul718e3742002-12-13 20:15:29 +0000386
Christian Frankecd40b322013-09-30 12:27:51 +0000387 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
388 token->type = TOKEN_KEYWORD;
389 token->keyword = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +0000390
Christian Frankecd40b322013-09-30 12:27:51 +0000391 keyword_vect = vector_init(VECTOR_MIN_SIZE);
392 vector_set(token->keyword, keyword_vect);
393
394 vector_set(state->curvect, token);
395 state->curvect = keyword_vect;
396}
397
398static void
399format_parser_begin_multiple(struct format_parser_state *state)
400{
401 struct cmd_token *token;
402
403 if (state->in_keyword == 1)
404 format_parser_error(state, "Keyword starting with '('");
405
406 if (state->in_multiple)
407 format_parser_error(state, "Nested group");
408
409 state->cp++;
410 state->in_multiple = 1;
411 state->just_read_word = 0;
412
413 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
414 token->type = TOKEN_MULTIPLE;
415 token->multiple = vector_init(VECTOR_MIN_SIZE);
416
417 vector_set(state->curvect, token);
418 if (state->curvect != state->topvect)
419 state->intvect = state->curvect;
420 state->curvect = token->multiple;
421}
422
423static void
424format_parser_end_keyword(struct format_parser_state *state)
425{
426 if (state->in_multiple
427 || !state->in_keyword)
428 format_parser_error(state, "Unexpected '}'");
429
430 if (state->in_keyword == 1)
431 format_parser_error(state, "Empty keyword group");
432
433 state->cp++;
434 state->in_keyword = 0;
435 state->curvect = state->topvect;
436}
437
438static void
439format_parser_end_multiple(struct format_parser_state *state)
440{
441 char *dummy;
442
443 if (!state->in_multiple)
Quentin Youngbe987c22016-06-16 11:26:44 -0400444 format_parser_error(state, "Unexpected ')'");
Christian Frankecd40b322013-09-30 12:27:51 +0000445
446 if (vector_active(state->curvect) == 0)
447 format_parser_error(state, "Empty multiple section");
448
449 if (!state->just_read_word)
paul718e3742002-12-13 20:15:29 +0000450 {
Christian Frankecd40b322013-09-30 12:27:51 +0000451 /* There are constructions like
452 * 'show ip ospf database ... (self-originate|)'
453 * in use.
454 * The old parser reads a description string for the
455 * word '' between |) which will never match.
456 * Simulate this behvaior by dropping the next desc
457 * string in such a case. */
paul718e3742002-12-13 20:15:29 +0000458
Christian Frankecd40b322013-09-30 12:27:51 +0000459 dummy = format_parser_desc_str(state);
460 XFREE(MTYPE_CMD_TOKENS, dummy);
461 }
paul718e3742002-12-13 20:15:29 +0000462
Christian Frankecd40b322013-09-30 12:27:51 +0000463 state->cp++;
464 state->in_multiple = 0;
paul718e3742002-12-13 20:15:29 +0000465
Christian Frankecd40b322013-09-30 12:27:51 +0000466 if (state->intvect)
467 state->curvect = state->intvect;
468 else
469 state->curvect = state->topvect;
470}
paul718e3742002-12-13 20:15:29 +0000471
Christian Frankecd40b322013-09-30 12:27:51 +0000472static void
473format_parser_handle_pipe(struct format_parser_state *state)
474{
475 struct cmd_token *keyword_token;
476 vector keyword_vect;
paul718e3742002-12-13 20:15:29 +0000477
Christian Frankecd40b322013-09-30 12:27:51 +0000478 if (state->in_multiple)
479 {
480 state->just_read_word = 0;
481 state->cp++;
482 }
483 else if (state->in_keyword)
484 {
485 state->in_keyword = 1;
486 state->cp++;
paul718e3742002-12-13 20:15:29 +0000487
Christian Frankecd40b322013-09-30 12:27:51 +0000488 keyword_token = vector_slot(state->topvect,
489 vector_active(state->topvect) - 1);
490 keyword_vect = vector_init(VECTOR_MIN_SIZE);
491 vector_set(keyword_token->keyword, keyword_vect);
492 state->curvect = keyword_vect;
493 }
494 else
495 {
496 format_parser_error(state, "Unexpected '|'");
paul718e3742002-12-13 20:15:29 +0000497 }
498}
499
Christian Frankecd40b322013-09-30 12:27:51 +0000500static void
501format_parser_read_word(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000502{
Christian Frankecd40b322013-09-30 12:27:51 +0000503 const char *start;
504 int len;
505 char *cmd;
506 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +0000507
Christian Frankecd40b322013-09-30 12:27:51 +0000508 start = state->cp;
509
510 while (state->cp[0] != '\0'
511 && !strchr("\r\n(){}|", state->cp[0])
512 && !isspace((int)state->cp[0]))
513 state->cp++;
514
515 len = state->cp - start;
516 cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1);
517 memcpy(cmd, start, len);
518 cmd[len] = '\0';
519
520 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
521 token->type = TOKEN_TERMINAL;
David Lamparter14162932015-05-12 17:18:04 +0200522 if (strcmp (cmd, "A.B.C.D") == 0)
David Lamparter10bac802015-05-05 11:10:20 +0200523 token->terminal = TERMINAL_IPV4;
524 else if (strcmp (cmd, "A.B.C.D/M") == 0)
525 token->terminal = TERMINAL_IPV4_PREFIX;
526 else if (strcmp (cmd, "X:X::X:X") == 0)
527 token->terminal = TERMINAL_IPV6;
528 else if (strcmp (cmd, "X:X::X:X/M") == 0)
529 token->terminal = TERMINAL_IPV6_PREFIX;
David Lamparter14162932015-05-12 17:18:04 +0200530 else if (cmd[0] == '[')
531 token->terminal = TERMINAL_OPTION;
532 else if (cmd[0] == '.')
533 token->terminal = TERMINAL_VARARG;
534 else if (cmd[0] == '<')
535 token->terminal = TERMINAL_RANGE;
536 else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
537 token->terminal = TERMINAL_VARIABLE;
David Lamparter10bac802015-05-05 11:10:20 +0200538 else
539 token->terminal = TERMINAL_LITERAL;
540
Christian Frankecd40b322013-09-30 12:27:51 +0000541 token->cmd = cmd;
542 token->desc = format_parser_desc_str(state);
543 vector_set(state->curvect, token);
544
545 if (state->in_keyword == 1)
546 state->in_keyword = 2;
547
548 state->just_read_word = 1;
549}
550
551/**
552 * Parse a given command format string and build a tree of tokens from
553 * it that is suitable to be used by the command subsystem.
554 *
555 * @param string Command format string.
556 * @param descstr Description string.
557 * @return A vector of struct cmd_token representing the given command,
558 * or NULL on error.
559 */
560static vector
561cmd_parse_format(const char *string, const char *descstr)
562{
563 struct format_parser_state state;
564
565 if (string == NULL)
566 return NULL;
567
568 memset(&state, 0, sizeof(state));
569 state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE);
570 state.cp = state.string = string;
571 state.dp = descstr;
572
573 while (1)
paul718e3742002-12-13 20:15:29 +0000574 {
Christian Frankecd40b322013-09-30 12:27:51 +0000575 while (isspace((int)state.cp[0]) && state.cp[0] != '\0')
576 state.cp++;
577
578 switch (state.cp[0])
579 {
580 case '\0':
581 if (state.in_keyword
582 || state.in_multiple)
583 format_parser_error(&state, "Unclosed group/keyword");
584 return state.topvect;
585 case '{':
586 format_parser_begin_keyword(&state);
587 break;
588 case '(':
589 format_parser_begin_multiple(&state);
590 break;
591 case '}':
592 format_parser_end_keyword(&state);
593 break;
594 case ')':
595 format_parser_end_multiple(&state);
596 break;
597 case '|':
598 format_parser_handle_pipe(&state);
599 break;
600 default:
601 format_parser_read_word(&state);
602 }
paul718e3742002-12-13 20:15:29 +0000603 }
paul718e3742002-12-13 20:15:29 +0000604}
605
606/* Return prompt character of specified node. */
hasso8c328f12004-10-05 21:01:23 +0000607const char *
paul718e3742002-12-13 20:15:29 +0000608cmd_prompt (enum node_type node)
609{
610 struct cmd_node *cnode;
611
612 cnode = vector_slot (cmdvec, node);
613 return cnode->prompt;
614}
615
616/* Install a command into a node. */
617void
618install_element (enum node_type ntype, struct cmd_element *cmd)
619{
620 struct cmd_node *cnode;
pauleb820af2005-09-05 11:54:13 +0000621
622 /* cmd_init hasn't been called */
623 if (!cmdvec)
Paul Jakma92193662016-06-16 15:53:26 +0100624 {
625 fprintf (stderr, "%s called before cmd_init, breakage likely\n",
626 __func__);
627 return;
628 }
pauleb820af2005-09-05 11:54:13 +0000629
paul718e3742002-12-13 20:15:29 +0000630 cnode = vector_slot (cmdvec, ntype);
631
632 if (cnode == NULL)
633 {
634 fprintf (stderr, "Command node %d doesn't exist, please check it\n",
635 ntype);
636 exit (1);
637 }
Paul Jakma92193662016-06-16 15:53:26 +0100638
639 if (hash_lookup (cnode->cmd_hash, cmd) != NULL)
640 {
Nick Hilliard71e0ba72016-12-27 22:51:51 +0000641#ifdef DEV_BUILD
Paul Jakma92193662016-06-16 15:53:26 +0100642 fprintf (stderr,
643 "Multiple command installs to node %d of command:\n%s\n",
644 ntype, cmd->string);
Nick Hilliard71e0ba72016-12-27 22:51:51 +0000645#endif
Paul Jakma92193662016-06-16 15:53:26 +0100646 return;
647 }
648
649 assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern));
650
paul718e3742002-12-13 20:15:29 +0000651 vector_set (cnode->cmd_vector, cmd);
Christian Frankecd40b322013-09-30 12:27:51 +0000652 if (cmd->tokens == NULL)
653 cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
Donald Sharpb9ac2f32016-03-11 14:27:12 -0500654
655 if (ntype == VIEW_NODE)
656 install_element (ENABLE_NODE, cmd);
paul718e3742002-12-13 20:15:29 +0000657}
658
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300659static const unsigned char itoa64[] =
paul718e3742002-12-13 20:15:29 +0000660"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
661
ajs274a4a42004-12-07 15:39:31 +0000662static void
paul718e3742002-12-13 20:15:29 +0000663to64(char *s, long v, int n)
664{
665 while (--n >= 0)
666 {
667 *s++ = itoa64[v&0x3f];
668 v >>= 6;
669 }
670}
671
ajs274a4a42004-12-07 15:39:31 +0000672static char *
673zencrypt (const char *passwd)
paul718e3742002-12-13 20:15:29 +0000674{
675 char salt[6];
676 struct timeval tv;
677 char *crypt (const char *, const char *);
678
679 gettimeofday(&tv,0);
680
681 to64(&salt[0], random(), 3);
682 to64(&salt[3], tv.tv_usec, 3);
683 salt[5] = '\0';
684
685 return crypt (passwd, salt);
686}
687
688/* This function write configuration of this host. */
ajs274a4a42004-12-07 15:39:31 +0000689static int
paul718e3742002-12-13 20:15:29 +0000690config_write_host (struct vty *vty)
691{
692 if (host.name)
693 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
694
695 if (host.encrypt)
696 {
697 if (host.password_encrypt)
698 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
699 if (host.enable_encrypt)
700 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
701 }
702 else
703 {
704 if (host.password)
705 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
706 if (host.enable)
707 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
708 }
709
ajs274a4a42004-12-07 15:39:31 +0000710 if (zlog_default->default_lvl != LOG_DEBUG)
ajs82146b82004-12-07 17:15:55 +0000711 {
712 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
713 VTY_NEWLINE);
714 vty_out (vty, "log trap %s%s",
715 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
716 }
paul718e3742002-12-13 20:15:29 +0000717
ajs274a4a42004-12-07 15:39:31 +0000718 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
paul12ab19f2003-07-26 06:14:55 +0000719 {
ajs274a4a42004-12-07 15:39:31 +0000720 vty_out (vty, "log file %s", host.logfile);
721 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
722 vty_out (vty, " %s",
723 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
paul12ab19f2003-07-26 06:14:55 +0000724 vty_out (vty, "%s", VTY_NEWLINE);
725 }
ajs274a4a42004-12-07 15:39:31 +0000726
727 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
728 {
729 vty_out (vty, "log stdout");
730 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
731 vty_out (vty, " %s",
732 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
733 vty_out (vty, "%s", VTY_NEWLINE);
734 }
735
736 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
737 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
738 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
739 vty_out(vty,"log monitor %s%s",
740 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
741
742 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
743 {
744 vty_out (vty, "log syslog");
745 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
746 vty_out (vty, " %s",
747 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
748 vty_out (vty, "%s", VTY_NEWLINE);
749 }
750
751 if (zlog_default->facility != LOG_DAEMON)
752 vty_out (vty, "log facility %s%s",
753 facility_name(zlog_default->facility), VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000754
755 if (zlog_default->record_priority == 1)
756 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
757
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000758 if (zlog_default->timestamp_precision > 0)
759 vty_out (vty, "log timestamp precision %d%s",
760 zlog_default->timestamp_precision, VTY_NEWLINE);
761
paul718e3742002-12-13 20:15:29 +0000762 if (host.advanced)
763 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
764
765 if (host.encrypt)
766 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
767
768 if (host.lines >= 0)
769 vty_out (vty, "service terminal-length %d%s", host.lines,
770 VTY_NEWLINE);
771
paul3b0c5d92005-03-08 10:43:43 +0000772 if (host.motdfile)
773 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
774 else if (! host.motd)
paul718e3742002-12-13 20:15:29 +0000775 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
776
777 return 1;
778}
779
780/* Utility function for getting command vector. */
ajs274a4a42004-12-07 15:39:31 +0000781static vector
paul718e3742002-12-13 20:15:29 +0000782cmd_node_vector (vector v, enum node_type ntype)
783{
784 struct cmd_node *cnode = vector_slot (v, ntype);
785 return cnode->cmd_vector;
786}
787
paul718e3742002-12-13 20:15:29 +0000788/* Completion match types. */
789enum match_type
790{
791 no_match,
792 extend_match,
793 ipv4_prefix_match,
794 ipv4_match,
795 ipv6_prefix_match,
796 ipv6_match,
797 range_match,
798 vararg_match,
799 partly_match,
800 exact_match
801};
802
ajs274a4a42004-12-07 15:39:31 +0000803static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000804cmd_ipv4_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000805{
hasso8c328f12004-10-05 21:01:23 +0000806 const char *sp;
paul718e3742002-12-13 20:15:29 +0000807 int dots = 0, nums = 0;
808 char buf[4];
809
810 if (str == NULL)
811 return partly_match;
812
813 for (;;)
814 {
815 memset (buf, 0, sizeof (buf));
816 sp = str;
817 while (*str != '\0')
818 {
819 if (*str == '.')
820 {
821 if (dots >= 3)
822 return no_match;
823
824 if (*(str + 1) == '.')
825 return no_match;
826
827 if (*(str + 1) == '\0')
828 return partly_match;
829
830 dots++;
831 break;
832 }
833 if (!isdigit ((int) *str))
834 return no_match;
835
836 str++;
837 }
838
839 if (str - sp > 3)
840 return no_match;
841
842 strncpy (buf, sp, str - sp);
843 if (atoi (buf) > 255)
844 return no_match;
845
846 nums++;
847
848 if (*str == '\0')
849 break;
850
851 str++;
852 }
853
854 if (nums < 4)
855 return partly_match;
856
857 return exact_match;
858}
859
ajs274a4a42004-12-07 15:39:31 +0000860static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000861cmd_ipv4_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000862{
hasso8c328f12004-10-05 21:01:23 +0000863 const char *sp;
paul718e3742002-12-13 20:15:29 +0000864 int dots = 0;
865 char buf[4];
866
867 if (str == NULL)
868 return partly_match;
869
870 for (;;)
871 {
872 memset (buf, 0, sizeof (buf));
873 sp = str;
874 while (*str != '\0' && *str != '/')
875 {
876 if (*str == '.')
877 {
878 if (dots == 3)
879 return no_match;
880
881 if (*(str + 1) == '.' || *(str + 1) == '/')
882 return no_match;
883
884 if (*(str + 1) == '\0')
885 return partly_match;
886
887 dots++;
888 break;
889 }
890
891 if (!isdigit ((int) *str))
892 return no_match;
893
894 str++;
895 }
896
897 if (str - sp > 3)
898 return no_match;
899
900 strncpy (buf, sp, str - sp);
901 if (atoi (buf) > 255)
902 return no_match;
903
904 if (dots == 3)
905 {
906 if (*str == '/')
907 {
908 if (*(str + 1) == '\0')
909 return partly_match;
910
911 str++;
912 break;
913 }
914 else if (*str == '\0')
915 return partly_match;
916 }
917
918 if (*str == '\0')
919 return partly_match;
920
921 str++;
922 }
923
924 sp = str;
925 while (*str != '\0')
926 {
927 if (!isdigit ((int) *str))
928 return no_match;
929
930 str++;
931 }
932
933 if (atoi (sp) > 32)
934 return no_match;
935
936 return exact_match;
937}
938
939#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
940#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
941#define STATE_START 1
942#define STATE_COLON 2
943#define STATE_DOUBLE 3
944#define STATE_ADDR 4
945#define STATE_DOT 5
946#define STATE_SLASH 6
947#define STATE_MASK 7
948
paul22e0a9e2003-07-11 17:55:46 +0000949#ifdef HAVE_IPV6
950
ajs274a4a42004-12-07 15:39:31 +0000951static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000952cmd_ipv6_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000953{
hasso726f9b22003-05-25 21:04:54 +0000954 struct sockaddr_in6 sin6_dummy;
955 int ret;
paul718e3742002-12-13 20:15:29 +0000956
957 if (str == NULL)
958 return partly_match;
959
960 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
961 return no_match;
962
hasso726f9b22003-05-25 21:04:54 +0000963 /* use inet_pton that has a better support,
964 * for example inet_pton can support the automatic addresses:
965 * ::1.2.3.4
966 */
967 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
968
969 if (ret == 1)
970 return exact_match;
971
Roman Hoog Antink7c9c6ae2012-05-09 06:35:34 +0000972 return no_match;
paul718e3742002-12-13 20:15:29 +0000973}
974
ajs274a4a42004-12-07 15:39:31 +0000975static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000976cmd_ipv6_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000977{
978 int state = STATE_START;
979 int colons = 0, nums = 0, double_colon = 0;
980 int mask;
hasso8c328f12004-10-05 21:01:23 +0000981 const char *sp = NULL;
paul718e3742002-12-13 20:15:29 +0000982 char *endptr = NULL;
983
984 if (str == NULL)
985 return partly_match;
986
987 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
988 return no_match;
989
990 while (*str != '\0' && state != STATE_MASK)
991 {
992 switch (state)
993 {
994 case STATE_START:
995 if (*str == ':')
996 {
997 if (*(str + 1) != ':' && *(str + 1) != '\0')
998 return no_match;
999 colons--;
1000 state = STATE_COLON;
1001 }
1002 else
1003 {
1004 sp = str;
1005 state = STATE_ADDR;
1006 }
1007
1008 continue;
1009 case STATE_COLON:
1010 colons++;
1011 if (*(str + 1) == '/')
1012 return no_match;
1013 else if (*(str + 1) == ':')
1014 state = STATE_DOUBLE;
1015 else
1016 {
1017 sp = str + 1;
1018 state = STATE_ADDR;
1019 }
1020 break;
1021 case STATE_DOUBLE:
1022 if (double_colon)
1023 return no_match;
1024
1025 if (*(str + 1) == ':')
1026 return no_match;
1027 else
1028 {
1029 if (*(str + 1) != '\0' && *(str + 1) != '/')
1030 colons++;
1031 sp = str + 1;
1032
1033 if (*(str + 1) == '/')
1034 state = STATE_SLASH;
1035 else
1036 state = STATE_ADDR;
1037 }
1038
1039 double_colon++;
1040 nums += 1;
1041 break;
1042 case STATE_ADDR:
1043 if (*(str + 1) == ':' || *(str + 1) == '.'
1044 || *(str + 1) == '\0' || *(str + 1) == '/')
1045 {
1046 if (str - sp > 3)
1047 return no_match;
1048
1049 for (; sp <= str; sp++)
1050 if (*sp == '/')
1051 return no_match;
1052
1053 nums++;
1054
1055 if (*(str + 1) == ':')
1056 state = STATE_COLON;
1057 else if (*(str + 1) == '.')
David Lamparteraa5cf242012-07-19 16:11:50 +02001058 {
1059 if (colons || double_colon)
1060 state = STATE_DOT;
1061 else
1062 return no_match;
1063 }
paul718e3742002-12-13 20:15:29 +00001064 else if (*(str + 1) == '/')
1065 state = STATE_SLASH;
1066 }
1067 break;
1068 case STATE_DOT:
1069 state = STATE_ADDR;
1070 break;
1071 case STATE_SLASH:
1072 if (*(str + 1) == '\0')
1073 return partly_match;
1074
1075 state = STATE_MASK;
1076 break;
1077 default:
1078 break;
1079 }
1080
1081 if (nums > 11)
1082 return no_match;
1083
1084 if (colons > 7)
1085 return no_match;
1086
1087 str++;
1088 }
1089
1090 if (state < STATE_MASK)
1091 return partly_match;
1092
1093 mask = strtol (str, &endptr, 10);
1094 if (*endptr != '\0')
1095 return no_match;
1096
1097 if (mask < 0 || mask > 128)
1098 return no_match;
1099
paul718e3742002-12-13 20:15:29 +00001100 return exact_match;
1101}
1102
paul22e0a9e2003-07-11 17:55:46 +00001103#endif /* HAVE_IPV6 */
1104
paul718e3742002-12-13 20:15:29 +00001105#define DECIMAL_STRLEN_MAX 10
1106
ajs274a4a42004-12-07 15:39:31 +00001107static int
hasso8c328f12004-10-05 21:01:23 +00001108cmd_range_match (const char *range, const char *str)
paul718e3742002-12-13 20:15:29 +00001109{
1110 char *p;
1111 char buf[DECIMAL_STRLEN_MAX + 1];
1112 char *endptr = NULL;
1113 unsigned long min, max, val;
1114
1115 if (str == NULL)
1116 return 1;
1117
1118 val = strtoul (str, &endptr, 10);
1119 if (*endptr != '\0')
1120 return 0;
1121
1122 range++;
1123 p = strchr (range, '-');
1124 if (p == NULL)
1125 return 0;
1126 if (p - range > DECIMAL_STRLEN_MAX)
1127 return 0;
1128 strncpy (buf, range, p - range);
1129 buf[p - range] = '\0';
1130 min = strtoul (buf, &endptr, 10);
1131 if (*endptr != '\0')
1132 return 0;
1133
1134 range = p + 1;
1135 p = strchr (range, '>');
1136 if (p == NULL)
1137 return 0;
1138 if (p - range > DECIMAL_STRLEN_MAX)
1139 return 0;
1140 strncpy (buf, range, p - range);
1141 buf[p - range] = '\0';
1142 max = strtoul (buf, &endptr, 10);
1143 if (*endptr != '\0')
1144 return 0;
1145
1146 if (val < min || val > max)
1147 return 0;
1148
1149 return 1;
1150}
1151
ajs274a4a42004-12-07 15:39:31 +00001152static enum match_type
Christian Frankecd40b322013-09-30 12:27:51 +00001153cmd_word_match(struct cmd_token *token,
1154 enum filter_type filter,
1155 const char *word)
paul718e3742002-12-13 20:15:29 +00001156{
hasso8c328f12004-10-05 21:01:23 +00001157 const char *str;
paul718e3742002-12-13 20:15:29 +00001158 enum match_type match_type;
paul909a2152005-03-14 17:41:45 +00001159
Christian Frankecd40b322013-09-30 12:27:51 +00001160 str = token->cmd;
paul718e3742002-12-13 20:15:29 +00001161
Christian Frankecd40b322013-09-30 12:27:51 +00001162 if (filter == FILTER_RELAXED)
1163 if (!word || !strlen(word))
1164 return partly_match;
paul718e3742002-12-13 20:15:29 +00001165
Christian Frankecd40b322013-09-30 12:27:51 +00001166 if (!word)
1167 return no_match;
paul909a2152005-03-14 17:41:45 +00001168
David Lamparter10bac802015-05-05 11:10:20 +02001169 switch (token->terminal)
Christian Frankecd40b322013-09-30 12:27:51 +00001170 {
David Lamparter10bac802015-05-05 11:10:20 +02001171 case TERMINAL_VARARG:
1172 return vararg_match;
1173
1174 case TERMINAL_RANGE:
1175 if (cmd_range_match(str, word))
1176 return range_match;
1177 break;
1178
1179 case TERMINAL_IPV6:
1180 match_type = cmd_ipv6_match(word);
1181 if ((filter == FILTER_RELAXED && match_type != no_match)
Christian Frankecd40b322013-09-30 12:27:51 +00001182 || (filter == FILTER_STRICT && match_type == exact_match))
David Lamparter10bac802015-05-05 11:10:20 +02001183 return ipv6_match;
1184 break;
1185
1186 case TERMINAL_IPV6_PREFIX:
1187 match_type = cmd_ipv6_prefix_match(word);
1188 if ((filter == FILTER_RELAXED && match_type != no_match)
1189 || (filter == FILTER_STRICT && match_type == exact_match))
1190 return ipv6_prefix_match;
1191 break;
1192
1193 case TERMINAL_IPV4:
1194 match_type = cmd_ipv4_match(word);
1195 if ((filter == FILTER_RELAXED && match_type != no_match)
1196 || (filter == FILTER_STRICT && match_type == exact_match))
1197 return ipv4_match;
1198 break;
1199
1200 case TERMINAL_IPV4_PREFIX:
1201 match_type = cmd_ipv4_prefix_match(word);
1202 if ((filter == FILTER_RELAXED && match_type != no_match)
1203 || (filter == FILTER_STRICT && match_type == exact_match))
1204 return ipv4_prefix_match;
1205 break;
1206
1207 case TERMINAL_OPTION:
1208 case TERMINAL_VARIABLE:
1209 return extend_match;
1210
1211 case TERMINAL_LITERAL:
1212 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1213 {
1214 if (!strcmp(str, word))
1215 return exact_match;
1216 return partly_match;
1217 }
1218 if (filter == FILTER_STRICT && !strcmp(str, word))
1219 return exact_match;
1220 break;
1221
1222 default:
1223 assert (0);
Christian Frankecd40b322013-09-30 12:27:51 +00001224 }
paul718e3742002-12-13 20:15:29 +00001225
Christian Frankecd40b322013-09-30 12:27:51 +00001226 return no_match;
paul718e3742002-12-13 20:15:29 +00001227}
1228
Christian Frankecd40b322013-09-30 12:27:51 +00001229struct cmd_matcher
1230{
1231 struct cmd_element *cmd; /* The command element the matcher is using */
1232 enum filter_type filter; /* Whether to use strict or relaxed matching */
1233 vector vline; /* The tokenized commandline which is to be matched */
1234 unsigned int index; /* The index up to which matching should be done */
1235
1236 /* If set, construct a list of matches at the position given by index */
1237 enum match_type *match_type;
1238 vector *match;
1239
1240 unsigned int word_index; /* iterating over vline */
1241};
1242
1243static int
1244push_argument(int *argc, const char **argv, const char *arg)
1245{
1246 if (!arg || !strlen(arg))
1247 arg = NULL;
1248
1249 if (!argc || !argv)
1250 return 0;
1251
1252 if (*argc >= CMD_ARGC_MAX)
1253 return -1;
1254
1255 argv[(*argc)++] = arg;
1256 return 0;
1257}
1258
1259static void
1260cmd_matcher_record_match(struct cmd_matcher *matcher,
1261 enum match_type match_type,
1262 struct cmd_token *token)
1263{
1264 if (matcher->word_index != matcher->index)
1265 return;
1266
1267 if (matcher->match)
1268 {
1269 if (!*matcher->match)
1270 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1271 vector_set(*matcher->match, token);
1272 }
1273
1274 if (matcher->match_type)
1275 {
1276 if (match_type > *matcher->match_type)
1277 *matcher->match_type = match_type;
1278 }
1279}
1280
1281static int
1282cmd_matcher_words_left(struct cmd_matcher *matcher)
1283{
1284 return matcher->word_index < vector_active(matcher->vline);
1285}
1286
1287static const char*
1288cmd_matcher_get_word(struct cmd_matcher *matcher)
1289{
1290 assert(cmd_matcher_words_left(matcher));
1291
1292 return vector_slot(matcher->vline, matcher->word_index);
1293}
1294
1295static enum matcher_rv
1296cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1297 struct cmd_token *token,
1298 int *argc, const char **argv)
1299{
1300 const char *word;
1301 enum match_type word_match;
1302
1303 assert(token->type == TOKEN_TERMINAL);
1304
1305 if (!cmd_matcher_words_left(matcher))
1306 {
David Lamparter10bac802015-05-05 11:10:20 +02001307 if (token->terminal == TERMINAL_OPTION)
Christian Frankecd40b322013-09-30 12:27:51 +00001308 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1309 else
1310 return MATCHER_INCOMPLETE;
1311 }
1312
1313 word = cmd_matcher_get_word(matcher);
1314 word_match = cmd_word_match(token, matcher->filter, word);
1315 if (word_match == no_match)
1316 return MATCHER_NO_MATCH;
1317
1318 /* We have to record the input word as argument if it matched
1319 * against a variable. */
David Lamparter14162932015-05-12 17:18:04 +02001320 if (TERMINAL_RECORD (token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001321 {
1322 if (push_argument(argc, argv, word))
1323 return MATCHER_EXCEED_ARGC_MAX;
1324 }
1325
1326 cmd_matcher_record_match(matcher, word_match, token);
1327
1328 matcher->word_index++;
1329
1330 /* A vararg token should consume all left over words as arguments */
David Lamparter10bac802015-05-05 11:10:20 +02001331 if (token->terminal == TERMINAL_VARARG)
Christian Frankecd40b322013-09-30 12:27:51 +00001332 while (cmd_matcher_words_left(matcher))
1333 {
1334 word = cmd_matcher_get_word(matcher);
1335 if (word && strlen(word))
1336 push_argument(argc, argv, word);
1337 matcher->word_index++;
1338 }
1339
1340 return MATCHER_OK;
1341}
1342
1343static enum matcher_rv
1344cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1345 struct cmd_token *token,
1346 int *argc, const char **argv)
1347{
1348 enum match_type multiple_match;
1349 unsigned int multiple_index;
1350 const char *word;
David Lamparterab90fc02015-03-03 09:07:25 +01001351 const char *arg = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001352 struct cmd_token *word_token;
1353 enum match_type word_match;
1354
1355 assert(token->type == TOKEN_MULTIPLE);
1356
1357 multiple_match = no_match;
1358
1359 if (!cmd_matcher_words_left(matcher))
1360 return MATCHER_INCOMPLETE;
1361
1362 word = cmd_matcher_get_word(matcher);
1363 for (multiple_index = 0;
1364 multiple_index < vector_active(token->multiple);
1365 multiple_index++)
1366 {
1367 word_token = vector_slot(token->multiple, multiple_index);
1368
1369 word_match = cmd_word_match(word_token, matcher->filter, word);
1370 if (word_match == no_match)
1371 continue;
1372
1373 cmd_matcher_record_match(matcher, word_match, word_token);
1374
1375 if (word_match > multiple_match)
1376 {
1377 multiple_match = word_match;
1378 arg = word;
1379 }
1380 /* To mimic the behavior of the old command implementation, we
1381 * tolerate any ambiguities here :/ */
1382 }
1383
1384 matcher->word_index++;
1385
1386 if (multiple_match == no_match)
1387 return MATCHER_NO_MATCH;
1388
1389 if (push_argument(argc, argv, arg))
1390 return MATCHER_EXCEED_ARGC_MAX;
1391
1392 return MATCHER_OK;
1393}
1394
1395static enum matcher_rv
1396cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1397 struct cmd_token *token,
1398 vector args_vector)
paul718e3742002-12-13 20:15:29 +00001399{
hasso8c328f12004-10-05 21:01:23 +00001400 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00001401 unsigned long keyword_mask;
1402 unsigned int keyword_found;
1403 enum match_type keyword_match;
1404 enum match_type word_match;
1405 vector keyword_vector;
1406 struct cmd_token *word_token;
1407 const char *word;
1408 int keyword_argc;
1409 const char **keyword_argv;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001410 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001411
1412 keyword_mask = 0;
1413 while (1)
1414 {
1415 if (!cmd_matcher_words_left(matcher))
1416 return MATCHER_OK;
1417
1418 word = cmd_matcher_get_word(matcher);
1419
1420 keyword_found = -1;
1421 keyword_match = no_match;
1422 for (i = 0; i < vector_active(token->keyword); i++)
1423 {
1424 if (keyword_mask & (1 << i))
1425 continue;
1426
1427 keyword_vector = vector_slot(token->keyword, i);
1428 word_token = vector_slot(keyword_vector, 0);
1429
1430 word_match = cmd_word_match(word_token, matcher->filter, word);
1431 if (word_match == no_match)
1432 continue;
1433
1434 cmd_matcher_record_match(matcher, word_match, word_token);
1435
1436 if (word_match > keyword_match)
1437 {
1438 keyword_match = word_match;
1439 keyword_found = i;
1440 }
1441 else if (word_match == keyword_match)
1442 {
1443 if (matcher->word_index != matcher->index || args_vector)
1444 return MATCHER_AMBIGUOUS;
1445 }
1446 }
1447
1448 if (keyword_found == (unsigned int)-1)
1449 return MATCHER_NO_MATCH;
1450
1451 matcher->word_index++;
1452
1453 if (matcher->word_index > matcher->index)
1454 return MATCHER_OK;
1455
1456 keyword_mask |= (1 << keyword_found);
1457
1458 if (args_vector)
1459 {
1460 keyword_argc = 0;
1461 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1462 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1463 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1464 keyword_argv[i] = (void*)-1;
1465 vector_set_index(args_vector, keyword_found, keyword_argv);
1466 }
1467 else
1468 {
1469 keyword_argv = NULL;
1470 }
1471
1472 keyword_vector = vector_slot(token->keyword, keyword_found);
1473 /* the keyword itself is at 0. We are only interested in the arguments,
1474 * so start counting at 1. */
1475 for (i = 1; i < vector_active(keyword_vector); i++)
1476 {
1477 word_token = vector_slot(keyword_vector, i);
1478
1479 switch (word_token->type)
1480 {
1481 case TOKEN_TERMINAL:
1482 rv = cmd_matcher_match_terminal(matcher, word_token,
1483 &keyword_argc, keyword_argv);
1484 break;
1485 case TOKEN_MULTIPLE:
1486 rv = cmd_matcher_match_multiple(matcher, word_token,
1487 &keyword_argc, keyword_argv);
1488 break;
1489 case TOKEN_KEYWORD:
1490 assert(!"Keywords should never be nested.");
1491 break;
1492 }
1493
1494 if (MATCHER_ERROR(rv))
1495 return rv;
1496
1497 if (matcher->word_index > matcher->index)
1498 return MATCHER_OK;
1499 }
1500 }
1501 /* not reached */
1502}
1503
1504static enum matcher_rv
1505cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1506 struct cmd_token *token,
1507 int *argc, const char **argv,
1508 vector keyword_args_vector)
1509{
1510 unsigned int i, j;
1511 const char **keyword_args;
1512 vector keyword_vector;
1513 struct cmd_token *word_token;
1514 const char *arg;
1515 enum matcher_rv rv;
1516
1517 rv = MATCHER_OK;
1518
1519 if (keyword_args_vector == NULL)
1520 return rv;
1521
1522 for (i = 0; i < vector_active(token->keyword); i++)
1523 {
1524 keyword_vector = vector_slot(token->keyword, i);
1525 keyword_args = vector_lookup(keyword_args_vector, i);
1526
1527 if (vector_active(keyword_vector) == 1)
1528 {
1529 /* this is a keyword without arguments */
1530 if (keyword_args)
1531 {
1532 word_token = vector_slot(keyword_vector, 0);
1533 arg = word_token->cmd;
1534 }
1535 else
1536 {
1537 arg = NULL;
1538 }
1539
1540 if (push_argument(argc, argv, arg))
1541 rv = MATCHER_EXCEED_ARGC_MAX;
1542 }
1543 else
1544 {
1545 /* this is a keyword with arguments */
1546 if (keyword_args)
1547 {
1548 /* the keyword was present, so just fill in the arguments */
1549 for (j = 0; keyword_args[j] != (void*)-1; j++)
1550 if (push_argument(argc, argv, keyword_args[j]))
1551 rv = MATCHER_EXCEED_ARGC_MAX;
1552 XFREE(MTYPE_TMP, keyword_args);
1553 }
1554 else
1555 {
1556 /* the keyword was not present, insert NULL for the arguments
1557 * the keyword would have taken. */
1558 for (j = 1; j < vector_active(keyword_vector); j++)
1559 {
1560 word_token = vector_slot(keyword_vector, j);
1561 if ((word_token->type == TOKEN_TERMINAL
David Lamparter14162932015-05-12 17:18:04 +02001562 && TERMINAL_RECORD (word_token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001563 || word_token->type == TOKEN_MULTIPLE)
1564 {
1565 if (push_argument(argc, argv, NULL))
1566 rv = MATCHER_EXCEED_ARGC_MAX;
1567 }
1568 }
1569 }
1570 }
1571 }
1572 vector_free(keyword_args_vector);
1573 return rv;
1574}
1575
1576static enum matcher_rv
1577cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1578 struct cmd_token *token,
1579 int *argc, const char **argv)
1580{
1581 vector keyword_args_vector;
1582 enum matcher_rv reader_rv;
1583 enum matcher_rv builder_rv;
1584
1585 assert(token->type == TOKEN_KEYWORD);
1586
1587 if (argc && argv)
1588 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1589 else
1590 keyword_args_vector = NULL;
1591
1592 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1593 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1594 argv, keyword_args_vector);
1595 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1596
1597 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1598 return builder_rv;
1599
1600 return reader_rv;
1601}
1602
1603static void
1604cmd_matcher_init(struct cmd_matcher *matcher,
1605 struct cmd_element *cmd,
1606 enum filter_type filter,
1607 vector vline,
1608 unsigned int index,
1609 enum match_type *match_type,
1610 vector *match)
1611{
1612 memset(matcher, 0, sizeof(*matcher));
1613
1614 matcher->cmd = cmd;
1615 matcher->filter = filter;
1616 matcher->vline = vline;
1617 matcher->index = index;
1618
1619 matcher->match_type = match_type;
1620 if (matcher->match_type)
1621 *matcher->match_type = no_match;
1622 matcher->match = match;
1623
1624 matcher->word_index = 0;
1625}
1626
1627static enum matcher_rv
1628cmd_element_match(struct cmd_element *cmd_element,
1629 enum filter_type filter,
1630 vector vline,
1631 unsigned int index,
1632 enum match_type *match_type,
1633 vector *match,
1634 int *argc,
1635 const char **argv)
1636{
1637 struct cmd_matcher matcher;
1638 unsigned int token_index;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001639 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001640
1641 cmd_matcher_init(&matcher, cmd_element, filter,
1642 vline, index, match_type, match);
1643
1644 if (argc != NULL)
1645 *argc = 0;
1646
1647 for (token_index = 0;
1648 token_index < vector_active(cmd_element->tokens);
1649 token_index++)
1650 {
1651 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1652
1653 switch (token->type)
1654 {
1655 case TOKEN_TERMINAL:
1656 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1657 break;
1658 case TOKEN_MULTIPLE:
1659 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1660 break;
1661 case TOKEN_KEYWORD:
1662 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1663 }
1664
1665 if (MATCHER_ERROR(rv))
1666 return rv;
1667
1668 if (matcher.word_index > index)
1669 return MATCHER_OK;
1670 }
1671
1672 /* return MATCHER_COMPLETE if all words were consumed */
1673 if (matcher.word_index >= vector_active(vline))
1674 return MATCHER_COMPLETE;
1675
1676 /* return MATCHER_COMPLETE also if only an empty word is left. */
1677 if (matcher.word_index == vector_active(vline) - 1
1678 && (!vector_slot(vline, matcher.word_index)
1679 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1680 return MATCHER_COMPLETE;
1681
1682 return MATCHER_NO_MATCH; /* command is too long to match */
1683}
1684
1685/**
1686 * Filter a given vector of commands against a given commandline and
1687 * calculate possible completions.
1688 *
1689 * @param commands A vector of struct cmd_element*. Commands that don't
1690 * match against the given command line will be overwritten
1691 * with NULL in that vector.
1692 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1693 * determines how incomplete commands are handled, compare with
1694 * cmd_word_match for details.
1695 * @param vline A vector of char* containing the tokenized commandline.
1696 * @param index Only match up to the given token of the commandline.
1697 * @param match_type Record the type of the best match here.
1698 * @param matches Record the matches here. For each cmd_element in the commands
1699 * vector, a match vector will be created in the matches vector.
1700 * That vector will contain all struct command_token* of the
1701 * cmd_element which matched against the given vline at the given
1702 * index.
1703 * @return A code specifying if an error occured. If all went right, it's
1704 * CMD_SUCCESS.
1705 */
1706static int
1707cmd_vector_filter(vector commands,
1708 enum filter_type filter,
1709 vector vline,
1710 unsigned int index,
1711 enum match_type *match_type,
1712 vector *matches)
1713{
1714 unsigned int i;
paul718e3742002-12-13 20:15:29 +00001715 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00001716 enum match_type best_match;
1717 enum match_type element_match;
1718 enum matcher_rv matcher_rv;
paul909a2152005-03-14 17:41:45 +00001719
Christian Frankecd40b322013-09-30 12:27:51 +00001720 best_match = no_match;
1721 *matches = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +00001722
Christian Frankecd40b322013-09-30 12:27:51 +00001723 for (i = 0; i < vector_active (commands); i++)
1724 if ((cmd_element = vector_slot (commands, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001725 {
Christian Frankecd40b322013-09-30 12:27:51 +00001726 vector_set_index(*matches, i, NULL);
1727 matcher_rv = cmd_element_match(cmd_element, filter,
1728 vline, index,
1729 &element_match,
1730 (vector*)&vector_slot(*matches, i),
1731 NULL, NULL);
1732 if (MATCHER_ERROR(matcher_rv))
1733 {
1734 vector_slot(commands, i) = NULL;
1735 if (matcher_rv == MATCHER_AMBIGUOUS)
1736 return CMD_ERR_AMBIGUOUS;
1737 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1738 return CMD_ERR_EXEED_ARGC_MAX;
1739 }
1740 else if (element_match > best_match)
1741 {
1742 best_match = element_match;
1743 }
paul718e3742002-12-13 20:15:29 +00001744 }
Christian Frankecd40b322013-09-30 12:27:51 +00001745 *match_type = best_match;
1746 return CMD_SUCCESS;
1747}
1748
1749/**
1750 * Check whether a given commandline is complete if used for a specific
1751 * cmd_element.
1752 *
1753 * @param cmd_element A cmd_element against which the commandline should be
1754 * checked.
1755 * @param vline The tokenized commandline.
1756 * @return 1 if the given commandline is complete, 0 otherwise.
1757 */
1758static int
1759cmd_is_complete(struct cmd_element *cmd_element,
1760 vector vline)
1761{
1762 enum matcher_rv rv;
1763
1764 rv = cmd_element_match(cmd_element,
1765 FILTER_RELAXED,
1766 vline, -1,
1767 NULL, NULL,
1768 NULL, NULL);
1769 return (rv == MATCHER_COMPLETE);
1770}
1771
1772/**
1773 * Parse a given commandline and construct a list of arguments for the
1774 * given command_element.
1775 *
1776 * @param cmd_element The cmd_element for which we want to construct arguments.
1777 * @param vline The tokenized commandline.
1778 * @param argc Where to store the argument count.
1779 * @param argv Where to store the argument list. Should be at least
1780 * CMD_ARGC_MAX elements long.
1781 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1782 */
1783static int
1784cmd_parse(struct cmd_element *cmd_element,
1785 vector vline,
1786 int *argc, const char **argv)
1787{
1788 enum matcher_rv rv = cmd_element_match(cmd_element,
1789 FILTER_RELAXED,
1790 vline, -1,
1791 NULL, NULL,
1792 argc, argv);
1793 switch (rv)
1794 {
1795 case MATCHER_COMPLETE:
1796 return CMD_SUCCESS;
1797
1798 case MATCHER_NO_MATCH:
1799 return CMD_ERR_NO_MATCH;
1800
1801 case MATCHER_AMBIGUOUS:
1802 return CMD_ERR_AMBIGUOUS;
1803
1804 case MATCHER_EXCEED_ARGC_MAX:
1805 return CMD_ERR_EXEED_ARGC_MAX;
1806
1807 default:
1808 return CMD_ERR_INCOMPLETE;
1809 }
paul718e3742002-12-13 20:15:29 +00001810}
1811
1812/* Check ambiguous match */
ajs274a4a42004-12-07 15:39:31 +00001813static int
Christian Frankecd40b322013-09-30 12:27:51 +00001814is_cmd_ambiguous (vector cmd_vector,
1815 const char *command,
1816 vector matches,
1817 enum match_type type)
paul718e3742002-12-13 20:15:29 +00001818{
hasso8c328f12004-10-05 21:01:23 +00001819 unsigned int i;
1820 unsigned int j;
1821 const char *str = NULL;
hasso8c328f12004-10-05 21:01:23 +00001822 const char *matched = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001823 vector match_vector;
1824 struct cmd_token *cmd_token;
paul909a2152005-03-14 17:41:45 +00001825
Christian Frankecd40b322013-09-30 12:27:51 +00001826 if (command == NULL)
1827 command = "";
1828
1829 for (i = 0; i < vector_active (matches); i++)
1830 if ((match_vector = vector_slot (matches, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001831 {
1832 int match = 0;
1833
Christian Frankecd40b322013-09-30 12:27:51 +00001834 for (j = 0; j < vector_active (match_vector); j++)
1835 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
paul909a2152005-03-14 17:41:45 +00001836 {
1837 enum match_type ret;
Christian Frankecd40b322013-09-30 12:27:51 +00001838
1839 assert(cmd_token->type == TOKEN_TERMINAL);
1840 if (cmd_token->type != TOKEN_TERMINAL)
1841 continue;
1842
1843 str = cmd_token->cmd;
paul718e3742002-12-13 20:15:29 +00001844
paul909a2152005-03-14 17:41:45 +00001845 switch (type)
1846 {
1847 case exact_match:
David Lamparter14162932015-05-12 17:18:04 +02001848 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001849 && strcmp (command, str) == 0)
1850 match++;
1851 break;
1852 case partly_match:
David Lamparter14162932015-05-12 17:18:04 +02001853 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001854 && strncmp (command, str, strlen (command)) == 0)
1855 {
1856 if (matched && strcmp (matched, str) != 0)
1857 return 1; /* There is ambiguous match. */
1858 else
1859 matched = str;
1860 match++;
1861 }
1862 break;
1863 case range_match:
1864 if (cmd_range_match (str, command))
1865 {
1866 if (matched && strcmp (matched, str) != 0)
1867 return 1;
1868 else
1869 matched = str;
1870 match++;
1871 }
1872 break;
1873#ifdef HAVE_IPV6
1874 case ipv6_match:
David Lamparter10bac802015-05-05 11:10:20 +02001875 if (cmd_token->terminal == TERMINAL_IPV6)
paul909a2152005-03-14 17:41:45 +00001876 match++;
1877 break;
1878 case ipv6_prefix_match:
1879 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1880 {
1881 if (ret == partly_match)
1882 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001883
paul909a2152005-03-14 17:41:45 +00001884 match++;
1885 }
1886 break;
1887#endif /* HAVE_IPV6 */
1888 case ipv4_match:
David Lamparter10bac802015-05-05 11:10:20 +02001889 if (cmd_token->terminal == TERMINAL_IPV4)
paul718e3742002-12-13 20:15:29 +00001890 match++;
paul909a2152005-03-14 17:41:45 +00001891 break;
1892 case ipv4_prefix_match:
1893 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1894 {
1895 if (ret == partly_match)
1896 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001897
paul909a2152005-03-14 17:41:45 +00001898 match++;
1899 }
1900 break;
1901 case extend_match:
David Lamparter14162932015-05-12 17:18:04 +02001902 if (TERMINAL_RECORD (cmd_token->terminal))
paul718e3742002-12-13 20:15:29 +00001903 match++;
paul909a2152005-03-14 17:41:45 +00001904 break;
1905 case no_match:
1906 default:
1907 break;
1908 }
1909 }
1910 if (!match)
Christian Frankecd40b322013-09-30 12:27:51 +00001911 vector_slot (cmd_vector, i) = NULL;
paul718e3742002-12-13 20:15:29 +00001912 }
1913 return 0;
1914}
1915
1916/* If src matches dst return dst string, otherwise return NULL */
ajs274a4a42004-12-07 15:39:31 +00001917static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001918cmd_entry_function (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001919{
David Lamparter10bac802015-05-05 11:10:20 +02001920 const char *dst = token->cmd;
1921
paul718e3742002-12-13 20:15:29 +00001922 /* Skip variable arguments. */
David Lamparter14162932015-05-12 17:18:04 +02001923 if (TERMINAL_RECORD (token->terminal))
1924 return NULL;
paul718e3742002-12-13 20:15:29 +00001925
1926 /* In case of 'command \t', given src is NULL string. */
1927 if (src == NULL)
1928 return dst;
1929
1930 /* Matched with input string. */
1931 if (strncmp (src, dst, strlen (src)) == 0)
1932 return dst;
1933
1934 return NULL;
1935}
1936
1937/* If src matches dst return dst string, otherwise return NULL */
1938/* This version will return the dst string always if it is
1939 CMD_VARIABLE for '?' key processing */
ajs274a4a42004-12-07 15:39:31 +00001940static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001941cmd_entry_function_desc (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001942{
David Lamparter10bac802015-05-05 11:10:20 +02001943 const char *dst = token->cmd;
paul718e3742002-12-13 20:15:29 +00001944
David Lamparter10bac802015-05-05 11:10:20 +02001945 switch (token->terminal)
paul718e3742002-12-13 20:15:29 +00001946 {
David Lamparter10bac802015-05-05 11:10:20 +02001947 case TERMINAL_VARARG:
1948 return dst;
1949
1950 case TERMINAL_RANGE:
1951 if (cmd_range_match (dst, src))
1952 return dst;
1953 else
1954 return NULL;
1955
1956 case TERMINAL_IPV6:
1957 if (cmd_ipv6_match (src))
1958 return dst;
1959 else
1960 return NULL;
1961
1962 case TERMINAL_IPV6_PREFIX:
1963 if (cmd_ipv6_prefix_match (src))
1964 return dst;
1965 else
1966 return NULL;
1967
1968 case TERMINAL_IPV4:
1969 if (cmd_ipv4_match (src))
1970 return dst;
1971 else
1972 return NULL;
1973
1974 case TERMINAL_IPV4_PREFIX:
1975 if (cmd_ipv4_prefix_match (src))
1976 return dst;
1977 else
1978 return NULL;
1979
1980 /* Optional or variable commands always match on '?' */
1981 case TERMINAL_OPTION:
1982 case TERMINAL_VARIABLE:
1983 return dst;
1984
1985 case TERMINAL_LITERAL:
1986 /* In case of 'command \t', given src is NULL string. */
1987 if (src == NULL)
1988 return dst;
1989
1990 if (strncmp (src, dst, strlen (src)) == 0)
1991 return dst;
1992 else
1993 return NULL;
1994
1995 default:
1996 assert(0);
David Lamparterf1fc3272015-05-13 12:44:50 +02001997 return NULL;
paul718e3742002-12-13 20:15:29 +00001998 }
paul718e3742002-12-13 20:15:29 +00001999}
2000
Christian Frankecd40b322013-09-30 12:27:51 +00002001/**
2002 * Check whether a string is already present in a vector of strings.
2003 * @param v A vector of char*.
2004 * @param str A char*.
2005 * @return 0 if str is already present in the vector, 1 otherwise.
2006 */
ajs274a4a42004-12-07 15:39:31 +00002007static int
hasso8c328f12004-10-05 21:01:23 +00002008cmd_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002009{
hasso8c328f12004-10-05 21:01:23 +00002010 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002011 char *match;
2012
paul55468c82005-03-14 20:19:01 +00002013 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +00002014 if ((match = vector_slot (v, i)) != NULL)
2015 if (strcmp (match, str) == 0)
2016 return 0;
2017 return 1;
2018}
2019
Christian Frankecd40b322013-09-30 12:27:51 +00002020/**
2021 * Check whether a struct cmd_token matching a given string is already
2022 * present in a vector of struct cmd_token.
2023 * @param v A vector of struct cmd_token*.
2024 * @param str A char* which should be searched for.
2025 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2026 * 1 otherwise.
2027 */
ajs274a4a42004-12-07 15:39:31 +00002028static int
hasso8c328f12004-10-05 21:01:23 +00002029desc_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002030{
hasso8c328f12004-10-05 21:01:23 +00002031 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00002032 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002033
paul55468c82005-03-14 20:19:01 +00002034 for (i = 0; i < vector_active (v); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00002035 if ((token = vector_slot (v, i)) != NULL)
2036 if (strcmp (token->cmd, str) == 0)
2037 return 0;
2038 return 1;
paul718e3742002-12-13 20:15:29 +00002039}
2040
ajs274a4a42004-12-07 15:39:31 +00002041static int
paulb92938a2002-12-13 21:20:42 +00002042cmd_try_do_shortcut (enum node_type node, char* first_word) {
2043 if ( first_word != NULL &&
2044 node != AUTH_NODE &&
2045 node != VIEW_NODE &&
2046 node != AUTH_ENABLE_NODE &&
2047 node != ENABLE_NODE &&
Paul Jakma62687ff2008-08-23 14:27:06 +01002048 node != RESTRICTED_NODE &&
paulb92938a2002-12-13 21:20:42 +00002049 0 == strcmp( "do", first_word ) )
2050 return 1;
2051 return 0;
2052}
2053
Christian Frankecd40b322013-09-30 12:27:51 +00002054static void
2055cmd_matches_free(vector *matches)
2056{
2057 unsigned int i;
2058 vector cmd_matches;
2059
2060 for (i = 0; i < vector_active(*matches); i++)
2061 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2062 vector_free(cmd_matches);
2063 vector_free(*matches);
2064 *matches = NULL;
2065}
2066
2067static int
2068cmd_describe_cmp(const void *a, const void *b)
2069{
2070 const struct cmd_token *first = *(struct cmd_token * const *)a;
2071 const struct cmd_token *second = *(struct cmd_token * const *)b;
2072
2073 return strcmp(first->cmd, second->cmd);
2074}
2075
2076static void
2077cmd_describe_sort(vector matchvec)
2078{
2079 qsort(matchvec->index, vector_active(matchvec),
2080 sizeof(void*), cmd_describe_cmp);
2081}
2082
paul718e3742002-12-13 20:15:29 +00002083/* '?' describe command support. */
ajs274a4a42004-12-07 15:39:31 +00002084static vector
paulb92938a2002-12-13 21:20:42 +00002085cmd_describe_command_real (vector vline, struct vty *vty, int *status)
paul718e3742002-12-13 20:15:29 +00002086{
paulb8961472005-03-14 17:35:52 +00002087 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002088 vector cmd_vector;
2089#define INIT_MATCHVEC_SIZE 10
2090 vector matchvec;
2091 struct cmd_element *cmd_element;
paulb8961472005-03-14 17:35:52 +00002092 unsigned int index;
paul54aba542003-08-21 20:28:24 +00002093 int ret;
2094 enum match_type match;
2095 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002096 vector matches = NULL;
2097 vector match_vector;
Donald Sharpf7332802015-07-17 22:36:57 -04002098 uint32_t command_found = 0;
2099 const char *last_word;
paul718e3742002-12-13 20:15:29 +00002100
2101 /* Set index. */
paul55468c82005-03-14 20:19:01 +00002102 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002103 {
2104 *status = CMD_ERR_NO_MATCH;
2105 return NULL;
2106 }
Christian Frankecd40b322013-09-30 12:27:51 +00002107
2108 index = vector_active (vline) - 1;
2109
paul718e3742002-12-13 20:15:29 +00002110 /* Make copy vector of current node's command vector. */
2111 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2112
2113 /* Prepare match vector */
2114 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2115
Christian Frankecd40b322013-09-30 12:27:51 +00002116 /* Filter commands and build a list how they could possibly continue. */
2117 for (i = 0; i <= index; i++)
2118 {
2119 command = vector_slot (vline, i);
paul718e3742002-12-13 20:15:29 +00002120
Christian Frankecd40b322013-09-30 12:27:51 +00002121 if (matches)
2122 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002123
Christian Frankecd40b322013-09-30 12:27:51 +00002124 ret = cmd_vector_filter(cmd_vector,
2125 FILTER_RELAXED,
2126 vline, i,
2127 &match,
2128 &matches);
paul718e3742002-12-13 20:15:29 +00002129
Christian Frankecd40b322013-09-30 12:27:51 +00002130 if (ret != CMD_SUCCESS)
2131 {
2132 vector_free (cmd_vector);
2133 vector_free (matchvec);
2134 cmd_matches_free(&matches);
2135 *status = ret;
2136 return NULL;
2137 }
paul718e3742002-12-13 20:15:29 +00002138
Christian Frankecd40b322013-09-30 12:27:51 +00002139 /* The last match may well be ambigious, so break here */
2140 if (i == index)
2141 break;
paul718e3742002-12-13 20:15:29 +00002142
Christian Frankecd40b322013-09-30 12:27:51 +00002143 if (match == vararg_match)
2144 {
2145 /* We found a vararg match - so we can throw out the current matches here
2146 * and don't need to continue checking the command input */
2147 unsigned int j, k;
2148
2149 for (j = 0; j < vector_active (matches); j++)
2150 if ((match_vector = vector_slot (matches, j)) != NULL)
2151 for (k = 0; k < vector_active (match_vector); k++)
2152 {
2153 struct cmd_token *token = vector_slot (match_vector, k);
2154 vector_set (matchvec, token);
2155 }
2156
2157 *status = CMD_SUCCESS;
2158 vector_set(matchvec, &token_cr);
2159 vector_free (cmd_vector);
2160 cmd_matches_free(&matches);
2161 cmd_describe_sort(matchvec);
2162 return matchvec;
2163 }
2164
2165 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2166 if (ret == 1)
2167 {
2168 vector_free (cmd_vector);
2169 vector_free (matchvec);
2170 cmd_matches_free(&matches);
2171 *status = CMD_ERR_AMBIGUOUS;
2172 return NULL;
2173 }
2174 else if (ret == 2)
2175 {
2176 vector_free (cmd_vector);
2177 vector_free (matchvec);
2178 cmd_matches_free(&matches);
2179 *status = CMD_ERR_NO_MATCH;
2180 return NULL;
2181 }
2182 }
paul54aba542003-08-21 20:28:24 +00002183
paul718e3742002-12-13 20:15:29 +00002184 /* Make description vector. */
Christian Frankecd40b322013-09-30 12:27:51 +00002185 for (i = 0; i < vector_active (matches); i++)
Donald Sharpf7332802015-07-17 22:36:57 -04002186 {
2187 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
2188 {
2189 unsigned int j;
2190 vector vline_trimmed;
paul718e3742002-12-13 20:15:29 +00002191
Donald Sharpf7332802015-07-17 22:36:57 -04002192 command_found++;
2193 last_word = vector_slot(vline, vector_active(vline) - 1);
2194 if (last_word == NULL || !strlen(last_word))
2195 {
2196 vline_trimmed = vector_copy(vline);
2197 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
paul718e3742002-12-13 20:15:29 +00002198
Donald Sharpf7332802015-07-17 22:36:57 -04002199 if (cmd_is_complete(cmd_element, vline_trimmed)
2200 && desc_unique_string(matchvec, command_cr))
2201 {
2202 if (match != vararg_match)
2203 vector_set(matchvec, &token_cr);
2204 }
Chris Caputo228da422009-07-18 05:44:03 +00002205
Donald Sharpf7332802015-07-17 22:36:57 -04002206 vector_free(vline_trimmed);
2207 }
Christian Frankecd40b322013-09-30 12:27:51 +00002208
Donald Sharpf7332802015-07-17 22:36:57 -04002209 match_vector = vector_slot (matches, i);
2210 if (match_vector)
2211 {
2212 for (j = 0; j < vector_active(match_vector); j++)
2213 {
2214 struct cmd_token *token = vector_slot(match_vector, j);
2215 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002216
Donald Sharpf7332802015-07-17 22:36:57 -04002217 string = cmd_entry_function_desc(command, token);
2218 if (string && desc_unique_string(matchvec, string))
2219 vector_set(matchvec, token);
2220 }
2221 }
2222 }
2223 }
2224
2225 /*
2226 * We can get into this situation when the command is complete
2227 * but the last part of the command is an optional piece of
2228 * the cli.
2229 */
2230 last_word = vector_slot(vline, vector_active(vline) - 1);
2231 if (command_found == 0 && (last_word == NULL || !strlen(last_word)))
2232 vector_set(matchvec, &token_cr);
2233
paul718e3742002-12-13 20:15:29 +00002234 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002235 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002236
2237 if (vector_slot (matchvec, 0) == NULL)
2238 {
2239 vector_free (matchvec);
paul909a2152005-03-14 17:41:45 +00002240 *status = CMD_ERR_NO_MATCH;
Paul Jakma5fc60512006-05-12 23:24:09 +00002241 return NULL;
paul718e3742002-12-13 20:15:29 +00002242 }
paul718e3742002-12-13 20:15:29 +00002243
Paul Jakma5fc60512006-05-12 23:24:09 +00002244 *status = CMD_SUCCESS;
Christian Frankecd40b322013-09-30 12:27:51 +00002245 cmd_describe_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002246 return matchvec;
2247}
2248
paulb92938a2002-12-13 21:20:42 +00002249vector
2250cmd_describe_command (vector vline, struct vty *vty, int *status)
2251{
2252 vector ret;
2253
2254 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2255 {
2256 enum node_type onode;
2257 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002258 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002259
2260 onode = vty->node;
2261 vty->node = ENABLE_NODE;
2262 /* We can try it on enable node, cos' the vty is authenticated */
2263
2264 shifted_vline = vector_init (vector_count(vline));
2265 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002266 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002267 {
2268 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2269 }
2270
2271 ret = cmd_describe_command_real (shifted_vline, vty, status);
2272
2273 vector_free(shifted_vline);
2274 vty->node = onode;
2275 return ret;
2276 }
2277
2278
2279 return cmd_describe_command_real (vline, vty, status);
2280}
2281
2282
paul718e3742002-12-13 20:15:29 +00002283/* Check LCD of matched command. */
ajs274a4a42004-12-07 15:39:31 +00002284static int
paul718e3742002-12-13 20:15:29 +00002285cmd_lcd (char **matched)
2286{
2287 int i;
2288 int j;
2289 int lcd = -1;
2290 char *s1, *s2;
2291 char c1, c2;
2292
2293 if (matched[0] == NULL || matched[1] == NULL)
2294 return 0;
2295
2296 for (i = 1; matched[i] != NULL; i++)
2297 {
2298 s1 = matched[i - 1];
2299 s2 = matched[i];
2300
2301 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2302 if (c1 != c2)
2303 break;
2304
2305 if (lcd < 0)
2306 lcd = j;
2307 else
2308 {
2309 if (lcd > j)
2310 lcd = j;
2311 }
2312 }
2313 return lcd;
2314}
2315
Christian Frankecd40b322013-09-30 12:27:51 +00002316static int
2317cmd_complete_cmp(const void *a, const void *b)
2318{
2319 const char *first = *(char * const *)a;
2320 const char *second = *(char * const *)b;
2321
2322 if (!first)
2323 {
2324 if (!second)
2325 return 0;
2326 return 1;
2327 }
2328 if (!second)
2329 return -1;
2330
2331 return strcmp(first, second);
2332}
2333
2334static void
2335cmd_complete_sort(vector matchvec)
2336{
2337 qsort(matchvec->index, vector_active(matchvec),
2338 sizeof(void*), cmd_complete_cmp);
2339}
2340
paul718e3742002-12-13 20:15:29 +00002341/* Command line completion support. */
ajs274a4a42004-12-07 15:39:31 +00002342static char **
Lou Berger67290032016-01-12 13:41:46 -05002343cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib)
paul718e3742002-12-13 20:15:29 +00002344{
paulb8961472005-03-14 17:35:52 +00002345 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002346 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2347#define INIT_MATCHVEC_SIZE 10
2348 vector matchvec;
paulb8961472005-03-14 17:35:52 +00002349 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002350 char **match_str;
Christian Frankecd40b322013-09-30 12:27:51 +00002351 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002352 char *command;
2353 int lcd;
Christian Frankecd40b322013-09-30 12:27:51 +00002354 vector matches = NULL;
2355 vector match_vector;
paul718e3742002-12-13 20:15:29 +00002356
paul55468c82005-03-14 20:19:01 +00002357 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002358 {
Paul Jakmad2519962006-05-12 23:19:37 +00002359 vector_free (cmd_vector);
paulb8961472005-03-14 17:35:52 +00002360 *status = CMD_ERR_NO_MATCH;
2361 return NULL;
2362 }
2363 else
paul55468c82005-03-14 20:19:01 +00002364 index = vector_active (vline) - 1;
paulb8961472005-03-14 17:35:52 +00002365
Christian Frankecd40b322013-09-30 12:27:51 +00002366 /* First, filter by command string */
2367 for (i = 0; i <= index; i++)
2368 {
2369 command = vector_slot (vline, i);
2370 enum match_type match;
2371 int ret;
paul718e3742002-12-13 20:15:29 +00002372
Christian Frankecd40b322013-09-30 12:27:51 +00002373 if (matches)
2374 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002375
Christian Frankecd40b322013-09-30 12:27:51 +00002376 /* First try completion match, if there is exactly match return 1 */
2377 ret = cmd_vector_filter(cmd_vector,
2378 FILTER_RELAXED,
2379 vline, i,
2380 &match,
2381 &matches);
2382
2383 if (ret != CMD_SUCCESS)
2384 {
2385 vector_free(cmd_vector);
2386 cmd_matches_free(&matches);
2387 *status = ret;
2388 return NULL;
2389 }
2390
2391 /* Break here - the completion mustn't be checked to be non-ambiguous */
2392 if (i == index)
2393 break;
2394
2395 /* If there is exact match then filter ambiguous match else check
2396 ambiguousness. */
2397 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2398 if (ret == 1)
2399 {
2400 vector_free (cmd_vector);
2401 cmd_matches_free(&matches);
2402 *status = CMD_ERR_AMBIGUOUS;
2403 return NULL;
2404 }
Christian Frankecd40b322013-09-30 12:27:51 +00002405 }
paul909a2152005-03-14 17:41:45 +00002406
paul718e3742002-12-13 20:15:29 +00002407 /* Prepare match vector. */
2408 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2409
Christian Frankecd40b322013-09-30 12:27:51 +00002410 /* Build the possible list of continuations into a list of completions */
2411 for (i = 0; i < vector_active (matches); i++)
2412 if ((match_vector = vector_slot (matches, i)))
paul718e3742002-12-13 20:15:29 +00002413 {
hasso8c328f12004-10-05 21:01:23 +00002414 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002415 unsigned int j;
paul909a2152005-03-14 17:41:45 +00002416
Christian Frankecd40b322013-09-30 12:27:51 +00002417 for (j = 0; j < vector_active (match_vector); j++)
2418 if ((token = vector_slot (match_vector, j)))
Lou Berger67290032016-01-12 13:41:46 -05002419 {
2420 string = cmd_entry_function (vector_slot (vline, index), token);
2421 if (string && cmd_unique_string (matchvec, string))
2422 vector_set (matchvec, (islib != 0 ?
2423 XSTRDUP (MTYPE_TMP, string) :
2424 strdup (string) /* rl freed */));
2425 }
paul718e3742002-12-13 20:15:29 +00002426 }
2427
2428 /* We don't need cmd_vector any more. */
2429 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002430 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002431
2432 /* No matched command */
2433 if (vector_slot (matchvec, 0) == NULL)
2434 {
2435 vector_free (matchvec);
2436
2437 /* In case of 'command \t' pattern. Do you need '?' command at
2438 the end of the line. */
2439 if (vector_slot (vline, index) == '\0')
2440 *status = CMD_ERR_NOTHING_TODO;
2441 else
2442 *status = CMD_ERR_NO_MATCH;
2443 return NULL;
2444 }
2445
2446 /* Only one matched */
2447 if (vector_slot (matchvec, 1) == NULL)
2448 {
2449 match_str = (char **) matchvec->index;
2450 vector_only_wrapper_free (matchvec);
2451 *status = CMD_COMPLETE_FULL_MATCH;
2452 return match_str;
2453 }
2454 /* Make it sure last element is NULL. */
2455 vector_set (matchvec, NULL);
2456
2457 /* Check LCD of matched strings. */
2458 if (vector_slot (vline, index) != NULL)
2459 {
2460 lcd = cmd_lcd ((char **) matchvec->index);
2461
2462 if (lcd)
2463 {
2464 int len = strlen (vector_slot (vline, index));
paul909a2152005-03-14 17:41:45 +00002465
paul718e3742002-12-13 20:15:29 +00002466 if (len < lcd)
2467 {
2468 char *lcdstr;
paul909a2152005-03-14 17:41:45 +00002469
Lou Berger67290032016-01-12 13:41:46 -05002470 lcdstr = (islib != 0 ?
2471 XMALLOC (MTYPE_TMP, lcd + 1) :
2472 malloc(lcd + 1));
paul718e3742002-12-13 20:15:29 +00002473 memcpy (lcdstr, matchvec->index[0], lcd);
2474 lcdstr[lcd] = '\0';
2475
paul718e3742002-12-13 20:15:29 +00002476 /* Free matchvec. */
paul55468c82005-03-14 20:19:01 +00002477 for (i = 0; i < vector_active (matchvec); i++)
Lou Berger67290032016-01-12 13:41:46 -05002478 {
2479 if (vector_slot (matchvec, i))
2480 {
2481 if (islib != 0)
2482 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
2483 else
2484 free (vector_slot (matchvec, i));
2485 }
2486 }
paul718e3742002-12-13 20:15:29 +00002487 vector_free (matchvec);
2488
paul909a2152005-03-14 17:41:45 +00002489 /* Make new matchvec. */
paul718e3742002-12-13 20:15:29 +00002490 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2491 vector_set (matchvec, lcdstr);
2492 match_str = (char **) matchvec->index;
2493 vector_only_wrapper_free (matchvec);
2494
2495 *status = CMD_COMPLETE_MATCH;
2496 return match_str;
2497 }
2498 }
2499 }
2500
2501 match_str = (char **) matchvec->index;
Christian Frankecd40b322013-09-30 12:27:51 +00002502 cmd_complete_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002503 vector_only_wrapper_free (matchvec);
2504 *status = CMD_COMPLETE_LIST_MATCH;
2505 return match_str;
2506}
2507
paulb92938a2002-12-13 21:20:42 +00002508char **
Lou Berger67290032016-01-12 13:41:46 -05002509cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)
paulb92938a2002-12-13 21:20:42 +00002510{
2511 char **ret;
2512
2513 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2514 {
2515 enum node_type onode;
2516 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002517 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002518
2519 onode = vty->node;
2520 vty->node = ENABLE_NODE;
2521 /* We can try it on enable node, cos' the vty is authenticated */
2522
2523 shifted_vline = vector_init (vector_count(vline));
2524 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002525 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002526 {
2527 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2528 }
2529
Lou Berger67290032016-01-12 13:41:46 -05002530 ret = cmd_complete_command_real (shifted_vline, vty, status, islib);
paulb92938a2002-12-13 21:20:42 +00002531
2532 vector_free(shifted_vline);
2533 vty->node = onode;
2534 return ret;
2535 }
2536
Lou Berger67290032016-01-12 13:41:46 -05002537 return cmd_complete_command_real (vline, vty, status, islib);
2538}
paulb92938a2002-12-13 21:20:42 +00002539
Lou Berger67290032016-01-12 13:41:46 -05002540char **
2541cmd_complete_command (vector vline, struct vty *vty, int *status)
2542{
2543 return cmd_complete_command_lib (vline, vty, status, 0);
paulb92938a2002-12-13 21:20:42 +00002544}
2545
2546/* return parent node */
2547/* MUST eventually converge on CONFIG_NODE */
hasso13bfca72005-01-23 21:42:25 +00002548enum node_type
ajs274a4a42004-12-07 15:39:31 +00002549node_parent ( enum node_type node )
paulb92938a2002-12-13 21:20:42 +00002550{
2551 enum node_type ret;
2552
paul9ab68122003-01-18 01:16:20 +00002553 assert (node > CONFIG_NODE);
2554
2555 switch (node)
2556 {
2557 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002558 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002559 case BGP_ENCAP_NODE:
2560 case BGP_ENCAPV6_NODE:
paul9ab68122003-01-18 01:16:20 +00002561 case BGP_IPV4_NODE:
2562 case BGP_IPV4M_NODE:
2563 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002564 case BGP_IPV6M_NODE:
paul9ab68122003-01-18 01:16:20 +00002565 ret = BGP_NODE;
2566 break;
2567 case KEYCHAIN_KEY_NODE:
2568 ret = KEYCHAIN_NODE;
2569 break;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002570 case LINK_PARAMS_NODE:
2571 ret = INTERFACE_NODE;
2572 break;
paul9ab68122003-01-18 01:16:20 +00002573 default:
2574 ret = CONFIG_NODE;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002575 break;
paulb92938a2002-12-13 21:20:42 +00002576 }
2577
2578 return ret;
2579}
2580
paul718e3742002-12-13 20:15:29 +00002581/* Execute command by argument vline vector. */
ajs274a4a42004-12-07 15:39:31 +00002582static int
Christian Frankecd40b322013-09-30 12:27:51 +00002583cmd_execute_command_real (vector vline,
2584 enum filter_type filter,
2585 struct vty *vty,
paulb8961472005-03-14 17:35:52 +00002586 struct cmd_element **cmd)
paul718e3742002-12-13 20:15:29 +00002587{
hasso8c328f12004-10-05 21:01:23 +00002588 unsigned int i;
2589 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002590 vector cmd_vector;
2591 struct cmd_element *cmd_element;
2592 struct cmd_element *matched_element;
2593 unsigned int matched_count, incomplete_count;
2594 int argc;
paul9035efa2004-10-10 11:56:56 +00002595 const char *argv[CMD_ARGC_MAX];
paul718e3742002-12-13 20:15:29 +00002596 enum match_type match = 0;
paul718e3742002-12-13 20:15:29 +00002597 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002598 int ret;
2599 vector matches;
paul718e3742002-12-13 20:15:29 +00002600
2601 /* Make copy of command elements. */
2602 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2603
paul55468c82005-03-14 20:19:01 +00002604 for (index = 0; index < vector_active (vline); index++)
Christian Frankecd40b322013-09-30 12:27:51 +00002605 {
2606 command = vector_slot (vline, index);
2607 ret = cmd_vector_filter(cmd_vector,
2608 filter,
2609 vline, index,
2610 &match,
2611 &matches);
paul718e3742002-12-13 20:15:29 +00002612
Christian Frankecd40b322013-09-30 12:27:51 +00002613 if (ret != CMD_SUCCESS)
2614 {
2615 cmd_matches_free(&matches);
2616 return ret;
2617 }
paul718e3742002-12-13 20:15:29 +00002618
Christian Frankecd40b322013-09-30 12:27:51 +00002619 if (match == vararg_match)
2620 {
2621 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002622 break;
Christian Frankecd40b322013-09-30 12:27:51 +00002623 }
paul718e3742002-12-13 20:15:29 +00002624
Christian Frankecd40b322013-09-30 12:27:51 +00002625 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2626 cmd_matches_free(&matches);
2627
2628 if (ret == 1)
2629 {
2630 vector_free(cmd_vector);
2631 return CMD_ERR_AMBIGUOUS;
2632 }
2633 else if (ret == 2)
2634 {
2635 vector_free(cmd_vector);
2636 return CMD_ERR_NO_MATCH;
2637 }
2638 }
paul718e3742002-12-13 20:15:29 +00002639
2640 /* Check matched count. */
2641 matched_element = NULL;
2642 matched_count = 0;
2643 incomplete_count = 0;
2644
paul55468c82005-03-14 20:19:01 +00002645 for (i = 0; i < vector_active (cmd_vector); i++)
paulb8961472005-03-14 17:35:52 +00002646 if ((cmd_element = vector_slot (cmd_vector, i)))
paul718e3742002-12-13 20:15:29 +00002647 {
Christian Frankecd40b322013-09-30 12:27:51 +00002648 if (cmd_is_complete(cmd_element, vline))
paul718e3742002-12-13 20:15:29 +00002649 {
2650 matched_element = cmd_element;
paul718e3742002-12-13 20:15:29 +00002651 matched_count++;
2652 }
2653 else
2654 {
2655 incomplete_count++;
2656 }
2657 }
paul909a2152005-03-14 17:41:45 +00002658
paul718e3742002-12-13 20:15:29 +00002659 /* Finish of using cmd_vector. */
2660 vector_free (cmd_vector);
2661
paul909a2152005-03-14 17:41:45 +00002662 /* To execute command, matched_count must be 1. */
2663 if (matched_count == 0)
paul718e3742002-12-13 20:15:29 +00002664 {
2665 if (incomplete_count)
2666 return CMD_ERR_INCOMPLETE;
2667 else
2668 return CMD_ERR_NO_MATCH;
2669 }
2670
paul909a2152005-03-14 17:41:45 +00002671 if (matched_count > 1)
paul718e3742002-12-13 20:15:29 +00002672 return CMD_ERR_AMBIGUOUS;
2673
Christian Frankecd40b322013-09-30 12:27:51 +00002674 ret = cmd_parse(matched_element, vline, &argc, argv);
2675 if (ret != CMD_SUCCESS)
2676 return ret;
paul718e3742002-12-13 20:15:29 +00002677
2678 /* For vtysh execution. */
2679 if (cmd)
2680 *cmd = matched_element;
2681
2682 if (matched_element->daemon)
2683 return CMD_SUCCESS_DAEMON;
2684
2685 /* Execute matched command. */
2686 return (*matched_element->func) (matched_element, vty, argc, argv);
2687}
2688
Christian Frankecd40b322013-09-30 12:27:51 +00002689/**
2690 * Execute a given command, handling things like "do ..." and checking
2691 * whether the given command might apply at a parent node if doesn't
2692 * apply for the current node.
2693 *
2694 * @param vline Command line input, vector of char* where each element is
2695 * one input token.
2696 * @param vty The vty context in which the command should be executed.
2697 * @param cmd Pointer where the struct cmd_element of the matched command
2698 * will be stored, if any. May be set to NULL if this info is
2699 * not needed.
2700 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2701 * @return The status of the command that has been executed or an error code
2702 * as to why no command could be executed.
2703 */
paulb92938a2002-12-13 21:20:42 +00002704int
hasso87d683b2005-01-16 23:31:54 +00002705cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2706 int vtysh) {
paul9ab68122003-01-18 01:16:20 +00002707 int ret, saved_ret, tried = 0;
2708 enum node_type onode, try_node;
2709
2710 onode = try_node = vty->node;
paulb92938a2002-12-13 21:20:42 +00002711
2712 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2713 {
2714 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002715 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002716
2717 vty->node = ENABLE_NODE;
2718 /* We can try it on enable node, cos' the vty is authenticated */
2719
2720 shifted_vline = vector_init (vector_count(vline));
2721 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002722 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002723 {
2724 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2725 }
2726
Christian Frankecd40b322013-09-30 12:27:51 +00002727 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002728
2729 vector_free(shifted_vline);
2730 vty->node = onode;
2731 return ret;
2732 }
2733
2734
Christian Frankecd40b322013-09-30 12:27:51 +00002735 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002736
hasso87d683b2005-01-16 23:31:54 +00002737 if (vtysh)
2738 return saved_ret;
2739
paulb92938a2002-12-13 21:20:42 +00002740 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
paul9ab68122003-01-18 01:16:20 +00002741 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
paulb92938a2002-12-13 21:20:42 +00002742 && vty->node > CONFIG_NODE )
2743 {
paul9ab68122003-01-18 01:16:20 +00002744 try_node = node_parent(try_node);
2745 vty->node = try_node;
Christian Frankecd40b322013-09-30 12:27:51 +00002746 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paul9ab68122003-01-18 01:16:20 +00002747 tried = 1;
2748 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
paulb92938a2002-12-13 21:20:42 +00002749 {
paul9ab68122003-01-18 01:16:20 +00002750 /* succesfull command, leave the node as is */
paulb92938a2002-12-13 21:20:42 +00002751 return ret;
2752 }
paulb92938a2002-12-13 21:20:42 +00002753 }
paul9ab68122003-01-18 01:16:20 +00002754 /* no command succeeded, reset the vty to the original node and
2755 return the error for this node */
2756 if ( tried )
2757 vty->node = onode;
2758 return saved_ret;
pauleda031f2003-01-18 00:39:19 +00002759}
2760
Christian Frankecd40b322013-09-30 12:27:51 +00002761/**
2762 * Execute a given command, matching it strictly against the current node.
2763 * This mode is used when reading config files.
2764 *
2765 * @param vline Command line input, vector of char* where each element is
2766 * one input token.
2767 * @param vty The vty context in which the command should be executed.
2768 * @param cmd Pointer where the struct cmd_element* of the matched command
2769 * will be stored, if any. May be set to NULL if this info is
2770 * not needed.
2771 * @return The status of the command that has been executed or an error code
2772 * as to why no command could be executed.
2773 */
paul718e3742002-12-13 20:15:29 +00002774int
paul909a2152005-03-14 17:41:45 +00002775cmd_execute_command_strict (vector vline, struct vty *vty,
paul718e3742002-12-13 20:15:29 +00002776 struct cmd_element **cmd)
2777{
Christian Frankecd40b322013-09-30 12:27:51 +00002778 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
paul718e3742002-12-13 20:15:29 +00002779}
2780
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002781/**
2782 * Parse one line of config, walking up the parse tree attempting to find a match
2783 *
2784 * @param vty The vty context in which the command should be executed.
2785 * @param cmd Pointer where the struct cmd_element* of the match command
2786 * will be stored, if any. May be set to NULL if this info is
2787 * not needed.
2788 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
2789 * or not.
2790 * @return The status of the command that has been executed or an error code
2791 * as to why no command could be executed.
2792 */
2793int
2794command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
2795{
2796 vector vline;
2797 int saved_node;
2798 int ret;
2799
2800 vline = cmd_make_strvec (vty->buf);
2801
2802 /* In case of comment line */
2803 if (vline == NULL)
2804 return CMD_SUCCESS;
2805
2806 /* Execute configuration command : this is strict match */
2807 ret = cmd_execute_command_strict (vline, vty, cmd);
2808
2809 saved_node = vty->node;
2810
2811 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2812 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2813 ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) {
2814 vty->node = node_parent(vty->node);
2815 ret = cmd_execute_command_strict (vline, vty, NULL);
2816 }
2817
2818 // If climbing the tree did not work then ignore the command and
2819 // stay at the same node
2820 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2821 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2822 ret != CMD_ERR_NOTHING_TODO)
2823 {
2824 vty->node = saved_node;
2825 }
2826
2827 cmd_free_strvec (vline);
2828
2829 return ret;
2830}
2831
paul718e3742002-12-13 20:15:29 +00002832/* Configration make from file. */
2833int
Steve Hillea555002009-07-28 16:36:14 -04002834config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
paul718e3742002-12-13 20:15:29 +00002835{
2836 int ret;
Steve Hillea555002009-07-28 16:36:14 -04002837 *line_num = 0;
paul718e3742002-12-13 20:15:29 +00002838
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002839 while (fgets (vty->buf, vty->max, fp))
paul718e3742002-12-13 20:15:29 +00002840 {
Steve Hillea555002009-07-28 16:36:14 -04002841 ++(*line_num);
paul718e3742002-12-13 20:15:29 +00002842
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002843 ret = command_config_read_one_line (vty, NULL, 0);
paul718e3742002-12-13 20:15:29 +00002844
hassoddd85ed2004-10-13 08:18:07 +00002845 if (ret != CMD_SUCCESS && ret != CMD_WARNING
2846 && ret != CMD_ERR_NOTHING_TODO)
paul718e3742002-12-13 20:15:29 +00002847 return ret;
2848 }
2849 return CMD_SUCCESS;
2850}
2851
2852/* Configration from terminal */
2853DEFUN (config_terminal,
2854 config_terminal_cmd,
2855 "configure terminal",
2856 "Configuration from vty interface\n"
2857 "Configuration terminal\n")
2858{
2859 if (vty_config_lock (vty))
2860 vty->node = CONFIG_NODE;
2861 else
2862 {
2863 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2864 return CMD_WARNING;
2865 }
2866 return CMD_SUCCESS;
2867}
2868
2869/* Enable command */
2870DEFUN (enable,
2871 config_enable_cmd,
2872 "enable",
2873 "Turn on privileged mode command\n")
2874{
2875 /* If enable password is NULL, change to ENABLE_NODE */
2876 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2877 vty->type == VTY_SHELL_SERV)
2878 vty->node = ENABLE_NODE;
2879 else
2880 vty->node = AUTH_ENABLE_NODE;
2881
2882 return CMD_SUCCESS;
2883}
2884
2885/* Disable command */
2886DEFUN (disable,
2887 config_disable_cmd,
2888 "disable",
2889 "Turn off privileged mode command\n")
2890{
2891 if (vty->node == ENABLE_NODE)
2892 vty->node = VIEW_NODE;
2893 return CMD_SUCCESS;
2894}
2895
2896/* Down vty node level. */
2897DEFUN (config_exit,
2898 config_exit_cmd,
2899 "exit",
2900 "Exit current mode and down to previous mode\n")
2901{
2902 switch (vty->node)
2903 {
2904 case VIEW_NODE:
2905 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002906 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002907 if (vty_shell (vty))
2908 exit (0);
2909 else
2910 vty->status = VTY_CLOSE;
2911 break;
2912 case CONFIG_NODE:
2913 vty->node = ENABLE_NODE;
2914 vty_config_unlock (vty);
2915 break;
2916 case INTERFACE_NODE:
2917 case ZEBRA_NODE:
2918 case BGP_NODE:
2919 case RIP_NODE:
2920 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002921 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002922 case OSPF_NODE:
2923 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002924 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002925 case KEYCHAIN_NODE:
2926 case MASC_NODE:
2927 case RMAP_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002928 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002929 case VTY_NODE:
2930 vty->node = CONFIG_NODE;
2931 break;
paul718e3742002-12-13 20:15:29 +00002932 case BGP_IPV4_NODE:
2933 case BGP_IPV4M_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002934 case BGP_VPNV4_NODE:
2935 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002936 case BGP_ENCAP_NODE:
2937 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00002938 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002939 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00002940 vty->node = BGP_NODE;
2941 break;
2942 case KEYCHAIN_KEY_NODE:
2943 vty->node = KEYCHAIN_NODE;
2944 break;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002945 case LINK_PARAMS_NODE:
2946 vty->node = INTERFACE_NODE;
2947 break;
paul718e3742002-12-13 20:15:29 +00002948 default:
2949 break;
2950 }
2951 return CMD_SUCCESS;
2952}
2953
2954/* quit is alias of exit. */
2955ALIAS (config_exit,
2956 config_quit_cmd,
2957 "quit",
2958 "Exit current mode and down to previous mode\n")
2959
2960/* End of configuration. */
2961DEFUN (config_end,
2962 config_end_cmd,
2963 "end",
2964 "End current mode and change to enable mode.")
2965{
2966 switch (vty->node)
2967 {
2968 case VIEW_NODE:
2969 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002970 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002971 /* Nothing to do. */
2972 break;
2973 case CONFIG_NODE:
2974 case INTERFACE_NODE:
2975 case ZEBRA_NODE:
2976 case RIP_NODE:
2977 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002978 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002979 case BGP_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002980 case BGP_ENCAP_NODE:
2981 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00002982 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002983 case BGP_VPNV6_NODE:
paul718e3742002-12-13 20:15:29 +00002984 case BGP_IPV4_NODE:
2985 case BGP_IPV4M_NODE:
2986 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002987 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00002988 case RMAP_NODE:
2989 case OSPF_NODE:
2990 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002991 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002992 case KEYCHAIN_NODE:
2993 case KEYCHAIN_KEY_NODE:
2994 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002995 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002996 case VTY_NODE:
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002997 case LINK_PARAMS_NODE:
paul718e3742002-12-13 20:15:29 +00002998 vty_config_unlock (vty);
2999 vty->node = ENABLE_NODE;
3000 break;
3001 default:
3002 break;
3003 }
3004 return CMD_SUCCESS;
3005}
3006
3007/* Show version. */
3008DEFUN (show_version,
3009 show_version_cmd,
3010 "show version",
3011 SHOW_STR
3012 "Displays zebra version\n")
3013{
hasso12f6ea22005-03-07 08:35:39 +00003014 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
3015 VTY_NEWLINE);
David Lamparter0be793e2012-11-27 01:34:56 +00003016 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
David Lamparter7abd8752014-11-22 10:43:29 -08003017 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
3018 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00003019
3020 return CMD_SUCCESS;
3021}
3022
3023/* Help display function for all node. */
3024DEFUN (config_help,
3025 config_help_cmd,
3026 "help",
3027 "Description of the interactive help system\n")
3028{
3029 vty_out (vty,
hasso6590f2c2004-10-19 20:40:08 +00003030 "Quagga VTY provides advanced help feature. When you need help,%s\
paul718e3742002-12-13 20:15:29 +00003031anytime at the command line please press '?'.%s\
3032%s\
3033If nothing matches, the help list will be empty and you must backup%s\
3034 until entering a '?' shows the available options.%s\
3035Two styles of help are provided:%s\
30361. Full help is available when you are ready to enter a%s\
3037command argument (e.g. 'show ?') and describes each possible%s\
3038argument.%s\
30392. Partial help is provided when an abbreviated argument is entered%s\
3040 and you want to know what arguments match the input%s\
3041 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3042 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3043 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
3044 return CMD_SUCCESS;
3045}
3046
3047/* Help display function for all node. */
3048DEFUN (config_list,
3049 config_list_cmd,
3050 "list",
3051 "Print command list\n")
3052{
hasso8c328f12004-10-05 21:01:23 +00003053 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003054 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
3055 struct cmd_element *cmd;
3056
paul55468c82005-03-14 20:19:01 +00003057 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
paul4275b1d2005-03-09 13:42:23 +00003058 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
3059 && !(cmd->attr == CMD_ATTR_DEPRECATED
3060 || cmd->attr == CMD_ATTR_HIDDEN))
paul718e3742002-12-13 20:15:29 +00003061 vty_out (vty, " %s%s", cmd->string,
3062 VTY_NEWLINE);
3063 return CMD_SUCCESS;
3064}
3065
3066/* Write current configuration into file. */
3067DEFUN (config_write_file,
3068 config_write_file_cmd,
3069 "write file",
3070 "Write running configuration to memory, network, or terminal\n"
3071 "Write to configuration file\n")
3072{
hasso8c328f12004-10-05 21:01:23 +00003073 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003074 int fd;
3075 struct cmd_node *node;
3076 char *config_file;
3077 char *config_file_tmp = NULL;
3078 char *config_file_sav = NULL;
paul05865c92005-10-26 05:49:54 +00003079 int ret = CMD_WARNING;
paul718e3742002-12-13 20:15:29 +00003080 struct vty *file_vty;
3081
3082 /* Check and see if we are operating under vtysh configuration */
3083 if (host.config == NULL)
3084 {
3085 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3086 VTY_NEWLINE);
3087 return CMD_WARNING;
3088 }
3089
3090 /* Get filename. */
3091 config_file = host.config;
3092
paul05865c92005-10-26 05:49:54 +00003093 config_file_sav =
3094 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
paul718e3742002-12-13 20:15:29 +00003095 strcpy (config_file_sav, config_file);
3096 strcat (config_file_sav, CONF_BACKUP_EXT);
3097
3098
paul05865c92005-10-26 05:49:54 +00003099 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
paul718e3742002-12-13 20:15:29 +00003100 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3101
3102 /* Open file to configuration write. */
3103 fd = mkstemp (config_file_tmp);
3104 if (fd < 0)
3105 {
3106 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3107 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003108 goto finished;
paul718e3742002-12-13 20:15:29 +00003109 }
3110
3111 /* Make vty for configuration file. */
3112 file_vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02003113 file_vty->wfd = fd;
paul718e3742002-12-13 20:15:29 +00003114 file_vty->type = VTY_FILE;
3115
3116 /* Config file header print. */
3117 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3118 vty_time_print (file_vty, 1);
3119 vty_out (file_vty, "!\n");
3120
paul55468c82005-03-14 20:19:01 +00003121 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003122 if ((node = vector_slot (cmdvec, i)) && node->func)
3123 {
3124 if ((*node->func) (file_vty))
3125 vty_out (file_vty, "!\n");
3126 }
3127 vty_close (file_vty);
3128
3129 if (unlink (config_file_sav) != 0)
3130 if (errno != ENOENT)
3131 {
3132 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3133 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003134 goto finished;
paul718e3742002-12-13 20:15:29 +00003135 }
3136 if (link (config_file, config_file_sav) != 0)
3137 {
3138 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3139 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003140 goto finished;
paul718e3742002-12-13 20:15:29 +00003141 }
3142 sync ();
3143 if (unlink (config_file) != 0)
3144 {
3145 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3146 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003147 goto finished;
paul718e3742002-12-13 20:15:29 +00003148 }
3149 if (link (config_file_tmp, config_file) != 0)
3150 {
3151 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3152 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003153 goto finished;
paul718e3742002-12-13 20:15:29 +00003154 }
paul718e3742002-12-13 20:15:29 +00003155 sync ();
3156
gdtaa593d52003-12-22 20:15:53 +00003157 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3158 {
3159 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
ajs6099b3b2004-11-20 02:06:59 +00003160 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003161 goto finished;
gdtaa593d52003-12-22 20:15:53 +00003162 }
3163
paul718e3742002-12-13 20:15:29 +00003164 vty_out (vty, "Configuration saved to %s%s", config_file,
3165 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003166 ret = CMD_SUCCESS;
3167
3168finished:
3169 unlink (config_file_tmp);
3170 XFREE (MTYPE_TMP, config_file_tmp);
3171 XFREE (MTYPE_TMP, config_file_sav);
3172 return ret;
paul718e3742002-12-13 20:15:29 +00003173}
3174
3175ALIAS (config_write_file,
3176 config_write_cmd,
3177 "write",
3178 "Write running configuration to memory, network, or terminal\n")
3179
3180ALIAS (config_write_file,
3181 config_write_memory_cmd,
3182 "write memory",
3183 "Write running configuration to memory, network, or terminal\n"
3184 "Write configuration to the file (same as write file)\n")
3185
3186ALIAS (config_write_file,
3187 copy_runningconfig_startupconfig_cmd,
3188 "copy running-config startup-config",
3189 "Copy configuration\n"
3190 "Copy running config to... \n"
3191 "Copy running config to startup config (same as write file)\n")
3192
3193/* Write current configuration into the terminal. */
3194DEFUN (config_write_terminal,
3195 config_write_terminal_cmd,
3196 "write terminal",
3197 "Write running configuration to memory, network, or terminal\n"
3198 "Write to terminal\n")
3199{
hasso8c328f12004-10-05 21:01:23 +00003200 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003201 struct cmd_node *node;
3202
3203 if (vty->type == VTY_SHELL_SERV)
3204 {
paul55468c82005-03-14 20:19:01 +00003205 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003206 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3207 {
3208 if ((*node->func) (vty))
3209 vty_out (vty, "!%s", VTY_NEWLINE);
3210 }
3211 }
3212 else
3213 {
3214 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3215 VTY_NEWLINE);
3216 vty_out (vty, "!%s", VTY_NEWLINE);
3217
paul55468c82005-03-14 20:19:01 +00003218 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003219 if ((node = vector_slot (cmdvec, i)) && node->func)
3220 {
3221 if ((*node->func) (vty))
3222 vty_out (vty, "!%s", VTY_NEWLINE);
3223 }
3224 vty_out (vty, "end%s",VTY_NEWLINE);
3225 }
3226 return CMD_SUCCESS;
3227}
3228
3229/* Write current configuration into the terminal. */
3230ALIAS (config_write_terminal,
3231 show_running_config_cmd,
3232 "show running-config",
3233 SHOW_STR
3234 "running configuration\n")
3235
3236/* Write startup configuration into the terminal. */
3237DEFUN (show_startup_config,
3238 show_startup_config_cmd,
3239 "show startup-config",
3240 SHOW_STR
3241 "Contentes of startup configuration\n")
3242{
3243 char buf[BUFSIZ];
3244 FILE *confp;
3245
3246 confp = fopen (host.config, "r");
3247 if (confp == NULL)
3248 {
3249 vty_out (vty, "Can't open configuration file [%s]%s",
3250 host.config, VTY_NEWLINE);
3251 return CMD_WARNING;
3252 }
3253
3254 while (fgets (buf, BUFSIZ, confp))
3255 {
3256 char *cp = buf;
3257
3258 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3259 cp++;
3260 *cp = '\0';
3261
3262 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3263 }
3264
3265 fclose (confp);
3266
3267 return CMD_SUCCESS;
3268}
3269
3270/* Hostname configuration */
3271DEFUN (config_hostname,
3272 hostname_cmd,
3273 "hostname WORD",
3274 "Set system's network name\n"
3275 "This system's network name\n")
3276{
3277 if (!isalpha((int) *argv[0]))
3278 {
3279 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3280 return CMD_WARNING;
3281 }
3282
3283 if (host.name)
paul05865c92005-10-26 05:49:54 +00003284 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003285
paul05865c92005-10-26 05:49:54 +00003286 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003287 return CMD_SUCCESS;
3288}
3289
3290DEFUN (config_no_hostname,
3291 no_hostname_cmd,
3292 "no hostname [HOSTNAME]",
3293 NO_STR
3294 "Reset system's network name\n"
3295 "Host name of this router\n")
3296{
3297 if (host.name)
paul05865c92005-10-26 05:49:54 +00003298 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003299 host.name = NULL;
3300 return CMD_SUCCESS;
3301}
3302
3303/* VTY interface password set. */
3304DEFUN (config_password, password_cmd,
3305 "password (8|) WORD",
3306 "Assign the terminal connection password\n"
3307 "Specifies a HIDDEN password will follow\n"
3308 "dummy string \n"
3309 "The HIDDEN line password string\n")
3310{
3311 /* Argument check. */
3312 if (argc == 0)
3313 {
3314 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3315 return CMD_WARNING;
3316 }
3317
3318 if (argc == 2)
3319 {
3320 if (*argv[0] == '8')
3321 {
3322 if (host.password)
paul05865c92005-10-26 05:49:54 +00003323 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003324 host.password = NULL;
3325 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003326 XFREE (MTYPE_HOST, host.password_encrypt);
3327 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003328 return CMD_SUCCESS;
3329 }
3330 else
3331 {
3332 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3333 return CMD_WARNING;
3334 }
3335 }
3336
3337 if (!isalnum ((int) *argv[0]))
3338 {
3339 vty_out (vty,
3340 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3341 return CMD_WARNING;
3342 }
3343
3344 if (host.password)
paul05865c92005-10-26 05:49:54 +00003345 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003346 host.password = NULL;
3347
3348 if (host.encrypt)
3349 {
3350 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003351 XFREE (MTYPE_HOST, host.password_encrypt);
3352 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003353 }
3354 else
paul05865c92005-10-26 05:49:54 +00003355 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003356
3357 return CMD_SUCCESS;
3358}
3359
3360ALIAS (config_password, password_text_cmd,
3361 "password LINE",
3362 "Assign the terminal connection password\n"
3363 "The UNENCRYPTED (cleartext) line password\n")
3364
3365/* VTY enable password set. */
3366DEFUN (config_enable_password, enable_password_cmd,
3367 "enable password (8|) WORD",
3368 "Modify enable password parameters\n"
3369 "Assign the privileged level password\n"
3370 "Specifies a HIDDEN password will follow\n"
3371 "dummy string \n"
3372 "The HIDDEN 'enable' password string\n")
3373{
3374 /* Argument check. */
3375 if (argc == 0)
3376 {
3377 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3378 return CMD_WARNING;
3379 }
3380
3381 /* Crypt type is specified. */
3382 if (argc == 2)
3383 {
3384 if (*argv[0] == '8')
3385 {
3386 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003387 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003388 host.enable = NULL;
3389
3390 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003391 XFREE (MTYPE_HOST, host.enable_encrypt);
3392 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003393
3394 return CMD_SUCCESS;
3395 }
3396 else
3397 {
3398 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3399 return CMD_WARNING;
3400 }
3401 }
3402
3403 if (!isalnum ((int) *argv[0]))
3404 {
3405 vty_out (vty,
3406 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3407 return CMD_WARNING;
3408 }
3409
3410 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003411 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003412 host.enable = NULL;
3413
3414 /* Plain password input. */
3415 if (host.encrypt)
3416 {
3417 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003418 XFREE (MTYPE_HOST, host.enable_encrypt);
3419 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003420 }
3421 else
paul05865c92005-10-26 05:49:54 +00003422 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003423
3424 return CMD_SUCCESS;
3425}
3426
3427ALIAS (config_enable_password,
3428 enable_password_text_cmd,
3429 "enable password LINE",
3430 "Modify enable password parameters\n"
3431 "Assign the privileged level password\n"
3432 "The UNENCRYPTED (cleartext) 'enable' password\n")
3433
3434/* VTY enable password delete. */
3435DEFUN (no_config_enable_password, no_enable_password_cmd,
3436 "no enable password",
3437 NO_STR
3438 "Modify enable password parameters\n"
3439 "Assign the privileged level password\n")
3440{
3441 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003442 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003443 host.enable = NULL;
3444
3445 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003446 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003447 host.enable_encrypt = NULL;
3448
3449 return CMD_SUCCESS;
3450}
3451
3452DEFUN (service_password_encrypt,
3453 service_password_encrypt_cmd,
3454 "service password-encryption",
3455 "Set up miscellaneous service\n"
3456 "Enable encrypted passwords\n")
3457{
3458 if (host.encrypt)
3459 return CMD_SUCCESS;
3460
3461 host.encrypt = 1;
3462
3463 if (host.password)
3464 {
3465 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003466 XFREE (MTYPE_HOST, host.password_encrypt);
3467 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
paul718e3742002-12-13 20:15:29 +00003468 }
3469 if (host.enable)
3470 {
3471 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003472 XFREE (MTYPE_HOST, host.enable_encrypt);
3473 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
paul718e3742002-12-13 20:15:29 +00003474 }
3475
3476 return CMD_SUCCESS;
3477}
3478
3479DEFUN (no_service_password_encrypt,
3480 no_service_password_encrypt_cmd,
3481 "no service password-encryption",
3482 NO_STR
3483 "Set up miscellaneous service\n"
3484 "Enable encrypted passwords\n")
3485{
3486 if (! host.encrypt)
3487 return CMD_SUCCESS;
3488
3489 host.encrypt = 0;
3490
3491 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003492 XFREE (MTYPE_HOST, host.password_encrypt);
paul718e3742002-12-13 20:15:29 +00003493 host.password_encrypt = NULL;
3494
3495 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003496 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003497 host.enable_encrypt = NULL;
3498
3499 return CMD_SUCCESS;
3500}
3501
3502DEFUN (config_terminal_length, config_terminal_length_cmd,
3503 "terminal length <0-512>",
3504 "Set terminal line parameters\n"
3505 "Set number of lines on a screen\n"
3506 "Number of lines on screen (0 for no pausing)\n")
3507{
3508 int lines;
3509 char *endptr = NULL;
3510
3511 lines = strtol (argv[0], &endptr, 10);
3512 if (lines < 0 || lines > 512 || *endptr != '\0')
3513 {
3514 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3515 return CMD_WARNING;
3516 }
3517 vty->lines = lines;
3518
3519 return CMD_SUCCESS;
3520}
3521
3522DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3523 "terminal no length",
3524 "Set terminal line parameters\n"
3525 NO_STR
3526 "Set number of lines on a screen\n")
3527{
3528 vty->lines = -1;
3529 return CMD_SUCCESS;
3530}
3531
3532DEFUN (service_terminal_length, service_terminal_length_cmd,
3533 "service terminal-length <0-512>",
3534 "Set up miscellaneous service\n"
3535 "System wide terminal length configuration\n"
3536 "Number of lines of VTY (0 means no line control)\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 host.lines = lines;
3548
3549 return CMD_SUCCESS;
3550}
3551
3552DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3553 "no service terminal-length [<0-512>]",
3554 NO_STR
3555 "Set up miscellaneous service\n"
3556 "System wide terminal length configuration\n"
3557 "Number of lines of VTY (0 means no line control)\n")
3558{
3559 host.lines = -1;
3560 return CMD_SUCCESS;
3561}
3562
ajs2885f722004-12-17 23:16:33 +00003563DEFUN_HIDDEN (do_echo,
3564 echo_cmd,
3565 "echo .MESSAGE",
3566 "Echo a message back to the vty\n"
3567 "The message to echo\n")
3568{
3569 char *message;
3570
ajsf6834d42005-01-28 20:28:35 +00003571 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3572 VTY_NEWLINE);
3573 if (message)
3574 XFREE(MTYPE_TMP, message);
ajs2885f722004-12-17 23:16:33 +00003575 return CMD_SUCCESS;
3576}
3577
ajs274a4a42004-12-07 15:39:31 +00003578DEFUN (config_logmsg,
3579 config_logmsg_cmd,
3580 "logmsg "LOG_LEVELS" .MESSAGE",
3581 "Send a message to enabled logging destinations\n"
3582 LOG_LEVEL_DESC
3583 "The message to send\n")
3584{
3585 int level;
3586 char *message;
3587
3588 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3589 return CMD_ERR_NO_MATCH;
3590
Christian Hammersfc951862011-03-23 13:07:55 +03003591 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
ajsf6834d42005-01-28 20:28:35 +00003592 if (message)
3593 XFREE(MTYPE_TMP, message);
ajs274a4a42004-12-07 15:39:31 +00003594 return CMD_SUCCESS;
3595}
3596
3597DEFUN (show_logging,
3598 show_logging_cmd,
3599 "show logging",
3600 SHOW_STR
3601 "Show current logging configuration\n")
3602{
3603 struct zlog *zl = zlog_default;
3604
3605 vty_out (vty, "Syslog logging: ");
3606 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3607 vty_out (vty, "disabled");
3608 else
3609 vty_out (vty, "level %s, facility %s, ident %s",
3610 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3611 facility_name(zl->facility), zl->ident);
3612 vty_out (vty, "%s", VTY_NEWLINE);
3613
3614 vty_out (vty, "Stdout logging: ");
3615 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3616 vty_out (vty, "disabled");
3617 else
3618 vty_out (vty, "level %s",
3619 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3620 vty_out (vty, "%s", VTY_NEWLINE);
3621
3622 vty_out (vty, "Monitor logging: ");
3623 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3624 vty_out (vty, "disabled");
3625 else
3626 vty_out (vty, "level %s",
3627 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3628 vty_out (vty, "%s", VTY_NEWLINE);
3629
3630 vty_out (vty, "File logging: ");
3631 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3632 !zl->fp)
3633 vty_out (vty, "disabled");
3634 else
3635 vty_out (vty, "level %s, filename %s",
3636 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3637 zl->filename);
3638 vty_out (vty, "%s", VTY_NEWLINE);
3639
3640 vty_out (vty, "Protocol name: %s%s",
3641 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3642 vty_out (vty, "Record priority: %s%s",
3643 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003644 vty_out (vty, "Timestamp precision: %d%s",
3645 zl->timestamp_precision, VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +00003646
3647 return CMD_SUCCESS;
3648}
3649
paul718e3742002-12-13 20:15:29 +00003650DEFUN (config_log_stdout,
3651 config_log_stdout_cmd,
3652 "log stdout",
3653 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003654 "Set stdout logging level\n")
paul718e3742002-12-13 20:15:29 +00003655{
ajs274a4a42004-12-07 15:39:31 +00003656 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3657 return CMD_SUCCESS;
3658}
3659
3660DEFUN (config_log_stdout_level,
3661 config_log_stdout_level_cmd,
3662 "log stdout "LOG_LEVELS,
3663 "Logging control\n"
3664 "Set stdout logging level\n"
3665 LOG_LEVEL_DESC)
3666{
3667 int level;
3668
3669 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3670 return CMD_ERR_NO_MATCH;
3671 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
paul718e3742002-12-13 20:15:29 +00003672 return CMD_SUCCESS;
3673}
3674
3675DEFUN (no_config_log_stdout,
3676 no_config_log_stdout_cmd,
ajs274a4a42004-12-07 15:39:31 +00003677 "no log stdout [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003678 NO_STR
3679 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003680 "Cancel logging to stdout\n"
3681 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003682{
ajs274a4a42004-12-07 15:39:31 +00003683 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003684 return CMD_SUCCESS;
3685}
3686
ajs274a4a42004-12-07 15:39:31 +00003687DEFUN (config_log_monitor,
3688 config_log_monitor_cmd,
3689 "log monitor",
paul718e3742002-12-13 20:15:29 +00003690 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003691 "Set terminal line (monitor) logging level\n")
3692{
3693 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3694 return CMD_SUCCESS;
3695}
3696
3697DEFUN (config_log_monitor_level,
3698 config_log_monitor_level_cmd,
3699 "log monitor "LOG_LEVELS,
3700 "Logging control\n"
3701 "Set terminal line (monitor) logging level\n"
3702 LOG_LEVEL_DESC)
3703{
3704 int level;
3705
3706 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3707 return CMD_ERR_NO_MATCH;
3708 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3709 return CMD_SUCCESS;
3710}
3711
3712DEFUN (no_config_log_monitor,
3713 no_config_log_monitor_cmd,
3714 "no log monitor [LEVEL]",
3715 NO_STR
3716 "Logging control\n"
3717 "Disable terminal line (monitor) logging\n"
3718 "Logging level\n")
3719{
3720 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3721 return CMD_SUCCESS;
3722}
3723
3724static int
3725set_log_file(struct vty *vty, const char *fname, int loglevel)
paul718e3742002-12-13 20:15:29 +00003726{
3727 int ret;
paul9035efa2004-10-10 11:56:56 +00003728 char *p = NULL;
3729 const char *fullpath;
3730
paul718e3742002-12-13 20:15:29 +00003731 /* Path detection. */
ajs274a4a42004-12-07 15:39:31 +00003732 if (! IS_DIRECTORY_SEP (*fname))
paul718e3742002-12-13 20:15:29 +00003733 {
paul9035efa2004-10-10 11:56:56 +00003734 char cwd[MAXPATHLEN+1];
3735 cwd[MAXPATHLEN] = '\0';
3736
3737 if (getcwd (cwd, MAXPATHLEN) == NULL)
3738 {
3739 zlog_err ("config_log_file: Unable to alloc mem!");
3740 return CMD_WARNING;
3741 }
3742
ajs274a4a42004-12-07 15:39:31 +00003743 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
paul9035efa2004-10-10 11:56:56 +00003744 == NULL)
3745 {
3746 zlog_err ("config_log_file: Unable to alloc mem!");
3747 return CMD_WARNING;
3748 }
ajs274a4a42004-12-07 15:39:31 +00003749 sprintf (p, "%s/%s", cwd, fname);
paul9035efa2004-10-10 11:56:56 +00003750 fullpath = p;
paul718e3742002-12-13 20:15:29 +00003751 }
3752 else
ajs274a4a42004-12-07 15:39:31 +00003753 fullpath = fname;
paul718e3742002-12-13 20:15:29 +00003754
ajs274a4a42004-12-07 15:39:31 +00003755 ret = zlog_set_file (NULL, fullpath, loglevel);
paul718e3742002-12-13 20:15:29 +00003756
paul9035efa2004-10-10 11:56:56 +00003757 if (p)
3758 XFREE (MTYPE_TMP, p);
3759
paul718e3742002-12-13 20:15:29 +00003760 if (!ret)
3761 {
ajs274a4a42004-12-07 15:39:31 +00003762 vty_out (vty, "can't open logfile %s\n", fname);
paul718e3742002-12-13 20:15:29 +00003763 return CMD_WARNING;
3764 }
3765
3766 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003767 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003768
paul05865c92005-10-26 05:49:54 +00003769 host.logfile = XSTRDUP (MTYPE_HOST, fname);
paul718e3742002-12-13 20:15:29 +00003770
3771 return CMD_SUCCESS;
3772}
3773
ajs274a4a42004-12-07 15:39:31 +00003774DEFUN (config_log_file,
3775 config_log_file_cmd,
3776 "log file FILENAME",
3777 "Logging control\n"
3778 "Logging to file\n"
3779 "Logging filename\n")
3780{
3781 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3782}
3783
3784DEFUN (config_log_file_level,
3785 config_log_file_level_cmd,
3786 "log file FILENAME "LOG_LEVELS,
3787 "Logging control\n"
3788 "Logging to file\n"
3789 "Logging filename\n"
3790 LOG_LEVEL_DESC)
3791{
3792 int level;
3793
3794 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3795 return CMD_ERR_NO_MATCH;
3796 return set_log_file(vty, argv[0], level);
3797}
3798
paul718e3742002-12-13 20:15:29 +00003799DEFUN (no_config_log_file,
3800 no_config_log_file_cmd,
3801 "no log file [FILENAME]",
3802 NO_STR
3803 "Logging control\n"
3804 "Cancel logging to file\n"
3805 "Logging file name\n")
3806{
3807 zlog_reset_file (NULL);
3808
3809 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003810 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003811
3812 host.logfile = NULL;
3813
3814 return CMD_SUCCESS;
3815}
3816
ajs274a4a42004-12-07 15:39:31 +00003817ALIAS (no_config_log_file,
3818 no_config_log_file_level_cmd,
3819 "no log file FILENAME LEVEL",
3820 NO_STR
3821 "Logging control\n"
3822 "Cancel logging to file\n"
3823 "Logging file name\n"
3824 "Logging level\n")
3825
paul718e3742002-12-13 20:15:29 +00003826DEFUN (config_log_syslog,
3827 config_log_syslog_cmd,
3828 "log syslog",
3829 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003830 "Set syslog logging level\n")
paul718e3742002-12-13 20:15:29 +00003831{
ajs274a4a42004-12-07 15:39:31 +00003832 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003833 return CMD_SUCCESS;
3834}
3835
ajs274a4a42004-12-07 15:39:31 +00003836DEFUN (config_log_syslog_level,
3837 config_log_syslog_level_cmd,
3838 "log syslog "LOG_LEVELS,
paul12ab19f2003-07-26 06:14:55 +00003839 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003840 "Set syslog logging level\n"
3841 LOG_LEVEL_DESC)
paul12ab19f2003-07-26 06:14:55 +00003842{
ajs274a4a42004-12-07 15:39:31 +00003843 int level;
paul12ab19f2003-07-26 06:14:55 +00003844
ajs274a4a42004-12-07 15:39:31 +00003845 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3846 return CMD_ERR_NO_MATCH;
3847 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3848 return CMD_SUCCESS;
3849}
paul12ab19f2003-07-26 06:14:55 +00003850
ajs274a4a42004-12-07 15:39:31 +00003851DEFUN_DEPRECATED (config_log_syslog_facility,
3852 config_log_syslog_facility_cmd,
3853 "log syslog facility "LOG_FACILITIES,
3854 "Logging control\n"
3855 "Logging goes to syslog\n"
3856 "(Deprecated) Facility parameter for syslog messages\n"
3857 LOG_FACILITY_DESC)
3858{
3859 int facility;
paul12ab19f2003-07-26 06:14:55 +00003860
ajs274a4a42004-12-07 15:39:31 +00003861 if ((facility = facility_match(argv[0])) < 0)
3862 return CMD_ERR_NO_MATCH;
3863
3864 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003865 zlog_default->facility = facility;
paul718e3742002-12-13 20:15:29 +00003866 return CMD_SUCCESS;
3867}
3868
3869DEFUN (no_config_log_syslog,
3870 no_config_log_syslog_cmd,
ajs274a4a42004-12-07 15:39:31 +00003871 "no log syslog [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003872 NO_STR
3873 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003874 "Cancel logging to syslog\n"
3875 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003876{
ajs274a4a42004-12-07 15:39:31 +00003877 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003878 return CMD_SUCCESS;
3879}
3880
paul12ab19f2003-07-26 06:14:55 +00003881ALIAS (no_config_log_syslog,
3882 no_config_log_syslog_facility_cmd,
ajs274a4a42004-12-07 15:39:31 +00003883 "no log syslog facility "LOG_FACILITIES,
paul12ab19f2003-07-26 06:14:55 +00003884 NO_STR
3885 "Logging control\n"
3886 "Logging goes to syslog\n"
3887 "Facility parameter for syslog messages\n"
ajs274a4a42004-12-07 15:39:31 +00003888 LOG_FACILITY_DESC)
paul12ab19f2003-07-26 06:14:55 +00003889
ajs274a4a42004-12-07 15:39:31 +00003890DEFUN (config_log_facility,
3891 config_log_facility_cmd,
3892 "log facility "LOG_FACILITIES,
paul718e3742002-12-13 20:15:29 +00003893 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003894 "Facility parameter for syslog messages\n"
3895 LOG_FACILITY_DESC)
paul718e3742002-12-13 20:15:29 +00003896{
ajs274a4a42004-12-07 15:39:31 +00003897 int facility;
3898
3899 if ((facility = facility_match(argv[0])) < 0)
3900 return CMD_ERR_NO_MATCH;
3901 zlog_default->facility = facility;
3902 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +00003903}
3904
ajs274a4a42004-12-07 15:39:31 +00003905DEFUN (no_config_log_facility,
3906 no_config_log_facility_cmd,
3907 "no log facility [FACILITY]",
paul718e3742002-12-13 20:15:29 +00003908 NO_STR
3909 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003910 "Reset syslog facility to default (daemon)\n"
3911 "Syslog facility\n")
paul718e3742002-12-13 20:15:29 +00003912{
ajs274a4a42004-12-07 15:39:31 +00003913 zlog_default->facility = LOG_DAEMON;
3914 return CMD_SUCCESS;
3915}
3916
3917DEFUN_DEPRECATED (config_log_trap,
3918 config_log_trap_cmd,
3919 "log trap "LOG_LEVELS,
3920 "Logging control\n"
3921 "(Deprecated) Set logging level and default for all destinations\n"
3922 LOG_LEVEL_DESC)
3923{
3924 int new_level ;
3925 int i;
3926
3927 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3928 return CMD_ERR_NO_MATCH;
3929
3930 zlog_default->default_lvl = new_level;
3931 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3932 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3933 zlog_default->maxlvl[i] = new_level;
3934 return CMD_SUCCESS;
3935}
3936
3937DEFUN_DEPRECATED (no_config_log_trap,
3938 no_config_log_trap_cmd,
3939 "no log trap [LEVEL]",
3940 NO_STR
3941 "Logging control\n"
3942 "Permit all logging information\n"
3943 "Logging level\n")
3944{
3945 zlog_default->default_lvl = LOG_DEBUG;
paul718e3742002-12-13 20:15:29 +00003946 return CMD_SUCCESS;
3947}
3948
3949DEFUN (config_log_record_priority,
3950 config_log_record_priority_cmd,
3951 "log record-priority",
3952 "Logging control\n"
3953 "Log the priority of the message within the message\n")
3954{
3955 zlog_default->record_priority = 1 ;
3956 return CMD_SUCCESS;
3957}
3958
3959DEFUN (no_config_log_record_priority,
3960 no_config_log_record_priority_cmd,
3961 "no log record-priority",
3962 NO_STR
3963 "Logging control\n"
3964 "Do not log the priority of the message within the message\n")
3965{
3966 zlog_default->record_priority = 0 ;
3967 return CMD_SUCCESS;
3968}
3969
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003970DEFUN (config_log_timestamp_precision,
3971 config_log_timestamp_precision_cmd,
3972 "log timestamp precision <0-6>",
3973 "Logging control\n"
3974 "Timestamp configuration\n"
3975 "Set the timestamp precision\n"
3976 "Number of subsecond digits\n")
3977{
3978 if (argc != 1)
3979 {
3980 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
3981 return CMD_WARNING;
3982 }
3983
3984 VTY_GET_INTEGER_RANGE("Timestamp Precision",
3985 zlog_default->timestamp_precision, argv[0], 0, 6);
3986 return CMD_SUCCESS;
3987}
3988
3989DEFUN (no_config_log_timestamp_precision,
3990 no_config_log_timestamp_precision_cmd,
3991 "no log timestamp precision",
3992 NO_STR
3993 "Logging control\n"
3994 "Timestamp configuration\n"
3995 "Reset the timestamp precision to the default value of 0\n")
3996{
3997 zlog_default->timestamp_precision = 0 ;
3998 return CMD_SUCCESS;
3999}
4000
paul3b0c5d92005-03-08 10:43:43 +00004001DEFUN (banner_motd_file,
4002 banner_motd_file_cmd,
4003 "banner motd file [FILE]",
4004 "Set banner\n"
4005 "Banner for motd\n"
4006 "Banner from a file\n"
4007 "Filename\n")
4008{
paulb45da6f2005-03-08 15:16:57 +00004009 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004010 XFREE (MTYPE_HOST, host.motdfile);
4011 host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
paulb45da6f2005-03-08 15:16:57 +00004012
paul3b0c5d92005-03-08 10:43:43 +00004013 return CMD_SUCCESS;
4014}
paul718e3742002-12-13 20:15:29 +00004015
4016DEFUN (banner_motd_default,
4017 banner_motd_default_cmd,
4018 "banner motd default",
4019 "Set banner string\n"
4020 "Strings for motd\n"
4021 "Default string\n")
4022{
4023 host.motd = default_motd;
4024 return CMD_SUCCESS;
4025}
4026
4027DEFUN (no_banner_motd,
4028 no_banner_motd_cmd,
4029 "no banner motd",
4030 NO_STR
4031 "Set banner string\n"
4032 "Strings for motd\n")
4033{
4034 host.motd = NULL;
paul22085182005-03-08 16:00:12 +00004035 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004036 XFREE (MTYPE_HOST, host.motdfile);
paul3b0c5d92005-03-08 10:43:43 +00004037 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004038 return CMD_SUCCESS;
4039}
4040
Lou Bergerf9ec4192016-01-12 13:41:48 -05004041DEFUN (show_commandtree,
4042 show_commandtree_cmd,
4043 "show commandtree",
4044 NO_STR
4045 "Show command tree\n")
4046{
4047 /* TBD */
4048 vector cmd_vector;
4049 unsigned int i;
4050
4051 vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE);
4052
4053 /* vector of all commands installed at this node */
4054 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
4055
4056 /* loop over all commands at this node */
4057 for (i = 0; i < vector_active(cmd_vector); ++i)
4058 {
4059 struct cmd_element *cmd_element;
4060
4061 /* A cmd_element (seems to be) is an individual command */
4062 if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
4063 continue;
4064
4065 vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE);
4066 }
4067
4068 vector_free (cmd_vector);
4069 return CMD_SUCCESS;
4070}
4071
paul718e3742002-12-13 20:15:29 +00004072/* Set config filename. Called from vty.c */
4073void
4074host_config_set (char *filename)
4075{
Chris Caputo228da422009-07-18 05:44:03 +00004076 if (host.config)
4077 XFREE (MTYPE_HOST, host.config);
paul05865c92005-10-26 05:49:54 +00004078 host.config = XSTRDUP (MTYPE_HOST, filename);
paul718e3742002-12-13 20:15:29 +00004079}
4080
Christian Franke41de6292016-05-03 19:59:41 +02004081const char *
4082host_config_get (void)
4083{
4084 return host.config;
4085}
4086
Paul Jakma92193662016-06-16 15:53:26 +01004087static void
4088install_default_basic (enum node_type node)
paul718e3742002-12-13 20:15:29 +00004089{
4090 install_element (node, &config_exit_cmd);
4091 install_element (node, &config_quit_cmd);
paul718e3742002-12-13 20:15:29 +00004092 install_element (node, &config_help_cmd);
4093 install_element (node, &config_list_cmd);
Paul Jakma92193662016-06-16 15:53:26 +01004094}
paul718e3742002-12-13 20:15:29 +00004095
Paul Jakma92193662016-06-16 15:53:26 +01004096/* Install common/default commands for a privileged node */
4097void
4098install_default (enum node_type node)
4099{
4100 /* VIEW_NODE is inited below, via install_default_basic, and
4101 install_element's of commands to VIEW_NODE automatically are
4102 also installed to ENABLE_NODE.
4103
4104 For all other nodes, we must ensure install_default_basic is
4105 also called/
4106 */
4107 if (node != VIEW_NODE && node != ENABLE_NODE)
4108 install_default_basic (node);
4109
4110 install_element (node, &config_end_cmd);
paul718e3742002-12-13 20:15:29 +00004111 install_element (node, &config_write_terminal_cmd);
4112 install_element (node, &config_write_file_cmd);
4113 install_element (node, &config_write_memory_cmd);
4114 install_element (node, &config_write_cmd);
4115 install_element (node, &show_running_config_cmd);
4116}
4117
4118/* Initialize command interface. Install basic nodes and commands. */
4119void
4120cmd_init (int terminal)
4121{
Christian Frankecd40b322013-09-30 12:27:51 +00004122 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4123 token_cr.type = TOKEN_TERMINAL;
David Lamparter10bac802015-05-05 11:10:20 +02004124 token_cr.terminal = TERMINAL_LITERAL;
Christian Frankecd40b322013-09-30 12:27:51 +00004125 token_cr.cmd = command_cr;
4126 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
Chris Caputo228da422009-07-18 05:44:03 +00004127
paul718e3742002-12-13 20:15:29 +00004128 /* Allocate initial top vector of commands. */
4129 cmdvec = vector_init (VECTOR_MIN_SIZE);
Paul Jakma92193662016-06-16 15:53:26 +01004130
paul718e3742002-12-13 20:15:29 +00004131 /* Default host value settings. */
4132 host.name = NULL;
4133 host.password = NULL;
4134 host.enable = NULL;
4135 host.logfile = NULL;
4136 host.config = NULL;
4137 host.lines = -1;
4138 host.motd = default_motd;
paul3b0c5d92005-03-08 10:43:43 +00004139 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004140
4141 /* Install top nodes. */
4142 install_node (&view_node, NULL);
4143 install_node (&enable_node, NULL);
4144 install_node (&auth_node, NULL);
4145 install_node (&auth_enable_node, NULL);
Paul Jakma62687ff2008-08-23 14:27:06 +01004146 install_node (&restricted_node, NULL);
paul718e3742002-12-13 20:15:29 +00004147 install_node (&config_node, config_write_host);
4148
4149 /* Each node's basic commands. */
4150 install_element (VIEW_NODE, &show_version_cmd);
4151 if (terminal)
4152 {
Paul Jakma92193662016-06-16 15:53:26 +01004153 install_default_basic (VIEW_NODE);
4154
paul718e3742002-12-13 20:15:29 +00004155 install_element (VIEW_NODE, &config_enable_cmd);
4156 install_element (VIEW_NODE, &config_terminal_length_cmd);
4157 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004158 install_element (VIEW_NODE, &show_logging_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004159 install_element (VIEW_NODE, &show_commandtree_cmd);
ajs2885f722004-12-17 23:16:33 +00004160 install_element (VIEW_NODE, &echo_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004161
Paul Jakma62687ff2008-08-23 14:27:06 +01004162 install_element (RESTRICTED_NODE, &config_enable_cmd);
4163 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4164 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004165 install_element (RESTRICTED_NODE, &show_commandtree_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004166 install_element (RESTRICTED_NODE, &echo_cmd);
paul718e3742002-12-13 20:15:29 +00004167 }
4168
4169 if (terminal)
4170 {
4171 install_default (ENABLE_NODE);
4172 install_element (ENABLE_NODE, &config_disable_cmd);
4173 install_element (ENABLE_NODE, &config_terminal_cmd);
4174 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4175 }
4176 install_element (ENABLE_NODE, &show_startup_config_cmd);
paul718e3742002-12-13 20:15:29 +00004177
4178 if (terminal)
paul718e3742002-12-13 20:15:29 +00004179 {
ajs274a4a42004-12-07 15:39:31 +00004180 install_element (ENABLE_NODE, &config_logmsg_cmd);
hassoe7168df2004-10-03 20:11:32 +00004181
4182 install_default (CONFIG_NODE);
hassoea8e9d92004-10-07 21:32:14 +00004183 }
4184
4185 install_element (CONFIG_NODE, &hostname_cmd);
4186 install_element (CONFIG_NODE, &no_hostname_cmd);
hassoe7168df2004-10-03 20:11:32 +00004187
hassoea8e9d92004-10-07 21:32:14 +00004188 if (terminal)
4189 {
hassoe7168df2004-10-03 20:11:32 +00004190 install_element (CONFIG_NODE, &password_cmd);
4191 install_element (CONFIG_NODE, &password_text_cmd);
4192 install_element (CONFIG_NODE, &enable_password_cmd);
4193 install_element (CONFIG_NODE, &enable_password_text_cmd);
4194 install_element (CONFIG_NODE, &no_enable_password_cmd);
4195
paul718e3742002-12-13 20:15:29 +00004196 install_element (CONFIG_NODE, &config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004197 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
paul718e3742002-12-13 20:15:29 +00004198 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004199 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4200 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4201 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00004202 install_element (CONFIG_NODE, &config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004203 install_element (CONFIG_NODE, &config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004204 install_element (CONFIG_NODE, &no_config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004205 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004206 install_element (CONFIG_NODE, &config_log_syslog_cmd);
ajs274a4a42004-12-07 15:39:31 +00004207 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
paul12ab19f2003-07-26 06:14:55 +00004208 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004209 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
paul12ab19f2003-07-26 06:14:55 +00004210 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
ajs274a4a42004-12-07 15:39:31 +00004211 install_element (CONFIG_NODE, &config_log_facility_cmd);
4212 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004213 install_element (CONFIG_NODE, &config_log_trap_cmd);
4214 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4215 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4216 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004217 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4218 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
paul718e3742002-12-13 20:15:29 +00004219 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4220 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4221 install_element (CONFIG_NODE, &banner_motd_default_cmd);
paul3b0c5d92005-03-08 10:43:43 +00004222 install_element (CONFIG_NODE, &banner_motd_file_cmd);
paul718e3742002-12-13 20:15:29 +00004223 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4224 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4225 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
paul718e3742002-12-13 20:15:29 +00004226
paul354d1192005-04-25 16:26:42 +00004227 install_element (VIEW_NODE, &show_thread_cpu_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004228 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
Paul Jakmae276eb82010-01-09 16:15:00 +00004229
4230 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
paul354d1192005-04-25 16:26:42 +00004231 install_element (VIEW_NODE, &show_work_queues_cmd);
paul9ab68122003-01-18 01:16:20 +00004232 }
Lou Bergerf9ec4192016-01-12 13:41:48 -05004233 install_element (CONFIG_NODE, &show_commandtree_cmd);
Donald Sharpf31bab42015-06-19 19:26:19 -04004234 srandom(time(NULL));
paul718e3742002-12-13 20:15:29 +00004235}
Chris Caputo228da422009-07-18 05:44:03 +00004236
Christian Frankecd40b322013-09-30 12:27:51 +00004237static void
4238cmd_terminate_token(struct cmd_token *token)
4239{
4240 unsigned int i, j;
4241 vector keyword_vect;
4242
4243 if (token->multiple)
4244 {
4245 for (i = 0; i < vector_active(token->multiple); i++)
4246 cmd_terminate_token(vector_slot(token->multiple, i));
4247 vector_free(token->multiple);
4248 token->multiple = NULL;
4249 }
4250
4251 if (token->keyword)
4252 {
4253 for (i = 0; i < vector_active(token->keyword); i++)
4254 {
4255 keyword_vect = vector_slot(token->keyword, i);
4256 for (j = 0; j < vector_active(keyword_vect); j++)
4257 cmd_terminate_token(vector_slot(keyword_vect, j));
4258 vector_free(keyword_vect);
4259 }
4260 vector_free(token->keyword);
4261 token->keyword = NULL;
4262 }
4263
4264 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4265 XFREE(MTYPE_CMD_TOKENS, token->desc);
4266
4267 XFREE(MTYPE_CMD_TOKENS, token);
4268}
4269
4270static void
4271cmd_terminate_element(struct cmd_element *cmd)
4272{
4273 unsigned int i;
4274
4275 if (cmd->tokens == NULL)
4276 return;
4277
4278 for (i = 0; i < vector_active(cmd->tokens); i++)
4279 cmd_terminate_token(vector_slot(cmd->tokens, i));
4280
4281 vector_free(cmd->tokens);
4282 cmd->tokens = NULL;
4283}
4284
Chris Caputo228da422009-07-18 05:44:03 +00004285void
4286cmd_terminate ()
4287{
Christian Frankecd40b322013-09-30 12:27:51 +00004288 unsigned int i, j;
Chris Caputo228da422009-07-18 05:44:03 +00004289 struct cmd_node *cmd_node;
4290 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00004291 vector cmd_node_v;
Chris Caputo228da422009-07-18 05:44:03 +00004292
4293 if (cmdvec)
4294 {
4295 for (i = 0; i < vector_active (cmdvec); i++)
4296 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4297 {
4298 cmd_node_v = cmd_node->cmd_vector;
4299
4300 for (j = 0; j < vector_active (cmd_node_v); j++)
Christian Frankecd40b322013-09-30 12:27:51 +00004301 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4302 cmd_terminate_element(cmd_element);
Chris Caputo228da422009-07-18 05:44:03 +00004303
4304 vector_free (cmd_node_v);
Paul Jakma92193662016-06-16 15:53:26 +01004305 hash_clean (cmd_node->cmd_hash, NULL);
4306 hash_free (cmd_node->cmd_hash);
4307 cmd_node->cmd_hash = NULL;
Chris Caputo228da422009-07-18 05:44:03 +00004308 }
4309
4310 vector_free (cmdvec);
4311 cmdvec = NULL;
4312 }
4313
4314 if (command_cr)
Christian Frankecd40b322013-09-30 12:27:51 +00004315 XFREE(MTYPE_CMD_TOKENS, command_cr);
4316 if (token_cr.desc)
4317 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
Chris Caputo228da422009-07-18 05:44:03 +00004318 if (host.name)
4319 XFREE (MTYPE_HOST, host.name);
4320 if (host.password)
4321 XFREE (MTYPE_HOST, host.password);
4322 if (host.password_encrypt)
4323 XFREE (MTYPE_HOST, host.password_encrypt);
4324 if (host.enable)
4325 XFREE (MTYPE_HOST, host.enable);
4326 if (host.enable_encrypt)
4327 XFREE (MTYPE_HOST, host.enable_encrypt);
4328 if (host.logfile)
4329 XFREE (MTYPE_HOST, host.logfile);
4330 if (host.motdfile)
4331 XFREE (MTYPE_HOST, host.motdfile);
4332 if (host.config)
4333 XFREE (MTYPE_HOST, host.config);
4334}