blob: bb94c28897e3f8504d4802e9b6d7490f223eedb0 [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 {
Nick Hilliard71e0ba72016-12-27 22:51:51 +0000641#ifdef DEV_BUILD
Paul Jakma92193662016-06-16 15:53:26 +0100642 fprintf (stderr,
643 "Multiple command installs to node %d of command:\n%s\n",
644 ntype, cmd->string);
Nick Hilliard71e0ba72016-12-27 22:51:51 +0000645#endif
Paul Jakma92193662016-06-16 15:53:26 +0100646 return;
647 }
648
649 assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern));
650
paul718e3742002-12-13 20:15:29 +0000651 vector_set (cnode->cmd_vector, cmd);
Christian Frankecd40b322013-09-30 12:27:51 +0000652 if (cmd->tokens == NULL)
653 cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
Donald Sharpb9ac2f32016-03-11 14:27:12 -0500654
655 if (ntype == VIEW_NODE)
656 install_element (ENABLE_NODE, cmd);
paul718e3742002-12-13 20:15:29 +0000657}
658
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300659static const unsigned char itoa64[] =
paul718e3742002-12-13 20:15:29 +0000660"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
661
ajs274a4a42004-12-07 15:39:31 +0000662static void
paul718e3742002-12-13 20:15:29 +0000663to64(char *s, long v, int n)
664{
665 while (--n >= 0)
666 {
667 *s++ = itoa64[v&0x3f];
668 v >>= 6;
669 }
670}
671
ajs274a4a42004-12-07 15:39:31 +0000672static char *
673zencrypt (const char *passwd)
paul718e3742002-12-13 20:15:29 +0000674{
675 char salt[6];
676 struct timeval tv;
677 char *crypt (const char *, const char *);
678
679 gettimeofday(&tv,0);
680
681 to64(&salt[0], random(), 3);
682 to64(&salt[3], tv.tv_usec, 3);
683 salt[5] = '\0';
684
685 return crypt (passwd, salt);
686}
687
688/* This function write configuration of this host. */
ajs274a4a42004-12-07 15:39:31 +0000689static int
paul718e3742002-12-13 20:15:29 +0000690config_write_host (struct vty *vty)
691{
692 if (host.name)
693 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
694
695 if (host.encrypt)
696 {
697 if (host.password_encrypt)
698 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
699 if (host.enable_encrypt)
700 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
701 }
702 else
703 {
704 if (host.password)
705 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
706 if (host.enable)
707 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
708 }
709
ajs274a4a42004-12-07 15:39:31 +0000710 if (zlog_default->default_lvl != LOG_DEBUG)
ajs82146b82004-12-07 17:15:55 +0000711 {
712 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
713 VTY_NEWLINE);
714 vty_out (vty, "log trap %s%s",
715 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
716 }
paul718e3742002-12-13 20:15:29 +0000717
ajs274a4a42004-12-07 15:39:31 +0000718 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
paul12ab19f2003-07-26 06:14:55 +0000719 {
ajs274a4a42004-12-07 15:39:31 +0000720 vty_out (vty, "log file %s", host.logfile);
721 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
722 vty_out (vty, " %s",
723 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
paul12ab19f2003-07-26 06:14:55 +0000724 vty_out (vty, "%s", VTY_NEWLINE);
725 }
ajs274a4a42004-12-07 15:39:31 +0000726
727 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
728 {
729 vty_out (vty, "log stdout");
730 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
731 vty_out (vty, " %s",
732 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
733 vty_out (vty, "%s", VTY_NEWLINE);
734 }
735
736 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
737 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
738 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
739 vty_out(vty,"log monitor %s%s",
740 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
741
742 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
743 {
744 vty_out (vty, "log syslog");
745 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
746 vty_out (vty, " %s",
747 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
748 vty_out (vty, "%s", VTY_NEWLINE);
749 }
750
751 if (zlog_default->facility != LOG_DAEMON)
752 vty_out (vty, "log facility %s%s",
753 facility_name(zlog_default->facility), VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000754
755 if (zlog_default->record_priority == 1)
756 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
757
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000758 if (zlog_default->timestamp_precision > 0)
759 vty_out (vty, "log timestamp precision %d%s",
760 zlog_default->timestamp_precision, VTY_NEWLINE);
761
paul718e3742002-12-13 20:15:29 +0000762 if (host.advanced)
763 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
764
765 if (host.encrypt)
766 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
767
768 if (host.lines >= 0)
769 vty_out (vty, "service terminal-length %d%s", host.lines,
770 VTY_NEWLINE);
771
paul3b0c5d92005-03-08 10:43:43 +0000772 if (host.motdfile)
773 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
774 else if (! host.motd)
paul718e3742002-12-13 20:15:29 +0000775 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
776
777 return 1;
778}
779
780/* Utility function for getting command vector. */
ajs274a4a42004-12-07 15:39:31 +0000781static vector
paul718e3742002-12-13 20:15:29 +0000782cmd_node_vector (vector v, enum node_type ntype)
783{
784 struct cmd_node *cnode = vector_slot (v, ntype);
785 return cnode->cmd_vector;
786}
787
ajs274a4a42004-12-07 15:39:31 +0000788#if 0
789/* Filter command vector by symbol. This function is not actually used;
790 * should it be deleted? */
791static int
paul718e3742002-12-13 20:15:29 +0000792cmd_filter_by_symbol (char *command, char *symbol)
793{
794 int i, lim;
795
796 if (strcmp (symbol, "IPV4_ADDRESS") == 0)
797 {
798 i = 0;
799 lim = strlen (command);
800 while (i < lim)
801 {
802 if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
803 return 1;
804 i++;
805 }
806 return 0;
807 }
808 if (strcmp (symbol, "STRING") == 0)
809 {
810 i = 0;
811 lim = strlen (command);
812 while (i < lim)
813 {
814 if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
815 return 1;
816 i++;
817 }
818 return 0;
819 }
820 if (strcmp (symbol, "IFNAME") == 0)
821 {
822 i = 0;
823 lim = strlen (command);
824 while (i < lim)
825 {
826 if (! isalnum ((int) command[i]))
827 return 1;
828 i++;
829 }
830 return 0;
831 }
832 return 0;
833}
ajs274a4a42004-12-07 15:39:31 +0000834#endif
paul718e3742002-12-13 20:15:29 +0000835
836/* Completion match types. */
837enum match_type
838{
839 no_match,
840 extend_match,
841 ipv4_prefix_match,
842 ipv4_match,
843 ipv6_prefix_match,
844 ipv6_match,
845 range_match,
846 vararg_match,
847 partly_match,
848 exact_match
849};
850
ajs274a4a42004-12-07 15:39:31 +0000851static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000852cmd_ipv4_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000853{
hasso8c328f12004-10-05 21:01:23 +0000854 const char *sp;
paul718e3742002-12-13 20:15:29 +0000855 int dots = 0, nums = 0;
856 char buf[4];
857
858 if (str == NULL)
859 return partly_match;
860
861 for (;;)
862 {
863 memset (buf, 0, sizeof (buf));
864 sp = str;
865 while (*str != '\0')
866 {
867 if (*str == '.')
868 {
869 if (dots >= 3)
870 return no_match;
871
872 if (*(str + 1) == '.')
873 return no_match;
874
875 if (*(str + 1) == '\0')
876 return partly_match;
877
878 dots++;
879 break;
880 }
881 if (!isdigit ((int) *str))
882 return no_match;
883
884 str++;
885 }
886
887 if (str - sp > 3)
888 return no_match;
889
890 strncpy (buf, sp, str - sp);
891 if (atoi (buf) > 255)
892 return no_match;
893
894 nums++;
895
896 if (*str == '\0')
897 break;
898
899 str++;
900 }
901
902 if (nums < 4)
903 return partly_match;
904
905 return exact_match;
906}
907
ajs274a4a42004-12-07 15:39:31 +0000908static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000909cmd_ipv4_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000910{
hasso8c328f12004-10-05 21:01:23 +0000911 const char *sp;
paul718e3742002-12-13 20:15:29 +0000912 int dots = 0;
913 char buf[4];
914
915 if (str == NULL)
916 return partly_match;
917
918 for (;;)
919 {
920 memset (buf, 0, sizeof (buf));
921 sp = str;
922 while (*str != '\0' && *str != '/')
923 {
924 if (*str == '.')
925 {
926 if (dots == 3)
927 return no_match;
928
929 if (*(str + 1) == '.' || *(str + 1) == '/')
930 return no_match;
931
932 if (*(str + 1) == '\0')
933 return partly_match;
934
935 dots++;
936 break;
937 }
938
939 if (!isdigit ((int) *str))
940 return no_match;
941
942 str++;
943 }
944
945 if (str - sp > 3)
946 return no_match;
947
948 strncpy (buf, sp, str - sp);
949 if (atoi (buf) > 255)
950 return no_match;
951
952 if (dots == 3)
953 {
954 if (*str == '/')
955 {
956 if (*(str + 1) == '\0')
957 return partly_match;
958
959 str++;
960 break;
961 }
962 else if (*str == '\0')
963 return partly_match;
964 }
965
966 if (*str == '\0')
967 return partly_match;
968
969 str++;
970 }
971
972 sp = str;
973 while (*str != '\0')
974 {
975 if (!isdigit ((int) *str))
976 return no_match;
977
978 str++;
979 }
980
981 if (atoi (sp) > 32)
982 return no_match;
983
984 return exact_match;
985}
986
987#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
988#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
989#define STATE_START 1
990#define STATE_COLON 2
991#define STATE_DOUBLE 3
992#define STATE_ADDR 4
993#define STATE_DOT 5
994#define STATE_SLASH 6
995#define STATE_MASK 7
996
paul22e0a9e2003-07-11 17:55:46 +0000997#ifdef HAVE_IPV6
998
ajs274a4a42004-12-07 15:39:31 +0000999static enum match_type
hasso8c328f12004-10-05 21:01:23 +00001000cmd_ipv6_match (const char *str)
paul718e3742002-12-13 20:15:29 +00001001{
hasso726f9b22003-05-25 21:04:54 +00001002 struct sockaddr_in6 sin6_dummy;
1003 int ret;
paul718e3742002-12-13 20:15:29 +00001004
1005 if (str == NULL)
1006 return partly_match;
1007
1008 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
1009 return no_match;
1010
hasso726f9b22003-05-25 21:04:54 +00001011 /* use inet_pton that has a better support,
1012 * for example inet_pton can support the automatic addresses:
1013 * ::1.2.3.4
1014 */
1015 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
1016
1017 if (ret == 1)
1018 return exact_match;
1019
Roman Hoog Antink7c9c6ae2012-05-09 06:35:34 +00001020 return no_match;
paul718e3742002-12-13 20:15:29 +00001021}
1022
ajs274a4a42004-12-07 15:39:31 +00001023static enum match_type
hasso8c328f12004-10-05 21:01:23 +00001024cmd_ipv6_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +00001025{
1026 int state = STATE_START;
1027 int colons = 0, nums = 0, double_colon = 0;
1028 int mask;
hasso8c328f12004-10-05 21:01:23 +00001029 const char *sp = NULL;
paul718e3742002-12-13 20:15:29 +00001030 char *endptr = NULL;
1031
1032 if (str == NULL)
1033 return partly_match;
1034
1035 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
1036 return no_match;
1037
1038 while (*str != '\0' && state != STATE_MASK)
1039 {
1040 switch (state)
1041 {
1042 case STATE_START:
1043 if (*str == ':')
1044 {
1045 if (*(str + 1) != ':' && *(str + 1) != '\0')
1046 return no_match;
1047 colons--;
1048 state = STATE_COLON;
1049 }
1050 else
1051 {
1052 sp = str;
1053 state = STATE_ADDR;
1054 }
1055
1056 continue;
1057 case STATE_COLON:
1058 colons++;
1059 if (*(str + 1) == '/')
1060 return no_match;
1061 else if (*(str + 1) == ':')
1062 state = STATE_DOUBLE;
1063 else
1064 {
1065 sp = str + 1;
1066 state = STATE_ADDR;
1067 }
1068 break;
1069 case STATE_DOUBLE:
1070 if (double_colon)
1071 return no_match;
1072
1073 if (*(str + 1) == ':')
1074 return no_match;
1075 else
1076 {
1077 if (*(str + 1) != '\0' && *(str + 1) != '/')
1078 colons++;
1079 sp = str + 1;
1080
1081 if (*(str + 1) == '/')
1082 state = STATE_SLASH;
1083 else
1084 state = STATE_ADDR;
1085 }
1086
1087 double_colon++;
1088 nums += 1;
1089 break;
1090 case STATE_ADDR:
1091 if (*(str + 1) == ':' || *(str + 1) == '.'
1092 || *(str + 1) == '\0' || *(str + 1) == '/')
1093 {
1094 if (str - sp > 3)
1095 return no_match;
1096
1097 for (; sp <= str; sp++)
1098 if (*sp == '/')
1099 return no_match;
1100
1101 nums++;
1102
1103 if (*(str + 1) == ':')
1104 state = STATE_COLON;
1105 else if (*(str + 1) == '.')
David Lamparteraa5cf242012-07-19 16:11:50 +02001106 {
1107 if (colons || double_colon)
1108 state = STATE_DOT;
1109 else
1110 return no_match;
1111 }
paul718e3742002-12-13 20:15:29 +00001112 else if (*(str + 1) == '/')
1113 state = STATE_SLASH;
1114 }
1115 break;
1116 case STATE_DOT:
1117 state = STATE_ADDR;
1118 break;
1119 case STATE_SLASH:
1120 if (*(str + 1) == '\0')
1121 return partly_match;
1122
1123 state = STATE_MASK;
1124 break;
1125 default:
1126 break;
1127 }
1128
1129 if (nums > 11)
1130 return no_match;
1131
1132 if (colons > 7)
1133 return no_match;
1134
1135 str++;
1136 }
1137
1138 if (state < STATE_MASK)
1139 return partly_match;
1140
1141 mask = strtol (str, &endptr, 10);
1142 if (*endptr != '\0')
1143 return no_match;
1144
1145 if (mask < 0 || mask > 128)
1146 return no_match;
1147
1148/* I don't know why mask < 13 makes command match partly.
1149 Forgive me to make this comments. I Want to set static default route
1150 because of lack of function to originate default in ospf6d; sorry
1151 yasu
1152 if (mask < 13)
1153 return partly_match;
1154*/
1155
1156 return exact_match;
1157}
1158
paul22e0a9e2003-07-11 17:55:46 +00001159#endif /* HAVE_IPV6 */
1160
paul718e3742002-12-13 20:15:29 +00001161#define DECIMAL_STRLEN_MAX 10
1162
ajs274a4a42004-12-07 15:39:31 +00001163static int
hasso8c328f12004-10-05 21:01:23 +00001164cmd_range_match (const char *range, const char *str)
paul718e3742002-12-13 20:15:29 +00001165{
1166 char *p;
1167 char buf[DECIMAL_STRLEN_MAX + 1];
1168 char *endptr = NULL;
1169 unsigned long min, max, val;
1170
1171 if (str == NULL)
1172 return 1;
1173
1174 val = strtoul (str, &endptr, 10);
1175 if (*endptr != '\0')
1176 return 0;
1177
1178 range++;
1179 p = strchr (range, '-');
1180 if (p == NULL)
1181 return 0;
1182 if (p - range > DECIMAL_STRLEN_MAX)
1183 return 0;
1184 strncpy (buf, range, p - range);
1185 buf[p - range] = '\0';
1186 min = strtoul (buf, &endptr, 10);
1187 if (*endptr != '\0')
1188 return 0;
1189
1190 range = p + 1;
1191 p = strchr (range, '>');
1192 if (p == NULL)
1193 return 0;
1194 if (p - range > DECIMAL_STRLEN_MAX)
1195 return 0;
1196 strncpy (buf, range, p - range);
1197 buf[p - range] = '\0';
1198 max = strtoul (buf, &endptr, 10);
1199 if (*endptr != '\0')
1200 return 0;
1201
1202 if (val < min || val > max)
1203 return 0;
1204
1205 return 1;
1206}
1207
ajs274a4a42004-12-07 15:39:31 +00001208static enum match_type
Christian Frankecd40b322013-09-30 12:27:51 +00001209cmd_word_match(struct cmd_token *token,
1210 enum filter_type filter,
1211 const char *word)
paul718e3742002-12-13 20:15:29 +00001212{
hasso8c328f12004-10-05 21:01:23 +00001213 const char *str;
paul718e3742002-12-13 20:15:29 +00001214 enum match_type match_type;
paul909a2152005-03-14 17:41:45 +00001215
Christian Frankecd40b322013-09-30 12:27:51 +00001216 str = token->cmd;
paul718e3742002-12-13 20:15:29 +00001217
Christian Frankecd40b322013-09-30 12:27:51 +00001218 if (filter == FILTER_RELAXED)
1219 if (!word || !strlen(word))
1220 return partly_match;
paul718e3742002-12-13 20:15:29 +00001221
Christian Frankecd40b322013-09-30 12:27:51 +00001222 if (!word)
1223 return no_match;
paul909a2152005-03-14 17:41:45 +00001224
David Lamparter10bac802015-05-05 11:10:20 +02001225 switch (token->terminal)
Christian Frankecd40b322013-09-30 12:27:51 +00001226 {
David Lamparter10bac802015-05-05 11:10:20 +02001227 case TERMINAL_VARARG:
1228 return vararg_match;
1229
1230 case TERMINAL_RANGE:
1231 if (cmd_range_match(str, word))
1232 return range_match;
1233 break;
1234
1235 case TERMINAL_IPV6:
1236 match_type = cmd_ipv6_match(word);
1237 if ((filter == FILTER_RELAXED && match_type != no_match)
Christian Frankecd40b322013-09-30 12:27:51 +00001238 || (filter == FILTER_STRICT && match_type == exact_match))
David Lamparter10bac802015-05-05 11:10:20 +02001239 return ipv6_match;
1240 break;
1241
1242 case TERMINAL_IPV6_PREFIX:
1243 match_type = cmd_ipv6_prefix_match(word);
1244 if ((filter == FILTER_RELAXED && match_type != no_match)
1245 || (filter == FILTER_STRICT && match_type == exact_match))
1246 return ipv6_prefix_match;
1247 break;
1248
1249 case TERMINAL_IPV4:
1250 match_type = cmd_ipv4_match(word);
1251 if ((filter == FILTER_RELAXED && match_type != no_match)
1252 || (filter == FILTER_STRICT && match_type == exact_match))
1253 return ipv4_match;
1254 break;
1255
1256 case TERMINAL_IPV4_PREFIX:
1257 match_type = cmd_ipv4_prefix_match(word);
1258 if ((filter == FILTER_RELAXED && match_type != no_match)
1259 || (filter == FILTER_STRICT && match_type == exact_match))
1260 return ipv4_prefix_match;
1261 break;
1262
1263 case TERMINAL_OPTION:
1264 case TERMINAL_VARIABLE:
1265 return extend_match;
1266
1267 case TERMINAL_LITERAL:
1268 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1269 {
1270 if (!strcmp(str, word))
1271 return exact_match;
1272 return partly_match;
1273 }
1274 if (filter == FILTER_STRICT && !strcmp(str, word))
1275 return exact_match;
1276 break;
1277
1278 default:
1279 assert (0);
Christian Frankecd40b322013-09-30 12:27:51 +00001280 }
paul718e3742002-12-13 20:15:29 +00001281
Christian Frankecd40b322013-09-30 12:27:51 +00001282 return no_match;
paul718e3742002-12-13 20:15:29 +00001283}
1284
Christian Frankecd40b322013-09-30 12:27:51 +00001285struct cmd_matcher
1286{
1287 struct cmd_element *cmd; /* The command element the matcher is using */
1288 enum filter_type filter; /* Whether to use strict or relaxed matching */
1289 vector vline; /* The tokenized commandline which is to be matched */
1290 unsigned int index; /* The index up to which matching should be done */
1291
1292 /* If set, construct a list of matches at the position given by index */
1293 enum match_type *match_type;
1294 vector *match;
1295
1296 unsigned int word_index; /* iterating over vline */
1297};
1298
1299static int
1300push_argument(int *argc, const char **argv, const char *arg)
1301{
1302 if (!arg || !strlen(arg))
1303 arg = NULL;
1304
1305 if (!argc || !argv)
1306 return 0;
1307
1308 if (*argc >= CMD_ARGC_MAX)
1309 return -1;
1310
1311 argv[(*argc)++] = arg;
1312 return 0;
1313}
1314
1315static void
1316cmd_matcher_record_match(struct cmd_matcher *matcher,
1317 enum match_type match_type,
1318 struct cmd_token *token)
1319{
1320 if (matcher->word_index != matcher->index)
1321 return;
1322
1323 if (matcher->match)
1324 {
1325 if (!*matcher->match)
1326 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1327 vector_set(*matcher->match, token);
1328 }
1329
1330 if (matcher->match_type)
1331 {
1332 if (match_type > *matcher->match_type)
1333 *matcher->match_type = match_type;
1334 }
1335}
1336
1337static int
1338cmd_matcher_words_left(struct cmd_matcher *matcher)
1339{
1340 return matcher->word_index < vector_active(matcher->vline);
1341}
1342
1343static const char*
1344cmd_matcher_get_word(struct cmd_matcher *matcher)
1345{
1346 assert(cmd_matcher_words_left(matcher));
1347
1348 return vector_slot(matcher->vline, matcher->word_index);
1349}
1350
1351static enum matcher_rv
1352cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1353 struct cmd_token *token,
1354 int *argc, const char **argv)
1355{
1356 const char *word;
1357 enum match_type word_match;
1358
1359 assert(token->type == TOKEN_TERMINAL);
1360
1361 if (!cmd_matcher_words_left(matcher))
1362 {
David Lamparter10bac802015-05-05 11:10:20 +02001363 if (token->terminal == TERMINAL_OPTION)
Christian Frankecd40b322013-09-30 12:27:51 +00001364 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1365 else
1366 return MATCHER_INCOMPLETE;
1367 }
1368
1369 word = cmd_matcher_get_word(matcher);
1370 word_match = cmd_word_match(token, matcher->filter, word);
1371 if (word_match == no_match)
1372 return MATCHER_NO_MATCH;
1373
1374 /* We have to record the input word as argument if it matched
1375 * against a variable. */
David Lamparter14162932015-05-12 17:18:04 +02001376 if (TERMINAL_RECORD (token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001377 {
1378 if (push_argument(argc, argv, word))
1379 return MATCHER_EXCEED_ARGC_MAX;
1380 }
1381
1382 cmd_matcher_record_match(matcher, word_match, token);
1383
1384 matcher->word_index++;
1385
1386 /* A vararg token should consume all left over words as arguments */
David Lamparter10bac802015-05-05 11:10:20 +02001387 if (token->terminal == TERMINAL_VARARG)
Christian Frankecd40b322013-09-30 12:27:51 +00001388 while (cmd_matcher_words_left(matcher))
1389 {
1390 word = cmd_matcher_get_word(matcher);
1391 if (word && strlen(word))
1392 push_argument(argc, argv, word);
1393 matcher->word_index++;
1394 }
1395
1396 return MATCHER_OK;
1397}
1398
1399static enum matcher_rv
1400cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1401 struct cmd_token *token,
1402 int *argc, const char **argv)
1403{
1404 enum match_type multiple_match;
1405 unsigned int multiple_index;
1406 const char *word;
David Lamparterab90fc02015-03-03 09:07:25 +01001407 const char *arg = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001408 struct cmd_token *word_token;
1409 enum match_type word_match;
1410
1411 assert(token->type == TOKEN_MULTIPLE);
1412
1413 multiple_match = no_match;
1414
1415 if (!cmd_matcher_words_left(matcher))
1416 return MATCHER_INCOMPLETE;
1417
1418 word = cmd_matcher_get_word(matcher);
1419 for (multiple_index = 0;
1420 multiple_index < vector_active(token->multiple);
1421 multiple_index++)
1422 {
1423 word_token = vector_slot(token->multiple, multiple_index);
1424
1425 word_match = cmd_word_match(word_token, matcher->filter, word);
1426 if (word_match == no_match)
1427 continue;
1428
1429 cmd_matcher_record_match(matcher, word_match, word_token);
1430
1431 if (word_match > multiple_match)
1432 {
1433 multiple_match = word_match;
1434 arg = word;
1435 }
1436 /* To mimic the behavior of the old command implementation, we
1437 * tolerate any ambiguities here :/ */
1438 }
1439
1440 matcher->word_index++;
1441
1442 if (multiple_match == no_match)
1443 return MATCHER_NO_MATCH;
1444
1445 if (push_argument(argc, argv, arg))
1446 return MATCHER_EXCEED_ARGC_MAX;
1447
1448 return MATCHER_OK;
1449}
1450
1451static enum matcher_rv
1452cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1453 struct cmd_token *token,
1454 vector args_vector)
paul718e3742002-12-13 20:15:29 +00001455{
hasso8c328f12004-10-05 21:01:23 +00001456 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00001457 unsigned long keyword_mask;
1458 unsigned int keyword_found;
1459 enum match_type keyword_match;
1460 enum match_type word_match;
1461 vector keyword_vector;
1462 struct cmd_token *word_token;
1463 const char *word;
1464 int keyword_argc;
1465 const char **keyword_argv;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001466 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001467
1468 keyword_mask = 0;
1469 while (1)
1470 {
1471 if (!cmd_matcher_words_left(matcher))
1472 return MATCHER_OK;
1473
1474 word = cmd_matcher_get_word(matcher);
1475
1476 keyword_found = -1;
1477 keyword_match = no_match;
1478 for (i = 0; i < vector_active(token->keyword); i++)
1479 {
1480 if (keyword_mask & (1 << i))
1481 continue;
1482
1483 keyword_vector = vector_slot(token->keyword, i);
1484 word_token = vector_slot(keyword_vector, 0);
1485
1486 word_match = cmd_word_match(word_token, matcher->filter, word);
1487 if (word_match == no_match)
1488 continue;
1489
1490 cmd_matcher_record_match(matcher, word_match, word_token);
1491
1492 if (word_match > keyword_match)
1493 {
1494 keyword_match = word_match;
1495 keyword_found = i;
1496 }
1497 else if (word_match == keyword_match)
1498 {
1499 if (matcher->word_index != matcher->index || args_vector)
1500 return MATCHER_AMBIGUOUS;
1501 }
1502 }
1503
1504 if (keyword_found == (unsigned int)-1)
1505 return MATCHER_NO_MATCH;
1506
1507 matcher->word_index++;
1508
1509 if (matcher->word_index > matcher->index)
1510 return MATCHER_OK;
1511
1512 keyword_mask |= (1 << keyword_found);
1513
1514 if (args_vector)
1515 {
1516 keyword_argc = 0;
1517 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1518 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1519 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1520 keyword_argv[i] = (void*)-1;
1521 vector_set_index(args_vector, keyword_found, keyword_argv);
1522 }
1523 else
1524 {
1525 keyword_argv = NULL;
1526 }
1527
1528 keyword_vector = vector_slot(token->keyword, keyword_found);
1529 /* the keyword itself is at 0. We are only interested in the arguments,
1530 * so start counting at 1. */
1531 for (i = 1; i < vector_active(keyword_vector); i++)
1532 {
1533 word_token = vector_slot(keyword_vector, i);
1534
1535 switch (word_token->type)
1536 {
1537 case TOKEN_TERMINAL:
1538 rv = cmd_matcher_match_terminal(matcher, word_token,
1539 &keyword_argc, keyword_argv);
1540 break;
1541 case TOKEN_MULTIPLE:
1542 rv = cmd_matcher_match_multiple(matcher, word_token,
1543 &keyword_argc, keyword_argv);
1544 break;
1545 case TOKEN_KEYWORD:
1546 assert(!"Keywords should never be nested.");
1547 break;
1548 }
1549
1550 if (MATCHER_ERROR(rv))
1551 return rv;
1552
1553 if (matcher->word_index > matcher->index)
1554 return MATCHER_OK;
1555 }
1556 }
1557 /* not reached */
1558}
1559
1560static enum matcher_rv
1561cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1562 struct cmd_token *token,
1563 int *argc, const char **argv,
1564 vector keyword_args_vector)
1565{
1566 unsigned int i, j;
1567 const char **keyword_args;
1568 vector keyword_vector;
1569 struct cmd_token *word_token;
1570 const char *arg;
1571 enum matcher_rv rv;
1572
1573 rv = MATCHER_OK;
1574
1575 if (keyword_args_vector == NULL)
1576 return rv;
1577
1578 for (i = 0; i < vector_active(token->keyword); i++)
1579 {
1580 keyword_vector = vector_slot(token->keyword, i);
1581 keyword_args = vector_lookup(keyword_args_vector, i);
1582
1583 if (vector_active(keyword_vector) == 1)
1584 {
1585 /* this is a keyword without arguments */
1586 if (keyword_args)
1587 {
1588 word_token = vector_slot(keyword_vector, 0);
1589 arg = word_token->cmd;
1590 }
1591 else
1592 {
1593 arg = NULL;
1594 }
1595
1596 if (push_argument(argc, argv, arg))
1597 rv = MATCHER_EXCEED_ARGC_MAX;
1598 }
1599 else
1600 {
1601 /* this is a keyword with arguments */
1602 if (keyword_args)
1603 {
1604 /* the keyword was present, so just fill in the arguments */
1605 for (j = 0; keyword_args[j] != (void*)-1; j++)
1606 if (push_argument(argc, argv, keyword_args[j]))
1607 rv = MATCHER_EXCEED_ARGC_MAX;
1608 XFREE(MTYPE_TMP, keyword_args);
1609 }
1610 else
1611 {
1612 /* the keyword was not present, insert NULL for the arguments
1613 * the keyword would have taken. */
1614 for (j = 1; j < vector_active(keyword_vector); j++)
1615 {
1616 word_token = vector_slot(keyword_vector, j);
1617 if ((word_token->type == TOKEN_TERMINAL
David Lamparter14162932015-05-12 17:18:04 +02001618 && TERMINAL_RECORD (word_token->terminal))
Christian Frankecd40b322013-09-30 12:27:51 +00001619 || word_token->type == TOKEN_MULTIPLE)
1620 {
1621 if (push_argument(argc, argv, NULL))
1622 rv = MATCHER_EXCEED_ARGC_MAX;
1623 }
1624 }
1625 }
1626 }
1627 }
1628 vector_free(keyword_args_vector);
1629 return rv;
1630}
1631
1632static enum matcher_rv
1633cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1634 struct cmd_token *token,
1635 int *argc, const char **argv)
1636{
1637 vector keyword_args_vector;
1638 enum matcher_rv reader_rv;
1639 enum matcher_rv builder_rv;
1640
1641 assert(token->type == TOKEN_KEYWORD);
1642
1643 if (argc && argv)
1644 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1645 else
1646 keyword_args_vector = NULL;
1647
1648 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1649 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1650 argv, keyword_args_vector);
1651 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1652
1653 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1654 return builder_rv;
1655
1656 return reader_rv;
1657}
1658
1659static void
1660cmd_matcher_init(struct cmd_matcher *matcher,
1661 struct cmd_element *cmd,
1662 enum filter_type filter,
1663 vector vline,
1664 unsigned int index,
1665 enum match_type *match_type,
1666 vector *match)
1667{
1668 memset(matcher, 0, sizeof(*matcher));
1669
1670 matcher->cmd = cmd;
1671 matcher->filter = filter;
1672 matcher->vline = vline;
1673 matcher->index = index;
1674
1675 matcher->match_type = match_type;
1676 if (matcher->match_type)
1677 *matcher->match_type = no_match;
1678 matcher->match = match;
1679
1680 matcher->word_index = 0;
1681}
1682
1683static enum matcher_rv
1684cmd_element_match(struct cmd_element *cmd_element,
1685 enum filter_type filter,
1686 vector vline,
1687 unsigned int index,
1688 enum match_type *match_type,
1689 vector *match,
1690 int *argc,
1691 const char **argv)
1692{
1693 struct cmd_matcher matcher;
1694 unsigned int token_index;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001695 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001696
1697 cmd_matcher_init(&matcher, cmd_element, filter,
1698 vline, index, match_type, match);
1699
1700 if (argc != NULL)
1701 *argc = 0;
1702
1703 for (token_index = 0;
1704 token_index < vector_active(cmd_element->tokens);
1705 token_index++)
1706 {
1707 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1708
1709 switch (token->type)
1710 {
1711 case TOKEN_TERMINAL:
1712 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1713 break;
1714 case TOKEN_MULTIPLE:
1715 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1716 break;
1717 case TOKEN_KEYWORD:
1718 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1719 }
1720
1721 if (MATCHER_ERROR(rv))
1722 return rv;
1723
1724 if (matcher.word_index > index)
1725 return MATCHER_OK;
1726 }
1727
1728 /* return MATCHER_COMPLETE if all words were consumed */
1729 if (matcher.word_index >= vector_active(vline))
1730 return MATCHER_COMPLETE;
1731
1732 /* return MATCHER_COMPLETE also if only an empty word is left. */
1733 if (matcher.word_index == vector_active(vline) - 1
1734 && (!vector_slot(vline, matcher.word_index)
1735 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1736 return MATCHER_COMPLETE;
1737
1738 return MATCHER_NO_MATCH; /* command is too long to match */
1739}
1740
1741/**
1742 * Filter a given vector of commands against a given commandline and
1743 * calculate possible completions.
1744 *
1745 * @param commands A vector of struct cmd_element*. Commands that don't
1746 * match against the given command line will be overwritten
1747 * with NULL in that vector.
1748 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1749 * determines how incomplete commands are handled, compare with
1750 * cmd_word_match for details.
1751 * @param vline A vector of char* containing the tokenized commandline.
1752 * @param index Only match up to the given token of the commandline.
1753 * @param match_type Record the type of the best match here.
1754 * @param matches Record the matches here. For each cmd_element in the commands
1755 * vector, a match vector will be created in the matches vector.
1756 * That vector will contain all struct command_token* of the
1757 * cmd_element which matched against the given vline at the given
1758 * index.
1759 * @return A code specifying if an error occured. If all went right, it's
1760 * CMD_SUCCESS.
1761 */
1762static int
1763cmd_vector_filter(vector commands,
1764 enum filter_type filter,
1765 vector vline,
1766 unsigned int index,
1767 enum match_type *match_type,
1768 vector *matches)
1769{
1770 unsigned int i;
paul718e3742002-12-13 20:15:29 +00001771 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00001772 enum match_type best_match;
1773 enum match_type element_match;
1774 enum matcher_rv matcher_rv;
paul909a2152005-03-14 17:41:45 +00001775
Christian Frankecd40b322013-09-30 12:27:51 +00001776 best_match = no_match;
1777 *matches = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +00001778
Christian Frankecd40b322013-09-30 12:27:51 +00001779 for (i = 0; i < vector_active (commands); i++)
1780 if ((cmd_element = vector_slot (commands, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001781 {
Christian Frankecd40b322013-09-30 12:27:51 +00001782 vector_set_index(*matches, i, NULL);
1783 matcher_rv = cmd_element_match(cmd_element, filter,
1784 vline, index,
1785 &element_match,
1786 (vector*)&vector_slot(*matches, i),
1787 NULL, NULL);
1788 if (MATCHER_ERROR(matcher_rv))
1789 {
1790 vector_slot(commands, i) = NULL;
1791 if (matcher_rv == MATCHER_AMBIGUOUS)
1792 return CMD_ERR_AMBIGUOUS;
1793 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1794 return CMD_ERR_EXEED_ARGC_MAX;
1795 }
1796 else if (element_match > best_match)
1797 {
1798 best_match = element_match;
1799 }
paul718e3742002-12-13 20:15:29 +00001800 }
Christian Frankecd40b322013-09-30 12:27:51 +00001801 *match_type = best_match;
1802 return CMD_SUCCESS;
1803}
1804
1805/**
1806 * Check whether a given commandline is complete if used for a specific
1807 * cmd_element.
1808 *
1809 * @param cmd_element A cmd_element against which the commandline should be
1810 * checked.
1811 * @param vline The tokenized commandline.
1812 * @return 1 if the given commandline is complete, 0 otherwise.
1813 */
1814static int
1815cmd_is_complete(struct cmd_element *cmd_element,
1816 vector vline)
1817{
1818 enum matcher_rv rv;
1819
1820 rv = cmd_element_match(cmd_element,
1821 FILTER_RELAXED,
1822 vline, -1,
1823 NULL, NULL,
1824 NULL, NULL);
1825 return (rv == MATCHER_COMPLETE);
1826}
1827
1828/**
1829 * Parse a given commandline and construct a list of arguments for the
1830 * given command_element.
1831 *
1832 * @param cmd_element The cmd_element for which we want to construct arguments.
1833 * @param vline The tokenized commandline.
1834 * @param argc Where to store the argument count.
1835 * @param argv Where to store the argument list. Should be at least
1836 * CMD_ARGC_MAX elements long.
1837 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1838 */
1839static int
1840cmd_parse(struct cmd_element *cmd_element,
1841 vector vline,
1842 int *argc, const char **argv)
1843{
1844 enum matcher_rv rv = cmd_element_match(cmd_element,
1845 FILTER_RELAXED,
1846 vline, -1,
1847 NULL, NULL,
1848 argc, argv);
1849 switch (rv)
1850 {
1851 case MATCHER_COMPLETE:
1852 return CMD_SUCCESS;
1853
1854 case MATCHER_NO_MATCH:
1855 return CMD_ERR_NO_MATCH;
1856
1857 case MATCHER_AMBIGUOUS:
1858 return CMD_ERR_AMBIGUOUS;
1859
1860 case MATCHER_EXCEED_ARGC_MAX:
1861 return CMD_ERR_EXEED_ARGC_MAX;
1862
1863 default:
1864 return CMD_ERR_INCOMPLETE;
1865 }
paul718e3742002-12-13 20:15:29 +00001866}
1867
1868/* Check ambiguous match */
ajs274a4a42004-12-07 15:39:31 +00001869static int
Christian Frankecd40b322013-09-30 12:27:51 +00001870is_cmd_ambiguous (vector cmd_vector,
1871 const char *command,
1872 vector matches,
1873 enum match_type type)
paul718e3742002-12-13 20:15:29 +00001874{
hasso8c328f12004-10-05 21:01:23 +00001875 unsigned int i;
1876 unsigned int j;
1877 const char *str = NULL;
hasso8c328f12004-10-05 21:01:23 +00001878 const char *matched = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001879 vector match_vector;
1880 struct cmd_token *cmd_token;
paul909a2152005-03-14 17:41:45 +00001881
Christian Frankecd40b322013-09-30 12:27:51 +00001882 if (command == NULL)
1883 command = "";
1884
1885 for (i = 0; i < vector_active (matches); i++)
1886 if ((match_vector = vector_slot (matches, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001887 {
1888 int match = 0;
1889
Christian Frankecd40b322013-09-30 12:27:51 +00001890 for (j = 0; j < vector_active (match_vector); j++)
1891 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
paul909a2152005-03-14 17:41:45 +00001892 {
1893 enum match_type ret;
Christian Frankecd40b322013-09-30 12:27:51 +00001894
1895 assert(cmd_token->type == TOKEN_TERMINAL);
1896 if (cmd_token->type != TOKEN_TERMINAL)
1897 continue;
1898
1899 str = cmd_token->cmd;
paul718e3742002-12-13 20:15:29 +00001900
paul909a2152005-03-14 17:41:45 +00001901 switch (type)
1902 {
1903 case exact_match:
David Lamparter14162932015-05-12 17:18:04 +02001904 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001905 && strcmp (command, str) == 0)
1906 match++;
1907 break;
1908 case partly_match:
David Lamparter14162932015-05-12 17:18:04 +02001909 if (!TERMINAL_RECORD (cmd_token->terminal)
paul909a2152005-03-14 17:41:45 +00001910 && strncmp (command, str, strlen (command)) == 0)
1911 {
1912 if (matched && strcmp (matched, str) != 0)
1913 return 1; /* There is ambiguous match. */
1914 else
1915 matched = str;
1916 match++;
1917 }
1918 break;
1919 case range_match:
1920 if (cmd_range_match (str, command))
1921 {
1922 if (matched && strcmp (matched, str) != 0)
1923 return 1;
1924 else
1925 matched = str;
1926 match++;
1927 }
1928 break;
1929#ifdef HAVE_IPV6
1930 case ipv6_match:
David Lamparter10bac802015-05-05 11:10:20 +02001931 if (cmd_token->terminal == TERMINAL_IPV6)
paul909a2152005-03-14 17:41:45 +00001932 match++;
1933 break;
1934 case ipv6_prefix_match:
1935 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1936 {
1937 if (ret == partly_match)
1938 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001939
paul909a2152005-03-14 17:41:45 +00001940 match++;
1941 }
1942 break;
1943#endif /* HAVE_IPV6 */
1944 case ipv4_match:
David Lamparter10bac802015-05-05 11:10:20 +02001945 if (cmd_token->terminal == TERMINAL_IPV4)
paul718e3742002-12-13 20:15:29 +00001946 match++;
paul909a2152005-03-14 17:41:45 +00001947 break;
1948 case ipv4_prefix_match:
1949 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1950 {
1951 if (ret == partly_match)
1952 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001953
paul909a2152005-03-14 17:41:45 +00001954 match++;
1955 }
1956 break;
1957 case extend_match:
David Lamparter14162932015-05-12 17:18:04 +02001958 if (TERMINAL_RECORD (cmd_token->terminal))
paul718e3742002-12-13 20:15:29 +00001959 match++;
paul909a2152005-03-14 17:41:45 +00001960 break;
1961 case no_match:
1962 default:
1963 break;
1964 }
1965 }
1966 if (!match)
Christian Frankecd40b322013-09-30 12:27:51 +00001967 vector_slot (cmd_vector, i) = NULL;
paul718e3742002-12-13 20:15:29 +00001968 }
1969 return 0;
1970}
1971
1972/* If src matches dst return dst string, otherwise return NULL */
ajs274a4a42004-12-07 15:39:31 +00001973static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001974cmd_entry_function (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001975{
David Lamparter10bac802015-05-05 11:10:20 +02001976 const char *dst = token->cmd;
1977
paul718e3742002-12-13 20:15:29 +00001978 /* Skip variable arguments. */
David Lamparter14162932015-05-12 17:18:04 +02001979 if (TERMINAL_RECORD (token->terminal))
1980 return NULL;
paul718e3742002-12-13 20:15:29 +00001981
1982 /* In case of 'command \t', given src is NULL string. */
1983 if (src == NULL)
1984 return dst;
1985
1986 /* Matched with input string. */
1987 if (strncmp (src, dst, strlen (src)) == 0)
1988 return dst;
1989
1990 return NULL;
1991}
1992
1993/* If src matches dst return dst string, otherwise return NULL */
1994/* This version will return the dst string always if it is
1995 CMD_VARIABLE for '?' key processing */
ajs274a4a42004-12-07 15:39:31 +00001996static const char *
David Lamparter10bac802015-05-05 11:10:20 +02001997cmd_entry_function_desc (const char *src, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001998{
David Lamparter10bac802015-05-05 11:10:20 +02001999 const char *dst = token->cmd;
paul718e3742002-12-13 20:15:29 +00002000
David Lamparter10bac802015-05-05 11:10:20 +02002001 switch (token->terminal)
paul718e3742002-12-13 20:15:29 +00002002 {
David Lamparter10bac802015-05-05 11:10:20 +02002003 case TERMINAL_VARARG:
2004 return dst;
2005
2006 case TERMINAL_RANGE:
2007 if (cmd_range_match (dst, src))
2008 return dst;
2009 else
2010 return NULL;
2011
2012 case TERMINAL_IPV6:
2013 if (cmd_ipv6_match (src))
2014 return dst;
2015 else
2016 return NULL;
2017
2018 case TERMINAL_IPV6_PREFIX:
2019 if (cmd_ipv6_prefix_match (src))
2020 return dst;
2021 else
2022 return NULL;
2023
2024 case TERMINAL_IPV4:
2025 if (cmd_ipv4_match (src))
2026 return dst;
2027 else
2028 return NULL;
2029
2030 case TERMINAL_IPV4_PREFIX:
2031 if (cmd_ipv4_prefix_match (src))
2032 return dst;
2033 else
2034 return NULL;
2035
2036 /* Optional or variable commands always match on '?' */
2037 case TERMINAL_OPTION:
2038 case TERMINAL_VARIABLE:
2039 return dst;
2040
2041 case TERMINAL_LITERAL:
2042 /* In case of 'command \t', given src is NULL string. */
2043 if (src == NULL)
2044 return dst;
2045
2046 if (strncmp (src, dst, strlen (src)) == 0)
2047 return dst;
2048 else
2049 return NULL;
2050
2051 default:
2052 assert(0);
David Lamparterf1fc3272015-05-13 12:44:50 +02002053 return NULL;
paul718e3742002-12-13 20:15:29 +00002054 }
paul718e3742002-12-13 20:15:29 +00002055}
2056
Christian Frankecd40b322013-09-30 12:27:51 +00002057/**
2058 * Check whether a string is already present in a vector of strings.
2059 * @param v A vector of char*.
2060 * @param str A char*.
2061 * @return 0 if str is already present in the vector, 1 otherwise.
2062 */
ajs274a4a42004-12-07 15:39:31 +00002063static int
hasso8c328f12004-10-05 21:01:23 +00002064cmd_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002065{
hasso8c328f12004-10-05 21:01:23 +00002066 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002067 char *match;
2068
paul55468c82005-03-14 20:19:01 +00002069 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +00002070 if ((match = vector_slot (v, i)) != NULL)
2071 if (strcmp (match, str) == 0)
2072 return 0;
2073 return 1;
2074}
2075
Christian Frankecd40b322013-09-30 12:27:51 +00002076/**
2077 * Check whether a struct cmd_token matching a given string is already
2078 * present in a vector of struct cmd_token.
2079 * @param v A vector of struct cmd_token*.
2080 * @param str A char* which should be searched for.
2081 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2082 * 1 otherwise.
2083 */
ajs274a4a42004-12-07 15:39:31 +00002084static int
hasso8c328f12004-10-05 21:01:23 +00002085desc_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002086{
hasso8c328f12004-10-05 21:01:23 +00002087 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00002088 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002089
paul55468c82005-03-14 20:19:01 +00002090 for (i = 0; i < vector_active (v); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00002091 if ((token = vector_slot (v, i)) != NULL)
2092 if (strcmp (token->cmd, str) == 0)
2093 return 0;
2094 return 1;
paul718e3742002-12-13 20:15:29 +00002095}
2096
ajs274a4a42004-12-07 15:39:31 +00002097static int
paulb92938a2002-12-13 21:20:42 +00002098cmd_try_do_shortcut (enum node_type node, char* first_word) {
2099 if ( first_word != NULL &&
2100 node != AUTH_NODE &&
2101 node != VIEW_NODE &&
2102 node != AUTH_ENABLE_NODE &&
2103 node != ENABLE_NODE &&
Paul Jakma62687ff2008-08-23 14:27:06 +01002104 node != RESTRICTED_NODE &&
paulb92938a2002-12-13 21:20:42 +00002105 0 == strcmp( "do", first_word ) )
2106 return 1;
2107 return 0;
2108}
2109
Christian Frankecd40b322013-09-30 12:27:51 +00002110static void
2111cmd_matches_free(vector *matches)
2112{
2113 unsigned int i;
2114 vector cmd_matches;
2115
2116 for (i = 0; i < vector_active(*matches); i++)
2117 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2118 vector_free(cmd_matches);
2119 vector_free(*matches);
2120 *matches = NULL;
2121}
2122
2123static int
2124cmd_describe_cmp(const void *a, const void *b)
2125{
2126 const struct cmd_token *first = *(struct cmd_token * const *)a;
2127 const struct cmd_token *second = *(struct cmd_token * const *)b;
2128
2129 return strcmp(first->cmd, second->cmd);
2130}
2131
2132static void
2133cmd_describe_sort(vector matchvec)
2134{
2135 qsort(matchvec->index, vector_active(matchvec),
2136 sizeof(void*), cmd_describe_cmp);
2137}
2138
paul718e3742002-12-13 20:15:29 +00002139/* '?' describe command support. */
ajs274a4a42004-12-07 15:39:31 +00002140static vector
paulb92938a2002-12-13 21:20:42 +00002141cmd_describe_command_real (vector vline, struct vty *vty, int *status)
paul718e3742002-12-13 20:15:29 +00002142{
paulb8961472005-03-14 17:35:52 +00002143 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002144 vector cmd_vector;
2145#define INIT_MATCHVEC_SIZE 10
2146 vector matchvec;
2147 struct cmd_element *cmd_element;
paulb8961472005-03-14 17:35:52 +00002148 unsigned int index;
paul54aba542003-08-21 20:28:24 +00002149 int ret;
2150 enum match_type match;
2151 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002152 vector matches = NULL;
2153 vector match_vector;
Donald Sharpf7332802015-07-17 22:36:57 -04002154 uint32_t command_found = 0;
2155 const char *last_word;
paul718e3742002-12-13 20:15:29 +00002156
2157 /* Set index. */
paul55468c82005-03-14 20:19:01 +00002158 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002159 {
2160 *status = CMD_ERR_NO_MATCH;
2161 return NULL;
2162 }
Christian Frankecd40b322013-09-30 12:27:51 +00002163
2164 index = vector_active (vline) - 1;
2165
paul718e3742002-12-13 20:15:29 +00002166 /* Make copy vector of current node's command vector. */
2167 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2168
2169 /* Prepare match vector */
2170 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2171
Christian Frankecd40b322013-09-30 12:27:51 +00002172 /* Filter commands and build a list how they could possibly continue. */
2173 for (i = 0; i <= index; i++)
2174 {
2175 command = vector_slot (vline, i);
paul718e3742002-12-13 20:15:29 +00002176
Christian Frankecd40b322013-09-30 12:27:51 +00002177 if (matches)
2178 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002179
Christian Frankecd40b322013-09-30 12:27:51 +00002180 ret = cmd_vector_filter(cmd_vector,
2181 FILTER_RELAXED,
2182 vline, i,
2183 &match,
2184 &matches);
paul718e3742002-12-13 20:15:29 +00002185
Christian Frankecd40b322013-09-30 12:27:51 +00002186 if (ret != CMD_SUCCESS)
2187 {
2188 vector_free (cmd_vector);
2189 vector_free (matchvec);
2190 cmd_matches_free(&matches);
2191 *status = ret;
2192 return NULL;
2193 }
paul718e3742002-12-13 20:15:29 +00002194
Christian Frankecd40b322013-09-30 12:27:51 +00002195 /* The last match may well be ambigious, so break here */
2196 if (i == index)
2197 break;
paul718e3742002-12-13 20:15:29 +00002198
Christian Frankecd40b322013-09-30 12:27:51 +00002199 if (match == vararg_match)
2200 {
2201 /* We found a vararg match - so we can throw out the current matches here
2202 * and don't need to continue checking the command input */
2203 unsigned int j, k;
2204
2205 for (j = 0; j < vector_active (matches); j++)
2206 if ((match_vector = vector_slot (matches, j)) != NULL)
2207 for (k = 0; k < vector_active (match_vector); k++)
2208 {
2209 struct cmd_token *token = vector_slot (match_vector, k);
2210 vector_set (matchvec, token);
2211 }
2212
2213 *status = CMD_SUCCESS;
2214 vector_set(matchvec, &token_cr);
2215 vector_free (cmd_vector);
2216 cmd_matches_free(&matches);
2217 cmd_describe_sort(matchvec);
2218 return matchvec;
2219 }
2220
2221 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2222 if (ret == 1)
2223 {
2224 vector_free (cmd_vector);
2225 vector_free (matchvec);
2226 cmd_matches_free(&matches);
2227 *status = CMD_ERR_AMBIGUOUS;
2228 return NULL;
2229 }
2230 else if (ret == 2)
2231 {
2232 vector_free (cmd_vector);
2233 vector_free (matchvec);
2234 cmd_matches_free(&matches);
2235 *status = CMD_ERR_NO_MATCH;
2236 return NULL;
2237 }
2238 }
paul54aba542003-08-21 20:28:24 +00002239
paul718e3742002-12-13 20:15:29 +00002240 /* Make description vector. */
Christian Frankecd40b322013-09-30 12:27:51 +00002241 for (i = 0; i < vector_active (matches); i++)
Donald Sharpf7332802015-07-17 22:36:57 -04002242 {
2243 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
2244 {
2245 unsigned int j;
2246 vector vline_trimmed;
paul718e3742002-12-13 20:15:29 +00002247
Donald Sharpf7332802015-07-17 22:36:57 -04002248 command_found++;
2249 last_word = vector_slot(vline, vector_active(vline) - 1);
2250 if (last_word == NULL || !strlen(last_word))
2251 {
2252 vline_trimmed = vector_copy(vline);
2253 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
paul718e3742002-12-13 20:15:29 +00002254
Donald Sharpf7332802015-07-17 22:36:57 -04002255 if (cmd_is_complete(cmd_element, vline_trimmed)
2256 && desc_unique_string(matchvec, command_cr))
2257 {
2258 if (match != vararg_match)
2259 vector_set(matchvec, &token_cr);
2260 }
Chris Caputo228da422009-07-18 05:44:03 +00002261
Donald Sharpf7332802015-07-17 22:36:57 -04002262 vector_free(vline_trimmed);
2263 }
Christian Frankecd40b322013-09-30 12:27:51 +00002264
Donald Sharpf7332802015-07-17 22:36:57 -04002265 match_vector = vector_slot (matches, i);
2266 if (match_vector)
2267 {
2268 for (j = 0; j < vector_active(match_vector); j++)
2269 {
2270 struct cmd_token *token = vector_slot(match_vector, j);
2271 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002272
Donald Sharpf7332802015-07-17 22:36:57 -04002273 string = cmd_entry_function_desc(command, token);
2274 if (string && desc_unique_string(matchvec, string))
2275 vector_set(matchvec, token);
2276 }
2277 }
2278 }
2279 }
2280
2281 /*
2282 * We can get into this situation when the command is complete
2283 * but the last part of the command is an optional piece of
2284 * the cli.
2285 */
2286 last_word = vector_slot(vline, vector_active(vline) - 1);
2287 if (command_found == 0 && (last_word == NULL || !strlen(last_word)))
2288 vector_set(matchvec, &token_cr);
2289
paul718e3742002-12-13 20:15:29 +00002290 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002291 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002292
2293 if (vector_slot (matchvec, 0) == NULL)
2294 {
2295 vector_free (matchvec);
paul909a2152005-03-14 17:41:45 +00002296 *status = CMD_ERR_NO_MATCH;
Paul Jakma5fc60512006-05-12 23:24:09 +00002297 return NULL;
paul718e3742002-12-13 20:15:29 +00002298 }
paul718e3742002-12-13 20:15:29 +00002299
Paul Jakma5fc60512006-05-12 23:24:09 +00002300 *status = CMD_SUCCESS;
Christian Frankecd40b322013-09-30 12:27:51 +00002301 cmd_describe_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002302 return matchvec;
2303}
2304
paulb92938a2002-12-13 21:20:42 +00002305vector
2306cmd_describe_command (vector vline, struct vty *vty, int *status)
2307{
2308 vector ret;
2309
2310 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2311 {
2312 enum node_type onode;
2313 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002314 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002315
2316 onode = vty->node;
2317 vty->node = ENABLE_NODE;
2318 /* We can try it on enable node, cos' the vty is authenticated */
2319
2320 shifted_vline = vector_init (vector_count(vline));
2321 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002322 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002323 {
2324 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2325 }
2326
2327 ret = cmd_describe_command_real (shifted_vline, vty, status);
2328
2329 vector_free(shifted_vline);
2330 vty->node = onode;
2331 return ret;
2332 }
2333
2334
2335 return cmd_describe_command_real (vline, vty, status);
2336}
2337
2338
paul718e3742002-12-13 20:15:29 +00002339/* Check LCD of matched command. */
ajs274a4a42004-12-07 15:39:31 +00002340static int
paul718e3742002-12-13 20:15:29 +00002341cmd_lcd (char **matched)
2342{
2343 int i;
2344 int j;
2345 int lcd = -1;
2346 char *s1, *s2;
2347 char c1, c2;
2348
2349 if (matched[0] == NULL || matched[1] == NULL)
2350 return 0;
2351
2352 for (i = 1; matched[i] != NULL; i++)
2353 {
2354 s1 = matched[i - 1];
2355 s2 = matched[i];
2356
2357 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2358 if (c1 != c2)
2359 break;
2360
2361 if (lcd < 0)
2362 lcd = j;
2363 else
2364 {
2365 if (lcd > j)
2366 lcd = j;
2367 }
2368 }
2369 return lcd;
2370}
2371
Christian Frankecd40b322013-09-30 12:27:51 +00002372static int
2373cmd_complete_cmp(const void *a, const void *b)
2374{
2375 const char *first = *(char * const *)a;
2376 const char *second = *(char * const *)b;
2377
2378 if (!first)
2379 {
2380 if (!second)
2381 return 0;
2382 return 1;
2383 }
2384 if (!second)
2385 return -1;
2386
2387 return strcmp(first, second);
2388}
2389
2390static void
2391cmd_complete_sort(vector matchvec)
2392{
2393 qsort(matchvec->index, vector_active(matchvec),
2394 sizeof(void*), cmd_complete_cmp);
2395}
2396
paul718e3742002-12-13 20:15:29 +00002397/* Command line completion support. */
ajs274a4a42004-12-07 15:39:31 +00002398static char **
Lou Berger67290032016-01-12 13:41:46 -05002399cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib)
paul718e3742002-12-13 20:15:29 +00002400{
paulb8961472005-03-14 17:35:52 +00002401 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002402 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2403#define INIT_MATCHVEC_SIZE 10
2404 vector matchvec;
paulb8961472005-03-14 17:35:52 +00002405 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002406 char **match_str;
Christian Frankecd40b322013-09-30 12:27:51 +00002407 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002408 char *command;
2409 int lcd;
Christian Frankecd40b322013-09-30 12:27:51 +00002410 vector matches = NULL;
2411 vector match_vector;
paul718e3742002-12-13 20:15:29 +00002412
paul55468c82005-03-14 20:19:01 +00002413 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002414 {
Paul Jakmad2519962006-05-12 23:19:37 +00002415 vector_free (cmd_vector);
paulb8961472005-03-14 17:35:52 +00002416 *status = CMD_ERR_NO_MATCH;
2417 return NULL;
2418 }
2419 else
paul55468c82005-03-14 20:19:01 +00002420 index = vector_active (vline) - 1;
paulb8961472005-03-14 17:35:52 +00002421
Christian Frankecd40b322013-09-30 12:27:51 +00002422 /* First, filter by command string */
2423 for (i = 0; i <= index; i++)
2424 {
2425 command = vector_slot (vline, i);
2426 enum match_type match;
2427 int ret;
paul718e3742002-12-13 20:15:29 +00002428
Christian Frankecd40b322013-09-30 12:27:51 +00002429 if (matches)
2430 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002431
Christian Frankecd40b322013-09-30 12:27:51 +00002432 /* First try completion match, if there is exactly match return 1 */
2433 ret = cmd_vector_filter(cmd_vector,
2434 FILTER_RELAXED,
2435 vline, i,
2436 &match,
2437 &matches);
2438
2439 if (ret != CMD_SUCCESS)
2440 {
2441 vector_free(cmd_vector);
2442 cmd_matches_free(&matches);
2443 *status = ret;
2444 return NULL;
2445 }
2446
2447 /* Break here - the completion mustn't be checked to be non-ambiguous */
2448 if (i == index)
2449 break;
2450
2451 /* If there is exact match then filter ambiguous match else check
2452 ambiguousness. */
2453 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2454 if (ret == 1)
2455 {
2456 vector_free (cmd_vector);
2457 cmd_matches_free(&matches);
2458 *status = CMD_ERR_AMBIGUOUS;
2459 return NULL;
2460 }
2461 /*
paul909a2152005-03-14 17:41:45 +00002462 else if (ret == 2)
2463 {
2464 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002465 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002466 *status = CMD_ERR_NO_MATCH;
2467 return NULL;
2468 }
2469 */
Christian Frankecd40b322013-09-30 12:27:51 +00002470 }
paul909a2152005-03-14 17:41:45 +00002471
paul718e3742002-12-13 20:15:29 +00002472 /* Prepare match vector. */
2473 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2474
Christian Frankecd40b322013-09-30 12:27:51 +00002475 /* Build the possible list of continuations into a list of completions */
2476 for (i = 0; i < vector_active (matches); i++)
2477 if ((match_vector = vector_slot (matches, i)))
paul718e3742002-12-13 20:15:29 +00002478 {
hasso8c328f12004-10-05 21:01:23 +00002479 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002480 unsigned int j;
paul909a2152005-03-14 17:41:45 +00002481
Christian Frankecd40b322013-09-30 12:27:51 +00002482 for (j = 0; j < vector_active (match_vector); j++)
2483 if ((token = vector_slot (match_vector, j)))
Lou Berger67290032016-01-12 13:41:46 -05002484 {
2485 string = cmd_entry_function (vector_slot (vline, index), token);
2486 if (string && cmd_unique_string (matchvec, string))
2487 vector_set (matchvec, (islib != 0 ?
2488 XSTRDUP (MTYPE_TMP, string) :
2489 strdup (string) /* rl freed */));
2490 }
paul718e3742002-12-13 20:15:29 +00002491 }
2492
2493 /* We don't need cmd_vector any more. */
2494 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002495 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002496
2497 /* No matched command */
2498 if (vector_slot (matchvec, 0) == NULL)
2499 {
2500 vector_free (matchvec);
2501
2502 /* In case of 'command \t' pattern. Do you need '?' command at
2503 the end of the line. */
2504 if (vector_slot (vline, index) == '\0')
2505 *status = CMD_ERR_NOTHING_TODO;
2506 else
2507 *status = CMD_ERR_NO_MATCH;
2508 return NULL;
2509 }
2510
2511 /* Only one matched */
2512 if (vector_slot (matchvec, 1) == NULL)
2513 {
2514 match_str = (char **) matchvec->index;
2515 vector_only_wrapper_free (matchvec);
2516 *status = CMD_COMPLETE_FULL_MATCH;
2517 return match_str;
2518 }
2519 /* Make it sure last element is NULL. */
2520 vector_set (matchvec, NULL);
2521
2522 /* Check LCD of matched strings. */
2523 if (vector_slot (vline, index) != NULL)
2524 {
2525 lcd = cmd_lcd ((char **) matchvec->index);
2526
2527 if (lcd)
2528 {
2529 int len = strlen (vector_slot (vline, index));
paul909a2152005-03-14 17:41:45 +00002530
paul718e3742002-12-13 20:15:29 +00002531 if (len < lcd)
2532 {
2533 char *lcdstr;
paul909a2152005-03-14 17:41:45 +00002534
Lou Berger67290032016-01-12 13:41:46 -05002535 lcdstr = (islib != 0 ?
2536 XMALLOC (MTYPE_TMP, lcd + 1) :
2537 malloc(lcd + 1));
paul718e3742002-12-13 20:15:29 +00002538 memcpy (lcdstr, matchvec->index[0], lcd);
2539 lcdstr[lcd] = '\0';
2540
2541 /* match_str = (char **) &lcdstr; */
2542
2543 /* Free matchvec. */
paul55468c82005-03-14 20:19:01 +00002544 for (i = 0; i < vector_active (matchvec); i++)
Lou Berger67290032016-01-12 13:41:46 -05002545 {
2546 if (vector_slot (matchvec, i))
2547 {
2548 if (islib != 0)
2549 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
2550 else
2551 free (vector_slot (matchvec, i));
2552 }
2553 }
paul718e3742002-12-13 20:15:29 +00002554 vector_free (matchvec);
2555
paul909a2152005-03-14 17:41:45 +00002556 /* Make new matchvec. */
paul718e3742002-12-13 20:15:29 +00002557 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2558 vector_set (matchvec, lcdstr);
2559 match_str = (char **) matchvec->index;
2560 vector_only_wrapper_free (matchvec);
2561
2562 *status = CMD_COMPLETE_MATCH;
2563 return match_str;
2564 }
2565 }
2566 }
2567
2568 match_str = (char **) matchvec->index;
Christian Frankecd40b322013-09-30 12:27:51 +00002569 cmd_complete_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002570 vector_only_wrapper_free (matchvec);
2571 *status = CMD_COMPLETE_LIST_MATCH;
2572 return match_str;
2573}
2574
paulb92938a2002-12-13 21:20:42 +00002575char **
Lou Berger67290032016-01-12 13:41:46 -05002576cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)
paulb92938a2002-12-13 21:20:42 +00002577{
2578 char **ret;
2579
2580 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2581 {
2582 enum node_type onode;
2583 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002584 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002585
2586 onode = vty->node;
2587 vty->node = ENABLE_NODE;
2588 /* We can try it on enable node, cos' the vty is authenticated */
2589
2590 shifted_vline = vector_init (vector_count(vline));
2591 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002592 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002593 {
2594 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2595 }
2596
Lou Berger67290032016-01-12 13:41:46 -05002597 ret = cmd_complete_command_real (shifted_vline, vty, status, islib);
paulb92938a2002-12-13 21:20:42 +00002598
2599 vector_free(shifted_vline);
2600 vty->node = onode;
2601 return ret;
2602 }
2603
Lou Berger67290032016-01-12 13:41:46 -05002604 return cmd_complete_command_real (vline, vty, status, islib);
2605}
paulb92938a2002-12-13 21:20:42 +00002606
Lou Berger67290032016-01-12 13:41:46 -05002607char **
2608cmd_complete_command (vector vline, struct vty *vty, int *status)
2609{
2610 return cmd_complete_command_lib (vline, vty, status, 0);
paulb92938a2002-12-13 21:20:42 +00002611}
2612
2613/* return parent node */
2614/* MUST eventually converge on CONFIG_NODE */
hasso13bfca72005-01-23 21:42:25 +00002615enum node_type
ajs274a4a42004-12-07 15:39:31 +00002616node_parent ( enum node_type node )
paulb92938a2002-12-13 21:20:42 +00002617{
2618 enum node_type ret;
2619
paul9ab68122003-01-18 01:16:20 +00002620 assert (node > CONFIG_NODE);
2621
2622 switch (node)
2623 {
2624 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05002625 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05002626 case BGP_ENCAP_NODE:
2627 case BGP_ENCAPV6_NODE:
paul9ab68122003-01-18 01:16:20 +00002628 case BGP_IPV4_NODE:
2629 case BGP_IPV4M_NODE:
2630 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002631 case BGP_IPV6M_NODE:
paul9ab68122003-01-18 01:16:20 +00002632 ret = BGP_NODE;
2633 break;
2634 case KEYCHAIN_KEY_NODE:
2635 ret = KEYCHAIN_NODE;
2636 break;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002637 case LINK_PARAMS_NODE:
2638 ret = INTERFACE_NODE;
2639 break;
paul9ab68122003-01-18 01:16:20 +00002640 default:
2641 ret = CONFIG_NODE;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02002642 break;
paulb92938a2002-12-13 21:20:42 +00002643 }
2644
2645 return ret;
2646}
2647
paul718e3742002-12-13 20:15:29 +00002648/* Execute command by argument vline vector. */
ajs274a4a42004-12-07 15:39:31 +00002649static int
Christian Frankecd40b322013-09-30 12:27:51 +00002650cmd_execute_command_real (vector vline,
2651 enum filter_type filter,
2652 struct vty *vty,
paulb8961472005-03-14 17:35:52 +00002653 struct cmd_element **cmd)
paul718e3742002-12-13 20:15:29 +00002654{
hasso8c328f12004-10-05 21:01:23 +00002655 unsigned int i;
2656 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002657 vector cmd_vector;
2658 struct cmd_element *cmd_element;
2659 struct cmd_element *matched_element;
2660 unsigned int matched_count, incomplete_count;
2661 int argc;
paul9035efa2004-10-10 11:56:56 +00002662 const char *argv[CMD_ARGC_MAX];
paul718e3742002-12-13 20:15:29 +00002663 enum match_type match = 0;
paul718e3742002-12-13 20:15:29 +00002664 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002665 int ret;
2666 vector matches;
paul718e3742002-12-13 20:15:29 +00002667
2668 /* Make copy of command elements. */
2669 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2670
paul55468c82005-03-14 20:19:01 +00002671 for (index = 0; index < vector_active (vline); index++)
Christian Frankecd40b322013-09-30 12:27:51 +00002672 {
2673 command = vector_slot (vline, index);
2674 ret = cmd_vector_filter(cmd_vector,
2675 filter,
2676 vline, index,
2677 &match,
2678 &matches);
paul718e3742002-12-13 20:15:29 +00002679
Christian Frankecd40b322013-09-30 12:27:51 +00002680 if (ret != CMD_SUCCESS)
2681 {
2682 cmd_matches_free(&matches);
2683 return ret;
2684 }
paul718e3742002-12-13 20:15:29 +00002685
Christian Frankecd40b322013-09-30 12:27:51 +00002686 if (match == vararg_match)
2687 {
2688 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002689 break;
Christian Frankecd40b322013-09-30 12:27:51 +00002690 }
paul718e3742002-12-13 20:15:29 +00002691
Christian Frankecd40b322013-09-30 12:27:51 +00002692 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2693 cmd_matches_free(&matches);
2694
2695 if (ret == 1)
2696 {
2697 vector_free(cmd_vector);
2698 return CMD_ERR_AMBIGUOUS;
2699 }
2700 else if (ret == 2)
2701 {
2702 vector_free(cmd_vector);
2703 return CMD_ERR_NO_MATCH;
2704 }
2705 }
paul718e3742002-12-13 20:15:29 +00002706
2707 /* Check matched count. */
2708 matched_element = NULL;
2709 matched_count = 0;
2710 incomplete_count = 0;
2711
paul55468c82005-03-14 20:19:01 +00002712 for (i = 0; i < vector_active (cmd_vector); i++)
paulb8961472005-03-14 17:35:52 +00002713 if ((cmd_element = vector_slot (cmd_vector, i)))
paul718e3742002-12-13 20:15:29 +00002714 {
Christian Frankecd40b322013-09-30 12:27:51 +00002715 if (cmd_is_complete(cmd_element, vline))
paul718e3742002-12-13 20:15:29 +00002716 {
2717 matched_element = cmd_element;
paul718e3742002-12-13 20:15:29 +00002718 matched_count++;
2719 }
2720 else
2721 {
2722 incomplete_count++;
2723 }
2724 }
paul909a2152005-03-14 17:41:45 +00002725
paul718e3742002-12-13 20:15:29 +00002726 /* Finish of using cmd_vector. */
2727 vector_free (cmd_vector);
2728
paul909a2152005-03-14 17:41:45 +00002729 /* To execute command, matched_count must be 1. */
2730 if (matched_count == 0)
paul718e3742002-12-13 20:15:29 +00002731 {
2732 if (incomplete_count)
2733 return CMD_ERR_INCOMPLETE;
2734 else
2735 return CMD_ERR_NO_MATCH;
2736 }
2737
paul909a2152005-03-14 17:41:45 +00002738 if (matched_count > 1)
paul718e3742002-12-13 20:15:29 +00002739 return CMD_ERR_AMBIGUOUS;
2740
Christian Frankecd40b322013-09-30 12:27:51 +00002741 ret = cmd_parse(matched_element, vline, &argc, argv);
2742 if (ret != CMD_SUCCESS)
2743 return ret;
paul718e3742002-12-13 20:15:29 +00002744
2745 /* For vtysh execution. */
2746 if (cmd)
2747 *cmd = matched_element;
2748
2749 if (matched_element->daemon)
2750 return CMD_SUCCESS_DAEMON;
2751
2752 /* Execute matched command. */
2753 return (*matched_element->func) (matched_element, vty, argc, argv);
2754}
2755
Christian Frankecd40b322013-09-30 12:27:51 +00002756/**
2757 * Execute a given command, handling things like "do ..." and checking
2758 * whether the given command might apply at a parent node if doesn't
2759 * apply for the current node.
2760 *
2761 * @param vline Command line input, vector of char* where each element is
2762 * one input token.
2763 * @param vty The vty context in which the command should be executed.
2764 * @param cmd Pointer where the struct cmd_element of the matched command
2765 * will be stored, if any. May be set to NULL if this info is
2766 * not needed.
2767 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2768 * @return The status of the command that has been executed or an error code
2769 * as to why no command could be executed.
2770 */
paulb92938a2002-12-13 21:20:42 +00002771int
hasso87d683b2005-01-16 23:31:54 +00002772cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2773 int vtysh) {
paul9ab68122003-01-18 01:16:20 +00002774 int ret, saved_ret, tried = 0;
2775 enum node_type onode, try_node;
2776
2777 onode = try_node = vty->node;
paulb92938a2002-12-13 21:20:42 +00002778
2779 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2780 {
2781 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002782 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002783
2784 vty->node = ENABLE_NODE;
2785 /* We can try it on enable node, cos' the vty is authenticated */
2786
2787 shifted_vline = vector_init (vector_count(vline));
2788 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002789 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002790 {
2791 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2792 }
2793
Christian Frankecd40b322013-09-30 12:27:51 +00002794 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002795
2796 vector_free(shifted_vline);
2797 vty->node = onode;
2798 return ret;
2799 }
2800
2801
Christian Frankecd40b322013-09-30 12:27:51 +00002802 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002803
hasso87d683b2005-01-16 23:31:54 +00002804 if (vtysh)
2805 return saved_ret;
2806
paulb92938a2002-12-13 21:20:42 +00002807 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
paul9ab68122003-01-18 01:16:20 +00002808 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
paulb92938a2002-12-13 21:20:42 +00002809 && vty->node > CONFIG_NODE )
2810 {
paul9ab68122003-01-18 01:16:20 +00002811 try_node = node_parent(try_node);
2812 vty->node = try_node;
Christian Frankecd40b322013-09-30 12:27:51 +00002813 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paul9ab68122003-01-18 01:16:20 +00002814 tried = 1;
2815 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
paulb92938a2002-12-13 21:20:42 +00002816 {
paul9ab68122003-01-18 01:16:20 +00002817 /* succesfull command, leave the node as is */
paulb92938a2002-12-13 21:20:42 +00002818 return ret;
2819 }
paulb92938a2002-12-13 21:20:42 +00002820 }
paul9ab68122003-01-18 01:16:20 +00002821 /* no command succeeded, reset the vty to the original node and
2822 return the error for this node */
2823 if ( tried )
2824 vty->node = onode;
2825 return saved_ret;
pauleda031f2003-01-18 00:39:19 +00002826}
2827
Christian Frankecd40b322013-09-30 12:27:51 +00002828/**
2829 * Execute a given command, matching it strictly against the current node.
2830 * This mode is used when reading config files.
2831 *
2832 * @param vline Command line input, vector of char* where each element is
2833 * one input token.
2834 * @param vty The vty context in which the command should be executed.
2835 * @param cmd Pointer where the struct cmd_element* of the matched command
2836 * will be stored, if any. May be set to NULL if this info is
2837 * not needed.
2838 * @return The status of the command that has been executed or an error code
2839 * as to why no command could be executed.
2840 */
paul718e3742002-12-13 20:15:29 +00002841int
paul909a2152005-03-14 17:41:45 +00002842cmd_execute_command_strict (vector vline, struct vty *vty,
paul718e3742002-12-13 20:15:29 +00002843 struct cmd_element **cmd)
2844{
Christian Frankecd40b322013-09-30 12:27:51 +00002845 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
paul718e3742002-12-13 20:15:29 +00002846}
2847
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002848/**
2849 * Parse one line of config, walking up the parse tree attempting to find a match
2850 *
2851 * @param vty The vty context in which the command should be executed.
2852 * @param cmd Pointer where the struct cmd_element* of the match command
2853 * will be stored, if any. May be set to NULL if this info is
2854 * not needed.
2855 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
2856 * or not.
2857 * @return The status of the command that has been executed or an error code
2858 * as to why no command could be executed.
2859 */
2860int
2861command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
2862{
2863 vector vline;
2864 int saved_node;
2865 int ret;
2866
2867 vline = cmd_make_strvec (vty->buf);
2868
2869 /* In case of comment line */
2870 if (vline == NULL)
2871 return CMD_SUCCESS;
2872
2873 /* Execute configuration command : this is strict match */
2874 ret = cmd_execute_command_strict (vline, vty, cmd);
2875
2876 saved_node = vty->node;
2877
2878 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2879 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2880 ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) {
2881 vty->node = node_parent(vty->node);
2882 ret = cmd_execute_command_strict (vline, vty, NULL);
2883 }
2884
2885 // If climbing the tree did not work then ignore the command and
2886 // stay at the same node
2887 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
2888 ret != CMD_SUCCESS && ret != CMD_WARNING &&
2889 ret != CMD_ERR_NOTHING_TODO)
2890 {
2891 vty->node = saved_node;
2892 }
2893
2894 cmd_free_strvec (vline);
2895
2896 return ret;
2897}
2898
paul718e3742002-12-13 20:15:29 +00002899/* Configration make from file. */
2900int
Steve Hillea555002009-07-28 16:36:14 -04002901config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
paul718e3742002-12-13 20:15:29 +00002902{
2903 int ret;
Steve Hillea555002009-07-28 16:36:14 -04002904 *line_num = 0;
paul718e3742002-12-13 20:15:29 +00002905
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002906 while (fgets (vty->buf, vty->max, fp))
paul718e3742002-12-13 20:15:29 +00002907 {
Steve Hillea555002009-07-28 16:36:14 -04002908 ++(*line_num);
paul718e3742002-12-13 20:15:29 +00002909
Donald Sharpd8aa4be2015-09-28 20:10:40 -04002910 ret = command_config_read_one_line (vty, NULL, 0);
paul718e3742002-12-13 20:15:29 +00002911
hassoddd85ed2004-10-13 08:18:07 +00002912 if (ret != CMD_SUCCESS && ret != CMD_WARNING
2913 && ret != CMD_ERR_NOTHING_TODO)
paul718e3742002-12-13 20:15:29 +00002914 return ret;
2915 }
2916 return CMD_SUCCESS;
2917}
2918
2919/* Configration from terminal */
2920DEFUN (config_terminal,
2921 config_terminal_cmd,
2922 "configure terminal",
2923 "Configuration from vty interface\n"
2924 "Configuration terminal\n")
2925{
2926 if (vty_config_lock (vty))
2927 vty->node = CONFIG_NODE;
2928 else
2929 {
2930 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2931 return CMD_WARNING;
2932 }
2933 return CMD_SUCCESS;
2934}
2935
2936/* Enable command */
2937DEFUN (enable,
2938 config_enable_cmd,
2939 "enable",
2940 "Turn on privileged mode command\n")
2941{
2942 /* If enable password is NULL, change to ENABLE_NODE */
2943 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2944 vty->type == VTY_SHELL_SERV)
2945 vty->node = ENABLE_NODE;
2946 else
2947 vty->node = AUTH_ENABLE_NODE;
2948
2949 return CMD_SUCCESS;
2950}
2951
2952/* Disable command */
2953DEFUN (disable,
2954 config_disable_cmd,
2955 "disable",
2956 "Turn off privileged mode command\n")
2957{
2958 if (vty->node == ENABLE_NODE)
2959 vty->node = VIEW_NODE;
2960 return CMD_SUCCESS;
2961}
2962
2963/* Down vty node level. */
2964DEFUN (config_exit,
2965 config_exit_cmd,
2966 "exit",
2967 "Exit current mode and down to previous mode\n")
2968{
2969 switch (vty->node)
2970 {
2971 case VIEW_NODE:
2972 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002973 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002974 if (vty_shell (vty))
2975 exit (0);
2976 else
2977 vty->status = VTY_CLOSE;
2978 break;
2979 case CONFIG_NODE:
2980 vty->node = ENABLE_NODE;
2981 vty_config_unlock (vty);
2982 break;
2983 case INTERFACE_NODE:
2984 case ZEBRA_NODE:
2985 case BGP_NODE:
2986 case RIP_NODE:
2987 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002988 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002989 case OSPF_NODE:
2990 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002991 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002992 case KEYCHAIN_NODE:
2993 case MASC_NODE:
2994 case RMAP_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002995 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002996 case VTY_NODE:
2997 vty->node = CONFIG_NODE;
2998 break;
paul718e3742002-12-13 20:15:29 +00002999 case BGP_IPV4_NODE:
3000 case BGP_IPV4M_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05003001 case BGP_VPNV4_NODE:
3002 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05003003 case BGP_ENCAP_NODE:
3004 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00003005 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00003006 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00003007 vty->node = BGP_NODE;
3008 break;
3009 case KEYCHAIN_KEY_NODE:
3010 vty->node = KEYCHAIN_NODE;
3011 break;
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02003012 case LINK_PARAMS_NODE:
3013 vty->node = INTERFACE_NODE;
3014 break;
paul718e3742002-12-13 20:15:29 +00003015 default:
3016 break;
3017 }
3018 return CMD_SUCCESS;
3019}
3020
3021/* quit is alias of exit. */
3022ALIAS (config_exit,
3023 config_quit_cmd,
3024 "quit",
3025 "Exit current mode and down to previous mode\n")
3026
3027/* End of configuration. */
3028DEFUN (config_end,
3029 config_end_cmd,
3030 "end",
3031 "End current mode and change to enable mode.")
3032{
3033 switch (vty->node)
3034 {
3035 case VIEW_NODE:
3036 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01003037 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00003038 /* Nothing to do. */
3039 break;
3040 case CONFIG_NODE:
3041 case INTERFACE_NODE:
3042 case ZEBRA_NODE:
3043 case RIP_NODE:
3044 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01003045 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00003046 case BGP_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -05003047 case BGP_ENCAP_NODE:
3048 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +00003049 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -05003050 case BGP_VPNV6_NODE:
paul718e3742002-12-13 20:15:29 +00003051 case BGP_IPV4_NODE:
3052 case BGP_IPV4M_NODE:
3053 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00003054 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00003055 case RMAP_NODE:
3056 case OSPF_NODE:
3057 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00003058 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00003059 case KEYCHAIN_NODE:
3060 case KEYCHAIN_KEY_NODE:
3061 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02003062 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00003063 case VTY_NODE:
Olivier Dugeonae51c9d2016-04-19 16:21:46 +02003064 case LINK_PARAMS_NODE:
paul718e3742002-12-13 20:15:29 +00003065 vty_config_unlock (vty);
3066 vty->node = ENABLE_NODE;
3067 break;
3068 default:
3069 break;
3070 }
3071 return CMD_SUCCESS;
3072}
3073
3074/* Show version. */
3075DEFUN (show_version,
3076 show_version_cmd,
3077 "show version",
3078 SHOW_STR
3079 "Displays zebra version\n")
3080{
hasso12f6ea22005-03-07 08:35:39 +00003081 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
3082 VTY_NEWLINE);
David Lamparter0be793e2012-11-27 01:34:56 +00003083 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
David Lamparter7abd8752014-11-22 10:43:29 -08003084 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
3085 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00003086
3087 return CMD_SUCCESS;
3088}
3089
3090/* Help display function for all node. */
3091DEFUN (config_help,
3092 config_help_cmd,
3093 "help",
3094 "Description of the interactive help system\n")
3095{
3096 vty_out (vty,
hasso6590f2c2004-10-19 20:40:08 +00003097 "Quagga VTY provides advanced help feature. When you need help,%s\
paul718e3742002-12-13 20:15:29 +00003098anytime at the command line please press '?'.%s\
3099%s\
3100If nothing matches, the help list will be empty and you must backup%s\
3101 until entering a '?' shows the available options.%s\
3102Two styles of help are provided:%s\
31031. Full help is available when you are ready to enter a%s\
3104command argument (e.g. 'show ?') and describes each possible%s\
3105argument.%s\
31062. Partial help is provided when an abbreviated argument is entered%s\
3107 and you want to know what arguments match the input%s\
3108 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3109 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3110 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
3111 return CMD_SUCCESS;
3112}
3113
3114/* Help display function for all node. */
3115DEFUN (config_list,
3116 config_list_cmd,
3117 "list",
3118 "Print command list\n")
3119{
hasso8c328f12004-10-05 21:01:23 +00003120 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003121 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
3122 struct cmd_element *cmd;
3123
paul55468c82005-03-14 20:19:01 +00003124 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
paul4275b1d2005-03-09 13:42:23 +00003125 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
3126 && !(cmd->attr == CMD_ATTR_DEPRECATED
3127 || cmd->attr == CMD_ATTR_HIDDEN))
paul718e3742002-12-13 20:15:29 +00003128 vty_out (vty, " %s%s", cmd->string,
3129 VTY_NEWLINE);
3130 return CMD_SUCCESS;
3131}
3132
3133/* Write current configuration into file. */
3134DEFUN (config_write_file,
3135 config_write_file_cmd,
3136 "write file",
3137 "Write running configuration to memory, network, or terminal\n"
3138 "Write to configuration file\n")
3139{
hasso8c328f12004-10-05 21:01:23 +00003140 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003141 int fd;
3142 struct cmd_node *node;
3143 char *config_file;
3144 char *config_file_tmp = NULL;
3145 char *config_file_sav = NULL;
paul05865c92005-10-26 05:49:54 +00003146 int ret = CMD_WARNING;
paul718e3742002-12-13 20:15:29 +00003147 struct vty *file_vty;
3148
3149 /* Check and see if we are operating under vtysh configuration */
3150 if (host.config == NULL)
3151 {
3152 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3153 VTY_NEWLINE);
3154 return CMD_WARNING;
3155 }
3156
3157 /* Get filename. */
3158 config_file = host.config;
3159
paul05865c92005-10-26 05:49:54 +00003160 config_file_sav =
3161 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
paul718e3742002-12-13 20:15:29 +00003162 strcpy (config_file_sav, config_file);
3163 strcat (config_file_sav, CONF_BACKUP_EXT);
3164
3165
paul05865c92005-10-26 05:49:54 +00003166 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
paul718e3742002-12-13 20:15:29 +00003167 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3168
3169 /* Open file to configuration write. */
3170 fd = mkstemp (config_file_tmp);
3171 if (fd < 0)
3172 {
3173 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3174 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003175 goto finished;
paul718e3742002-12-13 20:15:29 +00003176 }
3177
3178 /* Make vty for configuration file. */
3179 file_vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02003180 file_vty->wfd = fd;
paul718e3742002-12-13 20:15:29 +00003181 file_vty->type = VTY_FILE;
3182
3183 /* Config file header print. */
3184 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3185 vty_time_print (file_vty, 1);
3186 vty_out (file_vty, "!\n");
3187
paul55468c82005-03-14 20:19:01 +00003188 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003189 if ((node = vector_slot (cmdvec, i)) && node->func)
3190 {
3191 if ((*node->func) (file_vty))
3192 vty_out (file_vty, "!\n");
3193 }
3194 vty_close (file_vty);
3195
3196 if (unlink (config_file_sav) != 0)
3197 if (errno != ENOENT)
3198 {
3199 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3200 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003201 goto finished;
paul718e3742002-12-13 20:15:29 +00003202 }
3203 if (link (config_file, config_file_sav) != 0)
3204 {
3205 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3206 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003207 goto finished;
paul718e3742002-12-13 20:15:29 +00003208 }
3209 sync ();
3210 if (unlink (config_file) != 0)
3211 {
3212 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3213 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003214 goto finished;
paul718e3742002-12-13 20:15:29 +00003215 }
3216 if (link (config_file_tmp, config_file) != 0)
3217 {
3218 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3219 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003220 goto finished;
paul718e3742002-12-13 20:15:29 +00003221 }
paul718e3742002-12-13 20:15:29 +00003222 sync ();
3223
gdtaa593d52003-12-22 20:15:53 +00003224 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3225 {
3226 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
ajs6099b3b2004-11-20 02:06:59 +00003227 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003228 goto finished;
gdtaa593d52003-12-22 20:15:53 +00003229 }
3230
paul718e3742002-12-13 20:15:29 +00003231 vty_out (vty, "Configuration saved to %s%s", config_file,
3232 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003233 ret = CMD_SUCCESS;
3234
3235finished:
3236 unlink (config_file_tmp);
3237 XFREE (MTYPE_TMP, config_file_tmp);
3238 XFREE (MTYPE_TMP, config_file_sav);
3239 return ret;
paul718e3742002-12-13 20:15:29 +00003240}
3241
3242ALIAS (config_write_file,
3243 config_write_cmd,
3244 "write",
3245 "Write running configuration to memory, network, or terminal\n")
3246
3247ALIAS (config_write_file,
3248 config_write_memory_cmd,
3249 "write memory",
3250 "Write running configuration to memory, network, or terminal\n"
3251 "Write configuration to the file (same as write file)\n")
3252
3253ALIAS (config_write_file,
3254 copy_runningconfig_startupconfig_cmd,
3255 "copy running-config startup-config",
3256 "Copy configuration\n"
3257 "Copy running config to... \n"
3258 "Copy running config to startup config (same as write file)\n")
3259
3260/* Write current configuration into the terminal. */
3261DEFUN (config_write_terminal,
3262 config_write_terminal_cmd,
3263 "write terminal",
3264 "Write running configuration to memory, network, or terminal\n"
3265 "Write to terminal\n")
3266{
hasso8c328f12004-10-05 21:01:23 +00003267 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003268 struct cmd_node *node;
3269
3270 if (vty->type == VTY_SHELL_SERV)
3271 {
paul55468c82005-03-14 20:19:01 +00003272 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003273 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3274 {
3275 if ((*node->func) (vty))
3276 vty_out (vty, "!%s", VTY_NEWLINE);
3277 }
3278 }
3279 else
3280 {
3281 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3282 VTY_NEWLINE);
3283 vty_out (vty, "!%s", VTY_NEWLINE);
3284
paul55468c82005-03-14 20:19:01 +00003285 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003286 if ((node = vector_slot (cmdvec, i)) && node->func)
3287 {
3288 if ((*node->func) (vty))
3289 vty_out (vty, "!%s", VTY_NEWLINE);
3290 }
3291 vty_out (vty, "end%s",VTY_NEWLINE);
3292 }
3293 return CMD_SUCCESS;
3294}
3295
3296/* Write current configuration into the terminal. */
3297ALIAS (config_write_terminal,
3298 show_running_config_cmd,
3299 "show running-config",
3300 SHOW_STR
3301 "running configuration\n")
3302
3303/* Write startup configuration into the terminal. */
3304DEFUN (show_startup_config,
3305 show_startup_config_cmd,
3306 "show startup-config",
3307 SHOW_STR
3308 "Contentes of startup configuration\n")
3309{
3310 char buf[BUFSIZ];
3311 FILE *confp;
3312
3313 confp = fopen (host.config, "r");
3314 if (confp == NULL)
3315 {
3316 vty_out (vty, "Can't open configuration file [%s]%s",
3317 host.config, VTY_NEWLINE);
3318 return CMD_WARNING;
3319 }
3320
3321 while (fgets (buf, BUFSIZ, confp))
3322 {
3323 char *cp = buf;
3324
3325 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3326 cp++;
3327 *cp = '\0';
3328
3329 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3330 }
3331
3332 fclose (confp);
3333
3334 return CMD_SUCCESS;
3335}
3336
3337/* Hostname configuration */
3338DEFUN (config_hostname,
3339 hostname_cmd,
3340 "hostname WORD",
3341 "Set system's network name\n"
3342 "This system's network name\n")
3343{
3344 if (!isalpha((int) *argv[0]))
3345 {
3346 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3347 return CMD_WARNING;
3348 }
3349
3350 if (host.name)
paul05865c92005-10-26 05:49:54 +00003351 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003352
paul05865c92005-10-26 05:49:54 +00003353 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003354 return CMD_SUCCESS;
3355}
3356
3357DEFUN (config_no_hostname,
3358 no_hostname_cmd,
3359 "no hostname [HOSTNAME]",
3360 NO_STR
3361 "Reset system's network name\n"
3362 "Host name of this router\n")
3363{
3364 if (host.name)
paul05865c92005-10-26 05:49:54 +00003365 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003366 host.name = NULL;
3367 return CMD_SUCCESS;
3368}
3369
3370/* VTY interface password set. */
3371DEFUN (config_password, password_cmd,
3372 "password (8|) WORD",
3373 "Assign the terminal connection password\n"
3374 "Specifies a HIDDEN password will follow\n"
3375 "dummy string \n"
3376 "The HIDDEN line password string\n")
3377{
3378 /* Argument check. */
3379 if (argc == 0)
3380 {
3381 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3382 return CMD_WARNING;
3383 }
3384
3385 if (argc == 2)
3386 {
3387 if (*argv[0] == '8')
3388 {
3389 if (host.password)
paul05865c92005-10-26 05:49:54 +00003390 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003391 host.password = NULL;
3392 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003393 XFREE (MTYPE_HOST, host.password_encrypt);
3394 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003395 return CMD_SUCCESS;
3396 }
3397 else
3398 {
3399 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3400 return CMD_WARNING;
3401 }
3402 }
3403
3404 if (!isalnum ((int) *argv[0]))
3405 {
3406 vty_out (vty,
3407 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3408 return CMD_WARNING;
3409 }
3410
3411 if (host.password)
paul05865c92005-10-26 05:49:54 +00003412 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003413 host.password = NULL;
3414
3415 if (host.encrypt)
3416 {
3417 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003418 XFREE (MTYPE_HOST, host.password_encrypt);
3419 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003420 }
3421 else
paul05865c92005-10-26 05:49:54 +00003422 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003423
3424 return CMD_SUCCESS;
3425}
3426
3427ALIAS (config_password, password_text_cmd,
3428 "password LINE",
3429 "Assign the terminal connection password\n"
3430 "The UNENCRYPTED (cleartext) line password\n")
3431
3432/* VTY enable password set. */
3433DEFUN (config_enable_password, enable_password_cmd,
3434 "enable password (8|) WORD",
3435 "Modify enable password parameters\n"
3436 "Assign the privileged level password\n"
3437 "Specifies a HIDDEN password will follow\n"
3438 "dummy string \n"
3439 "The HIDDEN 'enable' password string\n")
3440{
3441 /* Argument check. */
3442 if (argc == 0)
3443 {
3444 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3445 return CMD_WARNING;
3446 }
3447
3448 /* Crypt type is specified. */
3449 if (argc == 2)
3450 {
3451 if (*argv[0] == '8')
3452 {
3453 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003454 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003455 host.enable = NULL;
3456
3457 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003458 XFREE (MTYPE_HOST, host.enable_encrypt);
3459 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003460
3461 return CMD_SUCCESS;
3462 }
3463 else
3464 {
3465 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3466 return CMD_WARNING;
3467 }
3468 }
3469
3470 if (!isalnum ((int) *argv[0]))
3471 {
3472 vty_out (vty,
3473 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3474 return CMD_WARNING;
3475 }
3476
3477 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003478 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003479 host.enable = NULL;
3480
3481 /* Plain password input. */
3482 if (host.encrypt)
3483 {
3484 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003485 XFREE (MTYPE_HOST, host.enable_encrypt);
3486 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003487 }
3488 else
paul05865c92005-10-26 05:49:54 +00003489 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003490
3491 return CMD_SUCCESS;
3492}
3493
3494ALIAS (config_enable_password,
3495 enable_password_text_cmd,
3496 "enable password LINE",
3497 "Modify enable password parameters\n"
3498 "Assign the privileged level password\n"
3499 "The UNENCRYPTED (cleartext) 'enable' password\n")
3500
3501/* VTY enable password delete. */
3502DEFUN (no_config_enable_password, no_enable_password_cmd,
3503 "no enable password",
3504 NO_STR
3505 "Modify enable password parameters\n"
3506 "Assign the privileged level password\n")
3507{
3508 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003509 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003510 host.enable = NULL;
3511
3512 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003513 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003514 host.enable_encrypt = NULL;
3515
3516 return CMD_SUCCESS;
3517}
3518
3519DEFUN (service_password_encrypt,
3520 service_password_encrypt_cmd,
3521 "service password-encryption",
3522 "Set up miscellaneous service\n"
3523 "Enable encrypted passwords\n")
3524{
3525 if (host.encrypt)
3526 return CMD_SUCCESS;
3527
3528 host.encrypt = 1;
3529
3530 if (host.password)
3531 {
3532 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003533 XFREE (MTYPE_HOST, host.password_encrypt);
3534 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
paul718e3742002-12-13 20:15:29 +00003535 }
3536 if (host.enable)
3537 {
3538 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003539 XFREE (MTYPE_HOST, host.enable_encrypt);
3540 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
paul718e3742002-12-13 20:15:29 +00003541 }
3542
3543 return CMD_SUCCESS;
3544}
3545
3546DEFUN (no_service_password_encrypt,
3547 no_service_password_encrypt_cmd,
3548 "no service password-encryption",
3549 NO_STR
3550 "Set up miscellaneous service\n"
3551 "Enable encrypted passwords\n")
3552{
3553 if (! host.encrypt)
3554 return CMD_SUCCESS;
3555
3556 host.encrypt = 0;
3557
3558 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003559 XFREE (MTYPE_HOST, host.password_encrypt);
paul718e3742002-12-13 20:15:29 +00003560 host.password_encrypt = NULL;
3561
3562 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003563 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003564 host.enable_encrypt = NULL;
3565
3566 return CMD_SUCCESS;
3567}
3568
3569DEFUN (config_terminal_length, config_terminal_length_cmd,
3570 "terminal length <0-512>",
3571 "Set terminal line parameters\n"
3572 "Set number of lines on a screen\n"
3573 "Number of lines on screen (0 for no pausing)\n")
3574{
3575 int lines;
3576 char *endptr = NULL;
3577
3578 lines = strtol (argv[0], &endptr, 10);
3579 if (lines < 0 || lines > 512 || *endptr != '\0')
3580 {
3581 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3582 return CMD_WARNING;
3583 }
3584 vty->lines = lines;
3585
3586 return CMD_SUCCESS;
3587}
3588
3589DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3590 "terminal no length",
3591 "Set terminal line parameters\n"
3592 NO_STR
3593 "Set number of lines on a screen\n")
3594{
3595 vty->lines = -1;
3596 return CMD_SUCCESS;
3597}
3598
3599DEFUN (service_terminal_length, service_terminal_length_cmd,
3600 "service terminal-length <0-512>",
3601 "Set up miscellaneous service\n"
3602 "System wide terminal length configuration\n"
3603 "Number of lines of VTY (0 means no line control)\n")
3604{
3605 int lines;
3606 char *endptr = NULL;
3607
3608 lines = strtol (argv[0], &endptr, 10);
3609 if (lines < 0 || lines > 512 || *endptr != '\0')
3610 {
3611 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3612 return CMD_WARNING;
3613 }
3614 host.lines = lines;
3615
3616 return CMD_SUCCESS;
3617}
3618
3619DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3620 "no service terminal-length [<0-512>]",
3621 NO_STR
3622 "Set up miscellaneous service\n"
3623 "System wide terminal length configuration\n"
3624 "Number of lines of VTY (0 means no line control)\n")
3625{
3626 host.lines = -1;
3627 return CMD_SUCCESS;
3628}
3629
ajs2885f722004-12-17 23:16:33 +00003630DEFUN_HIDDEN (do_echo,
3631 echo_cmd,
3632 "echo .MESSAGE",
3633 "Echo a message back to the vty\n"
3634 "The message to echo\n")
3635{
3636 char *message;
3637
ajsf6834d42005-01-28 20:28:35 +00003638 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3639 VTY_NEWLINE);
3640 if (message)
3641 XFREE(MTYPE_TMP, message);
ajs2885f722004-12-17 23:16:33 +00003642 return CMD_SUCCESS;
3643}
3644
ajs274a4a42004-12-07 15:39:31 +00003645DEFUN (config_logmsg,
3646 config_logmsg_cmd,
3647 "logmsg "LOG_LEVELS" .MESSAGE",
3648 "Send a message to enabled logging destinations\n"
3649 LOG_LEVEL_DESC
3650 "The message to send\n")
3651{
3652 int level;
3653 char *message;
3654
3655 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3656 return CMD_ERR_NO_MATCH;
3657
Christian Hammersfc951862011-03-23 13:07:55 +03003658 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
ajsf6834d42005-01-28 20:28:35 +00003659 if (message)
3660 XFREE(MTYPE_TMP, message);
ajs274a4a42004-12-07 15:39:31 +00003661 return CMD_SUCCESS;
3662}
3663
3664DEFUN (show_logging,
3665 show_logging_cmd,
3666 "show logging",
3667 SHOW_STR
3668 "Show current logging configuration\n")
3669{
3670 struct zlog *zl = zlog_default;
3671
3672 vty_out (vty, "Syslog logging: ");
3673 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3674 vty_out (vty, "disabled");
3675 else
3676 vty_out (vty, "level %s, facility %s, ident %s",
3677 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3678 facility_name(zl->facility), zl->ident);
3679 vty_out (vty, "%s", VTY_NEWLINE);
3680
3681 vty_out (vty, "Stdout logging: ");
3682 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3683 vty_out (vty, "disabled");
3684 else
3685 vty_out (vty, "level %s",
3686 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3687 vty_out (vty, "%s", VTY_NEWLINE);
3688
3689 vty_out (vty, "Monitor logging: ");
3690 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3691 vty_out (vty, "disabled");
3692 else
3693 vty_out (vty, "level %s",
3694 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3695 vty_out (vty, "%s", VTY_NEWLINE);
3696
3697 vty_out (vty, "File logging: ");
3698 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3699 !zl->fp)
3700 vty_out (vty, "disabled");
3701 else
3702 vty_out (vty, "level %s, filename %s",
3703 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3704 zl->filename);
3705 vty_out (vty, "%s", VTY_NEWLINE);
3706
3707 vty_out (vty, "Protocol name: %s%s",
3708 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3709 vty_out (vty, "Record priority: %s%s",
3710 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003711 vty_out (vty, "Timestamp precision: %d%s",
3712 zl->timestamp_precision, VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +00003713
3714 return CMD_SUCCESS;
3715}
3716
paul718e3742002-12-13 20:15:29 +00003717DEFUN (config_log_stdout,
3718 config_log_stdout_cmd,
3719 "log stdout",
3720 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003721 "Set stdout logging level\n")
paul718e3742002-12-13 20:15:29 +00003722{
ajs274a4a42004-12-07 15:39:31 +00003723 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3724 return CMD_SUCCESS;
3725}
3726
3727DEFUN (config_log_stdout_level,
3728 config_log_stdout_level_cmd,
3729 "log stdout "LOG_LEVELS,
3730 "Logging control\n"
3731 "Set stdout logging level\n"
3732 LOG_LEVEL_DESC)
3733{
3734 int level;
3735
3736 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3737 return CMD_ERR_NO_MATCH;
3738 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
paul718e3742002-12-13 20:15:29 +00003739 return CMD_SUCCESS;
3740}
3741
3742DEFUN (no_config_log_stdout,
3743 no_config_log_stdout_cmd,
ajs274a4a42004-12-07 15:39:31 +00003744 "no log stdout [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003745 NO_STR
3746 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003747 "Cancel logging to stdout\n"
3748 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003749{
ajs274a4a42004-12-07 15:39:31 +00003750 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003751 return CMD_SUCCESS;
3752}
3753
ajs274a4a42004-12-07 15:39:31 +00003754DEFUN (config_log_monitor,
3755 config_log_monitor_cmd,
3756 "log monitor",
paul718e3742002-12-13 20:15:29 +00003757 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003758 "Set terminal line (monitor) logging level\n")
3759{
3760 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3761 return CMD_SUCCESS;
3762}
3763
3764DEFUN (config_log_monitor_level,
3765 config_log_monitor_level_cmd,
3766 "log monitor "LOG_LEVELS,
3767 "Logging control\n"
3768 "Set terminal line (monitor) logging level\n"
3769 LOG_LEVEL_DESC)
3770{
3771 int level;
3772
3773 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3774 return CMD_ERR_NO_MATCH;
3775 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3776 return CMD_SUCCESS;
3777}
3778
3779DEFUN (no_config_log_monitor,
3780 no_config_log_monitor_cmd,
3781 "no log monitor [LEVEL]",
3782 NO_STR
3783 "Logging control\n"
3784 "Disable terminal line (monitor) logging\n"
3785 "Logging level\n")
3786{
3787 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3788 return CMD_SUCCESS;
3789}
3790
3791static int
3792set_log_file(struct vty *vty, const char *fname, int loglevel)
paul718e3742002-12-13 20:15:29 +00003793{
3794 int ret;
paul9035efa2004-10-10 11:56:56 +00003795 char *p = NULL;
3796 const char *fullpath;
3797
paul718e3742002-12-13 20:15:29 +00003798 /* Path detection. */
ajs274a4a42004-12-07 15:39:31 +00003799 if (! IS_DIRECTORY_SEP (*fname))
paul718e3742002-12-13 20:15:29 +00003800 {
paul9035efa2004-10-10 11:56:56 +00003801 char cwd[MAXPATHLEN+1];
3802 cwd[MAXPATHLEN] = '\0';
3803
3804 if (getcwd (cwd, MAXPATHLEN) == NULL)
3805 {
3806 zlog_err ("config_log_file: Unable to alloc mem!");
3807 return CMD_WARNING;
3808 }
3809
ajs274a4a42004-12-07 15:39:31 +00003810 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
paul9035efa2004-10-10 11:56:56 +00003811 == NULL)
3812 {
3813 zlog_err ("config_log_file: Unable to alloc mem!");
3814 return CMD_WARNING;
3815 }
ajs274a4a42004-12-07 15:39:31 +00003816 sprintf (p, "%s/%s", cwd, fname);
paul9035efa2004-10-10 11:56:56 +00003817 fullpath = p;
paul718e3742002-12-13 20:15:29 +00003818 }
3819 else
ajs274a4a42004-12-07 15:39:31 +00003820 fullpath = fname;
paul718e3742002-12-13 20:15:29 +00003821
ajs274a4a42004-12-07 15:39:31 +00003822 ret = zlog_set_file (NULL, fullpath, loglevel);
paul718e3742002-12-13 20:15:29 +00003823
paul9035efa2004-10-10 11:56:56 +00003824 if (p)
3825 XFREE (MTYPE_TMP, p);
3826
paul718e3742002-12-13 20:15:29 +00003827 if (!ret)
3828 {
ajs274a4a42004-12-07 15:39:31 +00003829 vty_out (vty, "can't open logfile %s\n", fname);
paul718e3742002-12-13 20:15:29 +00003830 return CMD_WARNING;
3831 }
3832
3833 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003834 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003835
paul05865c92005-10-26 05:49:54 +00003836 host.logfile = XSTRDUP (MTYPE_HOST, fname);
paul718e3742002-12-13 20:15:29 +00003837
3838 return CMD_SUCCESS;
3839}
3840
ajs274a4a42004-12-07 15:39:31 +00003841DEFUN (config_log_file,
3842 config_log_file_cmd,
3843 "log file FILENAME",
3844 "Logging control\n"
3845 "Logging to file\n"
3846 "Logging filename\n")
3847{
3848 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3849}
3850
3851DEFUN (config_log_file_level,
3852 config_log_file_level_cmd,
3853 "log file FILENAME "LOG_LEVELS,
3854 "Logging control\n"
3855 "Logging to file\n"
3856 "Logging filename\n"
3857 LOG_LEVEL_DESC)
3858{
3859 int level;
3860
3861 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3862 return CMD_ERR_NO_MATCH;
3863 return set_log_file(vty, argv[0], level);
3864}
3865
paul718e3742002-12-13 20:15:29 +00003866DEFUN (no_config_log_file,
3867 no_config_log_file_cmd,
3868 "no log file [FILENAME]",
3869 NO_STR
3870 "Logging control\n"
3871 "Cancel logging to file\n"
3872 "Logging file name\n")
3873{
3874 zlog_reset_file (NULL);
3875
3876 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003877 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003878
3879 host.logfile = NULL;
3880
3881 return CMD_SUCCESS;
3882}
3883
ajs274a4a42004-12-07 15:39:31 +00003884ALIAS (no_config_log_file,
3885 no_config_log_file_level_cmd,
3886 "no log file FILENAME LEVEL",
3887 NO_STR
3888 "Logging control\n"
3889 "Cancel logging to file\n"
3890 "Logging file name\n"
3891 "Logging level\n")
3892
paul718e3742002-12-13 20:15:29 +00003893DEFUN (config_log_syslog,
3894 config_log_syslog_cmd,
3895 "log syslog",
3896 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003897 "Set syslog logging level\n")
paul718e3742002-12-13 20:15:29 +00003898{
ajs274a4a42004-12-07 15:39:31 +00003899 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003900 return CMD_SUCCESS;
3901}
3902
ajs274a4a42004-12-07 15:39:31 +00003903DEFUN (config_log_syslog_level,
3904 config_log_syslog_level_cmd,
3905 "log syslog "LOG_LEVELS,
paul12ab19f2003-07-26 06:14:55 +00003906 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003907 "Set syslog logging level\n"
3908 LOG_LEVEL_DESC)
paul12ab19f2003-07-26 06:14:55 +00003909{
ajs274a4a42004-12-07 15:39:31 +00003910 int level;
paul12ab19f2003-07-26 06:14:55 +00003911
ajs274a4a42004-12-07 15:39:31 +00003912 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3913 return CMD_ERR_NO_MATCH;
3914 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3915 return CMD_SUCCESS;
3916}
paul12ab19f2003-07-26 06:14:55 +00003917
ajs274a4a42004-12-07 15:39:31 +00003918DEFUN_DEPRECATED (config_log_syslog_facility,
3919 config_log_syslog_facility_cmd,
3920 "log syslog facility "LOG_FACILITIES,
3921 "Logging control\n"
3922 "Logging goes to syslog\n"
3923 "(Deprecated) Facility parameter for syslog messages\n"
3924 LOG_FACILITY_DESC)
3925{
3926 int facility;
paul12ab19f2003-07-26 06:14:55 +00003927
ajs274a4a42004-12-07 15:39:31 +00003928 if ((facility = facility_match(argv[0])) < 0)
3929 return CMD_ERR_NO_MATCH;
3930
3931 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003932 zlog_default->facility = facility;
paul718e3742002-12-13 20:15:29 +00003933 return CMD_SUCCESS;
3934}
3935
3936DEFUN (no_config_log_syslog,
3937 no_config_log_syslog_cmd,
ajs274a4a42004-12-07 15:39:31 +00003938 "no log syslog [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003939 NO_STR
3940 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003941 "Cancel logging to syslog\n"
3942 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003943{
ajs274a4a42004-12-07 15:39:31 +00003944 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003945 return CMD_SUCCESS;
3946}
3947
paul12ab19f2003-07-26 06:14:55 +00003948ALIAS (no_config_log_syslog,
3949 no_config_log_syslog_facility_cmd,
ajs274a4a42004-12-07 15:39:31 +00003950 "no log syslog facility "LOG_FACILITIES,
paul12ab19f2003-07-26 06:14:55 +00003951 NO_STR
3952 "Logging control\n"
3953 "Logging goes to syslog\n"
3954 "Facility parameter for syslog messages\n"
ajs274a4a42004-12-07 15:39:31 +00003955 LOG_FACILITY_DESC)
paul12ab19f2003-07-26 06:14:55 +00003956
ajs274a4a42004-12-07 15:39:31 +00003957DEFUN (config_log_facility,
3958 config_log_facility_cmd,
3959 "log facility "LOG_FACILITIES,
paul718e3742002-12-13 20:15:29 +00003960 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003961 "Facility parameter for syslog messages\n"
3962 LOG_FACILITY_DESC)
paul718e3742002-12-13 20:15:29 +00003963{
ajs274a4a42004-12-07 15:39:31 +00003964 int facility;
3965
3966 if ((facility = facility_match(argv[0])) < 0)
3967 return CMD_ERR_NO_MATCH;
3968 zlog_default->facility = facility;
3969 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +00003970}
3971
ajs274a4a42004-12-07 15:39:31 +00003972DEFUN (no_config_log_facility,
3973 no_config_log_facility_cmd,
3974 "no log facility [FACILITY]",
paul718e3742002-12-13 20:15:29 +00003975 NO_STR
3976 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003977 "Reset syslog facility to default (daemon)\n"
3978 "Syslog facility\n")
paul718e3742002-12-13 20:15:29 +00003979{
ajs274a4a42004-12-07 15:39:31 +00003980 zlog_default->facility = LOG_DAEMON;
3981 return CMD_SUCCESS;
3982}
3983
3984DEFUN_DEPRECATED (config_log_trap,
3985 config_log_trap_cmd,
3986 "log trap "LOG_LEVELS,
3987 "Logging control\n"
3988 "(Deprecated) Set logging level and default for all destinations\n"
3989 LOG_LEVEL_DESC)
3990{
3991 int new_level ;
3992 int i;
3993
3994 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3995 return CMD_ERR_NO_MATCH;
3996
3997 zlog_default->default_lvl = new_level;
3998 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3999 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
4000 zlog_default->maxlvl[i] = new_level;
4001 return CMD_SUCCESS;
4002}
4003
4004DEFUN_DEPRECATED (no_config_log_trap,
4005 no_config_log_trap_cmd,
4006 "no log trap [LEVEL]",
4007 NO_STR
4008 "Logging control\n"
4009 "Permit all logging information\n"
4010 "Logging level\n")
4011{
4012 zlog_default->default_lvl = LOG_DEBUG;
paul718e3742002-12-13 20:15:29 +00004013 return CMD_SUCCESS;
4014}
4015
4016DEFUN (config_log_record_priority,
4017 config_log_record_priority_cmd,
4018 "log record-priority",
4019 "Logging control\n"
4020 "Log the priority of the message within the message\n")
4021{
4022 zlog_default->record_priority = 1 ;
4023 return CMD_SUCCESS;
4024}
4025
4026DEFUN (no_config_log_record_priority,
4027 no_config_log_record_priority_cmd,
4028 "no log record-priority",
4029 NO_STR
4030 "Logging control\n"
4031 "Do not log the priority of the message within the message\n")
4032{
4033 zlog_default->record_priority = 0 ;
4034 return CMD_SUCCESS;
4035}
4036
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004037DEFUN (config_log_timestamp_precision,
4038 config_log_timestamp_precision_cmd,
4039 "log timestamp precision <0-6>",
4040 "Logging control\n"
4041 "Timestamp configuration\n"
4042 "Set the timestamp precision\n"
4043 "Number of subsecond digits\n")
4044{
4045 if (argc != 1)
4046 {
4047 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
4048 return CMD_WARNING;
4049 }
4050
4051 VTY_GET_INTEGER_RANGE("Timestamp Precision",
4052 zlog_default->timestamp_precision, argv[0], 0, 6);
4053 return CMD_SUCCESS;
4054}
4055
4056DEFUN (no_config_log_timestamp_precision,
4057 no_config_log_timestamp_precision_cmd,
4058 "no log timestamp precision",
4059 NO_STR
4060 "Logging control\n"
4061 "Timestamp configuration\n"
4062 "Reset the timestamp precision to the default value of 0\n")
4063{
4064 zlog_default->timestamp_precision = 0 ;
4065 return CMD_SUCCESS;
4066}
4067
paul3b0c5d92005-03-08 10:43:43 +00004068DEFUN (banner_motd_file,
4069 banner_motd_file_cmd,
4070 "banner motd file [FILE]",
4071 "Set banner\n"
4072 "Banner for motd\n"
4073 "Banner from a file\n"
4074 "Filename\n")
4075{
paulb45da6f2005-03-08 15:16:57 +00004076 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004077 XFREE (MTYPE_HOST, host.motdfile);
4078 host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
paulb45da6f2005-03-08 15:16:57 +00004079
paul3b0c5d92005-03-08 10:43:43 +00004080 return CMD_SUCCESS;
4081}
paul718e3742002-12-13 20:15:29 +00004082
4083DEFUN (banner_motd_default,
4084 banner_motd_default_cmd,
4085 "banner motd default",
4086 "Set banner string\n"
4087 "Strings for motd\n"
4088 "Default string\n")
4089{
4090 host.motd = default_motd;
4091 return CMD_SUCCESS;
4092}
4093
4094DEFUN (no_banner_motd,
4095 no_banner_motd_cmd,
4096 "no banner motd",
4097 NO_STR
4098 "Set banner string\n"
4099 "Strings for motd\n")
4100{
4101 host.motd = NULL;
paul22085182005-03-08 16:00:12 +00004102 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00004103 XFREE (MTYPE_HOST, host.motdfile);
paul3b0c5d92005-03-08 10:43:43 +00004104 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004105 return CMD_SUCCESS;
4106}
4107
Lou Bergerf9ec4192016-01-12 13:41:48 -05004108DEFUN (show_commandtree,
4109 show_commandtree_cmd,
4110 "show commandtree",
4111 NO_STR
4112 "Show command tree\n")
4113{
4114 /* TBD */
4115 vector cmd_vector;
4116 unsigned int i;
4117
4118 vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE);
4119
4120 /* vector of all commands installed at this node */
4121 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
4122
4123 /* loop over all commands at this node */
4124 for (i = 0; i < vector_active(cmd_vector); ++i)
4125 {
4126 struct cmd_element *cmd_element;
4127
4128 /* A cmd_element (seems to be) is an individual command */
4129 if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
4130 continue;
4131
4132 vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE);
4133 }
4134
4135 vector_free (cmd_vector);
4136 return CMD_SUCCESS;
4137}
4138
paul718e3742002-12-13 20:15:29 +00004139/* Set config filename. Called from vty.c */
4140void
4141host_config_set (char *filename)
4142{
Chris Caputo228da422009-07-18 05:44:03 +00004143 if (host.config)
4144 XFREE (MTYPE_HOST, host.config);
paul05865c92005-10-26 05:49:54 +00004145 host.config = XSTRDUP (MTYPE_HOST, filename);
paul718e3742002-12-13 20:15:29 +00004146}
4147
Christian Franke41de6292016-05-03 19:59:41 +02004148const char *
4149host_config_get (void)
4150{
4151 return host.config;
4152}
4153
Paul Jakma92193662016-06-16 15:53:26 +01004154static void
4155install_default_basic (enum node_type node)
paul718e3742002-12-13 20:15:29 +00004156{
4157 install_element (node, &config_exit_cmd);
4158 install_element (node, &config_quit_cmd);
paul718e3742002-12-13 20:15:29 +00004159 install_element (node, &config_help_cmd);
4160 install_element (node, &config_list_cmd);
Paul Jakma92193662016-06-16 15:53:26 +01004161}
paul718e3742002-12-13 20:15:29 +00004162
Paul Jakma92193662016-06-16 15:53:26 +01004163/* Install common/default commands for a privileged node */
4164void
4165install_default (enum node_type node)
4166{
4167 /* VIEW_NODE is inited below, via install_default_basic, and
4168 install_element's of commands to VIEW_NODE automatically are
4169 also installed to ENABLE_NODE.
4170
4171 For all other nodes, we must ensure install_default_basic is
4172 also called/
4173 */
4174 if (node != VIEW_NODE && node != ENABLE_NODE)
4175 install_default_basic (node);
4176
4177 install_element (node, &config_end_cmd);
paul718e3742002-12-13 20:15:29 +00004178 install_element (node, &config_write_terminal_cmd);
4179 install_element (node, &config_write_file_cmd);
4180 install_element (node, &config_write_memory_cmd);
4181 install_element (node, &config_write_cmd);
4182 install_element (node, &show_running_config_cmd);
4183}
4184
4185/* Initialize command interface. Install basic nodes and commands. */
4186void
4187cmd_init (int terminal)
4188{
Christian Frankecd40b322013-09-30 12:27:51 +00004189 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4190 token_cr.type = TOKEN_TERMINAL;
David Lamparter10bac802015-05-05 11:10:20 +02004191 token_cr.terminal = TERMINAL_LITERAL;
Christian Frankecd40b322013-09-30 12:27:51 +00004192 token_cr.cmd = command_cr;
4193 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
Chris Caputo228da422009-07-18 05:44:03 +00004194
paul718e3742002-12-13 20:15:29 +00004195 /* Allocate initial top vector of commands. */
4196 cmdvec = vector_init (VECTOR_MIN_SIZE);
Paul Jakma92193662016-06-16 15:53:26 +01004197
paul718e3742002-12-13 20:15:29 +00004198 /* Default host value settings. */
4199 host.name = NULL;
4200 host.password = NULL;
4201 host.enable = NULL;
4202 host.logfile = NULL;
4203 host.config = NULL;
4204 host.lines = -1;
4205 host.motd = default_motd;
paul3b0c5d92005-03-08 10:43:43 +00004206 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004207
4208 /* Install top nodes. */
4209 install_node (&view_node, NULL);
4210 install_node (&enable_node, NULL);
4211 install_node (&auth_node, NULL);
4212 install_node (&auth_enable_node, NULL);
Paul Jakma62687ff2008-08-23 14:27:06 +01004213 install_node (&restricted_node, NULL);
paul718e3742002-12-13 20:15:29 +00004214 install_node (&config_node, config_write_host);
4215
4216 /* Each node's basic commands. */
4217 install_element (VIEW_NODE, &show_version_cmd);
4218 if (terminal)
4219 {
Paul Jakma92193662016-06-16 15:53:26 +01004220 install_default_basic (VIEW_NODE);
4221
paul718e3742002-12-13 20:15:29 +00004222 install_element (VIEW_NODE, &config_enable_cmd);
4223 install_element (VIEW_NODE, &config_terminal_length_cmd);
4224 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004225 install_element (VIEW_NODE, &show_logging_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004226 install_element (VIEW_NODE, &show_commandtree_cmd);
ajs2885f722004-12-17 23:16:33 +00004227 install_element (VIEW_NODE, &echo_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004228
Paul Jakma62687ff2008-08-23 14:27:06 +01004229 install_element (RESTRICTED_NODE, &config_enable_cmd);
4230 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4231 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
Lou Bergerf9ec4192016-01-12 13:41:48 -05004232 install_element (RESTRICTED_NODE, &show_commandtree_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004233 install_element (RESTRICTED_NODE, &echo_cmd);
paul718e3742002-12-13 20:15:29 +00004234 }
4235
4236 if (terminal)
4237 {
4238 install_default (ENABLE_NODE);
4239 install_element (ENABLE_NODE, &config_disable_cmd);
4240 install_element (ENABLE_NODE, &config_terminal_cmd);
4241 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4242 }
4243 install_element (ENABLE_NODE, &show_startup_config_cmd);
paul718e3742002-12-13 20:15:29 +00004244
4245 if (terminal)
paul718e3742002-12-13 20:15:29 +00004246 {
ajs274a4a42004-12-07 15:39:31 +00004247 install_element (ENABLE_NODE, &config_logmsg_cmd);
hassoe7168df2004-10-03 20:11:32 +00004248
4249 install_default (CONFIG_NODE);
hassoea8e9d92004-10-07 21:32:14 +00004250 }
4251
4252 install_element (CONFIG_NODE, &hostname_cmd);
4253 install_element (CONFIG_NODE, &no_hostname_cmd);
hassoe7168df2004-10-03 20:11:32 +00004254
hassoea8e9d92004-10-07 21:32:14 +00004255 if (terminal)
4256 {
hassoe7168df2004-10-03 20:11:32 +00004257 install_element (CONFIG_NODE, &password_cmd);
4258 install_element (CONFIG_NODE, &password_text_cmd);
4259 install_element (CONFIG_NODE, &enable_password_cmd);
4260 install_element (CONFIG_NODE, &enable_password_text_cmd);
4261 install_element (CONFIG_NODE, &no_enable_password_cmd);
4262
paul718e3742002-12-13 20:15:29 +00004263 install_element (CONFIG_NODE, &config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004264 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
paul718e3742002-12-13 20:15:29 +00004265 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004266 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4267 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4268 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00004269 install_element (CONFIG_NODE, &config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004270 install_element (CONFIG_NODE, &config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004271 install_element (CONFIG_NODE, &no_config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004272 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004273 install_element (CONFIG_NODE, &config_log_syslog_cmd);
ajs274a4a42004-12-07 15:39:31 +00004274 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
paul12ab19f2003-07-26 06:14:55 +00004275 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004276 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
paul12ab19f2003-07-26 06:14:55 +00004277 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
ajs274a4a42004-12-07 15:39:31 +00004278 install_element (CONFIG_NODE, &config_log_facility_cmd);
4279 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004280 install_element (CONFIG_NODE, &config_log_trap_cmd);
4281 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4282 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4283 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004284 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4285 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
paul718e3742002-12-13 20:15:29 +00004286 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4287 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4288 install_element (CONFIG_NODE, &banner_motd_default_cmd);
paul3b0c5d92005-03-08 10:43:43 +00004289 install_element (CONFIG_NODE, &banner_motd_file_cmd);
paul718e3742002-12-13 20:15:29 +00004290 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4291 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4292 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
paul718e3742002-12-13 20:15:29 +00004293
paul354d1192005-04-25 16:26:42 +00004294 install_element (VIEW_NODE, &show_thread_cpu_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004295 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
Paul Jakmae276eb82010-01-09 16:15:00 +00004296
4297 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
paul354d1192005-04-25 16:26:42 +00004298 install_element (VIEW_NODE, &show_work_queues_cmd);
paul9ab68122003-01-18 01:16:20 +00004299 }
Lou Bergerf9ec4192016-01-12 13:41:48 -05004300 install_element (CONFIG_NODE, &show_commandtree_cmd);
Donald Sharpf31bab42015-06-19 19:26:19 -04004301 srandom(time(NULL));
paul718e3742002-12-13 20:15:29 +00004302}
Chris Caputo228da422009-07-18 05:44:03 +00004303
Christian Frankecd40b322013-09-30 12:27:51 +00004304static void
4305cmd_terminate_token(struct cmd_token *token)
4306{
4307 unsigned int i, j;
4308 vector keyword_vect;
4309
4310 if (token->multiple)
4311 {
4312 for (i = 0; i < vector_active(token->multiple); i++)
4313 cmd_terminate_token(vector_slot(token->multiple, i));
4314 vector_free(token->multiple);
4315 token->multiple = NULL;
4316 }
4317
4318 if (token->keyword)
4319 {
4320 for (i = 0; i < vector_active(token->keyword); i++)
4321 {
4322 keyword_vect = vector_slot(token->keyword, i);
4323 for (j = 0; j < vector_active(keyword_vect); j++)
4324 cmd_terminate_token(vector_slot(keyword_vect, j));
4325 vector_free(keyword_vect);
4326 }
4327 vector_free(token->keyword);
4328 token->keyword = NULL;
4329 }
4330
4331 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4332 XFREE(MTYPE_CMD_TOKENS, token->desc);
4333
4334 XFREE(MTYPE_CMD_TOKENS, token);
4335}
4336
4337static void
4338cmd_terminate_element(struct cmd_element *cmd)
4339{
4340 unsigned int i;
4341
4342 if (cmd->tokens == NULL)
4343 return;
4344
4345 for (i = 0; i < vector_active(cmd->tokens); i++)
4346 cmd_terminate_token(vector_slot(cmd->tokens, i));
4347
4348 vector_free(cmd->tokens);
4349 cmd->tokens = NULL;
4350}
4351
Chris Caputo228da422009-07-18 05:44:03 +00004352void
4353cmd_terminate ()
4354{
Christian Frankecd40b322013-09-30 12:27:51 +00004355 unsigned int i, j;
Chris Caputo228da422009-07-18 05:44:03 +00004356 struct cmd_node *cmd_node;
4357 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00004358 vector cmd_node_v;
Chris Caputo228da422009-07-18 05:44:03 +00004359
4360 if (cmdvec)
4361 {
4362 for (i = 0; i < vector_active (cmdvec); i++)
4363 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4364 {
4365 cmd_node_v = cmd_node->cmd_vector;
4366
4367 for (j = 0; j < vector_active (cmd_node_v); j++)
Christian Frankecd40b322013-09-30 12:27:51 +00004368 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4369 cmd_terminate_element(cmd_element);
Chris Caputo228da422009-07-18 05:44:03 +00004370
4371 vector_free (cmd_node_v);
Paul Jakma92193662016-06-16 15:53:26 +01004372 hash_clean (cmd_node->cmd_hash, NULL);
4373 hash_free (cmd_node->cmd_hash);
4374 cmd_node->cmd_hash = NULL;
Chris Caputo228da422009-07-18 05:44:03 +00004375 }
4376
4377 vector_free (cmdvec);
4378 cmdvec = NULL;
4379 }
4380
4381 if (command_cr)
Christian Frankecd40b322013-09-30 12:27:51 +00004382 XFREE(MTYPE_CMD_TOKENS, command_cr);
4383 if (token_cr.desc)
4384 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
Chris Caputo228da422009-07-18 05:44:03 +00004385 if (host.name)
4386 XFREE (MTYPE_HOST, host.name);
4387 if (host.password)
4388 XFREE (MTYPE_HOST, host.password);
4389 if (host.password_encrypt)
4390 XFREE (MTYPE_HOST, host.password_encrypt);
4391 if (host.enable)
4392 XFREE (MTYPE_HOST, host.enable);
4393 if (host.enable_encrypt)
4394 XFREE (MTYPE_HOST, host.enable_encrypt);
4395 if (host.logfile)
4396 XFREE (MTYPE_HOST, host.logfile);
4397 if (host.motdfile)
4398 XFREE (MTYPE_HOST, host.motdfile);
4399 if (host.config)
4400 XFREE (MTYPE_HOST, host.config);
4401}