blob: ab46fc4a6d115bcee74f4713b1e4bad08e266892 [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)
444 format_parser_error(state, "Unepexted ')'");
445
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 {
641 fprintf (stderr,
642 "Multiple command installs to node %d of command:\n%s\n",
643 ntype, cmd->string);
644 return;
645 }
646
647 assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern));
648
paul718e3742002-12-13 20:15:29 +0000649 vector_set (cnode->cmd_vector, cmd);
Christian Frankecd40b322013-09-30 12:27:51 +0000650 if (cmd->tokens == NULL)
651 cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
Donald Sharpb9ac2f32016-03-11 14:27:12 -0500652
653 if (ntype == VIEW_NODE)
654 install_element (ENABLE_NODE, cmd);
paul718e3742002-12-13 20:15:29 +0000655}
656
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300657static const unsigned char itoa64[] =
paul718e3742002-12-13 20:15:29 +0000658"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
659
ajs274a4a42004-12-07 15:39:31 +0000660static void
paul718e3742002-12-13 20:15:29 +0000661to64(char *s, long v, int n)
662{
663 while (--n >= 0)
664 {
665 *s++ = itoa64[v&0x3f];
666 v >>= 6;
667 }
668}
669
ajs274a4a42004-12-07 15:39:31 +0000670static char *
671zencrypt (const char *passwd)
paul718e3742002-12-13 20:15:29 +0000672{
673 char salt[6];
674 struct timeval tv;
675 char *crypt (const char *, const char *);
676
677 gettimeofday(&tv,0);
678
679 to64(&salt[0], random(), 3);
680 to64(&salt[3], tv.tv_usec, 3);
681 salt[5] = '\0';
682
683 return crypt (passwd, salt);
684}
685
686/* This function write configuration of this host. */
ajs274a4a42004-12-07 15:39:31 +0000687static int
paul718e3742002-12-13 20:15:29 +0000688config_write_host (struct vty *vty)
689{
690 if (host.name)
691 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
692
693 if (host.encrypt)
694 {
695 if (host.password_encrypt)
696 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
697 if (host.enable_encrypt)
698 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
699 }
700 else
701 {
702 if (host.password)
703 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
704 if (host.enable)
705 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
706 }
707
ajs274a4a42004-12-07 15:39:31 +0000708 if (zlog_default->default_lvl != LOG_DEBUG)
ajs82146b82004-12-07 17:15:55 +0000709 {
710 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
711 VTY_NEWLINE);
712 vty_out (vty, "log trap %s%s",
713 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
714 }
paul718e3742002-12-13 20:15:29 +0000715
ajs274a4a42004-12-07 15:39:31 +0000716 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
paul12ab19f2003-07-26 06:14:55 +0000717 {
ajs274a4a42004-12-07 15:39:31 +0000718 vty_out (vty, "log file %s", host.logfile);
719 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
720 vty_out (vty, " %s",
721 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
paul12ab19f2003-07-26 06:14:55 +0000722 vty_out (vty, "%s", VTY_NEWLINE);
723 }
ajs274a4a42004-12-07 15:39:31 +0000724
725 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
726 {
727 vty_out (vty, "log stdout");
728 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
729 vty_out (vty, " %s",
730 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
731 vty_out (vty, "%s", VTY_NEWLINE);
732 }
733
734 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
735 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
736 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
737 vty_out(vty,"log monitor %s%s",
738 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
739
740 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
741 {
742 vty_out (vty, "log syslog");
743 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
744 vty_out (vty, " %s",
745 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
746 vty_out (vty, "%s", VTY_NEWLINE);
747 }
748
749 if (zlog_default->facility != LOG_DAEMON)
750 vty_out (vty, "log facility %s%s",
751 facility_name(zlog_default->facility), VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000752
753 if (zlog_default->record_priority == 1)
754 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
755
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000756 if (zlog_default->timestamp_precision > 0)
757 vty_out (vty, "log timestamp precision %d%s",
758 zlog_default->timestamp_precision, VTY_NEWLINE);
759
paul718e3742002-12-13 20:15:29 +0000760 if (host.advanced)
761 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
762
763 if (host.encrypt)
764 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
765
766 if (host.lines >= 0)
767 vty_out (vty, "service terminal-length %d%s", host.lines,
768 VTY_NEWLINE);
769
paul3b0c5d92005-03-08 10:43:43 +0000770 if (host.motdfile)
771 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
772 else if (! host.motd)
paul718e3742002-12-13 20:15:29 +0000773 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
774
775 return 1;
776}
777
778/* Utility function for getting command vector. */
ajs274a4a42004-12-07 15:39:31 +0000779static vector
paul718e3742002-12-13 20:15:29 +0000780cmd_node_vector (vector v, enum node_type ntype)
781{
782 struct cmd_node *cnode = vector_slot (v, ntype);
783 return cnode->cmd_vector;
784}
785
ajs274a4a42004-12-07 15:39:31 +0000786#if 0
787/* Filter command vector by symbol. This function is not actually used;
788 * should it be deleted? */
789static int
paul718e3742002-12-13 20:15:29 +0000790cmd_filter_by_symbol (char *command, char *symbol)
791{
792 int i, lim;
793
794 if (strcmp (symbol, "IPV4_ADDRESS") == 0)
795 {
796 i = 0;
797 lim = strlen (command);
798 while (i < lim)
799 {
800 if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
801 return 1;
802 i++;
803 }
804 return 0;
805 }
806 if (strcmp (symbol, "STRING") == 0)
807 {
808 i = 0;
809 lim = strlen (command);
810 while (i < lim)
811 {
812 if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
813 return 1;
814 i++;
815 }
816 return 0;
817 }
818 if (strcmp (symbol, "IFNAME") == 0)
819 {
820 i = 0;
821 lim = strlen (command);
822 while (i < lim)
823 {
824 if (! isalnum ((int) command[i]))
825 return 1;
826 i++;
827 }
828 return 0;
829 }
830 return 0;
831}
ajs274a4a42004-12-07 15:39:31 +0000832#endif
paul718e3742002-12-13 20:15:29 +0000833
834/* Completion match types. */
835enum match_type
836{
837 no_match,
838 extend_match,
839 ipv4_prefix_match,
840 ipv4_match,
841 ipv6_prefix_match,
842 ipv6_match,
843 range_match,
844 vararg_match,
845 partly_match,
846 exact_match
847};
848
ajs274a4a42004-12-07 15:39:31 +0000849static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000850cmd_ipv4_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000851{
hasso8c328f12004-10-05 21:01:23 +0000852 const char *sp;
paul718e3742002-12-13 20:15:29 +0000853 int dots = 0, nums = 0;
854 char buf[4];
855
856 if (str == NULL)
857 return partly_match;
858
859 for (;;)
860 {
861 memset (buf, 0, sizeof (buf));
862 sp = str;
863 while (*str != '\0')
864 {
865 if (*str == '.')
866 {
867 if (dots >= 3)
868 return no_match;
869
870 if (*(str + 1) == '.')
871 return no_match;
872
873 if (*(str + 1) == '\0')
874 return partly_match;
875
876 dots++;
877 break;
878 }
879 if (!isdigit ((int) *str))
880 return no_match;
881
882 str++;
883 }
884
885 if (str - sp > 3)
886 return no_match;
887
888 strncpy (buf, sp, str - sp);
889 if (atoi (buf) > 255)
890 return no_match;
891
892 nums++;
893
894 if (*str == '\0')
895 break;
896
897 str++;
898 }
899
900 if (nums < 4)
901 return partly_match;
902
903 return exact_match;
904}
905
ajs274a4a42004-12-07 15:39:31 +0000906static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000907cmd_ipv4_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000908{
hasso8c328f12004-10-05 21:01:23 +0000909 const char *sp;
paul718e3742002-12-13 20:15:29 +0000910 int dots = 0;
911 char buf[4];
912
913 if (str == NULL)
914 return partly_match;
915
916 for (;;)
917 {
918 memset (buf, 0, sizeof (buf));
919 sp = str;
920 while (*str != '\0' && *str != '/')
921 {
922 if (*str == '.')
923 {
924 if (dots == 3)
925 return no_match;
926
927 if (*(str + 1) == '.' || *(str + 1) == '/')
928 return no_match;
929
930 if (*(str + 1) == '\0')
931 return partly_match;
932
933 dots++;
934 break;
935 }
936
937 if (!isdigit ((int) *str))
938 return no_match;
939
940 str++;
941 }
942
943 if (str - sp > 3)
944 return no_match;
945
946 strncpy (buf, sp, str - sp);
947 if (atoi (buf) > 255)
948 return no_match;
949
950 if (dots == 3)
951 {
952 if (*str == '/')
953 {
954 if (*(str + 1) == '\0')
955 return partly_match;
956
957 str++;
958 break;
959 }
960 else if (*str == '\0')
961 return partly_match;
962 }
963
964 if (*str == '\0')
965 return partly_match;
966
967 str++;
968 }
969
970 sp = str;
971 while (*str != '\0')
972 {
973 if (!isdigit ((int) *str))
974 return no_match;
975
976 str++;
977 }
978
979 if (atoi (sp) > 32)
980 return no_match;
981
982 return exact_match;
983}
984
985#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
986#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
987#define STATE_START 1
988#define STATE_COLON 2
989#define STATE_DOUBLE 3
990#define STATE_ADDR 4
991#define STATE_DOT 5
992#define STATE_SLASH 6
993#define STATE_MASK 7
994
paul22e0a9e2003-07-11 17:55:46 +0000995#ifdef HAVE_IPV6
996
ajs274a4a42004-12-07 15:39:31 +0000997static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000998cmd_ipv6_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000999{
hasso726f9b22003-05-25 21:04:54 +00001000 struct sockaddr_in6 sin6_dummy;
1001 int ret;
paul718e3742002-12-13 20:15:29 +00001002
1003 if (str == NULL)
1004 return partly_match;
1005
1006 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
1007 return no_match;
1008
hasso726f9b22003-05-25 21:04:54 +00001009 /* use inet_pton that has a better support,
1010 * for example inet_pton can support the automatic addresses:
1011 * ::1.2.3.4
1012 */
1013 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1014
1015 if (ret == 1)
1016 return exact_match;
1017
Roman Hoog Antink7c9c6ae2012-05-09 06:35:34 +00001018 return no_match;
paul718e3742002-12-13 20:15:29 +00001019}
1020
ajs274a4a42004-12-07 15:39:31 +00001021static enum match_type
hasso8c328f12004-10-05 21:01:23 +00001022cmd_ipv6_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +00001023{
1024 int state = STATE_START;
1025 int colons = 0, nums = 0, double_colon = 0;
1026 int mask;
hasso8c328f12004-10-05 21:01:23 +00001027 const char *sp = NULL;
paul718e3742002-12-13 20:15:29 +00001028 char *endptr = NULL;
1029
1030 if (str == NULL)
1031 return partly_match;
1032
1033 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
1034 return no_match;
1035
1036 while (*str != '\0' && state != STATE_MASK)
1037 {
1038 switch (state)
1039 {
1040 case STATE_START:
1041 if (*str == ':')
1042 {
1043 if (*(str + 1) != ':' && *(str + 1) != '\0')
1044 return no_match;
1045 colons--;
1046 state = STATE_COLON;
1047 }
1048 else
1049 {
1050 sp = str;
1051 state = STATE_ADDR;
1052 }
1053
1054 continue;
1055 case STATE_COLON:
1056 colons++;
1057 if (*(str + 1) == '/')
1058 return no_match;
1059 else if (*(str + 1) == ':')
1060 state = STATE_DOUBLE;
1061 else
1062 {
1063 sp = str + 1;
1064 state = STATE_ADDR;
1065 }
1066 break;
1067 case STATE_DOUBLE:
1068 if (double_colon)
1069 return no_match;
1070
1071 if (*(str + 1) == ':')
1072 return no_match;
1073 else
1074 {
1075 if (*(str + 1) != '\0' && *(str + 1) != '/')
1076 colons++;
1077 sp = str + 1;
1078
1079 if (*(str + 1) == '/')
1080 state = STATE_SLASH;
1081 else
1082 state = STATE_ADDR;
1083 }
1084
1085 double_colon++;
1086 nums += 1;
1087 break;
1088 case STATE_ADDR:
1089 if (*(str + 1) == ':' || *(str + 1) == '.'
1090 || *(str + 1) == '\0' || *(str + 1) == '/')
1091 {
1092 if (str - sp > 3)
1093 return no_match;
1094
1095 for (; sp <= str; sp++)
1096 if (*sp == '/')
1097 return no_match;
1098
1099 nums++;
1100
1101 if (*(str + 1) == ':')
1102 state = STATE_COLON;
1103 else if (*(str + 1) == '.')
David Lamparteraa5cf242012-07-19 16:11:50 +02001104 {
1105 if (colons || double_colon)
1106 state = STATE_DOT;
1107 else
1108 return no_match;
1109 }
paul718e3742002-12-13 20:15:29 +00001110 else if (*(str + 1) == '/')
1111 state = STATE_SLASH;
1112 }
1113 break;
1114 case STATE_DOT:
1115 state = STATE_ADDR;
1116 break;
1117 case STATE_SLASH:
1118 if (*(str + 1) == '\0')
1119 return partly_match;
1120
1121 state = STATE_MASK;
1122 break;
1123 default:
1124 break;
1125 }
1126
1127 if (nums > 11)
1128 return no_match;
1129
1130 if (colons > 7)
1131 return no_match;
1132
1133 str++;
1134 }
1135
1136 if (state < STATE_MASK)
1137 return partly_match;
1138
1139 mask = strtol (str, &endptr, 10);
1140 if (*endptr != '\0')
1141 return no_match;
1142
1143 if (mask < 0 || mask > 128)
1144 return no_match;
1145
1146/* I don't know why mask < 13 makes command match partly.
1147 Forgive me to make this comments. I Want to set static default route
1148 because of lack of function to originate default in ospf6d; sorry
1149 yasu
1150 if (mask < 13)
1151 return partly_match;
1152*/
1153
1154 return exact_match;
1155}
1156
paul22e0a9e2003-07-11 17:55:46 +00001157#endif /* HAVE_IPV6 */
1158
paul718e3742002-12-13 20:15:29 +00001159#define DECIMAL_STRLEN_MAX 10
1160
ajs274a4a42004-12-07 15:39:31 +00001161static int
hasso8c328f12004-10-05 21:01:23 +00001162cmd_range_match (const char *range, const char *str)
paul718e3742002-12-13 20:15:29 +00001163{
1164 char *p;
1165 char buf[DECIMAL_STRLEN_MAX + 1];
1166 char *endptr = NULL;
1167 unsigned long min, max, val;
1168
1169 if (str == NULL)
1170 return 1;
1171
1172 val = strtoul (str, &endptr, 10);
1173 if (*endptr != '\0')
1174 return 0;
1175
1176 range++;
1177 p = strchr (range, '-');
1178 if (p == NULL)
1179 return 0;
1180 if (p - range > DECIMAL_STRLEN_MAX)
1181 return 0;
1182 strncpy (buf, range, p - range);
1183 buf[p - range] = '\0';
1184 min = strtoul (buf, &endptr, 10);
1185 if (*endptr != '\0')
1186 return 0;
1187
1188 range = p + 1;
1189 p = strchr (range, '>');
1190 if (p == NULL)
1191 return 0;
1192 if (p - range > DECIMAL_STRLEN_MAX)
1193 return 0;
1194 strncpy (buf, range, p - range);
1195 buf[p - range] = '\0';
1196 max = strtoul (buf, &endptr, 10);
1197 if (*endptr != '\0')
1198 return 0;
1199
1200 if (val < min || val > max)
1201 return 0;
1202
1203 return 1;
1204}
1205
ajs274a4a42004-12-07 15:39:31 +00001206static enum match_type
Christian Frankecd40b322013-09-30 12:27:51 +00001207cmd_word_match(struct cmd_token *token,
1208 enum filter_type filter,
1209 const char *word)
paul718e3742002-12-13 20:15:29 +00001210{
hasso8c328f12004-10-05 21:01:23 +00001211 const char *str;
paul718e3742002-12-13 20:15:29 +00001212 enum match_type match_type;
paul909a2152005-03-14 17:41:45 +00001213
Christian Frankecd40b322013-09-30 12:27:51 +00001214 str = token->cmd;
paul718e3742002-12-13 20:15:29 +00001215
Christian Frankecd40b322013-09-30 12:27:51 +00001216 if (filter == FILTER_RELAXED)
1217 if (!word || !strlen(word))
1218 return partly_match;
paul718e3742002-12-13 20:15:29 +00001219
Christian Frankecd40b322013-09-30 12:27:51 +00001220 if (!word)
1221 return no_match;
paul909a2152005-03-14 17:41:45 +00001222
David Lamparter10bac802015-05-05 11:10:20 +02001223 switch (token->terminal)
Christian Frankecd40b322013-09-30 12:27:51 +00001224 {
David Lamparter10bac802015-05-05 11:10:20 +02001225 case TERMINAL_VARARG:
1226 return vararg_match;
1227
1228 case TERMINAL_RANGE:
1229 if (cmd_range_match(str, word))
1230 return range_match;
1231 break;
1232
1233 case TERMINAL_IPV6:
1234 match_type = cmd_ipv6_match(word);
1235 if ((filter == FILTER_RELAXED && match_type != no_match)
Christian Frankecd40b322013-09-30 12:27:51 +00001236 || (filter == FILTER_STRICT && match_type == exact_match))
David Lamparter10bac802015-05-05 11:10:20 +02001237 return ipv6_match;
1238 break;
1239
1240 case TERMINAL_IPV6_PREFIX:
1241 match_type = cmd_ipv6_prefix_match(word);
1242 if ((filter == FILTER_RELAXED && match_type != no_match)
1243 || (filter == FILTER_STRICT && match_type == exact_match))
1244 return ipv6_prefix_match;
1245 break;
1246
1247 case TERMINAL_IPV4:
1248 match_type = cmd_ipv4_match(word);
1249 if ((filter == FILTER_RELAXED && match_type != no_match)
1250 || (filter == FILTER_STRICT && match_type == exact_match))
1251 return ipv4_match;
1252 break;
1253
1254 case TERMINAL_IPV4_PREFIX:
1255 match_type = cmd_ipv4_prefix_match(word);
1256 if ((filter == FILTER_RELAXED && match_type != no_match)
1257 || (filter == FILTER_STRICT && match_type == exact_match))
1258 return ipv4_prefix_match;
1259 break;
1260
1261 case TERMINAL_OPTION:
1262 case TERMINAL_VARIABLE:
1263 return extend_match;
1264
1265 case TERMINAL_LITERAL:
1266 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1267 {
1268 if (!strcmp(str, word))
1269 return exact_match;
1270 return partly_match;
1271 }
1272 if (filter == FILTER_STRICT && !strcmp(str, word))
1273 return exact_match;
1274 break;
1275
1276 default:
1277 assert (0);
Christian Frankecd40b322013-09-30 12:27:51 +00001278 }
paul718e3742002-12-13 20:15:29 +00001279
Christian Frankecd40b322013-09-30 12:27:51 +00001280 return no_match;
paul718e3742002-12-13 20:15:29 +00001281}
1282
Christian Frankecd40b322013-09-30 12:27:51 +00001283struct cmd_matcher
1284{
1285 struct cmd_element *cmd; /* The command element the matcher is using */
1286 enum filter_type filter; /* Whether to use strict or relaxed matching */
1287 vector vline; /* The tokenized commandline which is to be matched */
1288 unsigned int index; /* The index up to which matching should be done */
1289
1290 /* If set, construct a list of matches at the position given by index */
1291 enum match_type *match_type;
1292 vector *match;
1293
1294 unsigned int word_index; /* iterating over vline */
1295};
1296
1297static int
1298push_argument(int *argc, const char **argv, const char *arg)
1299{
1300 if (!arg || !strlen(arg))
1301 arg = NULL;
1302
1303 if (!argc || !argv)
1304 return 0;
1305
1306 if (*argc >= CMD_ARGC_MAX)
1307 return -1;
1308
1309 argv[(*argc)++] = arg;
1310 return 0;
1311}
1312
1313static void
1314cmd_matcher_record_match(struct cmd_matcher *matcher,
1315 enum match_type match_type,
1316 struct cmd_token *token)
1317{
1318 if (matcher->word_index != matcher->index)
1319 return;
1320
1321 if (matcher->match)
1322 {
1323 if (!*matcher->match)
1324 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1325 vector_set(*matcher->match, token);
1326 }
1327
1328 if (matcher->match_type)
1329 {
1330 if (match_type > *matcher->match_type)
1331 *matcher->match_type = match_type;
1332 }
1333}
1334
1335static int
1336cmd_matcher_words_left(struct cmd_matcher *matcher)
1337{
1338 return matcher->word_index < vector_active(matcher->vline);
1339}
1340
1341static const char*
1342cmd_matcher_get_word(struct cmd_matcher *matcher)
1343{
1344 assert(cmd_matcher_words_left(matcher));
1345
1346 return vector_slot(matcher->vline, matcher->word_index);
1347}
1348
1349static enum matcher_rv
1350cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1351 struct cmd_token *token,
1352 int *argc, const char **argv)
1353{
1354 const char *word;
1355 enum match_type word_match;
1356
1357 assert(token->type == TOKEN_TERMINAL);
1358
1359 if (!cmd_matcher_words_left(matcher))
1360 {
David Lamparter10bac802015-05-05 11:10:20 +02001361 if (token->terminal == TERMINAL_OPTION)
Christian Frankecd40b322013-09-30 12:27:51 +00001362 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1363 else
1364 return MATCHER_INCOMPLETE;
1365 }
1366
1367 word = cmd_matcher_get_word(matcher);
1368 word_match = cmd_word_match(token, matcher->filter, word);
1369 if (word_match == no_match)
1370 return MATCHER_NO_MATCH;
1371
1372 /* We have to record the input word as argument if it matched
1373 * against a variable. */
David Lamparter14162932015-05-12 17:18:04 +02001374 if (TERMINAL_RECORD (token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001375 {
1376 if (push_argument(argc, argv, word))
1377 return MATCHER_EXCEED_ARGC_MAX;
1378 }
1379
1380 cmd_matcher_record_match(matcher, word_match, token);
1381
1382 matcher->word_index++;
1383
1384 /* A vararg token should consume all left over words as arguments */
David Lamparter10bac802015-05-05 11:10:20 +02001385 if (token->terminal == TERMINAL_VARARG)
Christian Frankecd40b322013-09-30 12:27:51 +00001386 while (cmd_matcher_words_left(matcher))
1387 {
1388 word = cmd_matcher_get_word(matcher);
1389 if (word && strlen(word))
1390 push_argument(argc, argv, word);
1391 matcher->word_index++;
1392 }
1393
1394 return MATCHER_OK;
1395}
1396
1397static enum matcher_rv
1398cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1399 struct cmd_token *token,
1400 int *argc, const char **argv)
1401{
1402 enum match_type multiple_match;
1403 unsigned int multiple_index;
1404 const char *word;
David Lamparterab90fc02015-03-03 09:07:25 +01001405 const char *arg = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001406 struct cmd_token *word_token;
1407 enum match_type word_match;
1408
1409 assert(token->type == TOKEN_MULTIPLE);
1410
1411 multiple_match = no_match;
1412
1413 if (!cmd_matcher_words_left(matcher))
1414 return MATCHER_INCOMPLETE;
1415
1416 word = cmd_matcher_get_word(matcher);
1417 for (multiple_index = 0;
1418 multiple_index < vector_active(token->multiple);
1419 multiple_index++)
1420 {
1421 word_token = vector_slot(token->multiple, multiple_index);
1422
1423 word_match = cmd_word_match(word_token, matcher->filter, word);
1424 if (word_match == no_match)
1425 continue;
1426
1427 cmd_matcher_record_match(matcher, word_match, word_token);
1428
1429 if (word_match > multiple_match)
1430 {
1431 multiple_match = word_match;
1432 arg = word;
1433 }
1434 /* To mimic the behavior of the old command implementation, we
1435 * tolerate any ambiguities here :/ */
1436 }
1437
1438 matcher->word_index++;
1439
1440 if (multiple_match == no_match)
1441 return MATCHER_NO_MATCH;
1442
1443 if (push_argument(argc, argv, arg))
1444 return MATCHER_EXCEED_ARGC_MAX;
1445
1446 return MATCHER_OK;
1447}
1448
1449static enum matcher_rv
1450cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1451 struct cmd_token *token,
1452 vector args_vector)
paul718e3742002-12-13 20:15:29 +00001453{
hasso8c328f12004-10-05 21:01:23 +00001454 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00001455 unsigned long keyword_mask;
1456 unsigned int keyword_found;
1457 enum match_type keyword_match;
1458 enum match_type word_match;
1459 vector keyword_vector;
1460 struct cmd_token *word_token;
1461 const char *word;
1462 int keyword_argc;
1463 const char **keyword_argv;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001464 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001465
1466 keyword_mask = 0;
1467 while (1)
1468 {
1469 if (!cmd_matcher_words_left(matcher))
1470 return MATCHER_OK;
1471
1472 word = cmd_matcher_get_word(matcher);
1473
1474 keyword_found = -1;
1475 keyword_match = no_match;
1476 for (i = 0; i < vector_active(token->keyword); i++)
1477 {
1478 if (keyword_mask & (1 << i))
1479 continue;
1480
1481 keyword_vector = vector_slot(token->keyword, i);
1482 word_token = vector_slot(keyword_vector, 0);
1483
1484 word_match = cmd_word_match(word_token, matcher->filter, word);
1485 if (word_match == no_match)
1486 continue;
1487
1488 cmd_matcher_record_match(matcher, word_match, word_token);
1489
1490 if (word_match > keyword_match)
1491 {
1492 keyword_match = word_match;
1493 keyword_found = i;
1494 }
1495 else if (word_match == keyword_match)
1496 {
1497 if (matcher->word_index != matcher->index || args_vector)
1498 return MATCHER_AMBIGUOUS;
1499 }
1500 }
1501
1502 if (keyword_found == (unsigned int)-1)
1503 return MATCHER_NO_MATCH;
1504
1505 matcher->word_index++;
1506
1507 if (matcher->word_index > matcher->index)
1508 return MATCHER_OK;
1509
1510 keyword_mask |= (1 << keyword_found);
1511
1512 if (args_vector)
1513 {
1514 keyword_argc = 0;
1515 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1516 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1517 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1518 keyword_argv[i] = (void*)-1;
1519 vector_set_index(args_vector, keyword_found, keyword_argv);
1520 }
1521 else
1522 {
1523 keyword_argv = NULL;
1524 }
1525
1526 keyword_vector = vector_slot(token->keyword, keyword_found);
1527 /* the keyword itself is at 0. We are only interested in the arguments,
1528 * so start counting at 1. */
1529 for (i = 1; i < vector_active(keyword_vector); i++)
1530 {
1531 word_token = vector_slot(keyword_vector, i);
1532
1533 switch (word_token->type)
1534 {
1535 case TOKEN_TERMINAL:
1536 rv = cmd_matcher_match_terminal(matcher, word_token,
1537 &keyword_argc, keyword_argv);
1538 break;
1539 case TOKEN_MULTIPLE:
1540 rv = cmd_matcher_match_multiple(matcher, word_token,
1541 &keyword_argc, keyword_argv);
1542 break;
1543 case TOKEN_KEYWORD:
1544 assert(!"Keywords should never be nested.");
1545 break;
1546 }
1547
1548 if (MATCHER_ERROR(rv))
1549 return rv;
1550
1551 if (matcher->word_index > matcher->index)
1552 return MATCHER_OK;
1553 }
1554 }
1555 /* not reached */
1556}
1557
1558static enum matcher_rv
1559cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1560 struct cmd_token *token,
1561 int *argc, const char **argv,
1562 vector keyword_args_vector)
1563{
1564 unsigned int i, j;
1565 const char **keyword_args;
1566 vector keyword_vector;
1567 struct cmd_token *word_token;
1568 const char *arg;
1569 enum matcher_rv rv;
1570
1571 rv = MATCHER_OK;
1572
1573 if (keyword_args_vector == NULL)
1574 return rv;
1575
1576 for (i = 0; i < vector_active(token->keyword); i++)
1577 {
1578 keyword_vector = vector_slot(token->keyword, i);
1579 keyword_args = vector_lookup(keyword_args_vector, i);
1580
1581 if (vector_active(keyword_vector) == 1)
1582 {
1583 /* this is a keyword without arguments */
1584 if (keyword_args)
1585 {
1586 word_token = vector_slot(keyword_vector, 0);
1587 arg = word_token->cmd;
1588 }
1589 else
1590 {
1591 arg = NULL;
1592 }
1593
1594 if (push_argument(argc, argv, arg))
1595 rv = MATCHER_EXCEED_ARGC_MAX;
1596 }
1597 else
1598 {
1599 /* this is a keyword with arguments */
1600 if (keyword_args)
1601 {
1602 /* the keyword was present, so just fill in the arguments */
1603 for (j = 0; keyword_args[j] != (void*)-1; j++)
1604 if (push_argument(argc, argv, keyword_args[j]))
1605 rv = MATCHER_EXCEED_ARGC_MAX;
1606 XFREE(MTYPE_TMP, keyword_args);
1607 }
1608 else
1609 {
1610 /* the keyword was not present, insert NULL for the arguments
1611 * the keyword would have taken. */
1612 for (j = 1; j < vector_active(keyword_vector); j++)
1613 {
1614 word_token = vector_slot(keyword_vector, j);
1615 if ((word_token->type == TOKEN_TERMINAL
David Lamparter14162932015-05-12 17:18:04 +02001616 && TERMINAL_RECORD (word_token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001617 || word_token->type == TOKEN_MULTIPLE)
1618 {
1619 if (push_argument(argc, argv, NULL))
1620 rv = MATCHER_EXCEED_ARGC_MAX;
1621 }
1622 }
1623 }
1624 }
1625 }
1626 vector_free(keyword_args_vector);
1627 return rv;
1628}
1629
1630static enum matcher_rv
1631cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1632 struct cmd_token *token,
1633 int *argc, const char **argv)
1634{
1635 vector keyword_args_vector;
1636 enum matcher_rv reader_rv;
1637 enum matcher_rv builder_rv;
1638
1639 assert(token->type == TOKEN_KEYWORD);
1640
1641 if (argc && argv)
1642 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1643 else
1644 keyword_args_vector = NULL;
1645
1646 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1647 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1648 argv, keyword_args_vector);
1649 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1650
1651 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1652 return builder_rv;
1653
1654 return reader_rv;
1655}
1656
1657static void
1658cmd_matcher_init(struct cmd_matcher *matcher,
1659 struct cmd_element *cmd,
1660 enum filter_type filter,
1661 vector vline,
1662 unsigned int index,
1663 enum match_type *match_type,
1664 vector *match)
1665{
1666 memset(matcher, 0, sizeof(*matcher));
1667
1668 matcher->cmd = cmd;
1669 matcher->filter = filter;
1670 matcher->vline = vline;
1671 matcher->index = index;
1672
1673 matcher->match_type = match_type;
1674 if (matcher->match_type)
1675 *matcher->match_type = no_match;
1676 matcher->match = match;
1677
1678 matcher->word_index = 0;
1679}
1680
1681static enum matcher_rv
1682cmd_element_match(struct cmd_element *cmd_element,
1683 enum filter_type filter,
1684 vector vline,
1685 unsigned int index,
1686 enum match_type *match_type,
1687 vector *match,
1688 int *argc,
1689 const char **argv)
1690{
1691 struct cmd_matcher matcher;
1692 unsigned int token_index;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001693 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001694
1695 cmd_matcher_init(&matcher, cmd_element, filter,
1696 vline, index, match_type, match);
1697
1698 if (argc != NULL)
1699 *argc = 0;
1700
1701 for (token_index = 0;
1702 token_index < vector_active(cmd_element->tokens);
1703 token_index++)
1704 {
1705 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1706
1707 switch (token->type)
1708 {
1709 case TOKEN_TERMINAL:
1710 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1711 break;
1712 case TOKEN_MULTIPLE:
1713 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1714 break;
1715 case TOKEN_KEYWORD:
1716 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1717 }
1718
1719 if (MATCHER_ERROR(rv))
1720 return rv;
1721
1722 if (matcher.word_index > index)
1723 return MATCHER_OK;
1724 }
1725
1726 /* return MATCHER_COMPLETE if all words were consumed */
1727 if (matcher.word_index >= vector_active(vline))
1728 return MATCHER_COMPLETE;
1729
1730 /* return MATCHER_COMPLETE also if only an empty word is left. */
1731 if (matcher.word_index == vector_active(vline) - 1
1732 && (!vector_slot(vline, matcher.word_index)
1733 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1734 return MATCHER_COMPLETE;
1735
1736 return MATCHER_NO_MATCH; /* command is too long to match */
1737}
1738
1739/**
1740 * Filter a given vector of commands against a given commandline and
1741 * calculate possible completions.
1742 *
1743 * @param commands A vector of struct cmd_element*. Commands that don't
1744 * match against the given command line will be overwritten
1745 * with NULL in that vector.
1746 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1747 * determines how incomplete commands are handled, compare with
1748 * cmd_word_match for details.
1749 * @param vline A vector of char* containing the tokenized commandline.
1750 * @param index Only match up to the given token of the commandline.
1751 * @param match_type Record the type of the best match here.
1752 * @param matches Record the matches here. For each cmd_element in the commands
1753 * vector, a match vector will be created in the matches vector.
1754 * That vector will contain all struct command_token* of the
1755 * cmd_element which matched against the given vline at the given
1756 * index.
1757 * @return A code specifying if an error occured. If all went right, it's
1758 * CMD_SUCCESS.
1759 */
1760static int
1761cmd_vector_filter(vector commands,
1762 enum filter_type filter,
1763 vector vline,
1764 unsigned int index,
1765 enum match_type *match_type,
1766 vector *matches)
1767{
1768 unsigned int i;
paul718e3742002-12-13 20:15:29 +00001769 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00001770 enum match_type best_match;
1771 enum match_type element_match;
1772 enum matcher_rv matcher_rv;
paul909a2152005-03-14 17:41:45 +00001773
Christian Frankecd40b322013-09-30 12:27:51 +00001774 best_match = no_match;
1775 *matches = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +00001776
Christian Frankecd40b322013-09-30 12:27:51 +00001777 for (i = 0; i < vector_active (commands); i++)
1778 if ((cmd_element = vector_slot (commands, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001779 {
Christian Frankecd40b322013-09-30 12:27:51 +00001780 vector_set_index(*matches, i, NULL);
1781 matcher_rv = cmd_element_match(cmd_element, filter,
1782 vline, index,
1783 &element_match,
1784 (vector*)&vector_slot(*matches, i),
1785 NULL, NULL);
1786 if (MATCHER_ERROR(matcher_rv))
1787 {
1788 vector_slot(commands, i) = NULL;
1789 if (matcher_rv == MATCHER_AMBIGUOUS)
1790 return CMD_ERR_AMBIGUOUS;
1791 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1792 return CMD_ERR_EXEED_ARGC_MAX;
1793 }
1794 else if (element_match > best_match)
1795 {
1796 best_match = element_match;
1797 }
paul718e3742002-12-13 20:15:29 +00001798 }
Christian Frankecd40b322013-09-30 12:27:51 +00001799 *match_type = best_match;
1800 return CMD_SUCCESS;
1801}
1802
1803/**
1804 * Check whether a given commandline is complete if used for a specific
1805 * cmd_element.
1806 *
1807 * @param cmd_element A cmd_element against which the commandline should be
1808 * checked.
1809 * @param vline The tokenized commandline.
1810 * @return 1 if the given commandline is complete, 0 otherwise.
1811 */
1812static int
1813cmd_is_complete(struct cmd_element *cmd_element,
1814 vector vline)
1815{
1816 enum matcher_rv rv;
1817
1818 rv = cmd_element_match(cmd_element,
1819 FILTER_RELAXED,
1820 vline, -1,
1821 NULL, NULL,
1822 NULL, NULL);
1823 return (rv == MATCHER_COMPLETE);
1824}
1825
1826/**
1827 * Parse a given commandline and construct a list of arguments for the
1828 * given command_element.
1829 *
1830 * @param cmd_element The cmd_element for which we want to construct arguments.
1831 * @param vline The tokenized commandline.
1832 * @param argc Where to store the argument count.
1833 * @param argv Where to store the argument list. Should be at least
1834 * CMD_ARGC_MAX elements long.
1835 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1836 */
1837static int
1838cmd_parse(struct cmd_element *cmd_element,
1839 vector vline,
1840 int *argc, const char **argv)
1841{
1842 enum matcher_rv rv = cmd_element_match(cmd_element,
1843 FILTER_RELAXED,
1844 vline, -1,
1845 NULL, NULL,
1846 argc, argv);
1847 switch (rv)
1848 {
1849 case MATCHER_COMPLETE:
1850 return CMD_SUCCESS;
1851
1852 case MATCHER_NO_MATCH:
1853 return CMD_ERR_NO_MATCH;
1854
1855 case MATCHER_AMBIGUOUS:
1856 return CMD_ERR_AMBIGUOUS;
1857
1858 case MATCHER_EXCEED_ARGC_MAX:
1859 return CMD_ERR_EXEED_ARGC_MAX;
1860
1861 default:
1862 return CMD_ERR_INCOMPLETE;
1863 }
paul718e3742002-12-13 20:15:29 +00001864}
1865
1866/* Check ambiguous match */
ajs274a4a42004-12-07 15:39:31 +00001867static int
Christian Frankecd40b322013-09-30 12:27:51 +00001868is_cmd_ambiguous (vector cmd_vector,
1869 const char *command,
1870 vector matches,
1871 enum match_type type)
paul718e3742002-12-13 20:15:29 +00001872{
hasso8c328f12004-10-05 21:01:23 +00001873 unsigned int i;
1874 unsigned int j;
1875 const char *str = NULL;
hasso8c328f12004-10-05 21:01:23 +00001876 const char *matched = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001877 vector match_vector;
1878 struct cmd_token *cmd_token;
paul909a2152005-03-14 17:41:45 +00001879
Christian Frankecd40b322013-09-30 12:27:51 +00001880 if (command == NULL)
1881 command = "";
1882
1883 for (i = 0; i < vector_active (matches); i++)
1884 if ((match_vector = vector_slot (matches, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001885 {
1886 int match = 0;
1887
Christian Frankecd40b322013-09-30 12:27:51 +00001888 for (j = 0; j < vector_active (match_vector); j++)
1889 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
paul909a2152005-03-14 17:41:45 +00001890 {
1891 enum match_type ret;
Christian Frankecd40b322013-09-30 12:27:51 +00001892
1893 assert(cmd_token->type == TOKEN_TERMINAL);
1894 if (cmd_token->type != TOKEN_TERMINAL)
1895 continue;
1896
1897 str = cmd_token->cmd;
paul718e3742002-12-13 20:15:29 +00001898
paul909a2152005-03-14 17:41:45 +00001899 switch (type)
1900 {
1901 case exact_match:
David Lamparter14162932015-05-12 17:18:04 +02001902 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001903 && strcmp (command, str) == 0)
1904 match++;
1905 break;
1906 case partly_match:
David Lamparter14162932015-05-12 17:18:04 +02001907 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001908 && strncmp (command, str, strlen (command)) == 0)
1909 {
1910 if (matched && strcmp (matched, str) != 0)
1911 return 1; /* There is ambiguous match. */
1912 else
1913 matched = str;
1914 match++;
1915 }
1916 break;
1917 case range_match:
1918 if (cmd_range_match (str, command))
1919 {
1920 if (matched && strcmp (matched, str) != 0)
1921 return 1;
1922 else
1923 matched = str;
1924 match++;
1925 }
1926 break;
1927#ifdef HAVE_IPV6
1928 case ipv6_match:
David Lamparter10bac802015-05-05 11:10:20 +02001929 if (cmd_token->terminal == TERMINAL_IPV6)
paul909a2152005-03-14 17:41:45 +00001930 match++;
1931 break;
1932 case ipv6_prefix_match:
1933 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1934 {
1935 if (ret == partly_match)
1936 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001937
paul909a2152005-03-14 17:41:45 +00001938 match++;
1939 }
1940 break;
1941#endif /* HAVE_IPV6 */
1942 case ipv4_match:
David Lamparter10bac802015-05-05 11:10:20 +02001943 if (cmd_token->terminal == TERMINAL_IPV4)
paul718e3742002-12-13 20:15:29 +00001944 match++;
paul909a2152005-03-14 17:41:45 +00001945 break;
1946 case ipv4_prefix_match:
1947 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1948 {
1949 if (ret == partly_match)
1950 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001951
paul909a2152005-03-14 17:41:45 +00001952 match++;
1953 }
1954 break;
1955 case extend_match:
David Lamparter14162932015-05-12 17:18:04 +02001956 if (TERMINAL_RECORD (cmd_token->terminal))
paul718e3742002-12-13 20:15:29 +00001957 match++;
paul909a2152005-03-14 17:41:45 +00001958 break;
1959 case no_match:
1960 default:
1961 break;
1962 }
1963 }
1964 if (!match)
Christian Frankecd40b322013-09-30 12:27:51 +00001965 vector_slot (cmd_vector, i) = NULL;
paul718e3742002-12-13 20:15:29 +00001966 }
1967 return 0;
1968}
1969
1970/* If src matches dst return dst string, otherwise return NULL */
ajs274a4a42004-12-07 15:39:31 +00001971static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001972cmd_entry_function (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001973{
David Lamparter10bac802015-05-05 11:10:20 +02001974 const char *dst = token->cmd;
1975
paul718e3742002-12-13 20:15:29 +00001976 /* Skip variable arguments. */
David Lamparter14162932015-05-12 17:18:04 +02001977 if (TERMINAL_RECORD (token->terminal))
1978 return NULL;
paul718e3742002-12-13 20:15:29 +00001979
1980 /* In case of 'command \t', given src is NULL string. */
1981 if (src == NULL)
1982 return dst;
1983
1984 /* Matched with input string. */
1985 if (strncmp (src, dst, strlen (src)) == 0)
1986 return dst;
1987
1988 return NULL;
1989}
1990
1991/* If src matches dst return dst string, otherwise return NULL */
1992/* This version will return the dst string always if it is
1993 CMD_VARIABLE for '?' key processing */
ajs274a4a42004-12-07 15:39:31 +00001994static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001995cmd_entry_function_desc (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001996{
David Lamparter10bac802015-05-05 11:10:20 +02001997 const char *dst = token->cmd;
paul718e3742002-12-13 20:15:29 +00001998
David Lamparter10bac802015-05-05 11:10:20 +02001999 switch (token->terminal)
paul718e3742002-12-13 20:15:29 +00002000 {
David Lamparter10bac802015-05-05 11:10:20 +02002001 case TERMINAL_VARARG:
2002 return dst;
2003
2004 case TERMINAL_RANGE:
2005 if (cmd_range_match (dst, src))
2006 return dst;
2007 else
2008 return NULL;
2009
2010 case TERMINAL_IPV6:
2011 if (cmd_ipv6_match (src))
2012 return dst;
2013 else
2014 return NULL;
2015
2016 case TERMINAL_IPV6_PREFIX:
2017 if (cmd_ipv6_prefix_match (src))
2018 return dst;
2019 else
2020 return NULL;
2021
2022 case TERMINAL_IPV4:
2023 if (cmd_ipv4_match (src))
2024 return dst;
2025 else
2026 return NULL;
2027
2028 case TERMINAL_IPV4_PREFIX:
2029 if (cmd_ipv4_prefix_match (src))
2030 return dst;
2031 else
2032 return NULL;
2033
2034 /* Optional or variable commands always match on '?' */
2035 case TERMINAL_OPTION:
2036 case TERMINAL_VARIABLE:
2037 return dst;
2038
2039 case TERMINAL_LITERAL:
2040 /* In case of 'command \t', given src is NULL string. */
2041 if (src == NULL)
2042 return dst;
2043
2044 if (strncmp (src, dst, strlen (src)) == 0)
2045 return dst;
2046 else
2047 return NULL;
2048
2049 default:
2050 assert(0);
David Lamparterf1fc3272015-05-13 12:44:50 +02002051 return NULL;
paul718e3742002-12-13 20:15:29 +00002052 }
paul718e3742002-12-13 20:15:29 +00002053}
2054
Christian Frankecd40b322013-09-30 12:27:51 +00002055/**
2056 * Check whether a string is already present in a vector of strings.
2057 * @param v A vector of char*.
2058 * @param str A char*.
2059 * @return 0 if str is already present in the vector, 1 otherwise.
2060 */
ajs274a4a42004-12-07 15:39:31 +00002061static int
hasso8c328f12004-10-05 21:01:23 +00002062cmd_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002063{
hasso8c328f12004-10-05 21:01:23 +00002064 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002065 char *match;
2066
paul55468c82005-03-14 20:19:01 +00002067 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +00002068 if ((match = vector_slot (v, i)) != NULL)
2069 if (strcmp (match, str) == 0)
2070 return 0;
2071 return 1;
2072}
2073
Christian Frankecd40b322013-09-30 12:27:51 +00002074/**
2075 * Check whether a struct cmd_token matching a given string is already
2076 * present in a vector of struct cmd_token.
2077 * @param v A vector of struct cmd_token*.
2078 * @param str A char* which should be searched for.
2079 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2080 * 1 otherwise.
2081 */
ajs274a4a42004-12-07 15:39:31 +00002082static int
hasso8c328f12004-10-05 21:01:23 +00002083desc_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002084{
hasso8c328f12004-10-05 21:01:23 +00002085 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00002086 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002087
paul55468c82005-03-14 20:19:01 +00002088 for (i = 0; i < vector_active (v); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00002089 if ((token = vector_slot (v, i)) != NULL)
2090 if (strcmp (token->cmd, str) == 0)
2091 return 0;
2092 return 1;
paul718e3742002-12-13 20:15:29 +00002093}
2094
ajs274a4a42004-12-07 15:39:31 +00002095static int
paulb92938a2002-12-13 21:20:42 +00002096cmd_try_do_shortcut (enum node_type node, char* first_word) {
2097 if ( first_word != NULL &&
2098 node != AUTH_NODE &&
2099 node != VIEW_NODE &&
2100 node != AUTH_ENABLE_NODE &&
2101 node != ENABLE_NODE &&
Paul Jakma62687ff2008-08-23 14:27:06 +01002102 node != RESTRICTED_NODE &&
paulb92938a2002-12-13 21:20:42 +00002103 0 == strcmp( "do", first_word ) )
2104 return 1;
2105 return 0;
2106}
2107
Christian Frankecd40b322013-09-30 12:27:51 +00002108static void
2109cmd_matches_free(vector *matches)
2110{
2111 unsigned int i;
2112 vector cmd_matches;
2113
2114 for (i = 0; i < vector_active(*matches); i++)
2115 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2116 vector_free(cmd_matches);
2117 vector_free(*matches);
2118 *matches = NULL;
2119}
2120
2121static int
2122cmd_describe_cmp(const void *a, const void *b)
2123{
2124 const struct cmd_token *first = *(struct cmd_token * const *)a;
2125 const struct cmd_token *second = *(struct cmd_token * const *)b;
2126
2127 return strcmp(first->cmd, second->cmd);
2128}
2129
2130static void
2131cmd_describe_sort(vector matchvec)
2132{
2133 qsort(matchvec->index, vector_active(matchvec),
2134 sizeof(void*), cmd_describe_cmp);
2135}
2136
paul718e3742002-12-13 20:15:29 +00002137/* '?' describe command support. */
ajs274a4a42004-12-07 15:39:31 +00002138static vector
paulb92938a2002-12-13 21:20:42 +00002139cmd_describe_command_real (vector vline, struct vty *vty, int *status)
paul718e3742002-12-13 20:15:29 +00002140{
paulb8961472005-03-14 17:35:52 +00002141 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002142 vector cmd_vector;
2143#define INIT_MATCHVEC_SIZE 10
2144 vector matchvec;
2145 struct cmd_element *cmd_element;
paulb8961472005-03-14 17:35:52 +00002146 unsigned int index;
paul54aba542003-08-21 20:28:24 +00002147 int ret;
2148 enum match_type match;
2149 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002150 vector matches = NULL;
2151 vector match_vector;
Donald Sharpf7332802015-07-17 22:36:57 -04002152 uint32_t command_found = 0;
2153 const char *last_word;
paul718e3742002-12-13 20:15:29 +00002154
2155 /* Set index. */
paul55468c82005-03-14 20:19:01 +00002156 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002157 {
2158 *status = CMD_ERR_NO_MATCH;
2159 return NULL;
2160 }
Christian Frankecd40b322013-09-30 12:27:51 +00002161
2162 index = vector_active (vline) - 1;
2163
paul718e3742002-12-13 20:15:29 +00002164 /* Make copy vector of current node's command vector. */
2165 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2166
2167 /* Prepare match vector */
2168 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2169
Christian Frankecd40b322013-09-30 12:27:51 +00002170 /* Filter commands and build a list how they could possibly continue. */
2171 for (i = 0; i <= index; i++)
2172 {
2173 command = vector_slot (vline, i);
paul718e3742002-12-13 20:15:29 +00002174
Christian Frankecd40b322013-09-30 12:27:51 +00002175 if (matches)
2176 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002177
Christian Frankecd40b322013-09-30 12:27:51 +00002178 ret = cmd_vector_filter(cmd_vector,
2179 FILTER_RELAXED,
2180 vline, i,
2181 &match,
2182 &matches);
paul718e3742002-12-13 20:15:29 +00002183
Christian Frankecd40b322013-09-30 12:27:51 +00002184 if (ret != CMD_SUCCESS)
2185 {
2186 vector_free (cmd_vector);
2187 vector_free (matchvec);
2188 cmd_matches_free(&matches);
2189 *status = ret;
2190 return NULL;
2191 }
paul718e3742002-12-13 20:15:29 +00002192
Christian Frankecd40b322013-09-30 12:27:51 +00002193 /* The last match may well be ambigious, so break here */
2194 if (i == index)
2195 break;
paul718e3742002-12-13 20:15:29 +00002196
Christian Frankecd40b322013-09-30 12:27:51 +00002197 if (match == vararg_match)
2198 {
2199 /* We found a vararg match - so we can throw out the current matches here
2200 * and don't need to continue checking the command input */
2201 unsigned int j, k;
2202
2203 for (j = 0; j < vector_active (matches); j++)
2204 if ((match_vector = vector_slot (matches, j)) != NULL)
2205 for (k = 0; k < vector_active (match_vector); k++)
2206 {
2207 struct cmd_token *token = vector_slot (match_vector, k);
2208 vector_set (matchvec, token);
2209 }
2210
2211 *status = CMD_SUCCESS;
2212 vector_set(matchvec, &token_cr);
2213 vector_free (cmd_vector);
2214 cmd_matches_free(&matches);
2215 cmd_describe_sort(matchvec);
2216 return matchvec;
2217 }
2218
2219 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2220 if (ret == 1)
2221 {
2222 vector_free (cmd_vector);
2223 vector_free (matchvec);
2224 cmd_matches_free(&matches);
2225 *status = CMD_ERR_AMBIGUOUS;
2226 return NULL;
2227 }
2228 else if (ret == 2)
2229 {
2230 vector_free (cmd_vector);
2231 vector_free (matchvec);
2232 cmd_matches_free(&matches);
2233 *status = CMD_ERR_NO_MATCH;
2234 return NULL;
2235 }
2236 }
paul54aba542003-08-21 20:28:24 +00002237
paul718e3742002-12-13 20:15:29 +00002238 /* Make description vector. */
Christian Frankecd40b322013-09-30 12:27:51 +00002239 for (i = 0; i < vector_active (matches); i++)
Donald Sharpf7332802015-07-17 22:36:57 -04002240 {
2241 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
2242 {
2243 unsigned int j;
2244 vector vline_trimmed;
paul718e3742002-12-13 20:15:29 +00002245
Donald Sharpf7332802015-07-17 22:36:57 -04002246 command_found++;
2247 last_word = vector_slot(vline, vector_active(vline) - 1);
2248 if (last_word == NULL || !strlen(last_word))
2249 {
2250 vline_trimmed = vector_copy(vline);
2251 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
paul718e3742002-12-13 20:15:29 +00002252
Donald Sharpf7332802015-07-17 22:36:57 -04002253 if (cmd_is_complete(cmd_element, vline_trimmed)
2254 && desc_unique_string(matchvec, command_cr))
2255 {
2256 if (match != vararg_match)
2257 vector_set(matchvec, &token_cr);
2258 }
Chris Caputo228da422009-07-18 05:44:03 +00002259
Donald Sharpf7332802015-07-17 22:36:57 -04002260 vector_free(vline_trimmed);
2261 }
Christian Frankecd40b322013-09-30 12:27:51 +00002262
Donald Sharpf7332802015-07-17 22:36:57 -04002263 match_vector = vector_slot (matches, i);
2264 if (match_vector)
2265 {
2266 for (j = 0; j < vector_active(match_vector); j++)
2267 {
2268 struct cmd_token *token = vector_slot(match_vector, j);
2269 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002270
Donald Sharpf7332802015-07-17 22:36:57 -04002271 string = cmd_entry_function_desc(command, token);
2272 if (string && desc_unique_string(matchvec, string))
2273 vector_set(matchvec, token);
2274 }
2275 }
2276 }
2277 }
2278
2279 /*
2280 * We can get into this situation when the command is complete
2281 * but the last part of the command is an optional piece of
2282 * the cli.
2283 */
2284 last_word = vector_slot(vline, vector_active(vline) - 1);
2285 if (command_found == 0 && (last_word == NULL || !strlen(last_word)))
2286 vector_set(matchvec, &token_cr);
2287
paul718e3742002-12-13 20:15:29 +00002288 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002289 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002290
2291 if (vector_slot (matchvec, 0) == NULL)
2292 {
2293 vector_free (matchvec);
paul909a2152005-03-14 17:41:45 +00002294 *status = CMD_ERR_NO_MATCH;
Paul Jakma5fc60512006-05-12 23:24:09 +00002295 return NULL;
paul718e3742002-12-13 20:15:29 +00002296 }
paul718e3742002-12-13 20:15:29 +00002297
Paul Jakma5fc60512006-05-12 23:24:09 +00002298 *status = CMD_SUCCESS;
Christian Frankecd40b322013-09-30 12:27:51 +00002299 cmd_describe_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002300 return matchvec;
2301}
2302
paulb92938a2002-12-13 21:20:42 +00002303vector
2304cmd_describe_command (vector vline, struct vty *vty, int *status)
2305{
2306 vector ret;
2307
2308 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2309 {
2310 enum node_type onode;
2311 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002312 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002313
2314 onode = vty->node;
2315 vty->node = ENABLE_NODE;
2316 /* We can try it on enable node, cos' the vty is authenticated */
2317
2318 shifted_vline = vector_init (vector_count(vline));
2319 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002320 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002321 {
2322 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2323 }
2324
2325 ret = cmd_describe_command_real (shifted_vline, vty, status);
2326
2327 vector_free(shifted_vline);
2328 vty->node = onode;
2329 return ret;
2330 }
2331
2332
2333 return cmd_describe_command_real (vline, vty, status);
2334}
2335
2336
paul718e3742002-12-13 20:15:29 +00002337/* Check LCD of matched command. */
ajs274a4a42004-12-07 15:39:31 +00002338static int
paul718e3742002-12-13 20:15:29 +00002339cmd_lcd (char **matched)
2340{
2341 int i;
2342 int j;
2343 int lcd = -1;
2344 char *s1, *s2;
2345 char c1, c2;
2346
2347 if (matched[0] == NULL || matched[1] == NULL)
2348 return 0;
2349
2350 for (i = 1; matched[i] != NULL; i++)
2351 {
2352 s1 = matched[i - 1];
2353 s2 = matched[i];
2354
2355 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2356 if (c1 != c2)
2357 break;
2358
2359 if (lcd < 0)
2360 lcd = j;
2361 else
2362 {
2363 if (lcd > j)
2364 lcd = j;
2365 }
2366 }
2367 return lcd;
2368}
2369
Christian Frankecd40b322013-09-30 12:27:51 +00002370static int
2371cmd_complete_cmp(const void *a, const void *b)
2372{
2373 const char *first = *(char * const *)a;
2374 const char *second = *(char * const *)b;
2375
2376 if (!first)
2377 {
2378 if (!second)
2379 return 0;
2380 return 1;
2381 }
2382 if (!second)
2383 return -1;
2384
2385 return strcmp(first, second);
2386}
2387
2388static void
2389cmd_complete_sort(vector matchvec)
2390{
2391 qsort(matchvec->index, vector_active(matchvec),
2392 sizeof(void*), cmd_complete_cmp);
2393}
2394
paul718e3742002-12-13 20:15:29 +00002395/* Command line completion support. */
ajs274a4a42004-12-07 15:39:31 +00002396static char **
Lou Berger67290032016-01-12 13:41:46 -05002397cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib)
paul718e3742002-12-13 20:15:29 +00002398{
paulb8961472005-03-14 17:35:52 +00002399 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002400 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2401#define INIT_MATCHVEC_SIZE 10
2402 vector matchvec;
paulb8961472005-03-14 17:35:52 +00002403 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002404 char **match_str;
Christian Frankecd40b322013-09-30 12:27:51 +00002405 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002406 char *command;
2407 int lcd;
Christian Frankecd40b322013-09-30 12:27:51 +00002408 vector matches = NULL;
2409 vector match_vector;
paul718e3742002-12-13 20:15:29 +00002410
paul55468c82005-03-14 20:19:01 +00002411 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002412 {
Paul Jakmad2519962006-05-12 23:19:37 +00002413 vector_free (cmd_vector);
paulb8961472005-03-14 17:35:52 +00002414 *status = CMD_ERR_NO_MATCH;
2415 return NULL;
2416 }
2417 else
paul55468c82005-03-14 20:19:01 +00002418 index = vector_active (vline) - 1;
paulb8961472005-03-14 17:35:52 +00002419
Christian Frankecd40b322013-09-30 12:27:51 +00002420 /* First, filter by command string */
2421 for (i = 0; i <= index; i++)
2422 {
2423 command = vector_slot (vline, i);
2424 enum match_type match;
2425 int ret;
paul718e3742002-12-13 20:15:29 +00002426
Christian Frankecd40b322013-09-30 12:27:51 +00002427 if (matches)
2428 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002429
Christian Frankecd40b322013-09-30 12:27:51 +00002430 /* First try completion match, if there is exactly match return 1 */
2431 ret = cmd_vector_filter(cmd_vector,
2432 FILTER_RELAXED,
2433 vline, i,
2434 &match,
2435 &matches);
2436
2437 if (ret != CMD_SUCCESS)
2438 {
2439 vector_free(cmd_vector);
2440 cmd_matches_free(&matches);
2441 *status = ret;
2442 return NULL;
2443 }
2444
2445 /* Break here - the completion mustn't be checked to be non-ambiguous */
2446 if (i == index)
2447 break;
2448
2449 /* If there is exact match then filter ambiguous match else check
2450 ambiguousness. */
2451 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2452 if (ret == 1)
2453 {
2454 vector_free (cmd_vector);
2455 cmd_matches_free(&matches);
2456 *status = CMD_ERR_AMBIGUOUS;
2457 return NULL;
2458 }
2459 /*
paul909a2152005-03-14 17:41:45 +00002460 else if (ret == 2)
2461 {
2462 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002463 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002464 *status = CMD_ERR_NO_MATCH;
2465 return NULL;
2466 }
2467 */
Christian Frankecd40b322013-09-30 12:27:51 +00002468 }
paul909a2152005-03-14 17:41:45 +00002469
paul718e3742002-12-13 20:15:29 +00002470 /* Prepare match vector. */
2471 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2472
Christian Frankecd40b322013-09-30 12:27:51 +00002473 /* Build the possible list of continuations into a list of completions */
2474 for (i = 0; i < vector_active (matches); i++)
2475 if ((match_vector = vector_slot (matches, i)))
paul718e3742002-12-13 20:15:29 +00002476 {
hasso8c328f12004-10-05 21:01:23 +00002477 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002478 unsigned int j;
paul909a2152005-03-14 17:41:45 +00002479
Christian Frankecd40b322013-09-30 12:27:51 +00002480 for (j = 0; j < vector_active (match_vector); j++)
2481 if ((token = vector_slot (match_vector, j)))
Lou Berger67290032016-01-12 13:41:46 -05002482 {
2483 string = cmd_entry_function (vector_slot (vline, index), token);
2484 if (string && cmd_unique_string (matchvec, string))
2485 vector_set (matchvec, (islib != 0 ?
2486 XSTRDUP (MTYPE_TMP, string) :
2487 strdup (string) /* rl freed */));
2488 }
paul718e3742002-12-13 20:15:29 +00002489 }
2490
2491 /* We don't need cmd_vector any more. */
2492 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002493 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002494
2495 /* No matched command */
2496 if (vector_slot (matchvec, 0) == NULL)
2497 {
2498 vector_free (matchvec);
2499
2500 /* In case of 'command \t' pattern. Do you need '?' command at
2501 the end of the line. */
2502 if (vector_slot (vline, index) == '\0')
2503 *status = CMD_ERR_NOTHING_TODO;
2504 else
2505 *status = CMD_ERR_NO_MATCH;
2506 return NULL;
2507 }
2508
2509 /* Only one matched */
2510 if (vector_slot (matchvec, 1) == NULL)
2511 {
2512 match_str = (char **) matchvec->index;
2513 vector_only_wrapper_free (matchvec);
2514 *status = CMD_COMPLETE_FULL_MATCH;
2515 return match_str;
2516 }
2517 /* Make it sure last element is NULL. */
2518 vector_set (matchvec, NULL);
2519
2520 /* Check LCD of matched strings. */
2521 if (vector_slot (vline, index) != NULL)
2522 {
2523 lcd = cmd_lcd ((char **) matchvec->index);
2524
2525 if (lcd)
2526 {
2527 int len = strlen (vector_slot (vline, index));
paul909a2152005-03-14 17:41:45 +00002528
paul718e3742002-12-13 20:15:29 +00002529 if (len < lcd)
2530 {
2531 char *lcdstr;
paul909a2152005-03-14 17:41:45 +00002532
Lou Berger67290032016-01-12 13:41:46 -05002533 lcdstr = (islib != 0 ?
2534 XMALLOC (MTYPE_TMP, lcd + 1) :
2535 malloc(lcd + 1));
paul718e3742002-12-13 20:15:29 +00002536 memcpy (lcdstr, matchvec->index[0], lcd);
2537 lcdstr[lcd] = '\0';
2538
2539 /* match_str = (char **) &lcdstr; */
2540
2541 /* Free matchvec. */
paul55468c82005-03-14 20:19:01 +00002542 for (i = 0; i < vector_active (matchvec); i++)
Lou Berger67290032016-01-12 13:41:46 -05002543 {
2544 if (vector_slot (matchvec, i))
2545 {
2546 if (islib != 0)
2547 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
2548 else
2549 free (vector_slot (matchvec, i));
2550 }
2551 }
paul718e3742002-12-13 20:15:29 +00002552 vector_free (matchvec);
2553
paul909a2152005-03-14 17:41:45 +00002554 /* Make new matchvec. */
paul718e3742002-12-13 20:15:29 +00002555 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2556 vector_set (matchvec, lcdstr);
2557 match_str = (char **) matchvec->index;
2558 vector_only_wrapper_free (matchvec);
2559
2560 *status = CMD_COMPLETE_MATCH;
2561 return match_str;
2562 }
2563 }
2564 }
2565
2566 match_str = (char **) matchvec->index;
Christian Frankecd40b322013-09-30 12:27:51 +00002567 cmd_complete_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002568 vector_only_wrapper_free (matchvec);
2569 *status = CMD_COMPLETE_LIST_MATCH;
2570 return match_str;
2571}
2572
paulb92938a2002-12-13 21:20:42 +00002573char **
Lou Berger67290032016-01-12 13:41:46 -05002574cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)
paulb92938a2002-12-13 21:20:42 +00002575{
2576 char **ret;
2577
2578 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2579 {
2580 enum node_type onode;
2581 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002582 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002583
2584 onode = vty->node;
2585 vty->node = ENABLE_NODE;
2586 /* We can try it on enable node, cos' the vty is authenticated */
2587
2588 shifted_vline = vector_init (vector_count(vline));
2589 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002590 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002591 {
2592 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2593 }
2594
Lou Berger67290032016-01-12 13:41:46 -05002595 ret = cmd_complete_command_real (shifted_vline, vty, status, islib);
paulb92938a2002-12-13 21:20:42 +00002596
2597 vector_free(shifted_vline);
2598 vty->node = onode;
2599 return ret;
2600 }
2601
Lou Berger67290032016-01-12 13:41:46 -05002602 return cmd_complete_command_real (vline, vty, status, islib);
2603}
paulb92938a2002-12-13 21:20:42 +00002604
Lou Berger67290032016-01-12 13:41:46 -05002605char **
2606cmd_complete_command (vector vline, struct vty *vty, int *status)
2607{
2608 return cmd_complete_command_lib (vline, vty, status, 0);
paulb92938a2002-12-13 21:20:42 +00002609}
2610
2611/* return parent node */
2612/* MUST eventually converge on CONFIG_NODE */
hasso13bfca72005-01-23 21:42:25 +00002613enum node_type
ajs274a4a42004-12-07 15:39:31 +00002614node_parent ( enum node_type node )
paulb92938a2002-12-13 21:20:42 +00002615{
2616 enum node_type ret;
2617
paul9ab68122003-01-18 01:16:20 +00002618 assert (node > CONFIG_NODE);
2619
2620 switch (node)
2621 {
2622 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002623 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002624 case BGP_ENCAP_NODE:
2625 case BGP_ENCAPV6_NODE:
paul9ab68122003-01-18 01:16:20 +00002626 case BGP_IPV4_NODE:
2627 case BGP_IPV4M_NODE:
2628 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002629 case BGP_IPV6M_NODE:
paul9ab68122003-01-18 01:16:20 +00002630 ret = BGP_NODE;
2631 break;
2632 case KEYCHAIN_KEY_NODE:
2633 ret = KEYCHAIN_NODE;
2634 break;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002635 case LINK_PARAMS_NODE:
2636 ret = INTERFACE_NODE;
2637 break;
paul9ab68122003-01-18 01:16:20 +00002638 default:
2639 ret = CONFIG_NODE;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002640 break;
paulb92938a2002-12-13 21:20:42 +00002641 }
2642
2643 return ret;
2644}
2645
paul718e3742002-12-13 20:15:29 +00002646/* Execute command by argument vline vector. */
ajs274a4a42004-12-07 15:39:31 +00002647static int
Christian Frankecd40b322013-09-30 12:27:51 +00002648cmd_execute_command_real (vector vline,
2649 enum filter_type filter,
2650 struct vty *vty,
paulb8961472005-03-14 17:35:52 +00002651 struct cmd_element **cmd)
paul718e3742002-12-13 20:15:29 +00002652{
hasso8c328f12004-10-05 21:01:23 +00002653 unsigned int i;
2654 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002655 vector cmd_vector;
2656 struct cmd_element *cmd_element;
2657 struct cmd_element *matched_element;
2658 unsigned int matched_count, incomplete_count;
2659 int argc;
paul9035efa2004-10-10 11:56:56 +00002660 const char *argv[CMD_ARGC_MAX];
paul718e3742002-12-13 20:15:29 +00002661 enum match_type match = 0;
paul718e3742002-12-13 20:15:29 +00002662 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002663 int ret;
2664 vector matches;
paul718e3742002-12-13 20:15:29 +00002665
2666 /* Make copy of command elements. */
2667 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2668
paul55468c82005-03-14 20:19:01 +00002669 for (index = 0; index < vector_active (vline); index++)
Christian Frankecd40b322013-09-30 12:27:51 +00002670 {
2671 command = vector_slot (vline, index);
2672 ret = cmd_vector_filter(cmd_vector,
2673 filter,
2674 vline, index,
2675 &match,
2676 &matches);
paul718e3742002-12-13 20:15:29 +00002677
Christian Frankecd40b322013-09-30 12:27:51 +00002678 if (ret != CMD_SUCCESS)
2679 {
2680 cmd_matches_free(&matches);
2681 return ret;
2682 }
paul718e3742002-12-13 20:15:29 +00002683
Christian Frankecd40b322013-09-30 12:27:51 +00002684 if (match == vararg_match)
2685 {
2686 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002687 break;
Christian Frankecd40b322013-09-30 12:27:51 +00002688 }
paul718e3742002-12-13 20:15:29 +00002689
Christian Frankecd40b322013-09-30 12:27:51 +00002690 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2691 cmd_matches_free(&matches);
2692
2693 if (ret == 1)
2694 {
2695 vector_free(cmd_vector);
2696 return CMD_ERR_AMBIGUOUS;
2697 }
2698 else if (ret == 2)
2699 {
2700 vector_free(cmd_vector);
2701 return CMD_ERR_NO_MATCH;
2702 }
2703 }
paul718e3742002-12-13 20:15:29 +00002704
2705 /* Check matched count. */
2706 matched_element = NULL;
2707 matched_count = 0;
2708 incomplete_count = 0;
2709
paul55468c82005-03-14 20:19:01 +00002710 for (i = 0; i < vector_active (cmd_vector); i++)
paulb8961472005-03-14 17:35:52 +00002711 if ((cmd_element = vector_slot (cmd_vector, i)))
paul718e3742002-12-13 20:15:29 +00002712 {
Christian Frankecd40b322013-09-30 12:27:51 +00002713 if (cmd_is_complete(cmd_element, vline))
paul718e3742002-12-13 20:15:29 +00002714 {
2715 matched_element = cmd_element;
paul718e3742002-12-13 20:15:29 +00002716 matched_count++;
2717 }
2718 else
2719 {
2720 incomplete_count++;
2721 }
2722 }
paul909a2152005-03-14 17:41:45 +00002723
paul718e3742002-12-13 20:15:29 +00002724 /* Finish of using cmd_vector. */
2725 vector_free (cmd_vector);
2726
paul909a2152005-03-14 17:41:45 +00002727 /* To execute command, matched_count must be 1. */
2728 if (matched_count == 0)
paul718e3742002-12-13 20:15:29 +00002729 {
2730 if (incomplete_count)
2731 return CMD_ERR_INCOMPLETE;
2732 else
2733 return CMD_ERR_NO_MATCH;
2734 }
2735
paul909a2152005-03-14 17:41:45 +00002736 if (matched_count > 1)
paul718e3742002-12-13 20:15:29 +00002737 return CMD_ERR_AMBIGUOUS;
2738
Christian Frankecd40b322013-09-30 12:27:51 +00002739 ret = cmd_parse(matched_element, vline, &argc, argv);
2740 if (ret != CMD_SUCCESS)
2741 return ret;
paul718e3742002-12-13 20:15:29 +00002742
2743 /* For vtysh execution. */
2744 if (cmd)
2745 *cmd = matched_element;
2746
2747 if (matched_element->daemon)
2748 return CMD_SUCCESS_DAEMON;
2749
2750 /* Execute matched command. */
2751 return (*matched_element->func) (matched_element, vty, argc, argv);
2752}
2753
Christian Frankecd40b322013-09-30 12:27:51 +00002754/**
2755 * Execute a given command, handling things like "do ..." and checking
2756 * whether the given command might apply at a parent node if doesn't
2757 * apply for the current node.
2758 *
2759 * @param vline Command line input, vector of char* where each element is
2760 * one input token.
2761 * @param vty The vty context in which the command should be executed.
2762 * @param cmd Pointer where the struct cmd_element of the matched command
2763 * will be stored, if any. May be set to NULL if this info is
2764 * not needed.
2765 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2766 * @return The status of the command that has been executed or an error code
2767 * as to why no command could be executed.
2768 */
paulb92938a2002-12-13 21:20:42 +00002769int
hasso87d683b2005-01-16 23:31:54 +00002770cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2771 int vtysh) {
paul9ab68122003-01-18 01:16:20 +00002772 int ret, saved_ret, tried = 0;
2773 enum node_type onode, try_node;
2774
2775 onode = try_node = vty->node;
paulb92938a2002-12-13 21:20:42 +00002776
2777 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2778 {
2779 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002780 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002781
2782 vty->node = ENABLE_NODE;
2783 /* We can try it on enable node, cos' the vty is authenticated */
2784
2785 shifted_vline = vector_init (vector_count(vline));
2786 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002787 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002788 {
2789 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2790 }
2791
Christian Frankecd40b322013-09-30 12:27:51 +00002792 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002793
2794 vector_free(shifted_vline);
2795 vty->node = onode;
2796 return ret;
2797 }
2798
2799
Christian Frankecd40b322013-09-30 12:27:51 +00002800 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002801
hasso87d683b2005-01-16 23:31:54 +00002802 if (vtysh)
2803 return saved_ret;
2804
paulb92938a2002-12-13 21:20:42 +00002805 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
paul9ab68122003-01-18 01:16:20 +00002806 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
paulb92938a2002-12-13 21:20:42 +00002807 && vty->node > CONFIG_NODE )
2808 {
paul9ab68122003-01-18 01:16:20 +00002809 try_node = node_parent(try_node);
2810 vty->node = try_node;
Christian Frankecd40b322013-09-30 12:27:51 +00002811 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paul9ab68122003-01-18 01:16:20 +00002812 tried = 1;
2813 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
paulb92938a2002-12-13 21:20:42 +00002814 {
paul9ab68122003-01-18 01:16:20 +00002815 /* succesfull command, leave the node as is */
paulb92938a2002-12-13 21:20:42 +00002816 return ret;
2817 }
paulb92938a2002-12-13 21:20:42 +00002818 }
paul9ab68122003-01-18 01:16:20 +00002819 /* no command succeeded, reset the vty to the original node and
2820 return the error for this node */
2821 if ( tried )
2822 vty->node = onode;
2823 return saved_ret;
pauleda031f2003-01-18 00:39:19 +00002824}
2825
Christian Frankecd40b322013-09-30 12:27:51 +00002826/**
2827 * Execute a given command, matching it strictly against the current node.
2828 * This mode is used when reading config files.
2829 *
2830 * @param vline Command line input, vector of char* where each element is
2831 * one input token.
2832 * @param vty The vty context in which the command should be executed.
2833 * @param cmd Pointer where the struct cmd_element* of the matched command
2834 * will be stored, if any. May be set to NULL if this info is
2835 * not needed.
2836 * @return The status of the command that has been executed or an error code
2837 * as to why no command could be executed.
2838 */
paul718e3742002-12-13 20:15:29 +00002839int
paul909a2152005-03-14 17:41:45 +00002840cmd_execute_command_strict (vector vline, struct vty *vty,
paul718e3742002-12-13 20:15:29 +00002841 struct cmd_element **cmd)
2842{
Christian Frankecd40b322013-09-30 12:27:51 +00002843 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
paul718e3742002-12-13 20:15:29 +00002844}
2845
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002846/**
2847 * Parse one line of config, walking up the parse tree attempting to find a match
2848 *
2849 * @param vty The vty context in which the command should be executed.
2850 * @param cmd Pointer where the struct cmd_element* of the match command
2851 * will be stored, if any. May be set to NULL if this info is
2852 * not needed.
2853 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
2854 * or not.
2855 * @return The status of the command that has been executed or an error code
2856 * as to why no command could be executed.
2857 */
2858int
2859command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
2860{
2861 vector vline;
2862 int saved_node;
2863 int ret;
2864
2865 vline = cmd_make_strvec (vty->buf);
2866
2867 /* In case of comment line */
2868 if (vline == NULL)
2869 return CMD_SUCCESS;
2870
2871 /* Execute configuration command : this is strict match */
2872 ret = cmd_execute_command_strict (vline, vty, cmd);
2873
2874 saved_node = vty->node;
2875
2876 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2877 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2878 ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) {
2879 vty->node = node_parent(vty->node);
2880 ret = cmd_execute_command_strict (vline, vty, NULL);
2881 }
2882
2883 // If climbing the tree did not work then ignore the command and
2884 // stay at the same node
2885 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2886 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2887 ret != CMD_ERR_NOTHING_TODO)
2888 {
2889 vty->node = saved_node;
2890 }
2891
2892 cmd_free_strvec (vline);
2893
2894 return ret;
2895}
2896
paul718e3742002-12-13 20:15:29 +00002897/* Configration make from file. */
2898int
Steve Hillea555002009-07-28 16:36:14 -04002899config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
paul718e3742002-12-13 20:15:29 +00002900{
2901 int ret;
Steve Hillea555002009-07-28 16:36:14 -04002902 *line_num = 0;
paul718e3742002-12-13 20:15:29 +00002903
2904 while (fgets (vty->buf, VTY_BUFSIZ, fp))
2905 {
Steve Hillea555002009-07-28 16:36:14 -04002906 ++(*line_num);
paul718e3742002-12-13 20:15:29 +00002907
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002908 ret = command_config_read_one_line (vty, NULL, 0);
paul718e3742002-12-13 20:15:29 +00002909
hassoddd85ed2004-10-13 08:18:07 +00002910 if (ret != CMD_SUCCESS && ret != CMD_WARNING
2911 && ret != CMD_ERR_NOTHING_TODO)
paul718e3742002-12-13 20:15:29 +00002912 return ret;
2913 }
2914 return CMD_SUCCESS;
2915}
2916
2917/* Configration from terminal */
2918DEFUN (config_terminal,
2919 config_terminal_cmd,
2920 "configure terminal",
2921 "Configuration from vty interface\n"
2922 "Configuration terminal\n")
2923{
2924 if (vty_config_lock (vty))
2925 vty->node = CONFIG_NODE;
2926 else
2927 {
2928 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2929 return CMD_WARNING;
2930 }
2931 return CMD_SUCCESS;
2932}
2933
2934/* Enable command */
2935DEFUN (enable,
2936 config_enable_cmd,
2937 "enable",
2938 "Turn on privileged mode command\n")
2939{
2940 /* If enable password is NULL, change to ENABLE_NODE */
2941 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2942 vty->type == VTY_SHELL_SERV)
2943 vty->node = ENABLE_NODE;
2944 else
2945 vty->node = AUTH_ENABLE_NODE;
2946
2947 return CMD_SUCCESS;
2948}
2949
2950/* Disable command */
2951DEFUN (disable,
2952 config_disable_cmd,
2953 "disable",
2954 "Turn off privileged mode command\n")
2955{
2956 if (vty->node == ENABLE_NODE)
2957 vty->node = VIEW_NODE;
2958 return CMD_SUCCESS;
2959}
2960
2961/* Down vty node level. */
2962DEFUN (config_exit,
2963 config_exit_cmd,
2964 "exit",
2965 "Exit current mode and down to previous mode\n")
2966{
2967 switch (vty->node)
2968 {
2969 case VIEW_NODE:
2970 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002971 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002972 if (vty_shell (vty))
2973 exit (0);
2974 else
2975 vty->status = VTY_CLOSE;
2976 break;
2977 case CONFIG_NODE:
2978 vty->node = ENABLE_NODE;
2979 vty_config_unlock (vty);
2980 break;
2981 case INTERFACE_NODE:
2982 case ZEBRA_NODE:
2983 case BGP_NODE:
2984 case RIP_NODE:
2985 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002986 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002987 case OSPF_NODE:
2988 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002989 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002990 case KEYCHAIN_NODE:
2991 case MASC_NODE:
2992 case RMAP_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002993 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002994 case VTY_NODE:
2995 vty->node = CONFIG_NODE;
2996 break;
paul718e3742002-12-13 20:15:29 +00002997 case BGP_IPV4_NODE:
2998 case BGP_IPV4M_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002999 case BGP_VPNV4_NODE:
3000 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05003001 case BGP_ENCAP_NODE:
3002 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00003003 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00003004 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00003005 vty->node = BGP_NODE;
3006 break;
3007 case KEYCHAIN_KEY_NODE:
3008 vty->node = KEYCHAIN_NODE;
3009 break;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02003010 case LINK_PARAMS_NODE:
3011 vty->node = INTERFACE_NODE;
3012 break;
paul718e3742002-12-13 20:15:29 +00003013 default:
3014 break;
3015 }
3016 return CMD_SUCCESS;
3017}
3018
3019/* quit is alias of exit. */
3020ALIAS (config_exit,
3021 config_quit_cmd,
3022 "quit",
3023 "Exit current mode and down to previous mode\n")
3024
3025/* End of configuration. */
3026DEFUN (config_end,
3027 config_end_cmd,
3028 "end",
3029 "End current mode and change to enable mode.")
3030{
3031 switch (vty->node)
3032 {
3033 case VIEW_NODE:
3034 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01003035 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00003036 /* Nothing to do. */
3037 break;
3038 case CONFIG_NODE:
3039 case INTERFACE_NODE:
3040 case ZEBRA_NODE:
3041 case RIP_NODE:
3042 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01003043 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00003044 case BGP_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05003045 case BGP_ENCAP_NODE:
3046 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00003047 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05003048 case BGP_VPNV6_NODE:
paul718e3742002-12-13 20:15:29 +00003049 case BGP_IPV4_NODE:
3050 case BGP_IPV4M_NODE:
3051 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00003052 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00003053 case RMAP_NODE:
3054 case OSPF_NODE:
3055 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00003056 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00003057 case KEYCHAIN_NODE:
3058 case KEYCHAIN_KEY_NODE:
3059 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02003060 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00003061 case VTY_NODE:
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02003062 case LINK_PARAMS_NODE:
paul718e3742002-12-13 20:15:29 +00003063 vty_config_unlock (vty);
3064 vty->node = ENABLE_NODE;
3065 break;
3066 default:
3067 break;
3068 }
3069 return CMD_SUCCESS;
3070}
3071
3072/* Show version. */
3073DEFUN (show_version,
3074 show_version_cmd,
3075 "show version",
3076 SHOW_STR
3077 "Displays zebra version\n")
3078{
hasso12f6ea22005-03-07 08:35:39 +00003079 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
3080 VTY_NEWLINE);
David Lamparter0be793e2012-11-27 01:34:56 +00003081 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
David Lamparter7abd8752014-11-22 10:43:29 -08003082 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
3083 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00003084
3085 return CMD_SUCCESS;
3086}
3087
3088/* Help display function for all node. */
3089DEFUN (config_help,
3090 config_help_cmd,
3091 "help",
3092 "Description of the interactive help system\n")
3093{
3094 vty_out (vty,
hasso6590f2c2004-10-19 20:40:08 +00003095 "Quagga VTY provides advanced help feature. When you need help,%s\
paul718e3742002-12-13 20:15:29 +00003096anytime at the command line please press '?'.%s\
3097%s\
3098If nothing matches, the help list will be empty and you must backup%s\
3099 until entering a '?' shows the available options.%s\
3100Two styles of help are provided:%s\
31011. Full help is available when you are ready to enter a%s\
3102command argument (e.g. 'show ?') and describes each possible%s\
3103argument.%s\
31042. Partial help is provided when an abbreviated argument is entered%s\
3105 and you want to know what arguments match the input%s\
3106 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3107 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3108 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
3109 return CMD_SUCCESS;
3110}
3111
3112/* Help display function for all node. */
3113DEFUN (config_list,
3114 config_list_cmd,
3115 "list",
3116 "Print command list\n")
3117{
hasso8c328f12004-10-05 21:01:23 +00003118 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003119 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
3120 struct cmd_element *cmd;
3121
paul55468c82005-03-14 20:19:01 +00003122 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
paul4275b1d2005-03-09 13:42:23 +00003123 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
3124 && !(cmd->attr == CMD_ATTR_DEPRECATED
3125 || cmd->attr == CMD_ATTR_HIDDEN))
paul718e3742002-12-13 20:15:29 +00003126 vty_out (vty, " %s%s", cmd->string,
3127 VTY_NEWLINE);
3128 return CMD_SUCCESS;
3129}
3130
3131/* Write current configuration into file. */
3132DEFUN (config_write_file,
3133 config_write_file_cmd,
3134 "write file",
3135 "Write running configuration to memory, network, or terminal\n"
3136 "Write to configuration file\n")
3137{
hasso8c328f12004-10-05 21:01:23 +00003138 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003139 int fd;
3140 struct cmd_node *node;
3141 char *config_file;
3142 char *config_file_tmp = NULL;
3143 char *config_file_sav = NULL;
paul05865c92005-10-26 05:49:54 +00003144 int ret = CMD_WARNING;
paul718e3742002-12-13 20:15:29 +00003145 struct vty *file_vty;
3146
3147 /* Check and see if we are operating under vtysh configuration */
3148 if (host.config == NULL)
3149 {
3150 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3151 VTY_NEWLINE);
3152 return CMD_WARNING;
3153 }
3154
3155 /* Get filename. */
3156 config_file = host.config;
3157
paul05865c92005-10-26 05:49:54 +00003158 config_file_sav =
3159 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
paul718e3742002-12-13 20:15:29 +00003160 strcpy (config_file_sav, config_file);
3161 strcat (config_file_sav, CONF_BACKUP_EXT);
3162
3163
paul05865c92005-10-26 05:49:54 +00003164 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
paul718e3742002-12-13 20:15:29 +00003165 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3166
3167 /* Open file to configuration write. */
3168 fd = mkstemp (config_file_tmp);
3169 if (fd < 0)
3170 {
3171 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3172 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003173 goto finished;
paul718e3742002-12-13 20:15:29 +00003174 }
3175
3176 /* Make vty for configuration file. */
3177 file_vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02003178 file_vty->wfd = fd;
paul718e3742002-12-13 20:15:29 +00003179 file_vty->type = VTY_FILE;
3180
3181 /* Config file header print. */
3182 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3183 vty_time_print (file_vty, 1);
3184 vty_out (file_vty, "!\n");
3185
paul55468c82005-03-14 20:19:01 +00003186 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003187 if ((node = vector_slot (cmdvec, i)) && node->func)
3188 {
3189 if ((*node->func) (file_vty))
3190 vty_out (file_vty, "!\n");
3191 }
3192 vty_close (file_vty);
3193
3194 if (unlink (config_file_sav) != 0)
3195 if (errno != ENOENT)
3196 {
3197 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3198 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003199 goto finished;
paul718e3742002-12-13 20:15:29 +00003200 }
3201 if (link (config_file, config_file_sav) != 0)
3202 {
3203 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3204 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003205 goto finished;
paul718e3742002-12-13 20:15:29 +00003206 }
3207 sync ();
3208 if (unlink (config_file) != 0)
3209 {
3210 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3211 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003212 goto finished;
paul718e3742002-12-13 20:15:29 +00003213 }
3214 if (link (config_file_tmp, config_file) != 0)
3215 {
3216 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3217 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003218 goto finished;
paul718e3742002-12-13 20:15:29 +00003219 }
paul718e3742002-12-13 20:15:29 +00003220 sync ();
3221
gdtaa593d52003-12-22 20:15:53 +00003222 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3223 {
3224 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
ajs6099b3b2004-11-20 02:06:59 +00003225 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003226 goto finished;
gdtaa593d52003-12-22 20:15:53 +00003227 }
3228
paul718e3742002-12-13 20:15:29 +00003229 vty_out (vty, "Configuration saved to %s%s", config_file,
3230 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003231 ret = CMD_SUCCESS;
3232
3233finished:
3234 unlink (config_file_tmp);
3235 XFREE (MTYPE_TMP, config_file_tmp);
3236 XFREE (MTYPE_TMP, config_file_sav);
3237 return ret;
paul718e3742002-12-13 20:15:29 +00003238}
3239
3240ALIAS (config_write_file,
3241 config_write_cmd,
3242 "write",
3243 "Write running configuration to memory, network, or terminal\n")
3244
3245ALIAS (config_write_file,
3246 config_write_memory_cmd,
3247 "write memory",
3248 "Write running configuration to memory, network, or terminal\n"
3249 "Write configuration to the file (same as write file)\n")
3250
3251ALIAS (config_write_file,
3252 copy_runningconfig_startupconfig_cmd,
3253 "copy running-config startup-config",
3254 "Copy configuration\n"
3255 "Copy running config to... \n"
3256 "Copy running config to startup config (same as write file)\n")
3257
3258/* Write current configuration into the terminal. */
3259DEFUN (config_write_terminal,
3260 config_write_terminal_cmd,
3261 "write terminal",
3262 "Write running configuration to memory, network, or terminal\n"
3263 "Write to terminal\n")
3264{
hasso8c328f12004-10-05 21:01:23 +00003265 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003266 struct cmd_node *node;
3267
3268 if (vty->type == VTY_SHELL_SERV)
3269 {
paul55468c82005-03-14 20:19:01 +00003270 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003271 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3272 {
3273 if ((*node->func) (vty))
3274 vty_out (vty, "!%s", VTY_NEWLINE);
3275 }
3276 }
3277 else
3278 {
3279 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3280 VTY_NEWLINE);
3281 vty_out (vty, "!%s", VTY_NEWLINE);
3282
paul55468c82005-03-14 20:19:01 +00003283 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003284 if ((node = vector_slot (cmdvec, i)) && node->func)
3285 {
3286 if ((*node->func) (vty))
3287 vty_out (vty, "!%s", VTY_NEWLINE);
3288 }
3289 vty_out (vty, "end%s",VTY_NEWLINE);
3290 }
3291 return CMD_SUCCESS;
3292}
3293
3294/* Write current configuration into the terminal. */
3295ALIAS (config_write_terminal,
3296 show_running_config_cmd,
3297 "show running-config",
3298 SHOW_STR
3299 "running configuration\n")
3300
3301/* Write startup configuration into the terminal. */
3302DEFUN (show_startup_config,
3303 show_startup_config_cmd,
3304 "show startup-config",
3305 SHOW_STR
3306 "Contentes of startup configuration\n")
3307{
3308 char buf[BUFSIZ];
3309 FILE *confp;
3310
3311 confp = fopen (host.config, "r");
3312 if (confp == NULL)
3313 {
3314 vty_out (vty, "Can't open configuration file [%s]%s",
3315 host.config, VTY_NEWLINE);
3316 return CMD_WARNING;
3317 }
3318
3319 while (fgets (buf, BUFSIZ, confp))
3320 {
3321 char *cp = buf;
3322
3323 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3324 cp++;
3325 *cp = '\0';
3326
3327 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3328 }
3329
3330 fclose (confp);
3331
3332 return CMD_SUCCESS;
3333}
3334
3335/* Hostname configuration */
3336DEFUN (config_hostname,
3337 hostname_cmd,
3338 "hostname WORD",
3339 "Set system's network name\n"
3340 "This system's network name\n")
3341{
3342 if (!isalpha((int) *argv[0]))
3343 {
3344 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3345 return CMD_WARNING;
3346 }
3347
3348 if (host.name)
paul05865c92005-10-26 05:49:54 +00003349 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003350
paul05865c92005-10-26 05:49:54 +00003351 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003352 return CMD_SUCCESS;
3353}
3354
3355DEFUN (config_no_hostname,
3356 no_hostname_cmd,
3357 "no hostname [HOSTNAME]",
3358 NO_STR
3359 "Reset system's network name\n"
3360 "Host name of this router\n")
3361{
3362 if (host.name)
paul05865c92005-10-26 05:49:54 +00003363 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003364 host.name = NULL;
3365 return CMD_SUCCESS;
3366}
3367
3368/* VTY interface password set. */
3369DEFUN (config_password, password_cmd,
3370 "password (8|) WORD",
3371 "Assign the terminal connection password\n"
3372 "Specifies a HIDDEN password will follow\n"
3373 "dummy string \n"
3374 "The HIDDEN line password string\n")
3375{
3376 /* Argument check. */
3377 if (argc == 0)
3378 {
3379 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3380 return CMD_WARNING;
3381 }
3382
3383 if (argc == 2)
3384 {
3385 if (*argv[0] == '8')
3386 {
3387 if (host.password)
paul05865c92005-10-26 05:49:54 +00003388 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003389 host.password = NULL;
3390 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003391 XFREE (MTYPE_HOST, host.password_encrypt);
3392 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003393 return CMD_SUCCESS;
3394 }
3395 else
3396 {
3397 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3398 return CMD_WARNING;
3399 }
3400 }
3401
3402 if (!isalnum ((int) *argv[0]))
3403 {
3404 vty_out (vty,
3405 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3406 return CMD_WARNING;
3407 }
3408
3409 if (host.password)
paul05865c92005-10-26 05:49:54 +00003410 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003411 host.password = NULL;
3412
3413 if (host.encrypt)
3414 {
3415 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003416 XFREE (MTYPE_HOST, host.password_encrypt);
3417 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003418 }
3419 else
paul05865c92005-10-26 05:49:54 +00003420 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003421
3422 return CMD_SUCCESS;
3423}
3424
3425ALIAS (config_password, password_text_cmd,
3426 "password LINE",
3427 "Assign the terminal connection password\n"
3428 "The UNENCRYPTED (cleartext) line password\n")
3429
3430/* VTY enable password set. */
3431DEFUN (config_enable_password, enable_password_cmd,
3432 "enable password (8|) WORD",
3433 "Modify enable password parameters\n"
3434 "Assign the privileged level password\n"
3435 "Specifies a HIDDEN password will follow\n"
3436 "dummy string \n"
3437 "The HIDDEN 'enable' password string\n")
3438{
3439 /* Argument check. */
3440 if (argc == 0)
3441 {
3442 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3443 return CMD_WARNING;
3444 }
3445
3446 /* Crypt type is specified. */
3447 if (argc == 2)
3448 {
3449 if (*argv[0] == '8')
3450 {
3451 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003452 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003453 host.enable = NULL;
3454
3455 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003456 XFREE (MTYPE_HOST, host.enable_encrypt);
3457 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003458
3459 return CMD_SUCCESS;
3460 }
3461 else
3462 {
3463 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3464 return CMD_WARNING;
3465 }
3466 }
3467
3468 if (!isalnum ((int) *argv[0]))
3469 {
3470 vty_out (vty,
3471 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3472 return CMD_WARNING;
3473 }
3474
3475 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003476 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003477 host.enable = NULL;
3478
3479 /* Plain password input. */
3480 if (host.encrypt)
3481 {
3482 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003483 XFREE (MTYPE_HOST, host.enable_encrypt);
3484 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003485 }
3486 else
paul05865c92005-10-26 05:49:54 +00003487 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003488
3489 return CMD_SUCCESS;
3490}
3491
3492ALIAS (config_enable_password,
3493 enable_password_text_cmd,
3494 "enable password LINE",
3495 "Modify enable password parameters\n"
3496 "Assign the privileged level password\n"
3497 "The UNENCRYPTED (cleartext) 'enable' password\n")
3498
3499/* VTY enable password delete. */
3500DEFUN (no_config_enable_password, no_enable_password_cmd,
3501 "no enable password",
3502 NO_STR
3503 "Modify enable password parameters\n"
3504 "Assign the privileged level password\n")
3505{
3506 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003507 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003508 host.enable = NULL;
3509
3510 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003511 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003512 host.enable_encrypt = NULL;
3513
3514 return CMD_SUCCESS;
3515}
3516
3517DEFUN (service_password_encrypt,
3518 service_password_encrypt_cmd,
3519 "service password-encryption",
3520 "Set up miscellaneous service\n"
3521 "Enable encrypted passwords\n")
3522{
3523 if (host.encrypt)
3524 return CMD_SUCCESS;
3525
3526 host.encrypt = 1;
3527
3528 if (host.password)
3529 {
3530 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003531 XFREE (MTYPE_HOST, host.password_encrypt);
3532 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
paul718e3742002-12-13 20:15:29 +00003533 }
3534 if (host.enable)
3535 {
3536 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003537 XFREE (MTYPE_HOST, host.enable_encrypt);
3538 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
paul718e3742002-12-13 20:15:29 +00003539 }
3540
3541 return CMD_SUCCESS;
3542}
3543
3544DEFUN (no_service_password_encrypt,
3545 no_service_password_encrypt_cmd,
3546 "no service password-encryption",
3547 NO_STR
3548 "Set up miscellaneous service\n"
3549 "Enable encrypted passwords\n")
3550{
3551 if (! host.encrypt)
3552 return CMD_SUCCESS;
3553
3554 host.encrypt = 0;
3555
3556 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003557 XFREE (MTYPE_HOST, host.password_encrypt);
paul718e3742002-12-13 20:15:29 +00003558 host.password_encrypt = NULL;
3559
3560 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003561 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003562 host.enable_encrypt = NULL;
3563
3564 return CMD_SUCCESS;
3565}
3566
3567DEFUN (config_terminal_length, config_terminal_length_cmd,
3568 "terminal length <0-512>",
3569 "Set terminal line parameters\n"
3570 "Set number of lines on a screen\n"
3571 "Number of lines on screen (0 for no pausing)\n")
3572{
3573 int lines;
3574 char *endptr = NULL;
3575
3576 lines = strtol (argv[0], &endptr, 10);
3577 if (lines < 0 || lines > 512 || *endptr != '\0')
3578 {
3579 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3580 return CMD_WARNING;
3581 }
3582 vty->lines = lines;
3583
3584 return CMD_SUCCESS;
3585}
3586
3587DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3588 "terminal no length",
3589 "Set terminal line parameters\n"
3590 NO_STR
3591 "Set number of lines on a screen\n")
3592{
3593 vty->lines = -1;
3594 return CMD_SUCCESS;
3595}
3596
3597DEFUN (service_terminal_length, service_terminal_length_cmd,
3598 "service terminal-length <0-512>",
3599 "Set up miscellaneous service\n"
3600 "System wide terminal length configuration\n"
3601 "Number of lines of VTY (0 means no line control)\n")
3602{
3603 int lines;
3604 char *endptr = NULL;
3605
3606 lines = strtol (argv[0], &endptr, 10);
3607 if (lines < 0 || lines > 512 || *endptr != '\0')
3608 {
3609 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3610 return CMD_WARNING;
3611 }
3612 host.lines = lines;
3613
3614 return CMD_SUCCESS;
3615}
3616
3617DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3618 "no service terminal-length [<0-512>]",
3619 NO_STR
3620 "Set up miscellaneous service\n"
3621 "System wide terminal length configuration\n"
3622 "Number of lines of VTY (0 means no line control)\n")
3623{
3624 host.lines = -1;
3625 return CMD_SUCCESS;
3626}
3627
ajs2885f722004-12-17 23:16:33 +00003628DEFUN_HIDDEN (do_echo,
3629 echo_cmd,
3630 "echo .MESSAGE",
3631 "Echo a message back to the vty\n"
3632 "The message to echo\n")
3633{
3634 char *message;
3635
ajsf6834d42005-01-28 20:28:35 +00003636 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3637 VTY_NEWLINE);
3638 if (message)
3639 XFREE(MTYPE_TMP, message);
ajs2885f722004-12-17 23:16:33 +00003640 return CMD_SUCCESS;
3641}
3642
ajs274a4a42004-12-07 15:39:31 +00003643DEFUN (config_logmsg,
3644 config_logmsg_cmd,
3645 "logmsg "LOG_LEVELS" .MESSAGE",
3646 "Send a message to enabled logging destinations\n"
3647 LOG_LEVEL_DESC
3648 "The message to send\n")
3649{
3650 int level;
3651 char *message;
3652
3653 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3654 return CMD_ERR_NO_MATCH;
3655
Christian Hammersfc951862011-03-23 13:07:55 +03003656 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
ajsf6834d42005-01-28 20:28:35 +00003657 if (message)
3658 XFREE(MTYPE_TMP, message);
ajs274a4a42004-12-07 15:39:31 +00003659 return CMD_SUCCESS;
3660}
3661
3662DEFUN (show_logging,
3663 show_logging_cmd,
3664 "show logging",
3665 SHOW_STR
3666 "Show current logging configuration\n")
3667{
3668 struct zlog *zl = zlog_default;
3669
3670 vty_out (vty, "Syslog logging: ");
3671 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3672 vty_out (vty, "disabled");
3673 else
3674 vty_out (vty, "level %s, facility %s, ident %s",
3675 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3676 facility_name(zl->facility), zl->ident);
3677 vty_out (vty, "%s", VTY_NEWLINE);
3678
3679 vty_out (vty, "Stdout logging: ");
3680 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3681 vty_out (vty, "disabled");
3682 else
3683 vty_out (vty, "level %s",
3684 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3685 vty_out (vty, "%s", VTY_NEWLINE);
3686
3687 vty_out (vty, "Monitor logging: ");
3688 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3689 vty_out (vty, "disabled");
3690 else
3691 vty_out (vty, "level %s",
3692 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3693 vty_out (vty, "%s", VTY_NEWLINE);
3694
3695 vty_out (vty, "File logging: ");
3696 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3697 !zl->fp)
3698 vty_out (vty, "disabled");
3699 else
3700 vty_out (vty, "level %s, filename %s",
3701 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3702 zl->filename);
3703 vty_out (vty, "%s", VTY_NEWLINE);
3704
3705 vty_out (vty, "Protocol name: %s%s",
3706 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3707 vty_out (vty, "Record priority: %s%s",
3708 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003709 vty_out (vty, "Timestamp precision: %d%s",
3710 zl->timestamp_precision, VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +00003711
3712 return CMD_SUCCESS;
3713}
3714
paul718e3742002-12-13 20:15:29 +00003715DEFUN (config_log_stdout,
3716 config_log_stdout_cmd,
3717 "log stdout",
3718 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003719 "Set stdout logging level\n")
paul718e3742002-12-13 20:15:29 +00003720{
ajs274a4a42004-12-07 15:39:31 +00003721 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3722 return CMD_SUCCESS;
3723}
3724
3725DEFUN (config_log_stdout_level,
3726 config_log_stdout_level_cmd,
3727 "log stdout "LOG_LEVELS,
3728 "Logging control\n"
3729 "Set stdout logging level\n"
3730 LOG_LEVEL_DESC)
3731{
3732 int level;
3733
3734 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3735 return CMD_ERR_NO_MATCH;
3736 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
paul718e3742002-12-13 20:15:29 +00003737 return CMD_SUCCESS;
3738}
3739
3740DEFUN (no_config_log_stdout,
3741 no_config_log_stdout_cmd,
ajs274a4a42004-12-07 15:39:31 +00003742 "no log stdout [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003743 NO_STR
3744 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003745 "Cancel logging to stdout\n"
3746 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003747{
ajs274a4a42004-12-07 15:39:31 +00003748 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003749 return CMD_SUCCESS;
3750}
3751
ajs274a4a42004-12-07 15:39:31 +00003752DEFUN (config_log_monitor,
3753 config_log_monitor_cmd,
3754 "log monitor",
paul718e3742002-12-13 20:15:29 +00003755 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003756 "Set terminal line (monitor) logging level\n")
3757{
3758 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3759 return CMD_SUCCESS;
3760}
3761
3762DEFUN (config_log_monitor_level,
3763 config_log_monitor_level_cmd,
3764 "log monitor "LOG_LEVELS,
3765 "Logging control\n"
3766 "Set terminal line (monitor) logging level\n"
3767 LOG_LEVEL_DESC)
3768{
3769 int level;
3770
3771 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3772 return CMD_ERR_NO_MATCH;
3773 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3774 return CMD_SUCCESS;
3775}
3776
3777DEFUN (no_config_log_monitor,
3778 no_config_log_monitor_cmd,
3779 "no log monitor [LEVEL]",
3780 NO_STR
3781 "Logging control\n"
3782 "Disable terminal line (monitor) logging\n"
3783 "Logging level\n")
3784{
3785 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3786 return CMD_SUCCESS;
3787}
3788
3789static int
3790set_log_file(struct vty *vty, const char *fname, int loglevel)
paul718e3742002-12-13 20:15:29 +00003791{
3792 int ret;
paul9035efa2004-10-10 11:56:56 +00003793 char *p = NULL;
3794 const char *fullpath;
3795
paul718e3742002-12-13 20:15:29 +00003796 /* Path detection. */
ajs274a4a42004-12-07 15:39:31 +00003797 if (! IS_DIRECTORY_SEP (*fname))
paul718e3742002-12-13 20:15:29 +00003798 {
paul9035efa2004-10-10 11:56:56 +00003799 char cwd[MAXPATHLEN+1];
3800 cwd[MAXPATHLEN] = '\0';
3801
3802 if (getcwd (cwd, MAXPATHLEN) == NULL)
3803 {
3804 zlog_err ("config_log_file: Unable to alloc mem!");
3805 return CMD_WARNING;
3806 }
3807
ajs274a4a42004-12-07 15:39:31 +00003808 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
paul9035efa2004-10-10 11:56:56 +00003809 == NULL)
3810 {
3811 zlog_err ("config_log_file: Unable to alloc mem!");
3812 return CMD_WARNING;
3813 }
ajs274a4a42004-12-07 15:39:31 +00003814 sprintf (p, "%s/%s", cwd, fname);
paul9035efa2004-10-10 11:56:56 +00003815 fullpath = p;
paul718e3742002-12-13 20:15:29 +00003816 }
3817 else
ajs274a4a42004-12-07 15:39:31 +00003818 fullpath = fname;
paul718e3742002-12-13 20:15:29 +00003819
ajs274a4a42004-12-07 15:39:31 +00003820 ret = zlog_set_file (NULL, fullpath, loglevel);
paul718e3742002-12-13 20:15:29 +00003821
paul9035efa2004-10-10 11:56:56 +00003822 if (p)
3823 XFREE (MTYPE_TMP, p);
3824
paul718e3742002-12-13 20:15:29 +00003825 if (!ret)
3826 {
ajs274a4a42004-12-07 15:39:31 +00003827 vty_out (vty, "can't open logfile %s\n", fname);
paul718e3742002-12-13 20:15:29 +00003828 return CMD_WARNING;
3829 }
3830
3831 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003832 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003833
paul05865c92005-10-26 05:49:54 +00003834 host.logfile = XSTRDUP (MTYPE_HOST, fname);
paul718e3742002-12-13 20:15:29 +00003835
3836 return CMD_SUCCESS;
3837}
3838
ajs274a4a42004-12-07 15:39:31 +00003839DEFUN (config_log_file,
3840 config_log_file_cmd,
3841 "log file FILENAME",
3842 "Logging control\n"
3843 "Logging to file\n"
3844 "Logging filename\n")
3845{
3846 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3847}
3848
3849DEFUN (config_log_file_level,
3850 config_log_file_level_cmd,
3851 "log file FILENAME "LOG_LEVELS,
3852 "Logging control\n"
3853 "Logging to file\n"
3854 "Logging filename\n"
3855 LOG_LEVEL_DESC)
3856{
3857 int level;
3858
3859 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3860 return CMD_ERR_NO_MATCH;
3861 return set_log_file(vty, argv[0], level);
3862}
3863
paul718e3742002-12-13 20:15:29 +00003864DEFUN (no_config_log_file,
3865 no_config_log_file_cmd,
3866 "no log file [FILENAME]",
3867 NO_STR
3868 "Logging control\n"
3869 "Cancel logging to file\n"
3870 "Logging file name\n")
3871{
3872 zlog_reset_file (NULL);
3873
3874 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003875 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003876
3877 host.logfile = NULL;
3878
3879 return CMD_SUCCESS;
3880}
3881
ajs274a4a42004-12-07 15:39:31 +00003882ALIAS (no_config_log_file,
3883 no_config_log_file_level_cmd,
3884 "no log file FILENAME LEVEL",
3885 NO_STR
3886 "Logging control\n"
3887 "Cancel logging to file\n"
3888 "Logging file name\n"
3889 "Logging level\n")
3890
paul718e3742002-12-13 20:15:29 +00003891DEFUN (config_log_syslog,
3892 config_log_syslog_cmd,
3893 "log syslog",
3894 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003895 "Set syslog logging level\n")
paul718e3742002-12-13 20:15:29 +00003896{
ajs274a4a42004-12-07 15:39:31 +00003897 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003898 return CMD_SUCCESS;
3899}
3900
ajs274a4a42004-12-07 15:39:31 +00003901DEFUN (config_log_syslog_level,
3902 config_log_syslog_level_cmd,
3903 "log syslog "LOG_LEVELS,
paul12ab19f2003-07-26 06:14:55 +00003904 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003905 "Set syslog logging level\n"
3906 LOG_LEVEL_DESC)
paul12ab19f2003-07-26 06:14:55 +00003907{
ajs274a4a42004-12-07 15:39:31 +00003908 int level;
paul12ab19f2003-07-26 06:14:55 +00003909
ajs274a4a42004-12-07 15:39:31 +00003910 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3911 return CMD_ERR_NO_MATCH;
3912 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3913 return CMD_SUCCESS;
3914}
paul12ab19f2003-07-26 06:14:55 +00003915
ajs274a4a42004-12-07 15:39:31 +00003916DEFUN_DEPRECATED (config_log_syslog_facility,
3917 config_log_syslog_facility_cmd,
3918 "log syslog facility "LOG_FACILITIES,
3919 "Logging control\n"
3920 "Logging goes to syslog\n"
3921 "(Deprecated) Facility parameter for syslog messages\n"
3922 LOG_FACILITY_DESC)
3923{
3924 int facility;
paul12ab19f2003-07-26 06:14:55 +00003925
ajs274a4a42004-12-07 15:39:31 +00003926 if ((facility = facility_match(argv[0])) < 0)
3927 return CMD_ERR_NO_MATCH;
3928
3929 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003930 zlog_default->facility = facility;
paul718e3742002-12-13 20:15:29 +00003931 return CMD_SUCCESS;
3932}
3933
3934DEFUN (no_config_log_syslog,
3935 no_config_log_syslog_cmd,
ajs274a4a42004-12-07 15:39:31 +00003936 "no log syslog [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003937 NO_STR
3938 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003939 "Cancel logging to syslog\n"
3940 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003941{
ajs274a4a42004-12-07 15:39:31 +00003942 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003943 return CMD_SUCCESS;
3944}
3945
paul12ab19f2003-07-26 06:14:55 +00003946ALIAS (no_config_log_syslog,
3947 no_config_log_syslog_facility_cmd,
ajs274a4a42004-12-07 15:39:31 +00003948 "no log syslog facility "LOG_FACILITIES,
paul12ab19f2003-07-26 06:14:55 +00003949 NO_STR
3950 "Logging control\n"
3951 "Logging goes to syslog\n"
3952 "Facility parameter for syslog messages\n"
ajs274a4a42004-12-07 15:39:31 +00003953 LOG_FACILITY_DESC)
paul12ab19f2003-07-26 06:14:55 +00003954
ajs274a4a42004-12-07 15:39:31 +00003955DEFUN (config_log_facility,
3956 config_log_facility_cmd,
3957 "log facility "LOG_FACILITIES,
paul718e3742002-12-13 20:15:29 +00003958 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003959 "Facility parameter for syslog messages\n"
3960 LOG_FACILITY_DESC)
paul718e3742002-12-13 20:15:29 +00003961{
ajs274a4a42004-12-07 15:39:31 +00003962 int facility;
3963
3964 if ((facility = facility_match(argv[0])) < 0)
3965 return CMD_ERR_NO_MATCH;
3966 zlog_default->facility = facility;
3967 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +00003968}
3969
ajs274a4a42004-12-07 15:39:31 +00003970DEFUN (no_config_log_facility,
3971 no_config_log_facility_cmd,
3972 "no log facility [FACILITY]",
paul718e3742002-12-13 20:15:29 +00003973 NO_STR
3974 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003975 "Reset syslog facility to default (daemon)\n"
3976 "Syslog facility\n")
paul718e3742002-12-13 20:15:29 +00003977{
ajs274a4a42004-12-07 15:39:31 +00003978 zlog_default->facility = LOG_DAEMON;
3979 return CMD_SUCCESS;
3980}
3981
3982DEFUN_DEPRECATED (config_log_trap,
3983 config_log_trap_cmd,
3984 "log trap "LOG_LEVELS,
3985 "Logging control\n"
3986 "(Deprecated) Set logging level and default for all destinations\n"
3987 LOG_LEVEL_DESC)
3988{
3989 int new_level ;
3990 int i;
3991
3992 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3993 return CMD_ERR_NO_MATCH;
3994
3995 zlog_default->default_lvl = new_level;
3996 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3997 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3998 zlog_default->maxlvl[i] = new_level;
3999 return CMD_SUCCESS;
4000}
4001
4002DEFUN_DEPRECATED (no_config_log_trap,
4003 no_config_log_trap_cmd,
4004 "no log trap [LEVEL]",
4005 NO_STR
4006 "Logging control\n"
4007 "Permit all logging information\n"
4008 "Logging level\n")
4009{
4010 zlog_default->default_lvl = LOG_DEBUG;
paul718e3742002-12-13 20:15:29 +00004011 return CMD_SUCCESS;
4012}
4013
4014DEFUN (config_log_record_priority,
4015 config_log_record_priority_cmd,
4016 "log record-priority",
4017 "Logging control\n"
4018 "Log the priority of the message within the message\n")
4019{
4020 zlog_default->record_priority = 1 ;
4021 return CMD_SUCCESS;
4022}
4023
4024DEFUN (no_config_log_record_priority,
4025 no_config_log_record_priority_cmd,
4026 "no log record-priority",
4027 NO_STR
4028 "Logging control\n"
4029 "Do not log the priority of the message within the message\n")
4030{
4031 zlog_default->record_priority = 0 ;
4032 return CMD_SUCCESS;
4033}
4034
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004035DEFUN (config_log_timestamp_precision,
4036 config_log_timestamp_precision_cmd,
4037 "log timestamp precision <0-6>",
4038 "Logging control\n"
4039 "Timestamp configuration\n"
4040 "Set the timestamp precision\n"
4041 "Number of subsecond digits\n")
4042{
4043 if (argc != 1)
4044 {
4045 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
4046 return CMD_WARNING;
4047 }
4048
4049 VTY_GET_INTEGER_RANGE("Timestamp Precision",
4050 zlog_default->timestamp_precision, argv[0], 0, 6);
4051 return CMD_SUCCESS;
4052}
4053
4054DEFUN (no_config_log_timestamp_precision,
4055 no_config_log_timestamp_precision_cmd,
4056 "no log timestamp precision",
4057 NO_STR
4058 "Logging control\n"
4059 "Timestamp configuration\n"
4060 "Reset the timestamp precision to the default value of 0\n")
4061{
4062 zlog_default->timestamp_precision = 0 ;
4063 return CMD_SUCCESS;
4064}
4065
paul3b0c5d92005-03-08 10:43:43 +00004066DEFUN (banner_motd_file,
4067 banner_motd_file_cmd,
4068 "banner motd file [FILE]",
4069 "Set banner\n"
4070 "Banner for motd\n"
4071 "Banner from a file\n"
4072 "Filename\n")
4073{
paulb45da6f2005-03-08 15:16:57 +00004074 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004075 XFREE (MTYPE_HOST, host.motdfile);
4076 host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
paulb45da6f2005-03-08 15:16:57 +00004077
paul3b0c5d92005-03-08 10:43:43 +00004078 return CMD_SUCCESS;
4079}
paul718e3742002-12-13 20:15:29 +00004080
4081DEFUN (banner_motd_default,
4082 banner_motd_default_cmd,
4083 "banner motd default",
4084 "Set banner string\n"
4085 "Strings for motd\n"
4086 "Default string\n")
4087{
4088 host.motd = default_motd;
4089 return CMD_SUCCESS;
4090}
4091
4092DEFUN (no_banner_motd,
4093 no_banner_motd_cmd,
4094 "no banner motd",
4095 NO_STR
4096 "Set banner string\n"
4097 "Strings for motd\n")
4098{
4099 host.motd = NULL;
paul22085182005-03-08 16:00:12 +00004100 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004101 XFREE (MTYPE_HOST, host.motdfile);
paul3b0c5d92005-03-08 10:43:43 +00004102 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004103 return CMD_SUCCESS;
4104}
4105
Lou Bergerf9ec4192016-01-12 13:41:48 -05004106DEFUN (show_commandtree,
4107 show_commandtree_cmd,
4108 "show commandtree",
4109 NO_STR
4110 "Show command tree\n")
4111{
4112 /* TBD */
4113 vector cmd_vector;
4114 unsigned int i;
4115
4116 vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE);
4117
4118 /* vector of all commands installed at this node */
4119 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
4120
4121 /* loop over all commands at this node */
4122 for (i = 0; i < vector_active(cmd_vector); ++i)
4123 {
4124 struct cmd_element *cmd_element;
4125
4126 /* A cmd_element (seems to be) is an individual command */
4127 if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
4128 continue;
4129
4130 vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE);
4131 }
4132
4133 vector_free (cmd_vector);
4134 return CMD_SUCCESS;
4135}
4136
paul718e3742002-12-13 20:15:29 +00004137/* Set config filename. Called from vty.c */
4138void
4139host_config_set (char *filename)
4140{
Chris Caputo228da422009-07-18 05:44:03 +00004141 if (host.config)
4142 XFREE (MTYPE_HOST, host.config);
paul05865c92005-10-26 05:49:54 +00004143 host.config = XSTRDUP (MTYPE_HOST, filename);
paul718e3742002-12-13 20:15:29 +00004144}
4145
Christian Franke41de6292016-05-03 19:59:41 +02004146const char *
4147host_config_get (void)
4148{
4149 return host.config;
4150}
4151
Paul Jakma92193662016-06-16 15:53:26 +01004152static void
4153install_default_basic (enum node_type node)
paul718e3742002-12-13 20:15:29 +00004154{
4155 install_element (node, &config_exit_cmd);
4156 install_element (node, &config_quit_cmd);
paul718e3742002-12-13 20:15:29 +00004157 install_element (node, &config_help_cmd);
4158 install_element (node, &config_list_cmd);
Paul Jakma92193662016-06-16 15:53:26 +01004159}
paul718e3742002-12-13 20:15:29 +00004160
Paul Jakma92193662016-06-16 15:53:26 +01004161/* Install common/default commands for a privileged node */
4162void
4163install_default (enum node_type node)
4164{
4165 /* VIEW_NODE is inited below, via install_default_basic, and
4166 install_element's of commands to VIEW_NODE automatically are
4167 also installed to ENABLE_NODE.
4168
4169 For all other nodes, we must ensure install_default_basic is
4170 also called/
4171 */
4172 if (node != VIEW_NODE && node != ENABLE_NODE)
4173 install_default_basic (node);
4174
4175 install_element (node, &config_end_cmd);
paul718e3742002-12-13 20:15:29 +00004176 install_element (node, &config_write_terminal_cmd);
4177 install_element (node, &config_write_file_cmd);
4178 install_element (node, &config_write_memory_cmd);
4179 install_element (node, &config_write_cmd);
4180 install_element (node, &show_running_config_cmd);
4181}
4182
4183/* Initialize command interface. Install basic nodes and commands. */
4184void
4185cmd_init (int terminal)
4186{
Christian Frankecd40b322013-09-30 12:27:51 +00004187 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4188 token_cr.type = TOKEN_TERMINAL;
David Lamparter10bac802015-05-05 11:10:20 +02004189 token_cr.terminal = TERMINAL_LITERAL;
Christian Frankecd40b322013-09-30 12:27:51 +00004190 token_cr.cmd = command_cr;
4191 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
Chris Caputo228da422009-07-18 05:44:03 +00004192
paul718e3742002-12-13 20:15:29 +00004193 /* Allocate initial top vector of commands. */
4194 cmdvec = vector_init (VECTOR_MIN_SIZE);
Paul Jakma92193662016-06-16 15:53:26 +01004195
paul718e3742002-12-13 20:15:29 +00004196 /* Default host value settings. */
4197 host.name = NULL;
4198 host.password = NULL;
4199 host.enable = NULL;
4200 host.logfile = NULL;
4201 host.config = NULL;
4202 host.lines = -1;
4203 host.motd = default_motd;
paul3b0c5d92005-03-08 10:43:43 +00004204 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004205
4206 /* Install top nodes. */
4207 install_node (&view_node, NULL);
4208 install_node (&enable_node, NULL);
4209 install_node (&auth_node, NULL);
4210 install_node (&auth_enable_node, NULL);
Paul Jakma62687ff2008-08-23 14:27:06 +01004211 install_node (&restricted_node, NULL);
paul718e3742002-12-13 20:15:29 +00004212 install_node (&config_node, config_write_host);
4213
4214 /* Each node's basic commands. */
4215 install_element (VIEW_NODE, &show_version_cmd);
4216 if (terminal)
4217 {
Paul Jakma92193662016-06-16 15:53:26 +01004218 install_default_basic (VIEW_NODE);
4219
paul718e3742002-12-13 20:15:29 +00004220 install_element (VIEW_NODE, &config_enable_cmd);
4221 install_element (VIEW_NODE, &config_terminal_length_cmd);
4222 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004223 install_element (VIEW_NODE, &show_logging_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004224 install_element (VIEW_NODE, &show_commandtree_cmd);
ajs2885f722004-12-17 23:16:33 +00004225 install_element (VIEW_NODE, &echo_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004226
Paul Jakma62687ff2008-08-23 14:27:06 +01004227 install_element (RESTRICTED_NODE, &config_enable_cmd);
4228 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4229 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004230 install_element (RESTRICTED_NODE, &show_commandtree_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004231 install_element (RESTRICTED_NODE, &echo_cmd);
paul718e3742002-12-13 20:15:29 +00004232 }
4233
4234 if (terminal)
4235 {
4236 install_default (ENABLE_NODE);
4237 install_element (ENABLE_NODE, &config_disable_cmd);
4238 install_element (ENABLE_NODE, &config_terminal_cmd);
4239 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4240 }
4241 install_element (ENABLE_NODE, &show_startup_config_cmd);
paul718e3742002-12-13 20:15:29 +00004242
4243 if (terminal)
paul718e3742002-12-13 20:15:29 +00004244 {
ajs274a4a42004-12-07 15:39:31 +00004245 install_element (ENABLE_NODE, &config_logmsg_cmd);
hassoe7168df2004-10-03 20:11:32 +00004246
4247 install_default (CONFIG_NODE);
hassoea8e9d92004-10-07 21:32:14 +00004248 }
4249
4250 install_element (CONFIG_NODE, &hostname_cmd);
4251 install_element (CONFIG_NODE, &no_hostname_cmd);
hassoe7168df2004-10-03 20:11:32 +00004252
hassoea8e9d92004-10-07 21:32:14 +00004253 if (terminal)
4254 {
hassoe7168df2004-10-03 20:11:32 +00004255 install_element (CONFIG_NODE, &password_cmd);
4256 install_element (CONFIG_NODE, &password_text_cmd);
4257 install_element (CONFIG_NODE, &enable_password_cmd);
4258 install_element (CONFIG_NODE, &enable_password_text_cmd);
4259 install_element (CONFIG_NODE, &no_enable_password_cmd);
4260
paul718e3742002-12-13 20:15:29 +00004261 install_element (CONFIG_NODE, &config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004262 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
paul718e3742002-12-13 20:15:29 +00004263 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004264 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4265 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4266 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00004267 install_element (CONFIG_NODE, &config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004268 install_element (CONFIG_NODE, &config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004269 install_element (CONFIG_NODE, &no_config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004270 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004271 install_element (CONFIG_NODE, &config_log_syslog_cmd);
ajs274a4a42004-12-07 15:39:31 +00004272 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
paul12ab19f2003-07-26 06:14:55 +00004273 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004274 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
paul12ab19f2003-07-26 06:14:55 +00004275 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
ajs274a4a42004-12-07 15:39:31 +00004276 install_element (CONFIG_NODE, &config_log_facility_cmd);
4277 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004278 install_element (CONFIG_NODE, &config_log_trap_cmd);
4279 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4280 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4281 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004282 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4283 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
paul718e3742002-12-13 20:15:29 +00004284 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4285 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4286 install_element (CONFIG_NODE, &banner_motd_default_cmd);
paul3b0c5d92005-03-08 10:43:43 +00004287 install_element (CONFIG_NODE, &banner_motd_file_cmd);
paul718e3742002-12-13 20:15:29 +00004288 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4289 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4290 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
paul718e3742002-12-13 20:15:29 +00004291
paul354d1192005-04-25 16:26:42 +00004292 install_element (VIEW_NODE, &show_thread_cpu_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004293 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
Paul Jakmae276eb82010-01-09 16:15:00 +00004294
4295 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
paul354d1192005-04-25 16:26:42 +00004296 install_element (VIEW_NODE, &show_work_queues_cmd);
paul9ab68122003-01-18 01:16:20 +00004297 }
Lou Bergerf9ec4192016-01-12 13:41:48 -05004298 install_element (CONFIG_NODE, &show_commandtree_cmd);
Donald Sharpf31bab42015-06-19 19:26:19 -04004299 srandom(time(NULL));
paul718e3742002-12-13 20:15:29 +00004300}
Chris Caputo228da422009-07-18 05:44:03 +00004301
Christian Frankecd40b322013-09-30 12:27:51 +00004302static void
4303cmd_terminate_token(struct cmd_token *token)
4304{
4305 unsigned int i, j;
4306 vector keyword_vect;
4307
4308 if (token->multiple)
4309 {
4310 for (i = 0; i < vector_active(token->multiple); i++)
4311 cmd_terminate_token(vector_slot(token->multiple, i));
4312 vector_free(token->multiple);
4313 token->multiple = NULL;
4314 }
4315
4316 if (token->keyword)
4317 {
4318 for (i = 0; i < vector_active(token->keyword); i++)
4319 {
4320 keyword_vect = vector_slot(token->keyword, i);
4321 for (j = 0; j < vector_active(keyword_vect); j++)
4322 cmd_terminate_token(vector_slot(keyword_vect, j));
4323 vector_free(keyword_vect);
4324 }
4325 vector_free(token->keyword);
4326 token->keyword = NULL;
4327 }
4328
4329 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4330 XFREE(MTYPE_CMD_TOKENS, token->desc);
4331
4332 XFREE(MTYPE_CMD_TOKENS, token);
4333}
4334
4335static void
4336cmd_terminate_element(struct cmd_element *cmd)
4337{
4338 unsigned int i;
4339
4340 if (cmd->tokens == NULL)
4341 return;
4342
4343 for (i = 0; i < vector_active(cmd->tokens); i++)
4344 cmd_terminate_token(vector_slot(cmd->tokens, i));
4345
4346 vector_free(cmd->tokens);
4347 cmd->tokens = NULL;
4348}
4349
Chris Caputo228da422009-07-18 05:44:03 +00004350void
4351cmd_terminate ()
4352{
Christian Frankecd40b322013-09-30 12:27:51 +00004353 unsigned int i, j;
Chris Caputo228da422009-07-18 05:44:03 +00004354 struct cmd_node *cmd_node;
4355 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00004356 vector cmd_node_v;
Chris Caputo228da422009-07-18 05:44:03 +00004357
4358 if (cmdvec)
4359 {
4360 for (i = 0; i < vector_active (cmdvec); i++)
4361 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4362 {
4363 cmd_node_v = cmd_node->cmd_vector;
4364
4365 for (j = 0; j < vector_active (cmd_node_v); j++)
Christian Frankecd40b322013-09-30 12:27:51 +00004366 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4367 cmd_terminate_element(cmd_element);
Chris Caputo228da422009-07-18 05:44:03 +00004368
4369 vector_free (cmd_node_v);
Paul Jakma92193662016-06-16 15:53:26 +01004370 hash_clean (cmd_node->cmd_hash, NULL);
4371 hash_free (cmd_node->cmd_hash);
4372 cmd_node->cmd_hash = NULL;
Chris Caputo228da422009-07-18 05:44:03 +00004373 }
4374
4375 vector_free (cmdvec);
4376 cmdvec = NULL;
4377 }
4378
4379 if (command_cr)
Christian Frankecd40b322013-09-30 12:27:51 +00004380 XFREE(MTYPE_CMD_TOKENS, command_cr);
4381 if (token_cr.desc)
4382 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
Chris Caputo228da422009-07-18 05:44:03 +00004383 if (host.name)
4384 XFREE (MTYPE_HOST, host.name);
4385 if (host.password)
4386 XFREE (MTYPE_HOST, host.password);
4387 if (host.password_encrypt)
4388 XFREE (MTYPE_HOST, host.password_encrypt);
4389 if (host.enable)
4390 XFREE (MTYPE_HOST, host.enable);
4391 if (host.enable_encrypt)
4392 XFREE (MTYPE_HOST, host.enable_encrypt);
4393 if (host.logfile)
4394 XFREE (MTYPE_HOST, host.logfile);
4395 if (host.motdfile)
4396 XFREE (MTYPE_HOST, host.motdfile);
4397 if (host.config)
4398 XFREE (MTYPE_HOST, host.config);
4399}